If you push attached buttons, the protocol seems to be the same in the other direction:
Sampled from pushing attached physical buttons:
0xA0
0x04
0xxx
0xA1
where 0xxx seems to be:
0x00 both off
0x01 relay one on
0x02 relay two on
0x03 both relays on
Just made a working test sketch:
// the setup function runs once when you press reset or power the board void setup() { // initialize digital pin LED_BUILTIN as an output. Serial.begin(19200); } // the loop function runs over and over again forever void loop() { Serial.write(0xA0); Serial.write(0x04); Serial.write(0x01); Serial.write(0xA1); Serial.flush(); delay(1000); // wait for a second Serial.write(0xA0); Serial.write(0x04); Serial.write(0x03); Serial.write(0xA1); Serial.flush(); delay(1000); // wait for a second Serial.write(0xA0); Serial.write(0x04); Serial.write(0x00); Serial.write(0xA1); Serial.flush(); delay(1000); // wait for a second }
And a simple MQTT sketch:
// derived from the Basic MQTT example https://github.com/knolleary/pubsubclient/blob/master/examples/mqtt_basic/mqtt_basic.ino #include <ESP8266WiFi.h> #include <PubSubClient.h> // Update these with values suitable for your network. const char* ssid = "xxx"; const char* password = "yyy"; const char* mqtt_server = "zzz"; boolean relay1 = false; boolean relay2 = false; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(10); // We start by connecting to a WiFi network //Serial.println(); //Serial.print("Connecting to "); //Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); //Serial.print("."); } randomSeed(micros()); //Serial.println(""); //Serial.println("WiFi connected"); //Serial.println("IP address: "); //Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { boolean bset = false; payload[length] = '\0'; String sPayload = String((char *)payload); String sTopic = String(topic); if (sTopic == "sonoff/1/relay1/set") { if (sPayload == "1") { if (relay1 == false) bset = true; relay1 = true; } else { if (relay1) bset = true; relay1 = false; } } if (sTopic == "sonoff/1/relay2/set") { if (sPayload == "1") { if (relay2 == false) bset = true; relay2 = true; } else { if (relay2) bset = true; relay2 = false; } } if (bset) setrelays(); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { //Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "sonoff-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { // Once connected, publish an announcement... client.publish("sonoff/1", "connected"); setrelays(); // ... and resubscribe client.subscribe("sonoff/1/#"); } else { // Wait 5 seconds before retrying delay(5000); } } } void setup() { Serial.begin(19200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); client.loop(); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); } void setrelays() { byte b = 0; if (relay1) b++; if (relay2) b += 2; Serial.write(0xA0); Serial.write(0x04); Serial.write(b); Serial.write(0xA1); Serial.flush(); if (relay1) client.publish("sonoff/1/relay1", "1"); else client.publish("sonoff/1/relay1", "0"); if (relay2) client.publish("sonoff/1/relay2", "1"); else client.publish("sonoff/1/relay2", "0"); }
Markus.
This is encouraging - I've spent the morning tinkering with the Sonoff Dual and want to upload some 8266 Arduino code.
I can see data on the TX/RX pins as per your investigations when buttons are pressed.
Can I ask how you've uploaded the code above to the board? Is it through the same header? If so, how did you get into some sort of programming mode (i.e. GPIO0 low) - had hoped it would be button on front, but doesn't seem to be.
Thanks
Luke.
Hi Luke
I soldiered a wire to EN_FW and made a connection to ground on startup. I can take a picture, if you don't find the pin.
Cu,
Markus
I had a quick look on the 8266 datasheet, but couldn't find a reference to EN_FW pin. Be great if you could post a picture.
Thanks,
Luke
It is pin 15:
Great - many thanks.
Have got the blink sketch going and OTA.
Need to fiddle a bit further with MQTT, but great to have board programmable now.
Thanks
Luke.
Got MQTT working now. Also added some generic OTA code. Copy below for anyone else interested;
// derived from the Basic MQTT example https://github.com/knolleary/pubsubclient/blob/master/examples/mqtt_basic/mqtt_basic.ino #include <ESP8266WiFi.h> #include <PubSubClient.h> #include <ESP8266mDNS.h> #include <WiFiUdp.h> #include <ArduinoOTA.h> const char* host = "DUAL-SONOFF"; const char* esppwd = "****"; #define LED 13 #define LED_ON 0 #define LED_OFF 1 // Update these with values suitable for your network. const char* ssid = "xxxx"; const char* password = "xxxx"; const char* mqtt_server = "xxxx"; boolean relay1 = false; boolean relay2 = false; const unsigned long tUpdateLED = 10000; unsigned long tLEDFLASH; #define LEDFLASHMILLIS 10000 WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(10); // We start by connecting to a WiFi network //Serial.println(); //Serial.print("Connecting to "); //Serial.println(ssid); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); FlashLED(); //Serial.print("."); } randomSeed(micros()); //Serial.println(""); //Serial.println("WiFi connected"); //Serial.println("IP address: "); //Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { boolean bset = false; payload[length] = '\0'; String sPayload = String((char *)payload); String sTopic = String(topic); if (sTopic == "sonoff/1/relay1/set") { FlashLED(); if (sPayload == "1") { if (relay1 == false) bset = true; relay1 = true; } else { if (relay1) bset = true; relay1 = false; } } if (sTopic == "sonoff/1/relay2/set") { FlashLED(); FlashLED(); if (sPayload == "1") { if (relay2 == false) bset = true; relay2 = true; } else { if (relay2) bset = true; relay2 = false; } } if (bset) setrelays(); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { //Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "sonoff-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { // Once connected, publish an announcement... client.publish("sonoff/1", "connected"); setrelays(); // ... and resubscribe client.subscribe("sonoff/1/#"); } else { // Wait 5 seconds before retrying delay(5000); } } } void setup() { Serial.begin(19200); pinMode(LED, OUTPUT); FlashLED(); tLEDFLASH = millis(); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); client.loop(); ArduinoOTA.setHostname(host); ArduinoOTA.setPassword(esppwd); ArduinoOTA.onStart([]() { // switch off all the PWMs during upgrade }); ArduinoOTA.onEnd([]() { // do a fancy thing with our board led at end }); ArduinoOTA.onError([](ota_error_t error) { ESP.restart(); }); /* setup the OTA server */ ArduinoOTA.begin(); Serial.println("Ready"); } void loop() { ArduinoOTA.handle(); if ((millis() - tLEDFLASH) > tUpdateLED ) FlashLED(); if (!client.connected()) { reconnect(); } client.loop(); } void setrelays() { byte b = 0; if (relay1) b++; if (relay2) b += 2; Serial.write(0xA0); Serial.write(0x04); Serial.write(b); Serial.write(0xA1); Serial.flush(); if (relay1) client.publish("sonoff/1/relay1", "1"); else client.publish("sonoff/1/relay1", "0"); if (relay2) client.publish("sonoff/1/relay2", "1"); else client.publish("sonoff/1/relay2", "0"); } void FlashLED() { Serial.println("FlashLED triggered"); digitalWrite(LED, LED_ON); delay(50); digitalWrite(LED, LED_OFF); delay(20); tLEDFLASH = millis(); }
ArduinoOTA is new for me. I will have a look at it.
Added the buttons to my sketch. It really need cleanup (String/char* handling, code redundancy)
// derived from the Basic MQTT example https://github.com/knolleary/pubsubclient/blob/master/examples/mqtt_basic/mqtt_basic.ino #include <ESP8266WiFi.h> #include <PubSubClient.h> // Update these with values suitable for your network. String MQTT_SUBSCRIPTION = "sonoff/2/#"; String MQTT_PUBLISH = "sonoff/2"; const char* ssid = "xxx"; const char* password = "yyy"; const char* mqtt_server = "zzz"; boolean relay1 = false; boolean relay2 = false; int incomingByte = 0; int iStep = 0; int iNewState = 0; WiFiClient espClient; PubSubClient client(espClient); void setup_wifi() { delay(10); // We start by connecting to a WiFi network //Serial.println(); //Serial.print("Connecting to "); //Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); //Serial.print("."); } randomSeed(micros()); //Serial.println(""); //Serial.println("WiFi connected"); //Serial.println("IP address: "); //Serial.println(WiFi.localIP()); } void callback(char* topic, byte* payload, unsigned int length) { boolean bset = false; payload[length] = '\0'; String sPayload = String((char *)payload); String sTopic = String(topic); if (sTopic == MQTT_PUBLISH + "/relay1/set") { if (sPayload == "1") { if (relay1 == false) bset = true; relay1 = true; } else { if (relay1) bset = true; relay1 = false; } } if (sTopic == MQTT_PUBLISH + "/relay2/set") { if (sPayload == "1") { if (relay2 == false) bset = true; relay2 = true; } else { if (relay2) bset = true; relay2 = false; } } if (bset) setrelays(); } void reconnect() { // Loop until we're reconnected while (!client.connected()) { //Serial.print("Attempting MQTT connection..."); // Create a random client ID String clientId = "sonoff-"; clientId += String(random(0xffff), HEX); // Attempt to connect if (client.connect(clientId.c_str())) { // Once connected, publish an announcement... client.publish(MQTT_PUBLISH.c_str() , "connected"); setrelays(); // ... and resubscribe client.subscribe(MQTT_SUBSCRIPTION.c_str()); } else { // Wait 5 seconds before retrying delay(5000); } } } void setup() { Serial.begin(19200); setup_wifi(); client.setServer(mqtt_server, 1883); client.setCallback(callback); client.loop(); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); if (Serial.available() > 0) { // read the incoming byte: incomingByte = Serial.read(); if (incomingByte == 0xA0) { iStep = 1; } else if ((iStep == 1) && (incomingByte == 0x04)) { iStep = 2; } else if ((iStep == 2) && (incomingByte >= 0) && (incomingByte <= 3)) { iStep = 3; iNewState = incomingByte; } else if ((iStep == 3) && (incomingByte == 0xA1)) { iStep = 0; if (iNewState == 0) { relay1 = false; relay2 = false; } if (iNewState == 1) { relay1 = true; relay2 = false; } if (iNewState == 2) { relay1 = false; relay2 = true; } if (iNewState == 3) { relay1 = true; relay2 = true; } // client.publish(MQTT_PUBLISH.c_str(),String(iNewState).c_str()); if (relay1) client.publish((MQTT_PUBLISH + "/relay1").c_str(), "1"); else client.publish((MQTT_PUBLISH + "/relay1").c_str(), "0"); if (relay2) client.publish((MQTT_PUBLISH + "/relay2").c_str(), "1"); else client.publish((MQTT_PUBLISH + "/relay2").c_str(), "0"); } else iStep = 0; } } void setrelays() { byte b = 0; if (relay1) b++; if (relay2) b += 2; Serial.write(0xA0); Serial.write(0x04); Serial.write(b); Serial.write(0xA1); Serial.flush(); if (relay1) client.publish((MQTT_PUBLISH + "/relay1").c_str(), "1"); else client.publish((MQTT_PUBLISH + "/relay1").c_str(), "0"); if (relay2) client.publish((MQTT_PUBLISH + "/relay2").c_str(), "1"); else client.publish((MQTT_PUBLISH + "/relay2").c_str(), "0"); }
Hi Marcel
AFAIK only the dual has the additional MCU. The other should be hackable without revere engineering.
As I only have sonoff dual, I can't help you with a sketch.
Regards,
Markus
Is there no way to control ITEAD cloud from PC/Linux/Mac etc?
I don't want to alter my firmware because I have SonOff Pro and want to use RF aswell...
but I want to be able to switch on/off from the PC.
Markus Maeder
With the following information it should be possible to replace the original firmware.
Details from my protocol analyzer (attached to ERX/ETX:
19230,8,N,1
Sampled from pushing the button on the internet
0xA0
0x04
0xxx
0xA1
where 0xxx seems to be:
0x00 both off
0x01 relay one on
0x02 relay two on
0x03 both relays on
2 people have this question