Banalnie prosty pilot z ESP8266

  • 3 Odpowiedzi
  • 10091 Wyświetleń

0 użytkowników i 1 Gość przegląda ten wątek.

*

Offline Baael

  • * 2
  • 0
  • Nazwa i wersja ID: Homiq
Banalnie prosty pilot z ESP8266
« dnia: Listopad 12, 2015, 10:10:22 pm »
Zamiast drogiego na ZWave.

Przykładowy kod:
https://gist.github.com/Baael/1ba70000db7c539571cd

#include <espduino.h>
#include <mqtt.h>
#include <SoftwareSerial.h>
#include <IRremote.h>

// SSID sieci wifi
#define SSID    "ssid"

// Haslo sieci wifi
#define PASS    "pass"

// Nalezy podac adres brokera MQTT
#define BROKER  "192.168.0.1"

// nazwa pszczoly w naszym roju
char* bee_name = "hifi";

IRsend irsend;
MQTT mqtt(&esp);

SoftwareSerial espPort(11, 10); // TX, RX
ESP esp(&espPort, &Serial, 9);

// callback stanu polaczenia z siecia wifi
void wifiCb(void* response)
{
  uint32_t status;
  RESPONSE res(response);
  if (res.getArgc() == 1) {
    res.popArgs((uint8_t*)&status, 4);

    if (status == STATION_GOT_IP) { mqtt.connect(BROKER, 1883); }
  }
}

// callback polaczenia z brokerem MQTT
void mqttConnected(void* response)
{
  // subskrybcja kanalu
  mqtt.subscribe("bee/hifi/+");
  // zameldowanie sie pszczoly w ulu ;) Lubie takie nazewnictwo
  // mqtt.publish("swarm/bee/online", bee_name);
}

void mqttDisconnected(void* response){}
void mqttPublished(void* response){}

// callback otrzymanych danych z MQTT
void mqttData(void* response){

  // przetwarzamy otrzymane informacje, na temat i payload
  RESPONSE res(response);
  String topic = res.popString();
  String payload  = res.popString();

  // w zaleznosci od tematu wiadomosci
  // uruchamiamy odpowiednia funkcje z payloadem jako argumentem
  if (topic == "bee/hifi/volume") { setVolume(payload); }
  if (topic == "bee/hifi/mode")   { setMode(payload); }
  if (topic == "bee/hifi/power")  { triggerPower(); }

}


void setVolume(void* mode) {
  if (mode == "down"){ irsend.sendSony(0xc81, 12); }
  if (mode == "up")  { irsend.sendSony(0x481, 12); }
}

void setMode(void* mode) {
  if (data == "radio")  { irsend.sendSony(0x181, 12); }
  if (data == "dvd")    { irsend.sendSony(0xbe1, 12); }
  if (data == "aux")    { irsend.sendSony(0x761, 12); }
}

// przycisk power moze tylko przelaczac aktualny stan urzadzenia
// trzeba jakos domyslic sie czy jest wlaczone czy wylaczone
void triggerPower() {
  irsend.sendSony(0xa81, 12);
}

// inicjalizujemy modul esp8266
void setupESP8266() {
  esp.enable();
  delay(200);
  esp.reset();
  delay(200);

  // jesli zawiesi sie w tym miejscu to warto zewrzec na chwile RST modulu z GND
  while(!esp.ready());
  // podpinamy callback sieci wifi
  esp.wifiCb.attach(&wifiCb);
  // podlaczamy sie do istniejacej sieci wifi
  esp.wifiConnect(SSID,PASS);
}

// inicjalizacja MQTT
void setupMQTT() {
  mqtt.begin(bee_name, "admin", "Isb_C4OGD4c3", 120, 1);

  mqtt.connectedCb.attach(&mqttConnected);
  mqtt.disconnectedCb.attach(&mqttDisconnected);
  mqtt.publishedCb.attach(&mqttPublished);
  mqtt.dataCb.attach(&mqttData);
}

void setup() {
  espPort.begin(19200);

  setupESP8266();
  setupMQTT();
}


void loop() {
  esp.process();
}

Przydatne linki do przechwytywania sygnałów IR
http://www.makeuseof.com/tag/introducing-the-tv-devil-an-easy-remote-control-arduino-prank/
https://www.pjrc.com/teensy/td_libs_IRremote.html

Schemat:


Efekt :)


Potrzebne:
1. http://z3t0.github.io/Arduino-IRremote/
2. https://github.com/tuanpmt/espduino

*

Offline sztywniak

  • ***** 601
  • 23
  • Nazwa i wersja ID: HC2 3.60/ 4.37, Vera 1.7.1018
Odp: Banalnie prosty pilot z ESP8266
« Odpowiedź #1 dnia: Listopad 12, 2015, 11:38:50 pm »
fajne, dzieki za opis :-)
*

Offline Enc

  • ** 94
  • 3
  • Nazwa i wersja ID: OpenHab
Odp: Banalnie prosty pilot z ESP8266
« Odpowiedź #2 dnia: Styczeń 12, 2016, 07:49:04 pm »
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 -> MQTT
TOPIC: esp8266/02/receiver/RC5/12/ADDR
       -------------------  ^  ^    ^
  prefix ----/             |  |    |
  kodowanie ----------------/  |    |
  ilosc bitów -----------------/    |
  Adres (dotyczy PANASONIC) --------/
MESSAGE: kod zapisany dziesiętnie

kierunek MQTT -> IR
TOPIC: esp8266/02/sender/KOD/BITS/ADDR
       -----------------  ^  ^     ^
  prefix ----/           |  |     |
  kodowanie --------------/  |     |
  ilosc bitów ---------------/     |
  Adres (dotyczy PANASONIC) -------/
MESSAGE:   kod zapisany dziesiętnie
Komunikaty dekodera Nki
TOPIC: esp8266/02/sender/NC/HDMI   <- włączenie HDMI
TOPIC: esp8266/02/sender/NC/EURO  <- włączenie EURO

Przykładowe użycie w Openhabie

items
Switch   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
  }
}
« Ostatnia zmiana: Styczeń 12, 2016, 07:55:01 pm wysłana przez Enc »
... vendor agnostic ...
*

Offline Enc

  • ** 94
  • 3
  • Nazwa i wersja ID: OpenHab
Odp: Banalnie prosty pilot z ESP8266
« Odpowiedź #3 dnia: Maj 22, 2016, 01:02:20 pm »
Aktualna wersja IR transceiver-a jest na githubie:
https://github.com/enc-X/mqtt-ir-transceiver
  • Obsługuje tryby RAW - można programowować przez MQTT swoje kody a następnie wysyłać je przez IR
  • Wprowadziłem tryb konfiguracji WiFi i MQTT
... vendor agnostic ...