Control Rocker Switch with Arduino

Hi all,

I've got a roller door on the outside of a sliding door on my house, and a rocker switch on the inside, which is simply plugged into a socket in the wall. Press (and hold) up, the door goes up, press down the door goes down.

I've written some code on my arduino that has a rain sensor attached, so that it can detect if it is raining or not (senor is threaded through to the outside of the house). When it is raining, I want to "press down" to close the door, and "press up" when the sensor no longer thinks it is raining.

While doing the rain sensing part, I've just had the arduino connected to my computer (and powered) via usb. But I've no idea how to get the arduino to control the rocker switch (I simply need to mimic holding the switch up or down for 20 seconds to fully open or close the door, no inbetween state is required). The roller door is running off a 13amp fuse, so I suspect they'll need to be powered separately.

What equipment would I need to achieve part two of controlling the rocker switch, and what pitfalls do I need to be careful of? I don't mind completely replacing the rocker switch, or keeping it as a manual back-up override.

Thanks,
Stephen

sounds like you require a relay to replicate the pressing of the switch
as you are probably switching main voltages make sure the relay is opto isolated and switch off the mains supply to the garage door while working on it

  • Squashing the baby, cat, dog (is there an electric eye across the opening that stops the door from moving if there is an obstruction... like a garage door)

  • Cycling up and down when sensor thinks dry - wet - dry - wet (could use hysteresis to prevent)

  • What happens if the Arduino holds the switch down too long (does the system have limit switches built in?)

1 Like

Thanks for the reply. Do I need a two relay board, one each for up and down? Something like this:

I briefly opened up the rocker switch to have a look at it (after plugging it out), but it's not completely obvious what's happening inside without taking it apart further, which I may do at some point soon.

Seems like I will also need a separate 5v power supply so that I can properly isolate the power/voltage required for the roller door from the arduino.

Thanks for throwing out a few things to think about.

  1. Fortunately it's not really a place where anyone will be, and we've no pets or kids running around randomly. But something to think about for the future.

  2. I have written code that should be able to handle that ok, but it will need some proper field testing!

  3. If I manually hold down the up or down switch too long it doesn't seem to cause a problem for the roller door, so I assume it should be the same when I hook up the arduino via relays. But again something to think about when I get that far in field testing.

I would expect so- the rocker is probably a two way switch supplying power to up and down circuits depending on position

So your roller door provably has limit switches built-in. The problem comes when they fail (don't detect that the roller door is fully up or down. If that happens while you are around, you will probably hear funny noises or see smoke coming out of the roller door. If you're not around, ...

I would assume the time taken for the door to open/close would be programmed into the control algorithm - once the time limit is reached the relay would be deactivated

You can program for multiple safety features. Another might be to determine if the door begins moving or is stuck either open or closed.

Break your project down into little chunks as simple as you can and then imagine a way to simulate on the breadboard.

1)detect rain
2)open/close door
3)safety features
etc

You have coded for detecting rain and presumably you have this working and returning some sort of value. You might post your code and outputs. I would want this to be nicely encapsulated into a function that you can call at intervals that seem sensible to you. I would also want the output to be nice and stable ie if 1 = rain and 0 = no rain i would not want to see 0000101010111010111111 at the serial monitor. I would want to see 000011111 to show a clear switch from one state to another or, even better just 01 where the state only changes and gets displayed when there is a switch from dry to rain with hysteresis built in. (sorry if that is explained badly). This can all be done on a breadboard with a mister for rain. The result is you should have a clear output for when rain is detected and no 'bouncing' .

Step 2 can be simulated on the breadboard with leds in place of the relays. eg a green led for open door, red for close. do one at a time. You need to take the return value of your rain sensor function and use it to change the state (state machines are a useful tool) of your door. State could be open, closing, closed and opening. So, if state is open (because it has been dry) and the rainDetectFunction returns raining then set redLed on for whatever time is needed and then change state to closed. etc

For safety you can set a maximum time for any LED to be on and you can change state if eg a limit switch is reached or create a new state eg obstruction if an obstruction is detected by a beam etc.

