8-Channel relay controller with keypad and RS485 interface

 Posted by:   Posted on:   Updated on:  2022-12-31T19:45:20Z

Control up to 8 devices with this programmable relay box.

In the previous post I built a front panel with 8 push buttons which will be used to activate a module of 8 relays. Having so many I/O lines I had to come with a solution to be able to read and set each one of them with common microcontrollers. I ended up using 74HC165 for inputs and 74HC595 for outputs. These ICs are shift registers controlled using a serial synchronous protocol similar to SPI.

In this post you will see the entire outdoor unit. In the end there will be two units, the outdoor one with keypad and relays; the other is the indoor unit with Wi-Fi connectivity and MQTT capabilities. A keypad will be featured on this one too. I went with this approach because I want a robust implementation without Wi-Fi dependency. Nevertheless the keypad on outdoor unit can be remotely disabled to prevent unauthorized use. I decided to use two units after a failed design which implied the use of an ESP8266 board directly as the MCU of outdoor unit. I had problems with voltage levels (shift registers are both 3.3 V and 5 V compatible, however my relay board is 5 V only, while ESP8266 is 3.3 V only; besides that, 3.3 V applied to shift registers powered from 5 V is not recognized as digital HIGH).

Relay controller inside plastic box
Relay controller inside plastic box

The front panel used two shift registers, one 74HC595 to control button LEDs and one 74HC165 to get the state of each push button. Next, the main controller board will have another pair of these. One input register for reading an optional digital sensor and one output register to drive the relay board. Each of the two shift registers will be daisy chained and all of them will share a single clock.

Controller board

The heart of this controller is an Arduino Pro Mini (compatible) board with ATmega328p MCU running at 16 MHz on 5 V. It is very important to use Pro Mini instead of Nano because I implemented RS485 communication over the hardware serial port of ATmega328p and serial port pins must not be used by something else. For RS485 conversion I used the common MAX485.

I designed a PCB to fit the shift registers, MAX485, Arduino board and connectors. I had to get it manufactured because it is double sided and I couldn't make it at home. I used only through-hole components so it would be easy to assemble with common soldering tools.

RS485 Relay controller PCB
Relay controller PCB

In the top left corner is the power connector. On the opposite side you can see the Arduino board and screw connector for indoor unit (with GND, RS485 A, RS485 B and 5V supply voltage). There is a soldered fuse which protects in the eventuality of a short circuit on the remote side. Near the MCU board is an I2C connector for future expansion (or maybe an LCD?).

In the bottom left side you can see some colored pin connectors. These are for sensors (red and black are supply pins). The green pin socket accepts signals from sensors; each pin is by default pulled up (see the eight resistors on each side of 74HC165 - the leftmost IC).

RS485 Relay controller schematic
Relay controller schematic

There is nothing to say about the schematic. There is a solder jumper which allows inverting input data, but there's hardly any chance I should use it since I can do whatever I want with input data from software. For F1 fuse I used 3.6 x 10 mm 1.6 A slow fuse.

Complete assembly

Besides this custom made controller board, some additional modules are required:

I designed a 3D printable frame to keep everything in place. This frame can be fitted inside a plastic wiring box of 220 x 130 x 95 mm which I bought from a local hardware store. In the previous post you can see the front panel keypad inserted in the face cover of the wiring box.

Completed electronics assembly of relay controller
Completed electronics assembly of relay controller

WARNING! This project uses mains voltage. If you are going to build it, make sure you know what you are doing. You can see in the photo the live wires going from power distribution board towards each relay's common pin. There is also the mains power supply which has exposed mains voltage parts.

The 3D-printed frame is made of two parts. This is not because I couldn't print it all at once. However, I decided to use a separate support for power supply, because this one may differ.

Firmware and communication

I will not get into details regarding the source code, which you can download at the end of this post. However, it's worth mentioning the communication protocol. Since MAX485 is not able to perform full-duplex, I had to design this device as a slave. This means it will not send anything over RS485 bus unless requested by a master device. Baud rate is set to 9600.

