Arduino KAKU MQTT client

Today I took the day off to do something I have been planning for a while.

I made a KAKU (Klik aan klik uit) MQTT client to send and receive (new)KAKU signals.

In this post I will describe some essential steps and specifics of my setup.

After 3 years of continuous use I made an update to the firmware and posted the source code on GitHub for reference.

GOAL:

  • Receive (new) KAKU signals and send them to a MQTT broker.
  • Listen to a MQTT broker and translate the messages to (new) KAKU signals.

HARDWARE:

  • Arduino Uno and Ethernet shield.
  • Transmitter: cheap 433MHZ Chinese transmitter.
  • Receiver: Aurel RX-4M50RR30SF

For this setup a good receiver is vital, even the cheapest transmitter works fine, this is not the case for the receiver.
rx-4m50rr30sf

ARDUINO LIBRARIES:


First I made 2 dipole antennae, one for TX and one for RX.

ImageUploadedByTapatalk1359545878.832369

The transmitter and receiver are connected as follows, we need to switch the power to the transmitter and receiver so they won’t interfere with eachother.

#define PIN_RF_TX_VCC 6     // +5 volt / Vcc transmitter
#define PIN_RF_TX_DATA 5    // Data 433Mhz transmitter
#define PIN_RF_RX_VCC 4     // +5 volt / Vcc receiver
#define PIN_RF_RX_DATA 3    // Data 433Mhz receiver

The following includes are done for the Ethernet shield and MQTT, this can be easily adjusted for use with a WiFi shield or ESP8266.

#include SPI.h
#include Ethernet.h
#include Adafruit_MQTT.h
#include Adafruit_MQTT_Client.h

For the old and new style Klik aan Klik uit receiver and transmitter the following libraries are included. An interrupt chain is added to run both libraries simultaniously.

#include RemoteReceiver.h
#include RemoteTransmitter.h
#include NewRemoteReceiver.h
#include NewRemoteTransmitter.h
#include InterruptChain.h

After the includes we do some setup for the MQTT.

MQTT
#define AIO_SERVER      "xxx.xxx.xxx.xxx"
#define AIO_SERVERPORT  1883
#define AIO_USERNAME    "[username]"
#define AIO_KEY         "[password]"

We setup some MQTT feeds for sending and receiving.

// Setup a feed called 'kaku' for subscribing.
const char KAKU_SEND[] PROGMEM = "kaku/send";
Adafruit_MQTT_Subscribe kakusend = Adafruit_MQTT_Subscribe(&mqtt, KAKU_SEND);

const char NEWKAKU_SEND[] PROGMEM = "newkaku/send";
Adafruit_MQTT_Subscribe newkakusend = Adafruit_MQTT_Subscribe(&mqtt, NEWKAKU_SEND);


// Setup a feed called 'kaku' for publishing.
const char KAKU_FEED[] PROGMEM = "kaku/receive";
Adafruit_MQTT_Publish kaku  = Adafruit_MQTT_Publish(&mqtt, KAKU_FEED);

const char NEWKAKU_FEED[] PROGMEM = "newkaku/receive";
Adafruit_MQTT_Publish newkaku  = Adafruit_MQTT_Publish(&mqtt, NEWKAKU_FEED);

A really important part of the code is the interrupt handling, this so we can translate both old and new style KAKU signals realtime while running MQTT communications.

// Interrupt -1 to indicate you will call the interrupt handler with InterruptChain
RemoteReceiver::init(-1, 1, showOldCode);

// Again, interrupt -1 to indicate you will call the interrupt handler with InterruptChain
NewRemoteReceiver::init(-1, 1, showNewCode);


//interrupt 4 (pin 19) for Mega, 1 (pin3) for Uno
// Set interrupt mode CHANGE, instead of the default LOW.
InterruptChain::setMode(1, CHANGE);

// On interrupt, call the interrupt handlers of remote and sensor receivers
InterruptChain::addInterruptCallback(1, RemoteReceiver::interruptHandler);
InterruptChain::addInterruptCallback(1, NewRemoteReceiver::interruptHandler);

In our main loop we keep doing the following to keep the connection alive and check for new messages.

MQTT_connect();

// this is our 'wait for incoming subscription packets' busy subloop
Adafruit_MQTT_Subscribe *subscription;
while (subscription = mqtt.readSubscription(1000))

I will now descibe the four main functions of the sketch.


Send a KAKU signal based on the KAKU MQTT topic:

    if (subscription == &kakusend) {
      Serial.print(F("Transmitting:  "));

      digitalWrite(PIN_RF_RX_VCC, LOW); // turn of receiver
      digitalWrite(PIN_RF_TX_VCC, HIGH);

      //send data
      unsigned long code = atol((char *)kakusend.lastread);
      Serial.println(code);
      RemoteTransmitter::sendCode(PIN_RF_TX_DATA , code, 354, 4);

      digitalWrite(PIN_RF_RX_VCC, HIGH); // turn on receiver
      digitalWrite(PIN_RF_TX_VCC, LOW);
    }

Mind that we have to translate the incoming message to a unsigned long so the transmitter library knows what we are saying.


