[Tutorial] Connect your dumb Midea AC Unit to Openhab with MQTT

Hello everyone,

I have 2 Riello air conditioner unit, looking online I found that this Ac unit are in fact a rebranded Midea AC.
This AC Unit can be connected to a proprietary cloud via a WIFI USB key (ref: sk-103 or k02 wifi usb) sell online for 50€ each.
And I didn’t want to spend 100€ just to attach and esp8266 at my AC unit and link to my openhab instance.
So I looked around on github, and I found this repo : GitHub - Links2004/arduinoMideaAC: hack job for decoding and sending Midea AC Serial commands to use my AC with an ESP32 and MQTT.

So here how to do it.

Software Part :

Download the code from GitHub - Links2004/arduinoMideaAC: hack job for decoding and sending Midea AC Serial commands
Put midea.cpp and midea.h under src folder to \Arduino\libraries\midea
open midea.h and modify this line (if you don’t do that, the project will not build on ESP32) :

#ifdef __AVR__
    typedef void (*acSerialEvent)(ac_status_t * status);
  #else
    typedef std::function<void(ac_status_t * status)> acSerialEvent;
  #endif

with
typedef std::function<void(ac_status_t * status)> acSerialEvent;

Open arduinoIDE and create a new project.
copy paste this content into your IDE : #2 add MQTT example for Esp32 by Tukks · Pull Request #3 · Links2004/arduinoMideaAC · GitHub

Change SSID, Password, Mqtt server IP, and the topic if you want.
If you have 2 AC Unitm you need to change the Mqtt Client Id, and put something different for each one at this line :