I could have used Modbus RTU to control this unit (and I even did some tests with it), however in the end I sticked to a custom made protocol. Although it lacks proper CRC, my protocol seemed reliable enough.

Since there are 8 relays, I encoded information from all of them as a byte, each bit representing a relay, LED, sensor, or other flag. MSB is relay 1, while LSB is relay 8.

Each command starts with a byte representing the address of this device. I chose it to be 0x3E, which is character ">". Here are the supported commands (sent without quotes):

  1. Info command ">I" - responds with software information.
  2. Read command ">R" - the device responds with current relay state and sensor data. For each reported parameter, there are two hex encoded bytes sent, the first being the actual parameter, while the second is the inverted byte, as a measure of error control. An example is ">Rr 0 FF 0 FF" where the first "0" is relay state (all off), followed by "FF" (relay state, inverted). Next is "0" (sensor readings) followed by "FF" (sensor readings, inverted). 
  3. Write command ">W" allows setting relay states and LEDs (there is no logical use of setting a different LED state; they should always follow relay state, yet the firmware allows you to do that). For example to set relay 1 and relay 5 on, we create the following bit mask: 10001000 (0x88). Inverted, this is 01110111 (0x77). The command is ">W 88 77 88 77". First byte pair is relay state, while the second is LED state.
  4. Lock command ">L" allows you to disable any button on front panel. If a disabled button is pushed, its corresponding LED will blink a few times and relay state will not change. Keep in mind that relay state can be changed with write command (only the front panel is locked). Assuming there is nothing connected to relays 7 and 8, the lock mask is 00000011 (0x03) and 11111100 (0xFC) inverted. The command is ">L 3 FC".
  5. Sensor enable command ">SE" is used in a similar way to lock command. By default all sensor inputs are enabled, as if you would send ">SE FF 0".
  6. Sensor signal invert ">SI" enables or disables the inversion of sensor signal for each channel. By default, not inverted, as if you would send ">SI 0 FF".
  7. Sensor delay ">SD" works a bit different. It requires 8 arguments which are 16-bit hex encoded delays in seconds. This delay is the amount of time after a relay state change can be initiated again by a sensor. Assuming you have an outdoor light sensor, and some light flashes on it (lightning or some other source), you wouldn't like this event to trigger off outdoor lights, just to turn them back on in a second or less. This functionality is designed knowing that some sensors oscillate between on and off states rapidly. This delay does not prevent a button triggered change in relay state. By default, all sensors are set to 10 seconds (0x000A in hex). This value is sent only once per sensor (skipping second inverted argument), like this: ">SD A A A A A A A A", first being assigned to sensor/relay 1. Anything between 0 and FFFF is valid.

After a command is received, the device responds with the result. Response is identified by an "r" added after command. Taking as example ">R" command, the device will respond with ">Rr 0 FF 0 FF" assuming all relays and sensor inputs are off. Note that the device responds after each command, even if you issue a write command without arguments, it will respond with current arguments.

Because there is only one output (relay) and multiple input sources (button, sensor, command over RS485), output is set by last changed input.


NOTE: This electronic device is designed to stay powered on all time. To avoid risk of catching fire, use a properly rated power supply and make all mains voltage connections with care, using proper thickness wires. Do not exceed maximum current of relay switches.

I recommend doing some tests, keeping all relays turned on for a while. I was surprised to find that those relays warm up a bit when active. If this is the case for you too, some ventilation holes can be drilled in the enclosure. If you are in doubt, here is how you connect devices to this box:

Connections to relay and power distribution board
Connections to relay and power distribution board

After testing this device for a while, I'm still in doubt about what I should do next: a wired indoor unit or a complete redesign of the control board to add Wi-Fi functionality.


  1. KiCad project archive with included Gerber files (ready to send to PCB manufacturer).
  2. Arduino firmware (sketch source and precompiled binary for Pro Mini 16 MHz 5 V).
  3. 3D printable frame is available on Thingiverse: 5530365.

No comments :

Post a Comment

Please read the comments policy before publishing your comment.