Wczoraj wieczorem "popełniłem" podobny moduł.
Funkcje modułu:
- odbieranie sygnału IR z pilotów i publikowanie do brokera MQTT
- odbieranie komunikatu z brokera i transmisja podczerwienią z odpowiednim kodowanie
- wysłanie kodów do dekodera Nki w celu przełączenia wyjścia video HDMI i EURO - to był zapalnik do opracowania tego modułu.
Odbiornik IR podłączony jest do GPIO2 a dioda nadawcza przez tranzystor do GPIO0. Uwaga - trzeba dodać rezystor podciągający do GPIO0, inaczej ESP po restarcie będzie wchodził w tryb programowania.
Format komunikatów
kierunek IR -> MQTTTOPIC: esp8266/02/receiver/RC5/12/ADDR
------------------- ^ ^ ^
prefix ----/ | | |
kodowanie ----------------/ | |
ilosc bitów -----------------/ |
Adres (dotyczy PANASONIC) --------/
MESSAGE: kod zapisany dziesiętnie
kierunek MQTT -> IRTOPIC: esp8266/02/sender/KOD/BITS/ADDR
----------------- ^ ^ ^
prefix ----/ | | |
kodowanie --------------/ | |
ilosc bitów ---------------/ |
Adres (dotyczy PANASONIC) -------/
MESSAGE: kod zapisany dziesiętnie
Komunikaty dekodera NkiTOPIC: esp8266/02/sender/NC/HDMI <- włączenie HDMI
TOPIC: esp8266/02/sender/NC/EURO <- włączenie EURO
Przykładowe użycie w Openhabie
itemsSwitch ir_philips_on "Philips Power" (gIR) {mqtt=">[mosquitto:esp8266/02/sender/RC5/12:command:ON:56]"}
Switch ir_nc_hdmi "NC+ HDMI" <ir> (gIR) {mqtt=">[mosquitto:esp8266/02/sender/NC/HDMI:command:ON:1]"}
rules// Zauważyłem, że do Philipsa trzeba komendy wysyłać podwójnie
rule sendPhilipsOn
when
Item ir_philips_on changed from OFF to ON
then
Thread::sleep(500)
sendCommand(ir_philips_on,ON)
postUpdate(ir_philips_on,OFF)
end
// Warto "podnieść" klawisz po jego użyciu
rule buttonAutoRelease
when
Item ir_nc_hdmi changed from OFF to ON
or
Item ir_nc_euro changed from OFF to ON
then
Thread::sleep(500)
postUpdate(ir_nc_hdmi,OFF)
postUpdate(ir_nc_euro,OFF)
end
ToDo:
- obsługa kodów nieznanych i możliwość transmisji danych RAW
- obsługa kodów innych niż RC5 i NEC (akurat tych potrzebowałem)
/*
* MQTT IR server
* An IR LED must be connected to ESP8266 pin 0
* An IR receiver to pin 2
* used library:
* https://github.com/markszabo/IRremoteESP8266
* Version 0.1 January, 2016
*/
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <IRremoteESP8266.h>
#include <PubSubClient.h>
// PIN definition
#define RECV_PIN 2
#define SEND_PIN 0
const char* ssid = "....";
const char* password = "....";
const char* topicRaport = "esp8266/02/info";
const char* topicSubscribe = "esp8266/02/sender/#";
const char* topicPrefix = "esp8266/02/receiver";
const char* mqtt_server = "....";
const char* mqtt_user = "...";
const char* mqtt_pass = "...";
String clientName; // MQTT client name
char message_buff[100];
IRrecv irrecv(RECV_PIN);
IRsend irsend(SEND_PIN);
WiFiClient wifiClient;
void callback(char* topic, byte* payload, unsigned int length);
void connect_to_MQTT();
PubSubClient client(mqtt_server, 1883, callback, wifiClient);
// -----------------------------------------------------------------
// MQTT receiver
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();
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
unsigned int rawData_11[35] = {250,950, 200,2000, 200,1250, 200,2850, 200,1350, 200,1350, 200,1050, 200,2150, 200,12950, 200,950, 200,800, 200,800, 200,1200, 200,800, 200,2450, 200,800, 200,800, 200}; // UNKNOWN 290BC97A
unsigned int rawData_12[35] = {250,950, 200,2000, 200,1200, 200,2850, 200,1350, 200,1350, 200,1050, 200,2150, 200,12950, 200,950, 200,1900, 200,1900, 200,1200, 200,800, 200,2450, 200,800, 200,800, 200}; // UNKNOWN 8FEB0411
unsigned int rawData_91[35] = {250,950, 200,2000, 200,1200, 200,2850, 200,1350, 200,1350, 200,1100, 200,2150, 200,12950, 200,950, 200,1750, 200,800, 200,1200, 200,1200, 200,1100, 200,800, 200,800, 200}; // UNKNOWN 19B50A9
unsigned int rawData_92[35] = {250,950, 200,2000, 200,1250, 200,2850, 200,1350, 200,1350, 200,1100, 200,2150, 200,12950, 200,950, 200,2850, 200,1900, 200,1200, 200,1200, 200,1100, 200,800, 200,800, 200}; // UNKNOWN 442CD28F
Serial.println("Payload String: " + msgString);
if (msgTopic=="esp8266/02/sender/NC/HDMI") // *9
{
irsend.sendRaw(rawData_s1, 35, freq);
irsend.sendRaw(rawData_s2, 37, freq);
irsend.sendRaw(rawData_91, 35, freq);
irsend.sendRaw(rawData_92, 35, freq);
Serial.println("Send NC+ HDMI: *9");
}
else if (msgTopic=="esp8266/02/sender/NC/EURO") // *1
{
irsend.sendRaw(rawData_s1, 35, freq);
irsend.sendRaw(rawData_s2, 37, freq);
irsend.sendRaw(rawData_11, 35, freq);
irsend.sendRaw(rawData_12, 35, freq);
Serial.println("Send NC+ EURO: *1");
} else {
// struktura "esp8266/02/sender/typ[/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 i cos dalej
irTypStr = msgTopic.substring(18, endOfTyp);
endOfBits = msgTopic.indexOf("/",endOfTyp+1);
if (endOfBits== -1)
{
// irBits jest na koncy
irBitsStr = msgTopic.substring(endOfTyp+1);
} else {
// irBits i cos dalej
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);
}
/*
TODO:
case "UNKNOWN":
case "SONY":
case "RC6":
case "DISH":
case "SHARP":
case "JVC":
case "SANYO":
case "MITSUBISHI":
case "SAMSUNG":
case "LG":
case "WHYNTER":
case "AIWA_RC_T501":
case "PANASONIC":
*/
}
}
// -----------------------------------------------------------------
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(); // Start IR sender
irrecv.enableIRIn(); // Start the receiver
Serial.begin(115200);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
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);
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");
client.publish(topicRaport, (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]);
client.publish(topicRaport, (char*) myIpString);
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, "%s/%s/%d/%d", topicPrefix, myTmp, results.bits, results.panasonicAddress );
} else {
sprintf(myTopic, "%s/%s/%d", topicPrefix, myTmp, results.bits );
}
if (results.decode_type != UNKNOWN) {
sprintf(myValue, "%d", results.value);
client.publish((char*) myTopic, (char*) myValue );
}
irrecv.resume(); // Prepare for the next value
}
}