Sonoff 4ch example


#1

Hey all,

I use the diffrent sonoff products with my own firmware (thanks to Jonathan for the first setup :slight_smile: )
No i ordered the Sonoff 4ch. but i can’t find any usefull information about it.
I expect it to work like the sonoff dual with serial commands. but can’t find any information about it.
I would be realy hapy if someone has a example code or more information about the product
please note that the 4ch and the 4ch pro are complete diffrent products.

Regards
Mark


#2

Mark,

If you go to this website https://www.itead.cc/wiki/Sonoff_4CH, you will find almost all the info you need, hardware wise which you could use to write your own software. I suspect some of your existing code would be reusable.

See how you go.

David


#3

Hi David,

Thanks for the response.
I already know the site. But the problem is that they do not tell you wich system they use.
Like all the single relay products use gpio.
The dual one expect binar message on the serial console.

My best guess will be using the sonoff dual firmware and simply use 4 bits instead of 2.
Will give it a try this weekend and let you know.

Regards
Mark


#4

Hi Mark, The Sonoff 4ch uses IO5, IO12, IO14, IO15, to control the relays, so if you already have some software that interfaces to the 1 relay as in the Sonoff Basic, then you need to take the code and expand it.

I am not sure whether you are using the software installed when the device comes from the factory or using our own. But for example if you are using Jon’s ‘BasicOTARelay’ code, then you could expand it from simply using 1/0 for power On/Off you could use a 2 digit payload with say the first digit the relay you want to control and the second digit indicating on or off. You could also use the first digit as follows:-1:
0 = All relays selected
1 = Relay 1 selected
2 = Relay 2 selected
and so on. Therefore a payload of “01” would turn all relays on, and a payload of “20” would turn relay 2 off.

I hope this helps you a bit.

David


#5

Hi David.
thanks for your assistants. i noticed that the relay and the led are sharing a single IO port. an that the numbers are noted near the leds. (there was a small typo in your list)

const int relayPin1 = 12;  // Active high
const int relayPin2 = 5;  // Active high
const int relayPin3 = 4;  // Active high
const int relayPin4 = 15;  // Active high
const int ledPin   = 13;  // Active low
#define button1Pin          0
#define button2Pin          9
#define button3Pin          10
#define button4Pin          14

currently the buttons are not working as they should be. (the are random high / low) but that is not a big issue for me. (i place a regular switch before this to have a manual “off” anyway)

For those who are intrested in the code.
For sure i am not a programmer so the layout is horrible and there should be a lot of better ways of doing this. but it is working fine for me.

