MQTT IR transmiter/receiver

I implemented simple MQTT IR Transmitter/receiver with ESP. I used basic ESP01,
Here is code:

/*
 * IRremoteESP8266: IRServer - MQTT IR server
 * An IR LED must be connected to ESP8266 RX port (GPIO-3)
 * An IR receiver to GPIO 0
 * used library:
 * https://github.com/markszabo/IRremoteESP8266
 */

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <IRremoteESP8266.h>
#include <PubSubClient.h>

int RECV_PIN = 0; //an IR detector/demodulator is connected to GPIO pin 0

IRrecv irrecv(RECV_PIN);

const char* ssid = "MY-SSID";
const char* password = "MY-WIFI-PSK";

const char* topicRaportPrefix = "esp8266/02/info/";
const char* topicSubscribe = "esp8266/02/sender/#";
const char* topicPrefix = "esp8266/02/";

const char* mqtt_server = "10.10.10.10";
const char* mqtt_user = "mqtt_user";
const char* mqtt_pass = "mqtt_pass";

String clientName; // MQTT client name
char message_buff[100];



// Pin of IR sender
IRsend irsend(3); // rx port z RS232 

WiFiClient wifiClient;


void callback(char* topic, byte* payload, unsigned int length);
void connect_to_MQTT();

PubSubClient client(mqtt_server, 1883, callback, wifiClient);

// -----------------------------------------------------------------

void callback(char* topic, byte* payload, unsigned int length) {
  int i = 0;

  Serial.println("Message arrived:  topic: " + String(topic));
  Serial.println("Length: " + String(length,DEC));

  // create character buffer with ending null terminator (string)
  for(i=0; i<length; i++) {
    message_buff[i] = payload[i];
  }
  message_buff[i] = '\0';
  
  unsigned int freq=38;
  String msgString = String(message_buff);
  String msgTopic = String(topic);
  unsigned long msgInt = msgString.toInt();
 
  Serial.println("Payload String: " + msgString);
  
  // structure "esp8266/02/sender/type[/bits[/panasonic_address]]"
  int endOfBits;
  String irTypStr = "";
  String irBitsStr = "";
  int irBitsInt=-1;
  String irPanasAddrStr = "";
  
  int endOfTyp = msgTopic.indexOf("/",20);
  if (endOfTyp == -1)
  {
    // One element - only irTyp
    irTypStr  = msgTopic.substring(18);
  } else {
    // irTyp exists and something more
    irTypStr  = msgTopic.substring(18, endOfTyp);
    endOfBits = msgTopic.indexOf("/",endOfTyp+1);
    if (endOfBits== -1)
    {
      // irBits is last
      irBitsStr = msgTopic.substring(endOfTyp+1);
    } else {
      // irBits and something more
      irBitsStr = msgTopic.substring(endOfTyp+1, endOfBits);
      irPanasAddrStr = msgTopic.substring(endOfBits+1);
    }
    irBitsInt = irBitsStr.toInt();
  }
    
    
  Serial.println(irTypStr);
  Serial.println(irBitsStr);
  Serial.println(irPanasAddrStr);
  if (irTypStr=="NEC") 

    Serial.print("Send NEC:");
    Serial.println(msgInt);
    irsend.sendNEC(msgInt, 36);
  } else if (irTypStr=="RC5") {
    Serial.print("Send RC5:");
    Serial.print(msgInt);
    Serial.print(" (");
    Serial.print(irBitsInt);
    Serial.println("-bits)");
    irsend.sendRC5(msgInt, irBitsInt);
  }
    
}

// -----------------------------------------------------------------
String macToStr(const uint8_t* mac)
{
  String result;
  for (int i = 0; i < 6; ++i) {
    result += String(mac[i], 16);
    if (i < 5)
      result += ':';
  }
  return result;
}

// -----------------------------------------------------------------
void setup(void){
  irsend.begin();
  irrecv.enableIRIn();  // Start the receiver

  Serial.begin(115200,SERIAL_8N1,SERIAL_TX_ONLY);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  
  clientName += "esp8266-";
  uint8_t mac[6];
  WiFi.macAddress(mac);
  clientName += macToStr(mac);
  clientName += "-";
  clientName += String(micros() & 0xff, 16);

  connect_to_MQTT();

}

