Light Switch Controller

Hello,

Im looking to use a Wemos D1 Mini as a light switch controller - I have wired all my lights to a central point in the house where i have multiple Sonoff 4 Chan’s to turn the light on / off.

I have also wired a ethernet cable to each light switch and Im connecting the other end to the ground and one of the digital pins on the D1 Mini.

The idea being that when a light switch is toggled, the D1 Mini will send an MQTT message saying what switch was toggled and Home assistant will turn on / off the required light (as programmed).

Below is my code however it doesn’t seem to be working too well - it will send commands for the D1 input but that seems to be all.

#include <ESP8266WiFi.h>
#include <MQTTClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ESP8266HTTPUpdateServer.h>


/* ---------- DO NOT EDIT ANYTHING ABOVE THIS LINE ---------- */

//Only edit the settings in this section

/* WIFI Settings */
// Name of wifi network
const char* ssid = "Wifi Network";

// Password to wifi network
const char* password = "Wifi Password"; 

/* Web Updater Settings */
// Host Name of Device
const char* hostName = "Wemos-Loft-Switch_Control";

// Path to access firmware update page (Not Neccessary to change)
const char* update_path = "/firmware";

// Username to access the web update page
const char* update_username = "admin";

// Password to access the web update page
const char* update_password = "Admin Password";

/* MQTT Settings */
// Topic which sends commands
char* mqttTopic = "Loft/Switch_Control";

//MQTT Server IP Address
const char* server = "MQTT Host";

/* ---------- DO NOT EDIT ANYTHING BELOW THIS LINE ---------- */
int ledPin = 2; //D4

static byte lastSwitchState[8] = {1, 1, 1, 1, 1, 1, 1, 1}; //1 = Switch Unpressed, 0 = Switch Pressed
static byte SwitchArray[8] = {5, 4, 0, 16, 14, 12, 13, 15}; //D1, D2, D3, D0, D5, D6, D7, D8
static byte SwitchNumber[8] = {1, 2, 3, 4, 5, 6, 7, 8}; //Sequential Numbers

byte numberOfSwitches = 8;
byte lastSwitchPressed = 0;
long lastActivityTime = 0;

#define DEBOUNCE_DELAY 100

//webserver 
ESP8266WebServer httpServer(80);
ESP8266HTTPUpdateServer httpUpdater;

//MQTT
WiFiClient net;
MQTTClient client;

unsigned long lastMillis = 0;

//Connect to WiFI and MQTT
void connect();

//Setup pins, wifi, webserver and MQTT
void setup() 
{
  Serial.begin(115200);
  delay(100);
  //Set up Switches
  Serial.println("Setting input pull-ups");
  for( byte i = 0; i < numberOfSwitches; i++){
    pinMode(SwitchArray[i], INPUT_PULLUP);
    Serial.println("");
    Serial.print(SwitchArray[i]);
    
  }
  
  
  WiFi.mode(WIFI_STA);
  WiFi.hostname(hostName);
  WiFi.begin(ssid, password);
  client.begin(server, net);

  connect();

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP Address: ");
  Serial.println(WiFi.localIP());

  MDNS.begin(hostName);

  httpUpdater.setup(&httpServer, update_path, update_username, update_password);
  httpServer.begin();

  MDNS.addService("http", "tcp", 80);
  pinMode(ledPin, OUTPUT); // Set the LED Pin as an output
}

//Connect to wifi and MQTT
void connect() 
{
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(1000);
  }

  while (!client.connect(hostName))
  {
    delay(1000);
  }
}

void loop() 
{
  // MQTT Loop
  client.loop();

  // Make sure device is connected
  if(!client.connected()) 
  {
    connect();
  }

  httpServer.handleClient();

  byte i;
  for(i = 0; i <numberOfSwitches; i++) {
    processSwitchDigital(SwitchArray[i]);
  }
}

void processSwitchDigital(byte SwitchId)
{
    int sensorReading = digitalRead(SwitchArray[SwitchId]);

      //Serial.println( "Switch pressed" );
      if(lastSwitchState[SwitchId] != sensorReading)   // The Switch was previously un-pressed
      {
        if((millis() - lastActivityTime) > DEBOUNCE_DELAY)  // Proceed if we haven't seen a recent event on this Switch
        {
          lastActivityTime = millis();
    
          lastSwitchPressed = SwitchId;
          Serial.print("Switch ID: ");
          Serial.println(SwitchId);
          Serial.print("Switch Number: ");
          Serial.println(SwitchNumber[SwitchId]);
          
          client.publish(mqttTopic, String(SwitchNumber[SwitchId]).c_str());
          digitalWrite(ledPin,LOW);
          delay(50);
          digitalWrite(ledPin, HIGH);
          delay(50);
        }
            if(sensorReading == 0)  // Input pulled low to GND. Switch pressed.
            {
              lastSwitchState[SwitchId] = 0;
            }
            else 
            {
              lastSwitchState[SwitchId] = 1;
            }
      }

}

I have also looked into using Tasmota however there is only 4 Switch components that are available and i couldnt get the topic to change based on what switch had been toggled.

Any help would be appreciated!

Cheers all!

I use integer variables for the inputs, that have been set up by the arduino environment (I think) depending on the hardware. For instance

const int LIGHT_LEVEL_PIN = D8;

So I think you can do this to avoid the potential of having the wrong numbers here

    static byte SwitchArray[8] = {D1, D2, D3, D0, D5, D6, D7, D8}; 

Edit:
Actually the pin numbers are set in
./hardware/esp8266com/esp8266/variants/d1/pins_arduino.h which I think gets included as part of esp8266WiFi.h

Hi there, I have added this in and some of the inputs work and map to the GPIO number just fine. however not all the pins seem to register a signal.

If some of the pins work, it means the software is OK, you just need to check the hardware. :smiley:

The first thing to check is that you have selected the correct board type - it doesn’t change with the project in the arduino IDE and a I can’t tell you how many times I have ended up compiling for the wrong board. I believe it does make a difference which type of ESP8266 board you select as well, as the mapping is different for each.

After that, check your wiring and try a different board - they are so inexpensive I’m not sure what the quality control will be like on them.

Thanks, I have tried multiple of the same board and a different MCU board but doesn’t seem to work any differently.

I’m going to try the HomeESP setup to see if that works any better. (Seems a lot simpler if nothing else)

Hello, It’s been awhile since the last update. I used HomeESP to create my light switch control and it work’s well with the all the inputs triggering a different MQTT message which i then tie to an automation in Home Assistant.

However, it seems that periodically the esp8266 will reboot causing MQTT messages to be sent. This means that all the lights linked to that controller then get turned on. As you can imagine this is annoying, especially late at night. Any more support on this one?

I am currently writing an MQTT FAQ, and this is what I have for this problem. Please let me know if it helps.

{% linkable_title Switch turns on/off when it connects %}

When I connect my light/switch it immediately turns on/off

This is normally because the command topic with a payload of ON has been sent with a retain flag true. This causes the MQTT broker to store the message in its database.

Whenever a client connects and subscribes to that topic, such as when the tasmota becomes available, the broker sends the retained message, so your lamp turns on.

To clear the message from the mosquitto database, you need to send a message with that topic and a null payload and the retain flag set.

You can do this with mosquitto_pub using

$ mosquitto_pub -t 'cmnd/sonoff-bn-z1-01/POWER1' -n -r

You may also need parameters for the host (-h), password (-P) and user (-u), depending on your setup.

Hey, there are a ton of MQTT FAQ & KBA’s out there.

I don’t believe it is a retain issue, but I will check that!