the thing i miss the most in mqtt switches is the feedback. so on the bottom there is a status reporting (so when i am away from home i know for sure if the switch is on of off.

/*
 * Simple example MQTT client to run on the Itead Studio Sonoff with OTA
 * update support. Subscribes to a topic and watches for a message of
 * either "0" (turn off LED and relay) or "1" (turn on LED and relay)
 *
 * For more information see:
 *   http://www.superhouse.tv/17-home-automation-control-with-sonoff-arduino-openhab-and-mqtt/
 */

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>
#include <dummy.h>

/* current code version */
const char* Ver = "2.3";

/* WiFi Settings */
const char* host     = "Vld-Sonoff14";
const char* ssid     = "IOT";
const char* password = "****some random PSK*****";

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

/* Sonoff Outputs */
const int relayPin1 = 12;  // Active high
const int relayPin2 = 5;  // Active high
const int relayPin3 = 4;  // Active high
const int relayPin4 = 15;  // Active high
const int ledPin   = 13;  // Active low
#define button1Pin          0
#define button2Pin          9
#define button3Pin          10
#define button4Pin          14

boolean button1State = false;
boolean button2State = false;
boolean button3State = false;
boolean button4State = false;

boolean oldbutton1State = false;
boolean oldButton2State = false;
boolean oldButton3State = false;
boolean oldButton4State = false;
int oldstate1 = LOW;   // the previous reading from the input pin
int oldstate2 = LOW;   // the previous reading from the input pin
int oldstate3 = LOW;   // the previous reading from the input pin
int oldstate4 = LOW;   // the previous reading from the input pin
int count = 0;
int reboot = 0;/* Settings */

#define CLIENT_ID "Sonoff14"         // Client ID to send to the broker 
#define CLIENT_USER "sonof"
#define CLIENT_PSWD "**************"
IPAddress broker(172,16,20,60);          // Address of the MQTT broker


/**
 * MQTT callback to process messages
 */
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();

      
String top = topic; 

if (top == "/vld/vld-sonoff14/relay1") {
    // Examine only the first character of the message
    if(payload[0] == 49)              // Message "1" in ASCII (turn outputs ON)
    {
      digitalWrite(relayPin1, HIGH);
    } else if(payload[0] == 48)       // Message "0" in ASCII (turn outputs OFF)
    {
      digitalWrite(relayPin1, LOW);
    } else {
      Serial.println("Unknown value");
    }
  }
  
if (top == "/vld/vld-sonoff14/relay2") {
    // Examine only the first character of the message
    if(payload[0] == 49)              // Message "1" in ASCII (turn outputs ON)
    {
      digitalWrite(relayPin2, HIGH);
    } else if(payload[0] == 48)       // Message "0" in ASCII (turn outputs OFF)
    {
      digitalWrite(relayPin2, LOW);
    } else {
      Serial.println("Unknown value");
    }
  }
  
if (top == "/vld/vld-sonoff14/relay3") {
    // Examine only the first character of the message
    if(payload[0] == 49)              // Message "1" in ASCII (turn outputs ON)
    {
      digitalWrite(relayPin3, HIGH);
    } else if(payload[0] == 48)       // Message "0" in ASCII (turn outputs OFF)
    {
      digitalWrite(relayPin3, LOW);
    } else {
      Serial.println("Unknown value");
    }
  }
  
if (top == "/vld/vld-sonoff14/relay4") {
    // Examine only the first character of the message
    if(payload[0] == 49)              // Message "1" in ASCII (turn outputs ON)
    {
      digitalWrite(relayPin4, HIGH);
    } else if(payload[0] == 48)       // Message "0" in ASCII (turn outputs OFF)
    {
      digitalWrite(relayPin4, LOW);
    } else {
      Serial.println("Unknown value");
    }
  }

  
if (top == "/vld/vld-sonoff14/led") {
    // Examine only the first character of the message
    if(payload[0] == 49)              // Message "1" in ASCII (turn led ON)
    {
     digitalWrite(ledPin, LOW);      // LED is active-low, so this turns it on
    } else if(payload[0] == 48)       // Message "0" in ASCII (turn led OFF)
    {
    digitalWrite(ledPin, HIGH);     // LED is active-low, so this turns it off     
    } else {
      Serial.println("Unknown value");
    }
  }
}

WiFiClient wificlient;
PubSubClient client(wificlient);

/**
 * Attempt connection to MQTT broker and subscribe to command topic
 */
void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(CLIENT_ID, CLIENT_USER, CLIENT_PSWD)) {
      Serial.println("connected");
      if (reboot == 0){
      client.publish("/vld/state/vld-sonoff14/status", "started"); 
      } else {
      client.publish("/vld/state/vld-sonoff14/status", "reconnected");   
      }
      reboot++;
      client.publish("/vld/state/vld-sonoff14/version", Ver, true); 
      client.subscribe("/vld/vld-sonoff14/#");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}

/**
 * Setup
 */
void setup() {
  Serial.begin(115200);
  Serial.println("Booting");
  WiFi.mode(WIFI_STA);
  WiFi.hostname(host);
  WiFi.begin(ssid, password);
  Serial.println("WiFi begun");
  while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Connection Failed! Rebooting...");
    delay(5000);
    ESP.restart();
  }
  Serial.println("Proceeding");
  MDNS.begin(host);
  httpUpdater.setup(&httpServer);
  httpServer.begin();
//  Serial.printf("HTTPUpdateServer ready! Open http://%s.local/update in your browser\n", host);
  MDNS.addService("http", "tcp", 80);
  
  /* Set up the outputs. LED is active-low */
  pinMode(ledPin, OUTPUT);
  pinMode(relayPin1, OUTPUT);
  pinMode(relayPin2, OUTPUT);
  pinMode(relayPin3, OUTPUT);
  pinMode(relayPin4, OUTPUT);
  digitalWrite(ledPin, HIGH);
  digitalWrite(relayPin1, LOW);
  digitalWrite(relayPin2, LOW);
  digitalWrite(relayPin3, LOW);
  digitalWrite(relayPin4, LOW);
  /* Prepare MQTT client */
  client.setServer(broker, 8883);
  client.setCallback(callback);
}

/**
 * Main
 */