Send a NEW KAKU signal based on the NEW KAKU MQTT topic:

    if (subscription == &newkakusend) {
      Serial.print(F("Transmitting: "));

      digitalWrite(PIN_RF_RX_VCC, LOW); // turn of receiver
      digitalWrite(PIN_RF_TX_VCC, HIGH);

      String myString = (char *)newkakusend.lastread;
      int commaIndex = myString.indexOf(',');
      //  Search for the next comma just after the first
      int secondCommaIndex = myString.indexOf(',', commaIndex + 1);
      String firstValue = myString.substring(0, commaIndex);
      String secondValue = myString.substring(commaIndex + 1, secondCommaIndex);
      String thirdValue = myString.substring(secondCommaIndex + 1); 
      // To the end of the string

      long addr = firstValue.toInt();
      int unit = secondValue.toInt();
      boolean state = thirdValue.equals("true");
      int dim = thirdValue.toInt();

      Serial.print("addr: ");
      Serial.print(addr);
      Serial.print(" unit: ");
      Serial.print(unit);
      Serial.print(" state: ");
      Serial.println(thirdValue);

      NewRemoteTransmitter transmitter(addr, PIN_RF_TX_DATA, 260, 4);

      if (thirdValue.equals("true") || thirdValue.equals("false"))
      {
        if (unit = 99)  transmitter.sendGroup(state);
        else            transmitter.sendUnit(unit, state);
      }
      else  transmitter.sendDim(unit, dim);

      digitalWrite(PIN_RF_RX_VCC, HIGH); // turn on receiver
      digitalWrite(PIN_RF_TX_VCC, LOW);
    }

We will receive a comma separated string containing an address, unit number and the state.
If the unit number is 99 we will send to a group and is the state is 0-15 we will send a dim level.


Receive KAKU signal:

// shows the received code sent from an old-style remote switch
void showOldCode(unsigned long receivedCode, unsigned int period) {

  // Print the received code.
  Serial.print("Code: ");
  Serial.print(receivedCode);
  Serial.print(", period: ");
  Serial.print(period);
  Serial.println("us.");

  char* data;
  char message_buffer[100];
  data = dtostrf(receivedCode, 1, 0, message_buffer);

  if (! kaku.publish(data)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }

}

We will receive an unsigned long and will have to convert it properly to send it to the MQTT broker.


Receive NEW KAKU signal:

// Shows the received code sent from an new-style remote switch
void showNewCode(NewRemoteCode receivedCode) {
  // Print the received code.
  Serial.print("Addr ");
  Serial.print(receivedCode.address);

  if (receivedCode.groupBit) {
    Serial.print(" group");
    receivedCode.unit = 99;
  }
  else {
    Serial.print(" unit ");
    Serial.print(receivedCode.unit);
  }

  switch (receivedCode.switchType) {
    case NewRemoteCode::off:
      Serial.print(" off");
      break;
    case NewRemoteCode::on:
      Serial.print(" on");
      break;
    case NewRemoteCode::dim:
      Serial.print(" dim");
      Serial.print(receivedCode.dimLevel);
      break;
  }

  if (receivedCode.dimLevelPresent) {
    Serial.print(", dim level ");
    Serial.print(receivedCode.dimLevel);
  }

  Serial.print(", period: ");
  Serial.print(receivedCode.period);
  Serial.println("us.");

  String RECEIVE_NEWKAKU = "{\"addr\":\"";
  RECEIVE_NEWKAKU = RECEIVE_NEWKAKU + receivedCode.address + "\",\"unit\":\"" + receivedCode.unit;

  switch (receivedCode.switchType) {
    case NewRemoteCode::off:
      RECEIVE_NEWKAKU = RECEIVE_NEWKAKU + "\",\"state\":\"false\"";
      break;
    case NewRemoteCode::on:
      RECEIVE_NEWKAKU = RECEIVE_NEWKAKU + "\",\"state\":\"true\"";
      break;
    case NewRemoteCode::dim:
      RECEIVE_NEWKAKU = RECEIVE_NEWKAKU + "\",\"dim\":\"" + receivedCode.dimLevel;
      break;
  }


  RECEIVE_NEWKAKU = RECEIVE_NEWKAKU + "}" ;

  char BUF[128];

  RECEIVE_NEWKAKU.toCharArray(BUF, 128);

  if (! newkaku.publish(BUF)) {
    Serial.println(F("Failed"));
  } else {
    Serial.println(F("OK!"));
  }

}

A lot is going on here. We will decode the message including group and dim and send it as a JSON string for easy handling by Node-Red.

Some code clean-up and optimizing is to be done, as is stress testing.

For now I included the following fail safe in the code in case something else in the chain breaks.

void(* resetFunc) (void) = 0;     //declare reset function at address 0
if (retry >= 2) resetFunc();   //call reset
retry++;

A lot of code so lets end with a picture….

IMG_3457

Advertisements

5 thoughts on “Arduino KAKU MQTT client

Add yours

  1. After becoming a proud dad I have neglected this website for too long to be taken seriously.

    The Arduino is running stable for 3 years now, I will port the software to platformIO (C++) and add the sources to GitHub for reference.

    I do not know if the 433MHz library used is ESP compatible, the MQTT part I have already implemented in other projects and works great! (AsyncMqtt)
    A new device could be made much cheaper by using the great ESP8266 or ESP32, but why bother if this is still doing the job perfectly? 😉
    (It controls my bathroom/bedroom lights and sends push messages to my phone using Node-Red every day)

    I am not likely to make a newer version of the hardware as the KAKU devices are slowly being replaced by superior tech.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: