I am looking to control 18 latching relays from an Arduino Mega. The relays will control a series of 240V AC lights.
I have tried to do my own research, due diligence and follow best practices.
The problem: when I am turning the lights on and off, the board will appear to be 'hung', eventually the watchdog timer will reboot the board and it comes back on line and I can control it again. This only happens when there is AC load through the relays.
I have designed two PCBs, one a board with ULN2803s on, and a second board with the relays on.
I finally got round to wiring it up this weekend, and the code works well, the relays all work as intended UNTIL I turn the AC load on.
The relays are 12V coils, I power the Arduino with a 12v barrel jack, and take the 12V to power the coils from the VIN pin.
I use the flyback diode on the ULN2803 to minimise kickback voltages from the relay coils. I had hoped this was enough. And it seems to be. The code runs flawlessly when the relays are controlled but there is no 240V AC going through them. I can turn the relays on and off to my hearts content, and the board doesn't 'hang'.
As soon as I have the AC power going through the load, a couple of flicks of the relays will cause the hang up.
This leads me to suspect that I am getting EMI rather than bad code or issues with the kickback from the relay coils.
The Arduino code is:
#include <Ethernet.h>
#include <PubSubClient.h>
#include <avr/wdt.h>
#include <string.h>
// MAC Address
byte mac[] = {
0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x10
};
IPAddress ip(192, 168, 1, 8);
const int boardno = "One"; //Change this for each board
const char* mqtt_server = "192.168.1.3"; //MQTT Server IP Address
const char* clientID = "Relays"; //Arduino ID
const char* willTopic = "House/Availability/Relays/One"; //Will Topic
const char* willMessage = "offline"; //Disconnected Message
const char* MQTTUser = "XXXXX"; //MQTT User
const char* MQTTPass = "XXXXXXXXXXXX"; //MQTT Pass
const char board[19][50] = {
"Porch/Lights/Relays/One", "Hall/Lights/Relays/One", "Office/Lights/Relays/One",
"Lounge/Lights/Relays/One", "Lounge/Lights/Relays/Two", "Kitchen/Lights/Relays/One",
"Kitchen/Lights/Relays/Two", "DiningRoom/Lights/Relays/One", "Utility/Lights/Relays/One",
"BathroomOne/Lights/Relays/One", "Garage/Lights/Relays/One", "BackDoor/Lights/Relays/One",
"Shed/Lights/Relays/One", "GarageLoft/Lights/Relays/One", "BedroomOne/Lights/Relays/One",
"BedroomTwo/Lights/Relays/One", "BedroomThree/Lights/Relays/One", "BedroomFour/Lights/Relays/One"
};
//Initialise relay pins
int pins[36]; //vairable to store each pin
int nextpin;
int willQoS = 2; //Will QoS
boolean willRetain = true; //Will Retain
int counter = 0; //Used to reconnect MQTT server
EthernetClient ethClient; //Ethernet Client
PubSubClient client(mqtt_server, 1883, ethClient); //MQTT Client
void setup() {
wdt_enable(WDTO_8S); //Set up watchdog with 8s timeout
for (int i = 0; i < 36; i++) { //Assign 2 pins per relay starting from 2
pins[i] = i + 2;
if (i >= 10) { //Skip pin 10
pins[i]++;
}
if (i >= 21) { //Skip pin 21
pins[i]++;
}
pinMode(pins[i], OUTPUT);
}
/*
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect
}
*/
Ethernet.begin(mac, ip);
delay(2000); // Allow the hardware to sort itself out
if (client.connect(clientID, MQTTUser, MQTTPass, willTopic, willQoS, willRetain, willMessage, true)) { // Connecting to MQTT Broker
//Serial.print(clientID);
//Serial.println(" connected");
client.subscribe("House/RelayBoards/One/+/+/Relays/+"); //Subscribe to topic
}
/*
else {
Serial.print(clientID);
Serial.println(" connection to MQTT Broker failed...");
}
*/
client.publish(willTopic, "online", true); //Publish Online to MQTT
for (int i = 0; i < 18; i++) { //Publish relays controlled by this board
char topicbuild[35] = "House/RelayBoards/One/Layout/";
char cstr[20];
itoa(i, cstr, 10); //Turn the number of the relay into words
strcat(topicbuild, cstr); //Build the new topic
client.publish(topicbuild, board[i], true);
}
client.setCallback(callback); // Setup callback
}
// Reset Function delcared
void(* resetFunc) (void) = 0;
void loop() {
Ethernet.maintain();
if (!client.connected()) { // Reconnecting to MQTT server if connection is lost
// Serial.println("MQTT dropped");
reconnect();
}
client.loop(); //set up subcribing loop
delay(50);
wdt_reset(); //Reset watchdog
}
//Keep trying to reconnect
void reconnect() {
while (!client.connected()) {
//Serial.print("...");
if (client.connect(clientID, MQTTUser, MQTTPass, willTopic, willQoS, willRetain, willMessage, true)) {
client.publish(willTopic, "online", true); // Publish Online
// Serial.println("Connected");
for (int i = 0; i < 18; i++) {
char topicbuild[35] = "House/RelayBoards/One/Layout/";
char cstr[20];
itoa(i, cstr, 10);
strcat(topicbuild, cstr);
client.publish(topicbuild, board[i], true);
}
client.subscribe("House/RelayBoards/One/+/+/Relays/+"); //Subscribe to topic
counter = 0;
}
else {
//Serial.println(counter);
++counter;
if (counter > 5) resetFunc(); // reset board after 5 minutes of trying
delay(5000);
wdt_reset();
}
}
}
void callback(char* topic, byte * payload, unsigned int length) { // subscribe function for relay control
char message[length + 1];
// convert the payload from a byte array to a char array
memcpy(message, payload, length);
// add NULL terminator to message, making it a correct c-string
message[length] = '\0';
//Presevere topic
char topicOG[50];
strcpy(topicOG, topic);
char *strtokIndx; //Will be used as an index
const char s[2] = "/"; //The seperator
char newTopic[50] = "\0";
strtokIndx = strtok(topic, s); //Will be House
strtokIndx = strtok(NULL, s); //Will be relay boards
strtokIndx = strtok(NULL, s); //Get board number
strtokIndx = strtok(NULL, s); //Get room
strcat(newTopic, strtokIndx); //Add room to topic
strcat(newTopic, s); //Add seperator
strtokIndx = strtok(NULL, s); //Get what
strcat(newTopic, strtokIndx); //Add what to topic
strcat(newTopic, s); //Add seperator
strtokIndx = strtok(NULL, s); //Will be Reays
strcat(newTopic, strtokIndx); //Add Relays to topic
strcat(newTopic, s); //Add seperator
strtokIndx = strtok(NULL, s); //Get num
strcat(newTopic, strtokIndx); //Add num to topic
int check;
int mCheck;
const char *cmp = "ON";
char *state;
int whichpin; //Vairable for which pin needed
for (int i = 0; i < 18; i++) { //Step trhough each relay
check = strcmp(board[i], newTopic); //Topic found?
if (!check) { //Yes
mCheck = strcmp(message, cmp);
if (!mCheck) { //Turning it on?
whichpin = pins[2 * i + 1]; //Pick pin for on
state = "On";
//Serial.print(i);
//Serial.println(" ON");
//Serial.println(whichpin);
}
else { //Turning off?
whichpin = pins[2 * i]; //Pick pin for off
state = "Off";
//Serial.print(i);
//Serial.println(" OFF");
//Serial.println(whichpin);
}
break; //Don't need to keep looking
}
}
digitalWrite(whichpin, HIGH); //Turn relay pin on
char topicStat[50] = "House/";
strcat(topicStat, newTopic); //Build topic for state publush
client.publish(topicStat, message, true); // publish
strcat(topicOG, s); //Add separator
strcat(topicOG, state); //Build topic for 2nd state publish
delay(100); //Delay >50ms for relay to switch
digitalWrite(whichpin, LOW); //Turn relay pin on
client.publish(topicOG, "OFF", true); // publish
}
I will also attach pictures of the PCB boards and both their schematics.
The Arduino is mounted and connected via jumper wires to the ULN2803 board.
The ULN2803 board is connect via 5 ethernet cables to the relay board.
The relay board schematic looks a bit overwhelming, but essentially each latching relay has a COM and two 12V inputs on the coil side. There is nothing on the relay board except for the relays, 5 ethernet ports and screw terminals for the AC loads.
The relay board has 18 relays, which means there are 36 outputs from the Arduino.
This leads to 5 ULN2803s, each 2803 has a 100nf ceramic capacitor, a 10uf aluminium electrolytic capacitor and a Zener diode. All of these are positioned next to the power pins of 2803s. This was done based on advice I read online to help protect the 2803s form the kickback.
I also have utilised the built in flywheel diode on pin 10 of the 2803s.
The wires between the Arduino are neat and well organised. The 5 ethernet cables going to the relay board are straight and neat.
The crux of the question how do I go about reducing the (suspected) EMI I am getting when the AC power is on?