void loop() {

  if (WiFi.status() != WL_CONNECTED)
  {
    Serial.print("Connecting to ");
    Serial.print(ssid);
    Serial.println("...");
    WiFi.begin(ssid, password);

    if (WiFi.waitForConnectResult() != WL_CONNECTED)
      return;
    Serial.println("WiFi connected");
  }

  if (WiFi.status() == WL_CONNECTED) {
    if (!client.connected()) {
      reconnect();
    }
  }
  
  if (client.connected())
  {
  httpServer.handleClient();

    client.loop();
/*    buttons are not working correctly on the 4ch model
 *     
 *     
    button1State = digitalRead(button1Pin);
    if (button1State == LOW){
        count = 0;
         while(digitalRead(button1Pin)==LOW){ 
           delay(10); 
           count++;
          if (count > 500){ //button held 
            digitalWrite(ledPin, LOW);      // LED is active-low, so this turns it on
            delay(500);
            digitalWrite(ledPin, HIGH);      // LED is active-low, so this turns it on
            delay(500);
            digitalWrite(ledPin, LOW);      // LED is active-low, so this turns it on
            delay(500);
            digitalWrite(ledPin, HIGH);      // LED is active-low, so this turns it on
            delay(500);
            digitalWrite(ledPin, LOW);      // LED is active-low, so this turns it on
            client.publish("/vld/state/vld-sonoff14/status", "Reset");
            delay(2000);
            digitalWrite(ledPin, HIGH);      // LED is active-low, so this turns it on
            ESP.reset();
            
            }
                      
          if (count > 100){ //button held 
            digitalWrite(ledPin, LOW);      // LED is active-low, so this turns it on
            client.publish("/vld/vld-sonoff14/button", "2"); //button held
            }
         

         }
           if (( count > 2) && ( count < 100)){ 
            if ( digitalRead(relayPin1) == LOW ) { 
                 digitalWrite(relayPin1, HIGH);
                } else {
                digitalWrite(relayPin1, LOW); 
                }
                client.publish("/vld/vld-sonoff14/button", "1"); //button short
            } 
      }
    }

    button2State = digitalRead(button2Pin);
    if (button2State == LOW){
      if ( digitalRead(relayPin2) == LOW ) { 
                 digitalWrite(relayPin2, HIGH);
                } else {
                digitalWrite(relayPin2, LOW); 
                }
                client.publish("/vld/vld-sonoff14/button2", "1"); //button short

     } 
     
    button3State = digitalRead(button3Pin);
    if (button3State == LOW){
      if ( digitalRead(relayPin3) == LOW ) { 
                 digitalWrite(relayPin3, HIGH);
                } else {
                digitalWrite(relayPin3, LOW); 
                }
                client.publish("/vld/vld-sonoff14/button3", "1"); //button short

     }

    button4State = digitalRead(button4Pin);
    if (button4State == LOW){
      if ( digitalRead(relayPin4) == LOW ) { 
                 digitalWrite(relayPin4, HIGH);
                } else {
                digitalWrite(relayPin4, LOW); 
                }
                client.publish("/vld/vld-sonoff14/button4", "1"); //button short
*/
     }

    
    if ( oldstate1 != digitalRead(relayPin1)) {
      oldstate1 = digitalRead(relayPin1);
      if ( digitalRead(relayPin1) == LOW ) {
        client.publish("/vld/vld-sonoff14S/relay1", "0", true);
      } else {
        client.publish("/vld/vld-sonoff14S/relay1", "1", true);
        
      }
    }
        if ( oldstate2 != digitalRead(relayPin2)) {
      oldstate2 = digitalRead(relayPin2);
      if ( digitalRead(relayPin2) == LOW ) {
        client.publish("/vld/vld-sonoff14S/relay2", "0", true);
      } else {
        client.publish("/vld/vld-sonoff14S/relay2", "1", true);
        
      }
    }
        if ( oldstate3 != digitalRead(relayPin3)) {
      oldstate3 = digitalRead(relayPin3);
      if ( digitalRead(relayPin3) == LOW ) {
        client.publish("/vld/vld-sonoff14S/relay3", "0", true);
      } else {
        client.publish("/vld/vld-sonoff14S/relay3", "1", true);
        
      }
    }
        if ( oldstate4 != digitalRead(relayPin4)) {
      oldstate4 = digitalRead(relayPin4);
      if ( digitalRead(relayPin4) == LOW ) {
        client.publish("/vld/vld-sonoff14S/relay4", "0", true);
      } else {
        client.publish("/vld/vld-sonoff14S/relay4", "1", true);
        
      }
    }

    
  }

#6

Mark,

That is interesting because if you look closely at the circuit, it has a 1 before the 4 although it is a different size, so maybe it is not meant to be there. If your software works that’s great.

David


#7

I have something stange. i have multiple types of sonoff devices and they are all working as expected.
when the Wifi connection is lost they reconnect as soon as posible and nobody ever know they had lost theiere signal.
But when the 4ch reconnect to the Wifi it is turning all the relay’s on.
Since the device almost on top of the accesspoint this only happened twice. but it is realy anoying.

