How to detect if a 3 way AC switch has been turned on or off

I have one 3-way switch in my garage with the traveler wires going to a 3v relay which is controlled by an ESP8266.

It works but I don't know how to detect if someone turns on or off the garage lights with the manual 3-way wall switch.

How do I detect if the AC 120v wall switch has been turned on or off, so I can update the light status in my web server, and UPDATE my On or Off-web server button text to the proper text that the lights are ON or the lights are OFF, when the manual switch is turned on or off

Thanks for any help

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>

// Network configuration
const char* ssid = "MY_SSID";
const char* password = "XXXXXXXX";
IPAddress staticIP(10, 0, XX, XX); // Static IP address
IPAddress gateway(10, 0, XX, X); // Gateway IP address
IPAddress subnet(255, 255, 255, 0); // Subnet mask
IPAddress primaryDNS(8, 8, 8, 8); // Primary DNS
IPAddress secondaryDNS(8, 8, 4, 4); // Secondary DNS

ESP8266WebServer server(80);

// GPIO pin assignments
#define RELAY_PIN 5 // GPIO pin for relay control
#define SWITCH_PIN 4 // GPIO pin for SPDT switch input

// State machine variables
enum LightState { LIGHT_OFF, LIGHT_ON };
LightState lightState = LIGHT_OFF;
bool lastSwitchState = LOW;

// Timing variables
unsigned long previousMillis = 0;
const long interval = 50; // Debounce interval in milliseconds

void setup() {
  // Initialize serial communication
  Serial.begin(9600);

  // Set GPIO pin modes
  pinMode(RELAY_PIN, OUTPUT);
  pinMode(SWITCH_PIN, INPUT);

  // Start with the light off
  digitalWrite(RELAY_PIN, LOW);

  // Connect to Wi-Fi
  WiFi.config(staticIP, gateway, subnet, primaryDNS, secondaryDNS);
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println(WiFi.localIP());

  // Set up web server routes
  server.on("/", handleRoot);
  server.on("/toggle", handleToggle); // Route to handle button click
  server.onNotFound(handleNotFound);

  // Start the server
  server.begin();
  Serial.println("HTTP server started.");
}

void loop() {
  server.handleClient();
  checkSwitchState();
}

void checkSwitchState() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    bool switchState = digitalRead(SWITCH_PIN);

    if (switchState != lastSwitchState) {
      lastSwitchState = switchState;

      if (switchState == HIGH) {
        lightState = (lightState == LIGHT_OFF) ? LIGHT_ON : LIGHT_OFF;
        digitalWrite(RELAY_PIN, (lightState == LIGHT_ON) ? HIGH : LOW);
      }
    }
  }
}

void handleRoot() {
  String page = "<html><body>";
  page += "<h1 style='text-align:center;'>John's LED Garage Lights Controller</h1>";

  if (lightState == LIGHT_ON) {
    //page += "<p style='text-align:center;'><a href=\"/toggle\"><button style='background-color:green;color:white;font-size:20px;width:200px;height:50px;'>The Garage Lights Are ON</button></a></p>";
    page += "<p style='text-align:center;'><a href=\"/toggle\"><button style='background-color:green;color:white;font-size:20px;width:275px;height:50px;'>The Garage Lights Are ON</button></a></p>";
  } else {
    //page += "<p style='text-align:center;'><a href=\"/toggle\"><button style='background-color:red;color:white;font-size:20px;width:200px;height:50px;'>The Garage Lights Are OFF</button></a></p>";
    page += "<p style='text-align:center;'><a href=\"/toggle\"><button style='background-color:red;color:white;font-size:20px;width:275px;height:50px;'>The Garage Lights Are OFF</button></a></p>";
  }

  page += "</body></html>";

  server.send(200, "text/html", page);
}

void handleToggle() {
  // Toggle the light state
  lightState = (lightState == LIGHT_OFF) ? LIGHT_ON : LIGHT_OFF;
  digitalWrite(RELAY_PIN, (lightState == LIGHT_ON) ? HIGH : LOW);

  // Redirect back to the main page
  server.sendHeader("Location", "/");
  server.send(303);
}