When coding use the 'save as' button to create new iterative files with sensible names so any bugs are contained. When one step is complete think about how to encapsulate the code and make it clearer. Always describe your code with comments and use descriptive variable names. You should be able to make all this on a breadboard and get most problems worked out before going to the real thing.
When adding relays think about flyback, current limitations of the uC (microcontroller), and the more serious nature of AC power. The relay should be able to slot in where the led is but it will need to have electronics on it to make sure the coil is not being powered by the uC

Thanks for all the information.

I'll post some code when I update it further to use two leds to simulate the relays. The rain sensor I received was damaged, so am waiting for another to arrive in the post to test further.

As for the relays, I'm still unsure of what exactly I need. Are 5V relays correct, or do I need something higher, like 12 or 24v? The roller door is plugged into a normal UK socket, 220-240v, and has a 13 amp fuse in it. Do I need to ask the company who installed the door what the power rating of the motor is?

I plan to control the relays with a separate power supply. So I assume that the power supply I buy will need to match the voltage on the relays.

Also, the rocker switch simply has a live and neutral wired into and out of it, so I'm not sure how it is telling the motor to go up or down when the circuit is completed when a direction is pressed. I guess it is a D motor, and the switch is able to reverse the current. I've a small dc motor and some switches, so I'm going to emulate that to make sure I understand what is happening.

Cheers,
Stephen

You need to do a bit more research before playing with AC. You should be able to do safe diagnostics with a good multimeter but do research first.

As for relays the break into 2 main parts

  1. logic side
  2. switched side (the side your load is on)

On the logic side there is a coil. There will be information on the datasheet that says what voltage and current is required to power the coil. You should, with any component, look at the datasheet to see what its basic requirements are. You should also be aware of the basic effects of a coil to any circuit. When the magnetic field collapses, ie you turn the relay off (de energise the coil) the energy can be dumped into the logic side circuit and will damage components that can not handle it. When trying to connect a uC to a relay you need to make sure that there is stuff in between to match their requirements.

Many people just stick a wire from the pin of the uC direct to a relay coil side and don’t understand why it doesn’t work or it releases magic smoke. The coil has input requirements in terms of voltage and current and the uC has output limits generally below these. Some relays come with appropriate components and circuitry to allow an external (to the uC) power supply and to isolate the uC from the relay coil thus avoiding any spikes from frying it when the coil electric field collapses.

Terms to think about
Optoisolation
Fly back diodes
Logic level

I'm confused about the coil you mention on the logic side. Is that simply another term for relay? Or the coil inside the relay?

I don't plan to connect the arduino directly the the motor to be controlled, but rather have a separate power supply for the relays and motor, which will be controller by two cables from digital pins on the arduino to in pins on the relay. I know there's a lot of bad practices in the following video, but this is the direction I'm looking at:

He's obviously only got one relay to control, while I'm trying to control two (I assume I need two anyway, one to control each direction, although if I can just use one and reverse the flow of current that would be handy).

From what I can tell, I'll have three things plugged in:

  1. The original motor, as it is now
  2. The relay power supply which interrupts the power to the motor
  3. The mC itself with the sensor to read the rain and logic to decide to tell the relays to activate

What you show there is a relay ā€œmoduleā€ which is a relay with other electronics all on a circuit board. The relay itself is a simple device which contains a coil on one side and a switch on the other. Powering the coil creates a magnetic field which is strong enough to close the switch. You should understand the basics to know why the other components are required on the module.

The switch side should be isolated electrically from the coil side. The coil side is on the logic side of the circuit (where the uC lives). The switch side is on the load side of the circuit. A relay essentially allows a switch, that you might normally control manually (such as a household light switch), to be controlled by your uC.

Ok, so I've written more code than I hoped to, as the problem is a litle trickier that anticipated. I've tried to comment as best a possible, but I'll surmise here in bullet points:

  • Take a reading every 5 seconds
  • Keep a running average of 10 readings
  • Keep a trend on whether it is getting dryer or wetter
  • If either trend is strong, change state if needed
  • Only change, though if there has been a reasonable shift in the trend (+/- 10)
  • If there is a large downwards shift (indicating a heavy shower), close asap
  • If the average is high, keep open, or low, keep closed. This is to avoid a scenario where, for example is it still raining, but not as bad as before. I want to keep the door closed even if the sensor is getting a little dryer.