if (client.connect("acsalon", "openhabian", NULL)) {

  • Change acsalon for an Id you want,
  • openhabian is my username to connect to my mosquito broker, change if different.
  • NULL is for the password of mosquitto, put it if you have one

Now connect your ESP32, and upload the project.

Hardware Part :
Here’s what you need :

First you need to unsolder the USB female from the AC Unit. After, that solder 4 pin to the 4 new hole in the AC Unit ex-usb.
Link GPIO1 (Tx) and GPIO3 (RX) from ESP32 to BSCL and BSDA of the logic level converter.
Link the 3v3 pin of the esp32 to BVCC, and GND from esp32 to BGND
Link the AC Unit GND to AGND
Link the AC Unit 5V to AVCC
Link the AC Unit RX to ASCL
Link the AC Unit TX to ASCL

Now everything is connected.
Subscribe to “esp32/acbureau/state” and you will see some data coming.
if you want to control it, send to “esp32/acbureau/command” and send :
{“on”:true,“turbo”:false,“eco”:false,“soll”:23,“lamelle”:false,“mode”:4,“fan”:0}
Possible value :

  • on : true/false
  • turbo : true/false
  • eco : true/false
  • soll (temperature): number
  • lamelle: true/false
  • mode : number, Heat=“4”, Cool=“2”, Auto=“1”, Fan=“5”, Dry=“3”
  • fan : number, Min= 1, Medium = 2, High = 3 or Auto = 0

And your AC will power On.

If needed I can provide my items file too.

Hope it help someone, I searched for 3 month on internet and I found nothing until now.
A big thanks to Links2004 on github.

Cheers :slight_smile:

5 Likes

Hi Giuseppe,

thank you very much for your tutorial!

I have a Midea/ Dimstal split air conditioner and I built the serial interface as described to control and monitor the system with OH3.

Receiving the data works fine so far.
Topic:openhab/in/DGKlima/state
Data: [{“is”:22. 5, “outside”:17.5, “conf”:{“on”:true, “turbo”:false, “eco”:false, “soll”:23, “lamelle”:true, “mode”:2, “fan”:0}}]

Unfortunately, my AC system always turns off immediately when I send the following data via MQTT:
Topic: openhab/out/DGKlima/command
Data: {“on”:true,“soll”:18,“lamelle”:true,“mode”:2,“fan”:0}

Do you have any idea what this could be?

One more request:
Would you provide your Things/ Items files, that would be a great help.

Thanks
Dirk

Hello,

Did you try to send the whole json : {“on”:true,“turbo”:false,“eco”:false,“soll”:23,“lamelle”:false,“mode”:4,“fan”:0}
I see in your json, you miss some properties.
I’m not currently home, I will post here my items and thing with my rules. :slight_smile:

Edit :
I made an update to the ino script, the last one has a problem, sometime the wifi disconnect, here’s the updated one :

/**
   I used this code with an ESP32 devkit 1
   and with a logic level converter :
   https://www.aliexpress.com/item/32482721005.html?spm=a2g0s.9042311.0.0.1dc64c4dDrGSej
   or https://protosupplies.com/product/i2c-logic-level-converter-with-regulator-module/
   I linked GPIO1 (Tx) and GPIO3 (RX) to BSCL and BSDA of the level shifter
   Linked the 3v3 pin of the esp32 to BVCC, and GND from esp32 to BGND

   Unsolderer the USB port from AC unit
   Linked the AC Unit GND to AGND
   Linked the AC Unit 5V to AVCC
   Linked the AC Unit RX to ASCL
   Linked the AC Unit TX to ASCL

   NOTE : In the mideaAC.h file, in the source folder, I removed this part :
   #ifdef __AVR__
      typedef void (*acSerialEvent)(ac_status_t * status);
    #else
      typedef std::function<void(ac_status_t * status)> acSerialEvent;
    #endif
       and replaced with :
    typedef std::function<void(ac_status_t * status)> acSerialEvent;


    Mqtt output command example :
    {"on":false,"turbo":false,"eco":false,"soll":23,"lamelle":false,"mode":4,"fan":off}
    
*/
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <mideaAC.h>
#include <ArduinoJson.h>

// Replace the next variables with your SSID/Password combination
const char* ssid = "WIFI NAME";
const char* password = "PASSWORD";

// Add your MQTT Broker IP address, example:
const char* mqtt_server = "192.168.1.25";

// Replace the next variables with your SSID/Password combination
const char* mqttState = "esp32/acsalon/state";
const char* mqttCommand = "esp32/acsalon/command";

acSerial acSeria;

WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;
#define USE_SERIAL Serial2


void ACevent(ac_status_t * status) {

  if (client.connected()) {

    DynamicJsonDocument doc(1024);
    JsonArray array = doc.to<JsonArray>();


    JsonObject statusObj = array.createNestedObject();
    statusObj["ist"]     = status->ist;
    statusObj["aussen"]  = status->aussen;

    JsonObject confObj = statusObj.createNestedObject("conf");
    confObj["on"]      = status->conf.on;
    confObj["turbo"]   = status->conf.turbo;
    confObj["eco"]     = status->conf.eco;
    confObj["soll"]    = status->conf.soll;

    confObj["lamelle"] = status->conf.lamelle != acLamelleOff;
    confObj["mode"]    = (uint8_t)status->conf.mode;

    switch (status->conf.fan) {
      case acFAN1:
        confObj["fan"] = 1;
        break;
      case acFAN2:
        confObj["fan"] = 2;
        break;
      case acFAN3:
        confObj["fan"] = 3;
        break;
      case acFANA:
        confObj["fan"] = 0;
        break;
      default:
        confObj["fan"] = (uint8_t)status->conf.fan;
        break;
    }

    String output;
    serializeJson(doc, output);
    USE_SERIAL.print("[ACevent] JSON: ");
    USE_SERIAL.println(output);
    char charBuf[output.length() + 1];
    output.toCharArray(charBuf, output.length() + 1);
    // send state through mqttt
    client.publish(mqttState, charBuf);
  } else {
    USE_SERIAL.printf("[ACevent] ----------------------------\n");
    acSeria.print_status(status);
  }

}

void setup() {
  USE_SERIAL.begin(115200);

  setup_wifi();
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  Serial.begin(9600);
  acSeria.begin((Stream *)&Serial, "Serial");
  acSeria.send_getSN();

  acSeria.onStatusEvent(ACevent);
}

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  USE_SERIAL.println();
  USE_SERIAL.print("Connecting to ");
  USE_SERIAL.println(ssid);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    USE_SERIAL.print(".");
  }

  USE_SERIAL.println("");
  USE_SERIAL.println("WiFi connected");
  USE_SERIAL.println("IP address: ");
  USE_SERIAL.println(WiFi.localIP());
}