void handleNotFound() {
  server.send(404, "text/plain", "404: Not found");
}

Can you detect the current or lack of it going to the light or the voltage across it ?

1 Like

A current transformer (CT) can be used to noninvasively detect AC current flow in the supply lead.

1 Like

probably the cheapest way is to connect an old wall mobile charger to the same line and control it's output by the arduino

2 Likes

How would I detect 120v on the traveler wires?

Detecting voltage on the traveller wires will not tell you anything because the voltage will be in either one wire or the other. You need to detect voltage at the load end of the three-way switches, or current flow upstream from there.

2 Likes

If you do parallel switching, you never know what's going on. So rewire that manual switch to your esp .
In many cases smart switch like Shelly Plus could be ideal solution.

1 Like

So if I use a volage sensor that can handle 120v I connect it to the load and if the signal is high I know the light is on

Not if the lamp is burned out, loose in the socket, etc.

The current draw gives reliable evidence that the light is actually on.

1 Like

I am going to test on a 12v system with a SPDT switch, "to emulate a 3-way switch", a 12v voltage sensor, and the relay, what if I monitor the relay state and the voltage sensor and determine that way how to update the web server buttons color, and text, to state if the light is on or off, and change the web server accordingly. What do you think?

It is not very clear to me what your mains circuit looks like. Do you mean a 3 way lighting circuit, that is one controlled by 3 switches like this:

image
(picture from https://www.youtube.com/watch?v=fZFHrwDMSZk )

Or this:
image

My 12v switch has 6 pins, 2 common in the middle and 2 pins for each switch throw position

1 Like

Ok I set up a 12v test, which I will eventually go back to my to 120 AC, using SPDT Manual Switch, an ESP8266 E12 which takes 3.3v on the GPIO A0 pin, a 12v to 3.3v optocoupler, and a relay powering a 12v LED light with the signal wire on GPIO 5 "D1".

My SPDT switch has a single line "dash" on the top part of the switch a double line "two dashes" on the bottom of the switch and a circle in the middle

So the logic will be, with the SPDT switch in the down position "single dash pressed down" the voltage is on we send a "LOW Signal" light on

if the SPDT switch is pressed down on the double line side "HIGH Signal" Light is off

The web server buttons and colors and text should be when LOW, button color green, text "light is on"

The web server buttons and colors and text should be when HIGH, button color red, text "light is off"

Now the tricky part

Switch Position down on the single line "dash" side 12v on the "NC" relay terminal

if the relay is OFF the light is ON the web server button is green text, the light is on "relay Armature is connected to "NC"

if the relay is ON the light is OFF the web server button is red, text, the light is off, "relay Armature is connected to "NO"

Switch position press down on the double line side zero volts on the NC terminal The other SPDT switch terminal is on the relay "NO" terminal

If the relay is "off" the light is off, update the web server button is red, button text, the light is off , "the relay Armature is connected to "NC"

If the relay is "ON" the light is on, update the web server button is green, button text, the light is on, "the relay Armature is connected to "NO"

the testing script, it works, reverse logic HIGH is OFF, LOW is ON

// Define the analog pin connected to the voltage sensor
const int sensorPin = A0;

// Define the threshold for detecting the load state
const int threshold = 512; // Adjust this value based on your specific setup

void setup() {
  // Initialize the serial communication
  Serial.begin(9600);
}

void loop() {
  // Read the raw sensor value from the A0 pin
  int sensorValue = analogRead(sensorPin);

  // Print the raw sensor value for debugging
  Serial.print("Raw Sensor Value: ");
  Serial.println(sensorValue);

  // Determine the load state based on the sensor value
  if (sensorValue < threshold) {
    // Load is ON (signal is low) //signal reads 39 Light On
    Serial.println("Light is ON");
  } else {
    // Load is OFF (signal is high) //signal reads 1024 Light OFF
    Serial.println("Light is OFF");
  }

  // Add a delay to reduce the frequency of readings
  delay(500); // Adjust this value based on how often you want to check the load state
}

use a sonoff R1 mini

Too easy LOL