To be frank, I'm not too happy with it, but my limited testing has worked well enough, but not perfectly. Sometimes the door opens or closes when it probably shouldn't. I'm not sure of a good way to get a lot of good test data so I could simulate how it is working.

Any feedback appreciated, thanks,
Stephen

/*
Take 10 readings and use the average to work out a trend of whether it is getting wetter or dryer.
Open (if the trend is dryer and door is closed) or close (if the trend is wetter and the door is open) the door.
If there is a big drop in the reading, close the door immediately

Only actually change state if there is a decent movement on the trend from the time period after the last state
change (up or down 10 points)

TODO - There could be a dryer trend, but it could still be raining... maybe just set a threshold to just keep the
door closed, but that threshold will need to be worked out with some real world testing.
Replace LEDs with relays to control the DC motor.
*/
#define sensorPower 7
#define sensorAnalogPin A0
int greenLED = 3;
int redLED = 4;
int buzzer = 9;

const String OPEN = "OPEN";
const String CLOSED = "CLOSED";

int analogAverage[10];  // Store the most recent 10 readings
int analogTrend[10];  // is it getting wetter (+1) or dryer (-1)
int arrayCount = 0;  // use this to loop over the two arrays above
int lastTrendValue = 0;
int previousValue;
int lastStateChangeValue;

String state = OPEN;

void setup() {
  pinMode(sensorPower, OUTPUT);
  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(buzzer, OUTPUT);
	// Initially keep the sensor OFF
	digitalWrite(sensorPower, LOW);
	Serial.begin(9600);

  resetArrays();
}

void loop() {
  analogAverage[arrayCount] = readSensor();  // read sensor value into array

  int valAverage = arrayAverage(analogAverage, 10);

  boolean shouldOpen = shouldDoorOpenOrClose(valAverage);
  // increment, and if needed reset, the array counter
  incrementAndResetArrayCount();

  controlShutterDoor(shouldOpen, valAverage);

	delay(5000);	// Take a reading every 5 seconds
}

void resetArrays() {
  // fill average array with current reading over 5 seconds
  for (int i = 0; i < 10; i++) {
    analogAverage[i] = readSensor();
    analogTrend[i] = 0;  // reset trend to 0
    delay(500);
  }

  previousValue = arrayAverage(analogAverage, 10);
  lastStateChangeValue = previousValue;
}

/**
return false to close the door, true to open. 
*/
bool shouldDoorOpenOrClose(int valAverage) {
  // is the value on an upwards or downwards trend?
  Serial.print("valAverage = ");
  Serial.print(valAverage);
  Serial.print(", previousValue = ");
  Serial.println(previousValue);
  if (valAverage > previousValue) {  // getting dryer
    analogTrend[arrayCount] = -1;
    lastTrendValue = -1;
  } else if (valAverage < previousValue) {  // getting wetter
    analogTrend[arrayCount] = 1;
    lastTrendValue = 1;
  } else {
    analogTrend[arrayCount] = lastTrendValue;  // set this so that we don't hold on to previous trend value
  }

  arrayAverage(analogTrend, 10);  // just calling this for now to print out for debugging info

  // short cut out on very wet or very dry
  if (valAverage > 850) {
    // almost certainly quite dry even if the sensor is getting a bit moister
    shouldOpen = true;
  } else (valAverage < 450) {
    // almost certainly quite wet even if the sensor is getting a bit dryer
    shouldOpen = false;
  }

  // count the trend array to see if it is mostly getting wetter or dryer
  int dryerCount = 0;
  int wetterCount = 0;
  for (int i = 0; i < 10; i++) {
    if (analogTrend[i] == -1) {
      dryerCount++;
    } else if (analogTrend[i] == 1) {
      wetterCount++;
    }
  }

  int delta = previousValue - valAverage;
  previousValue = valAverage;
  if (delta > 20) { // significant drop, close door asap
    return false;
  }

  if (dryerCount > 6) {
    return true;
  } else if (wetterCount > 6) {
    return false;
  }
}