// -----------------------------------------------------------------
void connect_to_MQTT() {
  Serial.print("Connecting to ");
  Serial.print(mqtt_server);
  Serial.print(" as ");
  Serial.println(clientName);
  char myTopic[100];
  
  int is_conn = 0;
  while (is_conn == 0) {
    if (client.connect((char*) clientName.c_str(), mqtt_user, mqtt_pass)) {
      Serial.println("Connected to MQTT broker");
      sprintf(myTopic, "%sclient", topicRaportPrefix);
      client.publish((char*)myTopic, (char*) clientName.c_str());
      IPAddress myIp = WiFi.localIP();
      char myIpString[24];
      sprintf(myIpString, "%d.%d.%d.%d", myIp[0], myIp[1], myIp[2], myIp[3]);
      sprintf(myTopic, "%sip", topicRaportPrefix);
      client.publish((char*)myTopic, (char*) myIpString);
      sprintf(myTopic, "%stype", topicRaportPrefix);
      client.publish((char*)myTopic,"IR server");
      Serial.print("Topic is: ");
      Serial.println(topicSubscribe);
      if (client.subscribe(topicSubscribe)){
        Serial.println("Successfully subscribed");
      }

      is_conn = 1;
    }
    else {
      Serial.print("MQTT connect failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

// -----------------------------------------------------------------
// Encodig of data
void  encoding (decode_results *results, char * result_encoding)
{
  switch (results->decode_type) {
    default:
    case UNKNOWN:      strncpy(result_encoding,"UNKNOWN\0",8);       break ;
    case NEC:          strncpy(result_encoding,"NEC\0",4);           break ;
    case SONY:         strncpy(result_encoding,"SONY\0",5);          break ;
    case RC5:          strncpy(result_encoding,"RC5\0",4);           break ;
    case RC6:          strncpy(result_encoding,"RC6\0",4);           break ;
    case DISH:         strncpy(result_encoding,"DISH\0",5);          break ;
    case SHARP:        strncpy(result_encoding,"SHARP\0",6);         break ;
    case JVC:          strncpy(result_encoding,"JVC\0",4);           break ;
    case SANYO:        strncpy(result_encoding,"SANYO\0",6);         break ;
    case MITSUBISHI:   strncpy(result_encoding,"MITSUBISHI\0",11);   break ;
    case SAMSUNG:      strncpy(result_encoding,"SAMSUNG\0",8);       break ;
    case LG:           strncpy(result_encoding,"LG\0",3);            break ;
    case WHYNTER:      strncpy(result_encoding,"WHYNTER\0",8);       break ;
    case AIWA_RC_T501: strncpy(result_encoding,"AIWA_RC_T501\0",13); break ;
    case PANASONIC:    strncpy(result_encoding,"PANASONIC\0",11);    break ;
  }
}

// -----------------------------------------------------------------
void loop(void){
  client.loop();

  if (! client.connected()) {
    Serial.println("Not connected to MQTT....");
    connect_to_MQTT();
  }
  decode_results  results;        // Somewhere to store the results
  if (irrecv.decode(&results)) {  // Grab an IR code
    char myTopic[100];
    char myTmp[50];
    char myValue[500];
    encoding (&results, myTmp);
    if (results.decode_type == PANASONIC) { //Panasonic has address
      // struktura "prefix/typ/bits[/panasonic_address]"
      sprintf(myTopic, "%sreceiver/%s/%d/%d", topicPrefix, myTmp, results.bits, results.panasonicAddress );
    } else {
      sprintf(myTopic, "%sreceiver/%s/%d", topicPrefix, myTmp, results.bits );
    }
    if (results.decode_type != UNKNOWN) {
      // any other has code and bits
      sprintf(myValue, "%d", results.value);
      client.publish((char*) myTopic, (char*) myValue );
    }
    irrecv.resume();              // Prepare for the next value
  }
} 

I don’t have schematic - it’s very simple.

  • I have 8v power supply and DC-DC converter to 3,3V
  • There are pullup resistors 3,3k (or 10k) conneted between +3,3V and RESET, CH_PD, GPIO15 and GPIO-0 (See improved stability schematic http://esp8266.github.io/Arduino/versions/2.0.0/doc/boards.html )
  • Output PIN of TSOP IR receiver is connected to GPIO-0 pin (with pullup resistor).
  • To avoid problems with GPIO-0/2 stability I’m using GPIO-3 (RX port) for output and TX-only mode of port. To this port is connected simple amplifier with NPN transistor (see diagram - http://www.mikrocontroller.net/topic/377947 ).

Code is not finished - I implemented universal IR->MQTT and MQTT->IR only for NEC and Phillips because I need only this codecs - but there is no problem to add next.

All transmited IR codes are visible via MQTT (topic esp8266/02/receiver/#). So I can check code and add items to OH:

Number ir_in_lg "IR LG/NEC command [%d]"  <ir>   (gIR)             {mqtt="<[mosquitto:esp8266/02/receiver/NEC/32:state:default]"}
Switch   ir_philips_on "Philips Power" <ir> (gIR) {mqtt=">[mosquitto:esp8266/02/sender/RC5/12:command:ON:56]"}

and add use such items in rules:

rule lgTVOnOff
when
  Item ir_in_lg received update 551489775
then
  if (LgTvStatus.state==OFF) // TV is off (from LG-TV binding)
  {
    sendCommand(tv_backlight,ON) // Turn on TV backlight
  } else {
    sendCommand(tv_backlight,OFF) // Turn off TV backlight
  }
end

with:

sendCommand(ir_philips_on,ON)

I can send Philips power comand from rules.

I’m planning migration to Homie aware code with OTA support, but current version works fine for me so this task have now low prioritity :slight_smile:

Because it is installed in my living room I had to find nice enclosure - I used enclosure from old IR extender.
https://shopdelta.eu/wireless-remote-control-extension-vt2-sig_l2_p2282.html
I removed radio part and used only existing power suplly.

9 Likes

Good Day

Great Job!

Is it possible to add ESP-01 to arduino with same kind of coding to give more io’s?

Thank You

Albertus Geyser

Very nice!! I have been considering doing something similar. I had thought about putting a couple IR led’s in parallel, 2 wide beam, 2 narrow beam, kind of like those tv-bgone devices. This seems a very simple easy implementation though. I love it and I have a esp8266 just sitting in my bin.

Have you had to do anything with sending raw code? I have an HVAC in my office that doesnt’ have common codes, but I can steal the codes from the remote and playback.

I didn’t think about raw codes - I don’t need it. But I think that adding such support is easy - you have to only define how to format raw codes for ESP subscription. Or you can implement supprot for specific codes - I just added for my STB in function callback :

unsigned int  rawData_s1[35] = {250,950, 200,2000, 200,1200, 200,2850, 200,1350, 200,1350, 200,1050, 200,2150, 200,12950, 200,950, 200,1200, 200,800, 200,1200, 200,1600, 200,1200, 200,800, 200,800, 200};  // UNKNOWN C0092718
unsigned int  rawData_s2[37] = {100,5850, 200,950, 200,2000, 200,1200, 200,2850, 200,1350, 200,1350, 200,1050, 200,2150, 200,12950, 200,950, 200,2300, 200,1900, 200,1200, 200,1650, 200,1200, 200,800, 200,800, 200};  // UNKNOWN 4AE2F613
if (msgTopic=="esp8266/02/sender/NC/HDMI") // *9
{
  irsend.sendRaw(rawData_s1, 35, freq);
  irsend.sendRaw(rawData_s2, 37, freq);
} else {
  // support for common codes
}

Hi @mix_at_pl

Why are you sending two codes?
Is it a combination of codes?
I would like to send a combination of codes to my TV.

What if i want to select a channel (e.g. 105)

I have several ways to archieve this:
Enter 1,0,5 on the keypad and wait
Enter 0,1,0,5 on the keypad
Enter 1,0,5, ENTER on the keypad (prefered)

The SamsungTV Binding can not select the channel directly, sending multiple numbers is unstable, therefore i want to use the ESP8266.

I’m sending 2 codes because I emulate pressing 2 buttons sequence.
If you have issue with channel number my advise is to:

  • send via mqtt channel number (like 105)
  • convert message from broker to key sequence and send each key separately via IR
1 Like

I made one of these last weekend and used the raw codes for my AC.

Thanks!!

Is it possible to publish a state update to a certain Topic?

I have a number item for my TV Channels
(From 1 to 999)
If the channel is set to one, a command to the the Topic IR_mqtt/sender/TV/channel/1 should be sent.

How can i do this, using rules within openhab

I think that better is to do this on ESP level - as I wrote earlier. Send to ESP topic IR_mqtt/sender/TV/channel with value 999 at ESP level split 999 receive is as string and to 3 characters - and then send each number/number as IR. Keep OH configuration as simple as possible.

Thanks for your advice, at the moment i am using a mapping in my sitemap which uses my channel numbers. Within Arduino IDE, i created a few channels directly.

But when my provider changes the channel ID i have to flash a new sketch to the Arduino(ESP8266) and do some changes within my sitemap.
Therefore i should follow your advice ASAP.

Thats the modified code (or the main part)

   if (msgTopic=="IR_mqtt/sender/TV/ON") {irsend.sendRaw(S_pwr,68,freq);}
    //ARD HD
    //Servus TV HD
    if (msgTopic=="IR_mqtt/sender/TV/Channel" && msgString=="12") {irsend.sendRaw(S_1,68,freq);delay(100);irsend.sendRaw(S_2,68,freq);}
    //ZDF Neo HD
    if (msgTopic=="IR_mqtt/sender/TV/Channel" && msgString=="43") {irsend.sendRaw(S_4,68,freq);delay(100);irsend.sendRaw(S_3,68,freq);}
    //RTL
    if (msgTopic=="IR_mqtt/sender/TV/Channel" && msgString=="103") {irsend.sendRaw(S_1,68,freq);delay(100);irsend.sendRaw(S_0,68,freq);delay(100);irsend.sendRaw(S_3,68,freq);}

New version is on github:

This is great thanks for the work

Next version is available https://github.com/enc-X/mqtt-ir-transceiver , new features:

  • Code migrated to platformio
  • Support for Global Cache IR database codes
3 Likes

Nice :smile:

I have to check how I can build a similiar thing with the ESPs I have lying around in my workshop area.

Hi mix_at_pl

Thanks for this.

Just wanted to find out where do I input the data for wifi and mqtt ? is it in the main.cpp?

Thanks

Hi

I am getting this error in the code on platformio:

collect2: error: ld returned 1 exit status
*** [.pioenvs/esp01_1m/firmware.elf] Error 1

Can you please help?

Thanks.

Hi Phile

Do you know how to use platformio?

I am struggling with this.

Can you please help?

Thanks.

I just went through building and programming the IR MQTT tranceiver and want to thank mix_at_pl for the great work.

The building is straight forward following the schematic.
For the programming I installed the Platformio IDE following the instructions from http://docs.platformio.org/en/stable/ide/atom.html. You need to have Python 2.7.x installed for it to work correctly.
Once Platformio is loading with no errors, click on “Open Project” and select the folder mqtt-ir-transceiver-master (which is downloaded and unzipped from https://github.com/enc-X/mqtt-ir-transceiver/archive/master.zip).
From the list with files in the left tab open the platformio.ini and change the upload_port = com12 to the correct port with your ESP8266
Select “PlatformIO/Build” from the menu on top and wait for the build to finish. The needed libraries will be downloaded and installed automatically.
If no errors you will see “No issues” message on the bottom.
Next click “PlatformIO/upload” and keep an eye on the messages. If the COM port is not correct you’ll see errors ( error: Failed to open com12 etc).
Once successfully uploaded, reboot your ESP.

1 Like

After the reboot your new IR/MQTT ESP will broadcast an SSID that looks like this - IRTRANS-XXXXXX.
Connect to that SSID and a configuration page should open. If not find what is your gateway and paste that IP in your browser.
In that config page you need to configure the real WiFi network you want to connect to, the MQTT server IP and the prefix for your MQTT messages.

:slight_smile: Just noticed that mix_at_pl has updated the GitHub read.me with the instructions.
Again thank you for the great work:).