I checked the code twice.
During setup it has

  /* Set up the outputs. LED is active LOW, Relay is active HIGH */
  pinMode(ledPin, OUTPUT);
  pinMode(relayPin1, OUTPUT);
  pinMode(relayPin2, OUTPUT);
  pinMode(relayPin3, OUTPUT);
  pinMode(relayPin4, OUTPUT);
  digitalWrite(ledPin, HIGH);
  digitalWrite(relayPin1, LOW);
  digitalWrite(relayPin2, LOW);
  digitalWrite(relayPin3, LOW);
  digitalWrite(relayPin4, LOW);

I can tell for sure that the LOW pin state is correct because the callback is working with

if (top == “/vld/vld-sonoff14/relay1”) {
// Examine only the first character of the message
if(payload[0] == 49) // Message “1” in ASCII (turn outputs ON)
{
digitalWrite(relayPin1, HIGH);
} else if(payload[0] == 48) // Message “0” in ASCII (turn outputs OFF)
{
digitalWrite(relayPin1, LOW);
} else {
Serial.println(“Unknown value”);
}
}

The full code (the same as above but after a litle bit of cleanup.

/*

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>
#include <WiFiUdp.h>
#include <PubSubClient.h>
#include <dummy.h>

/* current code version /
const char
Ver = “2.3.1”;

/* WiFi Settings /
const char
host = “Vld-Sonoff14”;
const char* ssid = “”;
const char* password = “”;

ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

/* Sonoff Outputs */
const int relayPin1 = 12; // Active high
const int relayPin2 = 5; // Active high
const int relayPin3 = 4; // Active high
const int relayPin4 = 15; // Active high
const int ledPin = 13; // Active low

int oldstate1 = LOW; // the previous reading from the input pin
int oldstate2 = LOW; // the previous reading from the input pin
int oldstate3 = LOW; // the previous reading from the input pin
int oldstate4 = LOW; // the previous reading from the input pin
int count = 0;
int reboot = 0;/* Settings */

#define CLIENT_ID “Sonoff14” // Client ID to send to the broker
#define CLIENT_USER “”
#define CLIENT_PSWD ""
IPAddress broker(172,16,0,1); // Address of the MQTT broker

/**

  • MQTT callback to process messages
    /
    void callback(char
    topic, byte* payload, unsigned int length) {
    Serial.print(“Message arrived [”);
    Serial.print(topic);
    Serial.print("] ");
    for (int i=0;i<length;i++) {
    Serial.print((char)payload[i]);
    }
    Serial.println();

String top = topic;

if (top == “/vld/vld-sonoff14/relay1”) {
// Examine only the first character of the message
if(payload[0] == 49) // Message “1” in ASCII (turn outputs ON)
{
digitalWrite(relayPin1, HIGH);
} else if(payload[0] == 48) // Message “0” in ASCII (turn outputs OFF)
{
digitalWrite(relayPin1, LOW);
} else {
Serial.println(“Unknown value”);
}
}

if (top == “/vld/vld-sonoff14/relay2”) {
// Examine only the first character of the message
if(payload[0] == 49) // Message “1” in ASCII (turn outputs ON)
{
digitalWrite(relayPin2, HIGH);
} else if(payload[0] == 48) // Message “0” in ASCII (turn outputs OFF)
{
digitalWrite(relayPin2, LOW);
} else {
Serial.println(“Unknown value”);
}
}

if (top == “/vld/vld-sonoff14/relay3”) {
// Examine only the first character of the message
if(payload[0] == 49) // Message “1” in ASCII (turn outputs ON)
{
digitalWrite(relayPin3, HIGH);
} else if(payload[0] == 48) // Message “0” in ASCII (turn outputs OFF)
{
digitalWrite(relayPin3, LOW);
} else {
Serial.println(“Unknown value”);
}
}

if (top == “/vld/vld-sonoff14/relay4”) {
// Examine only the first character of the message
if(payload[0] == 49) // Message “1” in ASCII (turn outputs ON)
{
digitalWrite(relayPin4, HIGH);
} else if(payload[0] == 48) // Message “0” in ASCII (turn outputs OFF)
{
digitalWrite(relayPin4, LOW);
} else {
Serial.println(“Unknown value”);
}
}

if (top == “/vld/vld-sonoff14/led”) {
// Examine only the first character of the message
if(payload[0] == 49) // Message “1” in ASCII (turn led ON)
{
digitalWrite(ledPin, LOW); // LED is active-low, so this turns it on
} else if(payload[0] == 48) // Message “0” in ASCII (turn led OFF)
{
digitalWrite(ledPin, HIGH); // LED is active-low, so this turns it off
} else {
Serial.println(“Unknown value”);
}
}
}

WiFiClient wificlient;
PubSubClient client(wificlient);

/**

  • Attempt connection to MQTT broker and subscribe to command topic
    */
    void reconnect() {
    // Loop until we’re reconnected
    while (!client.connected()) {
    Serial.print(“Attempting MQTT connection…”);
    // Attempt to connect
    if (client.connect(CLIENT_ID, CLIENT_USER, CLIENT_PSWD)) {
    Serial.println(“connected”);
    if (reboot == 0){
    client.publish("/vld/state/vld-sonoff14/status", “started”);
    } else {
    client.publish("/vld/state/vld-sonoff14/status", “reconnected”);
    }
    reboot++;
    client.publish("/vld/state/vld-sonoff14/version", Ver, true);
    client.subscribe("/vld/vld-sonoff14/#");
    } else {
    Serial.print(“failed, rc=”);
    Serial.print(client.state());
    Serial.println(" try again in 5 seconds");
    // Wait 5 seconds before retrying
    delay(5000);
    }
    }
    }

/**

  • Setup
    */
    void setup() {
    Serial.begin(115200);
    Serial.println(“Booting”);
    WiFi.mode(WIFI_STA);
    WiFi.hostname(host);
    WiFi.begin(ssid, password);
    Serial.println(“WiFi begun”);
    while (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println(“Connection Failed! Rebooting…”);
    delay(5000);
    ESP.restart();
    }
    Serial.println(“Proceeding”);
    MDNS.begin(host);
    httpUpdater.setup(&httpServer);
    httpServer.begin();
    // Serial.printf(“HTTPUpdateServer ready! Open http://%s.local/update in your browser\n”, host);
    MDNS.addService(“http”, “tcp”, 80);

/* Set up the outputs. LED is active LOW, Relay is active High /
pinMode(ledPin, OUTPUT);
pinMode(relayPin1, OUTPUT);
pinMode(relayPin2, OUTPUT);
pinMode(relayPin3, OUTPUT);
pinMode(relayPin4, OUTPUT);
digitalWrite(ledPin, HIGH);
digitalWrite(relayPin1, LOW);
digitalWrite(relayPin2, LOW);
digitalWrite(relayPin3, LOW);
digitalWrite(relayPin4, LOW);
/
Prepare MQTT client */
client.setServer(broker, 8883);
client.setCallback(callback);
}

/**

  • Main
    */
    void loop() {

if (WiFi.status() != WL_CONNECTED)
{
Serial.print(“Connecting to “);
Serial.print(ssid);
Serial.println(”…”);
WiFi.begin(ssid, password);

if (WiFi.waitForConnectResult() != WL_CONNECTED)
  return;
Serial.println("WiFi connected");

}

if (WiFi.status() == WL_CONNECTED) {
if (!client.connected()) {
reconnect();
}
}

if (client.connected())
{
httpServer.handleClient();

client.loop();

 }


if ( oldstate1 != digitalRead(relayPin1)) {
  oldstate1 = digitalRead(relayPin1);
  if ( digitalRead(relayPin1) == LOW ) {
    client.publish("/vld/vld-sonoff14S/relay1", "0", true);
  } else {
    client.publish("/vld/vld-sonoff14S/relay1", "1", true);
    
  }
}
    if ( oldstate2 != digitalRead(relayPin2)) {
  oldstate2 = digitalRead(relayPin2);
  if ( digitalRead(relayPin2) == LOW ) {
    client.publish("/vld/vld-sonoff14S/relay2", "0", true);
  } else {
    client.publish("/vld/vld-sonoff14S/relay2", "1", true);
    
  }
}
    if ( oldstate3 != digitalRead(relayPin3)) {
  oldstate3 = digitalRead(relayPin3);
  if ( digitalRead(relayPin3) == LOW ) {
    client.publish("/vld/vld-sonoff14S/relay3", "0", true);
  } else {
    client.publish("/vld/vld-sonoff14S/relay3", "1", true);
    
  }
}
    if ( oldstate4 != digitalRead(relayPin4)) {
  oldstate4 = digitalRead(relayPin4);
  if ( digitalRead(relayPin4) == LOW ) {
    client.publish("/vld/vld-sonoff14S/relay4", "0", true);
  } else {
    client.publish("/vld/vld-sonoff14S/relay4", "1", true);
    
  }
}

}


#8

Mark,
Your software looks ok (without detailed examination) one thing you may want to check is whether MQTT is sending messages to the 4 channel device as soon as it logs in. This could be a last will message or a current retained status if my memory is correct.
David