void controlShutterDoor(bool shouldOpen, int valAverage) {
  int delta = abs(valAverage - lastStateChangeValue);  // only if long term trend moves the average up or down by 10 should we open/close the door
  Serial.println();
  Serial.print("Last state change value = ");
  Serial.println(lastStateChangeValue);
	if (shouldOpen) {
		Serial.println("Status: Clear");
    if (CLOSED == state) {
      if (delta > 10) {
        // open the door, takes about 20 seconds, lots of extra space to make this easier to see in serial monitor
        Serial.println("                                                                                          Opening the door, green light");
        flashAndSoundBuzzer(greenLED, 100);
        state = OPEN;
        onStateChange(5000);  // change to 5 minutes IRL on opening
      }
    } 
	} else { // should close
		Serial.println("Status: It's raining");
    if (OPEN == state) {
      if (delta > 10) {
        // close the door, takes about 20 seconds
        Serial.println("                                                                                          Closing the door, red light");
        flashAndSoundBuzzer(redLED, 100);
        state = CLOSED;
        onStateChange(5000); // change to 30 minutes IRL on closing
      }
    }
	}
}

void onStateChange(int delayMS) {
  delay(delayMS);
  resetArrays();
}

int readSensor() {
  //get the reading from the sensor and print it
  digitalWrite(sensorPower, HIGH);	// Turn the sensor ON
	delay(20);							// Allow power to settle
  int valAnalog = analogRead(sensorAnalogPin);
	digitalWrite(sensorPower, LOW);		// Turn the sensor OFF
	Serial.print("Analog Output: ");
	Serial.println(valAnalog);
  return valAnalog;
}

int arrayAverage(int values[], int size) {
  int total = 0;
  for ( int k = 0 ; k < size ; ++k ) {
    total += values[k];
    Serial.print(values[k]);
    Serial.print(", ");
  }
  return total / size;
}

void incrementAndResetArrayCount() {
  arrayCount++;
  if (arrayCount > 9) {
    arrayCount = 0;
  }
}

void flashAndSoundBuzzer(int ledPin, int buzzFrequency) {
  for (int i = 0; i < 10; i++) {
    digitalWrite(ledPin, HIGH);
    tone(buzzer, buzzFrequency, 500);
    delay(500);
    digitalWrite(ledPin, LOW);
    delay(500);
  }        
}

To me this is way over complicated to troubleshoot effectively because you are doing your whole project at once.

The final project has a number of steps it needs to achieve the effect you want but they don’t all have to be jumbled together from the start.

I would isolate the rain detection and do that entirely separately in a new sketch until I had a function that would reliably indicate if it was raining or not. You only need it to return one of two values, raining or not raining. It may, however, need to determine things such as is the rain starting or stopping or has it stopped completely for long enough to justify returning a ā€˜not raining’ value etc. You should test and troubleshoot this on its own and when working encapsulate it so that it can be imported into your final project most cleanly.

In a separate sketch you should work on open and close the door. I suggest a switch case structure. Work on this until you have the door working perfectly how you want it

Then think about a separate sketch to add in any safety sensors etc.

Finally combine one at a time.

Ie work on small chunk until perfect
Work on next small chunk until perfect
Merge 2 perfect chunks

Bugs always live in the difference between last working iteration and current working-on iteration

Why did you select this rate (seems too fast) and the 10 sample average?

You could save a few weeks or months of data to an SD card, and during that time keep notes when you happen to observe it working and not working as intended. Then use Excel or other means to reprocess the data, to tweak your algorithms.

I thought having a number of readings over the course of a minute would give me a decent average, and I would like the door to close relatively soon after it starts raining. But I'm not set on these numbers - just started with them, and thought I'd tweak them if necessary.

I was thinking of ways to record some good training data. I don't have an SD unit to do that yet, but have been considering it.

In the meantime, though, I've simplified by sensor reading logic to just look for a relatively low or high reading to determine whether to open or close the door. I think I was trying to be too clever...

Yeah, sometimes simpler is better. Also, if you use an ESP8266 or ESP32, you could save data to their non-volatile memory (LittleFS) and easily access the data "over the air." No SD card needed. Another advantage: if you want to tweak the code, you can also upload the new program "over the air." A caveat: they're 3.3v processors, so your relay modules, etc. need to deal with that.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.