├── esphome-configs ├── .gitignore └── tester-mahtanar.yaml └── README.md /esphome-configs/.gitignore: -------------------------------------------------------------------------------- 1 | # Gitignore settings for ESPHome 2 | # This is an example and may include too much for your use-case. 3 | # You can modify this file to suit your needs. 4 | /.esphome/ 5 | /secrets.yaml 6 | -------------------------------------------------------------------------------- /esphome-configs/tester-mahtanar.yaml: -------------------------------------------------------------------------------- 1 | # ESPHome config for a testing board used to ensure MahtanarM boards are functioning before shipping. 2 | 3 | esphome: 4 | name: tester-mahtanarm 5 | friendly_name: Tester MahtanarM 6 | 7 | esp32: 8 | board: esp32-c3-devkitm-1 9 | framework: 10 | type: arduino 11 | 12 | # Disable logging because we're 13 | logger: 14 | baud_rate: 0 15 | 16 | # Enable Home Assistant API 17 | api: 18 | encryption: 19 | key: "TCpgEjrIp4drdIibIOIJ86ccSZiWLQOpcKTQ3Q8D5uE=" 20 | 21 | ota: 22 | password: !secret ota_password 23 | 24 | wifi: 25 | ssid: !secret wifi_ssid 26 | password: !secret wifi_password 27 | 28 | captive_portal: 29 | 30 | # Status light 31 | light: 32 | - platform: neopixelbus 33 | id: status_led 34 | variant: ws2812 35 | num_leds: 1 36 | pin: 8 37 | internal: True 38 | default_transition_length: 0s 39 | 40 | interval: 41 | # Update status light based which packets were received successfully 42 | - interval: 200ms 43 | then: 44 | - lambda: |- 45 | switch((int)id(comm_status).state) { 46 | case 0: 47 | id(status_led).turn_off().perform(); 48 | break; 49 | case 1: 50 | id(status_led).turn_on().set_rgb(1,0.6,0).set_brightness(0.2).perform(); 51 | break; 52 | case 2: 53 | id(status_led).turn_on().set_rgb(1,1,0).set_brightness(0.2).perform(); 54 | break; 55 | case 3: 56 | id(status_led).turn_on().set_rgb(0,1,0).set_brightness(0.2).perform(); 57 | break; 58 | } 59 | # Send a custom test-ping packet every two seconds if we've received a 'hello' from a board. 60 | # This packet doesn't match any known Mitsubishi packets and should be forwarded back to the test 61 | # board. 62 | - interval: 2s 63 | then: 64 | - lambda: |- 65 | if (id(comm_status).state > 0){ 66 | std::vector testping{0xFC,0xAA,0x01,0x30,0x02,0x54,0x42,0x8D}; 67 | id(tstat_uart).write_array(&testping[0],8); 68 | } 69 | 70 | 71 | sensor: 72 | - platform: template 73 | id: comm_status 74 | filters: 75 | - timeout: 76 | timeout: 5s 77 | value: 0 78 | 79 | 80 | uart: 81 | - id: hp_uart 82 | baud_rate: 2400 83 | parity: EVEN 84 | rx_pin: 85 | number: GPIO7 86 | tx_pin: 87 | number: GPIO6 88 | debug: 89 | direction: BOTH 90 | dummy_receiver: true 91 | sequence: 92 | - lambda: |- 93 | std::vector conreq{0xFC,0x5A,0x01,0x30,0x02,0xCA,0x01,0xA8}; 94 | std::vector testping{0xFC,0xAA,0x01,0x30,0x02,0x54,0x42,0x8D}; 95 | std::vector testpong{0xFC,0xAB,0x01,0x30,0x02,0x54,0x42,0x8C}; 96 | UARTDebug::log_hex(direction, bytes, ' '); 97 | if (direction == uart::UART_DIRECTION_RX && conreq == bytes) { 98 | // We received a 'hello' from the board under test 99 | if (!id(comm_status).has_state() || id(comm_status).state <= 1) {id(comm_status).publish_state(1);} 100 | } else if (direction == uart::UART_DIRECTION_RX && testping == bytes) { 101 | // We received our custom ping packet! 102 | if (id(comm_status).state <= 2) {id(comm_status).publish_state(2);} 103 | id(hp_uart).write_array(&testpong[0],8); // Send a response 104 | } 105 | - id: tstat_uart 106 | baud_rate: 2400 107 | parity: EVEN 108 | rx_pin: 109 | number: GPIO5 110 | tx_pin: 111 | number: GPIO20 112 | debug: 113 | direction: BOTH 114 | dummy_receiver: true 115 | sequence: 116 | - lambda: |- 117 | std::vector testpong{0xFC,0xAB,0x01,0x30,0x02,0x54,0x42,0x8C}; 118 | UARTDebug::log_hex(direction, bytes, ' '); 119 | if (direction == uart::UART_DIRECTION_RX && testpong == bytes) { 120 | // We received the response from our ping packet! 121 | if (id(comm_status).state <= 3) {id(comm_status).publish_state(3);} 122 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mahtanar 2 | ### A hardware controller for Mitsubishi heat pumps using CN105 connector. 3 | 4 | 5 | 6 | Available for purchase at the [Tinwer Etsy Shop](https://www.etsy.com/listing/1762258422/mahtanar-heat-pump-controller) 7 | 8 | # ⚠️ 9 | 10 | ❗This project is **not** affiliated in any way with Mitsubishi. The hardware and software provided here are as-is, with all faults; and include no warranties express or implied. Following the procedures herein may void the warranty of your equipment and pose a risk of injury; proceed at your own risk!❗ 11 | 12 | ... that said we're doing our best to make sure things work and to help you through the process. Please feel free to create an issue here if you run into any trouble. 13 | 14 | ---- 15 | 16 | ## Getting Started 17 | 18 | While *in theory* it's possible to connect the Mahtanar board to your heat pump equipment and *then* provision it, this can make troubleshooting more difficult so it's recommended that at least for your first device you provision it while it's connected/powered over USB and connect it to your heat pump after it's all set up. 19 | 20 | ### Prerequisites: 21 | 22 | - Mahtanar board (WiFi or Ethernet model) 23 | - CN105 connector cable 24 | - (Semi-optional, see [note](#setup-note)) ESPHome installed and running on your network 25 | - Home Assistant configured with ESPHome integration 26 | 27 | #### Setup Note 28 | ℹ️ The Mahtanar board comes pre-flashed with a default firmware, and can by used in Home Assistant without any ESPHome interaction. If you're fine with the defaults, you don't need to "Take Control" using ESPHome (and can always do so later if needed). 29 | 30 | ### Setup 31 | 32 | 1. Connect the Mahtanar to a USB power source (ideally your computer for additional serial troubleshooting if needed). 33 | 2. If you have the WiFi version of the board, use your computer or mobile device to connect to the `mITP Setup` WiFi SSID with `mahtanar` as the password. You should be prompted to connect the device to your WiFi network; do so. If your device doesn't prompt you, open a browser to http://192.168.4.1 to connect to the ESPHome Wifi setup page. 34 | If you have the ethernet version of the board, just connect it to your network. 35 | 3. (Semi-optional, see [note](#setup-note)) After a moment, the device should show up on your ESPHome dashboard as `mitp-heat-pump-MACSUFFIX` with a "Take Control" option. Press "Take Control", and provide a friendly name. When prompted, press "Install". 36 | 4. Home Assistant should have discovered the device and will show it with the friendly name you provided. 37 | 5. Now that the device is provisioned, you can connect it to your heat pump using the CN105 connector cable and the port labeled `Heatpump` (or `HP1`) on the Mahtanar device. Because power is provided by the equipment, a USB connection is no longer required unless troubleshooting. This process can vary a bit depending on equipment, and more detailed information is provided below. 38 | 6. More information on configuring the ESPHome `mitsubishi_itp` component is [available here](https://muart-group.github.io/). The ESPHome configuration should be available for modification on your ESPHome dashboard. 39 | 40 | 41 | 42 | ### Troubleshooting 43 | 44 | Frequent troubleshooting tips will be added here, but a common step will likely be to use the configuration files listed below to reset the device back to its factory defaults (or as a starting point for a manual configuration) from the ESPHome dashboard: 45 | 46 | 1. Select the branch in this repository that matches the revision number printed on your PCB. 47 | 2. Copy the contents of either the `wifi` or `ethernet` yaml files into the config on your ESPHome dashboard, and press "Install". 48 | 49 | ## Connecting to Equipment 50 | 51 | More details to be added here, though some detailed tutorials can be found online. The gist of the process is that **WITH THE POWER DISCONNECTED FROM YOUR EQUIPMENT** (breaker off, or lockout switch engaged, depending), you will take the cover off of the wall unit, or panel off the air handler and locate the CN105 connector on the controller board. The connector is usually red. There should be some sort of knockout on the cover/panel near the controller board to route the cable outside of the unit. The cover/panel can then be reinstalled and **THEN** the power turned back on. You can then connect the other end of the cable to the Mahtanar. 52 | 53 | ### MHK2 54 | 55 | The `mitsubishi_itp` component also supports MHK2 thermostats which can be connected to the other port (labeled `Thermostat` or `TS1`) on the Mahtanar. Once again, configuration details are available from [this project](https://muart-group.github.io/). 56 | 57 | ## FAQ 58 | 59 |
60 | What if I don't have an MHK2? 61 | The board will still work fine without an MHK2; just connect only the `HP1` port to your equipment and leave the `TS1` port empty. 62 | 63 | If you don't plan on using a thermostat, the thermostat features can be disabled in the configuration streamline your experience. 64 | 65 | Comment or remove `uart_thermostat` to disable the feature: 66 | ``` yaml 67 | climate: 68 | - platform: mitsubishi_itp 69 | name: "Climate" 70 | uart_heatpump: hp_uart 71 | # uart_thermostat: tstat_uart # This and the uart component below can be removed if no thermostat connected 72 | ``` 73 | 74 | Additionally, you may want to remove the `uart` component to free up pins or allow for USB logging: 75 | ``` yaml 76 | uart: 77 | - id: hp_uart 78 | baud_rate: 2400 # For some equipment this may need to be 9600 79 | parity: EVEN 80 | rx_pin: 81 | number: GPIO1 82 | tx_pin: 83 | number: GPIO0 84 | # - id: tstat_uart 85 | # baud_rate: 2400 # For some equipment this may need to be 9600 86 | # parity: EVEN 87 | # rx_pin: 88 | # number: GPIO20 89 | # tx_pin: 90 | # number: GPIO21 91 | ``` 92 | 93 | On older configurations where logging had been disabled, you can reenable it if desired: 94 | ```yaml 95 | logger: 96 | # baud_rate: 0 97 | baud_rate: 115200 98 | ``` 99 |
100 | 101 |
102 | How do I re-flash the board? 103 | In this repository, choose the branch that matched the revision number printed on your PCB (e.g. `v1.3`). Copy either the Ethernet or WiFi configuration from this repository into your ESPHome dashboard and install. If you have previously configured the WiFi, in most circumstances you will not need to reconfigure it. 104 |
105 | 106 | ## Additional Config 107 |
108 | Status LED (v1.2 ONLY) 109 | The built-in LED can be used to show current system status by adding the configuration snippets below. The light can be turned on or off in Home Assistant and will remember its state, but brightness will need to be adjusted in the configuration. 110 | 111 | Add the `light` and a `script` to update it: 112 | ```yaml 113 | light: 114 | - platform: neopixelbus 115 | variant: ws2812x 116 | pin: GPIO08 117 | num_leds: 1 118 | id: status_led 119 | name: "Status LED" 120 | effects: 121 | - pulse: 122 | name: "Standby" 123 | min_brightness: 40% 124 | max_brightness: 60% 125 | transition_length: 1s 126 | update_interval: 1s 127 | restore_mode: RESTORE_DEFAULT_ON 128 | 129 | script: 130 | - id: update_status_led 131 | parameters: 132 | mode: int 133 | action: int 134 | then: 135 | - lambda: |- 136 | auto call = id(status_led).make_call(); 137 | switch (mode) { 138 | case CLIMATE_MODE_OFF: 139 | call.set_rgb(1,1,1); 140 | break; 141 | case CLIMATE_MODE_HEAT_COOL: 142 | call.set_rgb(.8,0,1); 143 | break; 144 | case CLIMATE_MODE_COOL: 145 | call.set_rgb(0,0.2,1); 146 | break; 147 | case CLIMATE_MODE_HEAT: 148 | call.set_rgb(1,0.2,0); 149 | break; 150 | case CLIMATE_MODE_FAN_ONLY: 151 | call.set_rgb(0,1,1); 152 | break; 153 | case CLIMATE_MODE_DRY: 154 | call.set_rgb(1,1,0); 155 | break; 156 | default: 157 | call.set_rgb(0,1,0); 158 | } 159 | 160 | if (action == CLIMATE_ACTION_IDLE) { 161 | call.set_effect("Standby"); 162 | } else { 163 | call.set_effect("none").set_brightness(0.6); 164 | } 165 | 166 | call.perform(); 167 | ``` 168 | 169 | Add an `on_boot` to show that the code is running, but heat pump is not-yet connected: 170 | ```yaml 171 | esphome: 172 | ... 173 | on_boot: 174 | - then: 175 | - lambda: |- 176 | id(status_led).make_call().set_rgb(1,1,1).set_effect("Standby").perform(); 177 | ``` 178 | 179 | Add an `on_state` to the climate component to call the script above: 180 | ```yaml 181 | climate: 182 | - platform: mitsubishi_itp 183 | ... 184 | on_state: 185 | then: 186 | - lambda: id(update_status_led)->execute(x.mode, x.action); 187 | ``` 188 |
189 | 190 |
191 | Status LED (v1.4 ONLY) 192 | On revision v1.4, there are two LEDs: one to indicate the board has 3.3v power, and the other programmable. By default the programmable LED will use the ESPHome [Status LED](https://esphome.io/components/status_led/) platform to provide basic status: 193 | 194 | ```yaml 195 | light: 196 | - platform: status_led 197 | name: "Status LED" 198 | pin: 199 | number: GPIO08 200 | inverted: true 201 | ignore_strapping_warning: true 202 | internal: true 203 | ``` 204 | 205 | The programmable LED can be disabled by commenting out the configuration above, or used for other purposes (e.g. by removing `internal: true` the LED will appear as a light in Home Assistant). 206 |
207 | 208 | ## Hardware Revisions 209 | 210 | ### v1.1 211 | 212 | 213 | 214 | - First "mass"-produced board. 215 | - Base PCB manufactured, but all components soldered by hand. 216 | 217 | ### v1.2 218 | 219 | 220 | - Included level-shifter and connectors in PCB design and had them assembled by manufacturer (other components still hand-soldered). 221 | - Removed LED footprint. 222 | 223 | ### v1.3 224 | 225 | 226 | - Switched from ESP32-C3 devboard to custom circuit using ESP32-C3-WROOM-02! 227 | - Switched from white to blue because of fine tolerances for solder mask near USB-C port. 228 | - Issue: Capacitor for debouncing `BOOT` button prevented normal boot on first power up. Pressing `RESET` would work; solution was to remove capacitor (as shown in photo). 229 | - Issue: Learned that WROOM module includes 499Ω resistor in series with UART TX pin preventing this pin from reaching 0v (only got to 0.6v). This was fine for test board but caused issues on actual Mitsubishi equipment. Solution was to remove the pullup resistors on that line (as shown in photo) to achieve 0.25v which was acceptable for Mitsubishi equipment. 230 | 231 | ### v1.4 232 | 233 | 234 | - Switched to hex buffer instead of bi-direcitonal levelshifter to work around issue with 499Ω resistor. 235 | - Added power and status LEDs. 236 | - Issue: Forgot to update version number on PCB silkscreen. 237 | - Issue: Learned that while wall-units have 5v pullup resistors on their CN105 ports, that some (most?) air handlers do not. This board design only included pullups for the TX lines, so it has issues receiving messages from air handlers. Solution is to manually install a 5v pullup resistor on the Heatpump TX line. 238 | 239 | ### v1.4b 240 | - Added pullups to the UART RX lines for the heat pump and thermostat. 241 | - Fixed silkscreen to actually say `v1.4b` 242 | 243 | 244 | --------------------------------------------------------------------------------