void callback(char* topic, byte* message, unsigned int length) {
  USE_SERIAL.print("Message arrived on topic: ");
  USE_SERIAL.print(topic);
  USE_SERIAL.print(". Message: ");
  StaticJsonDocument<256> doc;
  deserializeJson(doc, message, length);
  if (strcmp(topic, mqttCommand) == 0) {
  
    bool on        = doc["on"];
    bool turbo     = doc["turbo"];
    bool eco       = doc["eco"];
    ac_mode_t mode = (ac_mode_t)((uint8_t)doc["mode"]);
    bool lamelle   = doc["lamelle"];

    uint8_t fan    = doc["fan"];
    uint8_t soll   = doc["soll"];

    acSeria.send_conf_h(on, soll, fan, mode, lamelle, turbo, eco);
  }
  //acSeria.send_conf_h(true, 21, 1, acModeHeat, false, false, false);
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    if(WiFi.status() != WL_CONNECTED){
      WiFi.reconnect();

    }
    USE_SERIAL.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("acsalon", "openhabian", NULL)) {
      USE_SERIAL.println("connected");
      // Subscribe
      client.subscribe(mqttCommand);
    } else {
      USE_SERIAL.print("failed, rc=");
      USE_SERIAL.print(client.state());
      USE_SERIAL.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
void loop() {
  
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  acSeria.loop();

  long now = millis();
  if (now - lastMsg > 5000) {
    lastMsg = now;
    acSeria.send_status(client.connected(), true);
    acSeria.request_status();
  }
}

Hi,
thanks for the update of the arduino sketch, i will compile and flash it later.

I tried to send the complete JSON, this caused the air conditioner to turn off immediately.
The short version without ECO Mode and TURBO Mode now works for me. Great!

I am currently testing everything with “MQTT Spy” and not yet with openHAB.

For the setup in openHAB 3.1 I would like to use your Things/Items/Rules.

Thanks a lot for your support.

Cheers

Hello,

My rule :

triggers:
  - id: "4"
    configuration:
      groupName: SalonThermostat
    type: core.GroupCommandTrigger
conditions: []
actions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript
      script: >-
        var logger =
        Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' +
        ctx.ruleUID);      

        var onOff = ir.getItem("ACSalonOnOff").state + '' === 'OFF' ? false : true;

        var turbo = ir.getItem("ACSalonTurbo").state+ '' === 'OFF' ? false : true;

        var eco = ir.getItem("ACSalonEco").state+ '' === 'OFF' ? false : true;

        var lamelle = ir.getItem("ACSalonLamelle").state+ '' === 'OFF' ? false : true;

        var mode = ir.getItem("ACSalonMode").state + '' ; var fan = ir.getItem("ACSalonFan").state + '' ; var temp = ir.getItem("ACSalonTemp").state; var logger = Java.type('org.slf4j.LoggerFactory').getLogger('org.openhab.rule.' + ctx.ruleUID); 


        logger.info(event.itemName);


        switch (event.itemName) {
            case 'ACSalonMode':
                mode = event.itemCommand;
                break;
            case 'ACSalonFan':
                fan = event.itemCommand;
                break;
            case 'ACSalonLamelle':
                lamelle = event.itemCommand + '' === 'OFF' ? false : true;
                break;
            case 'ACSalonEco':
                eco = event.itemCommand + ''=== 'OFF' ? false : true;
                break;
            case 'ACSalonTurbo':
                turbo = event.itemCommand + ''=== 'OFF' ? false : true;
                break;
            case 'ACSalonOnOff':
                onOff = event.itemCommand + ''=== 'OFF' ? false : true;
                break;
            case 'ACSalonTemp':
                temp = event.itemCommand;
                break;                
            default:






                
        }

        actions.get("mqtt", "mqtt:broker:9c3815a8")
            .publishMQTT("esp32/acsalon/command", "{\"on\":" + onOff + ",\"turbo\":" + turbo + ",\"eco\":" + eco + ",\"soll\":" + temp + ",\"lamelle\":" + lamelle + ",\"mode\":" + mode + ",\"fan\":" + fan + "}");
    type: script.ScriptAction

My things :
NB : The command topic is a fake command topic, the command part is controlled by the rule

UID: mqtt:topic:9c3815a8:AC_SALON
label: AC Salon
thingTypeUID: mqtt:topic
configuration: {}
bridgeUID: mqtt:broker:9c3815a8
channels:
  - id: MODE
    channelTypeUID: mqtt:string
    label: Ac Salon Mode
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.mode
  - id: TEMPERARURE
    channelTypeUID: mqtt:number
    label: Ac Salon Temperature
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      min: 16
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.soll
      max: 30
  - id: ON_OFF
    channelTypeUID: mqtt:switch
    label: AC Salon ON Off
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.on
      off: "false"
      on: "true"
  - id: ECO
    channelTypeUID: mqtt:switch
    label: AC Salon Eco
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.eco
      off: "false"
      on: "true"
  - id: TURBO
    channelTypeUID: mqtt:switch
    label: AC Salon turbo
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.turbo
      off: "false"
      on: "true"
  - id: FAN
    channelTypeUID: mqtt:string
    label: Ac Salon FAN
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.fan
  - id: LAMELLE
    channelTypeUID: mqtt:switch
    label: AC Salon LAMELLE
    description: null
    configuration:
      commandTopic: esp32/acsalon/POURRI
      stateTopic: esp32/acsalon/state
      transformationPattern: JSONPATH:$.[0]conf.lamelle
      off: "false"
      on: "true"

I don’t know how to export my items from the UI, do you have any clue?

Cheers :slight_smile:

I think that’s missing at the moment, but API Explorer can show the structure of Items in a similar way.

1 Like

Hello,

just wow! It works!

I have adapted your Things and Rules to my needs and created my Items.
I still create my Items and Things in files, so I can edit them better and also export them.

If someone is interested in my configuration for the Midea air conditioner, I can make them available here (they are in german though).

My big thanks go to Giuseppe and the original developer “Links2004” on github.

1 Like