Andy's Blog
Project S1.10 - Bed Headboard Control
Sometimes buying cheap can be a blessing in disguise. In the case of our new memory foam bed base, if we hadn't decided to skimp, we may not have realized the joy of an intregrated bed in our smart home scheme.
Overview
A few years ago we purchased a new memory foam bed and, as is the case with most beds, you had to also purchase one of their "approved bases" in order to maintain a warranty. So, we opted for the least expensive base that also included the ability raise and lower the head of the bed. A few days later our mattress and our American Adjustables cheap base with wired controls (one for each side of the bed) was delivered and setup.
The first thing we noticed was that the cords in the showroom that were nice and long were not what we had on our bed! The ones in the showroom had extensions. These extensions were NOT included with our bed and interesting enough, no one was interested in selling us a pair of extension cables. This left us with controls that barely reached the top of the bed and which, we had to turn over to use.
You get what you pay for.
After about 6 months we notices that the strain relief collars on one of the controls was starting to tear. A few months later - bare wire. Fast forward about a year, one of the controls no longer works and the other is getting a bit twitchy.
Again, no one was interested in selling us replacements. Then it hit me!
"Wait a damn minute! Aren't I building a home automation and CONTROL system?!?"
Discovery
I wanted to see what I could do with what I had so I took the defective controller appart and wow! there was a lot of crap in there! What was all of this stuff for? I stepped back and just looked at the lines coming in and out. I was able to see that there was a 12 volt supply line and a ground line. The other two lines looked like signal lines for the two up and down buttons. So, I took a chance and just jumpered the 12v supply over to the "up" signal line. The bed started to move! I then moved my jumper wire over to the "down" signal line and the bed went down!
In the box there are contacts for 4 more signal lines and 4 more buttons. My best guess is that this controller board is used in all of their wired base models and they just put different face plates over the board as needed. So, I had a working theory. I tested the supply line to see if it dipped during use or spiked at any time - nope. There was very little current draw over the signal lines but they did require 12 volts to activate the motor.
Design
So, a simple contact connection between VCC and the signal pin each is all that is needed but how is that done within my system. Two relays would be required for the contacts and a ESP8266 would work for the wireless controller. Since the relays can require more current than the digital pins can supply, it is best to use a optoisolator between the controller and relay. This will allow a small signal to turn on a higher current switch (in this case a light driven transistor) and safely activate the relay coil. Also, an optoisolator isolates the controller from any spikes that can form when the relay coil is released and the magnetic field collapses. Fortunately, relay modules exist that already contain all of this circuitry and for a pretty low price.
The bed will be the power supply for the controller and in order to handle the 12 volts supplied, we'll need a variable buck converter. A very inexpensive one has been linked below and works beautifully. The 12V line and GND lines connect to the input side of the converter and the regulated 5 volt side connectes to the VCC of the NodeMCU and the VCC pin on the relay module.The ground (-) on the converter connects to the GND pins on both the relay module and the NodeMCU.
The NodeMCU connects to IN1 using D1 (GPIO5) and IN2 using D2 (GPIO4). The up signal line on the bed connects to the normally open screw terminal for relay 1 and the down signal line connects to relay 2 normally open terminal block connector. You'll need to connect the 12v wire that is connected to the input side of the buck converter also to the other terminal block connection for BOTH relay 1 and 2.
Parts
- 1 - NodeMCU
- 1 - Duel Relay Module with current isolation
- 1 - Variable input/output buck Converter
- 1 - Breadboard PCB
- 2 - M3x 8 screws
Enclosure
The enclosure can be found here: https://www.tinkercad.com/things/hzMbhaqhhAU
I designed a simple enclosuer with Tinkercad that allows for the bed control cable to enter the box between strain relief brackets inside the box. There are slots for a zip tie here that would help keep the cable from sliding but in my case the fit was very snug and didn't need it. The cover is bolted down with 2 M3x 8 screws. I would recommend uploading the code and testing the system before mounting it in the box.
I used hot glue to hold the boards in place but this may not be necessary.
Code
The code can be found here: https://github.com/AndrewHoover/SimpleBedRelayController
NOTE: This project uses the pubsubclient library which can be added in the Library Manager in the Arduino IDE.
The code here is pretty basic and was designed based on the Cover MQTT type in Home Assistant. The idea here is simple: when you press the up or down button in home assistant, the appropriate relay closes until the stop command is received OR the system reaches a maximum activation time and stops.
The max time is meant as a safety precaution in case the user forgets to stop it or if the command was accidentally sent and it is unlikely that someone will stop it. We don't want the relay on indefinitely or any unexpected problems with the bed due to prolonged contact closure
In my code, this timeout is 20 seconds and can be adjusted in the global variable block at the top of the code.
A second safety precaution was to always make sure that if I'm activating one relay, an intentional deactivation of the other is always called, even if the last state was already off. I don't want to send 12v down both signal lines since I don't know what that will do to the bed.
Other than that, the bed publishes that it is available every 60 seconds and thats it. No bells or whistles here. Since the bed doesn't have position feedback, I couldn't send back any to Home Assistant.
Integration
In HA I have a covers.yaml file with the following definition:
- platform: mqtt
name: "Master Bed Headboard"
command_topic: "master_bed_control/cmd"
availability_topic: "master_bed_control/availability"
qos: 0
retain: false
payload_open: "UP"
payload_close: "DOWN"
payload_stop: "STOP"
payload_available: "online"
payload_not_available: "offline"
optimistic: true
Additionally, I have defined two buttons on my Bedside Control Panel to raise and lower the headboard. The buttons on the control panel are defined as MQTT sensors:
- platform: mqtt
state_topic: "bedside_cp1/button5"
name: "Bedside CP1 Button 5"
icon: mdi:circle
- platform: mqtt
state_topic: "bedside_cp1/button6"
name: "Bedside CP1 Button 6"
icon: mdi:circle
... and 4 automation rules for when each button is pressed and released. When pressed, the up or down command is sent to the bed depending on which button was activated, when the button is released, the stop command is sent:
- id: '1548308650383'
alias: MBR Bedside CP Button 5a
trigger:
- entity_id: sensor.bedside_cp1_button_5
from: 'Off'
platform: state
to: 'On'
condition: []
action:
- data:
entity_id: ' cover.master_bed_headboard'
service: cover.open_cover
- id: '1548308758911'
alias: MBR Bedside CP Button 5b
trigger:
- entity_id: sensor.bedside_cp1_button_5
from: 'On'
platform: state
to: 'Off'
condition: []
action:
- data:
entity_id: ' cover.master_bed_headboard'
service: cover.stop_cover
- id: '1548308863495'
alias: MBR Bedside CP Button 6a
trigger:
- entity_id: sensor.bedside_cp1_button_6
from: 'Off'
platform: state
to: 'On'
condition: []
action:
- data:
entity_id: ' cover.master_bed_headboard'
service: cover.close_cover
- id: '1548308911467'
alias: MBR Bedside CP Button 6b
trigger:
- entity_id: sensor.bedside_cp1_button_6
from: 'On'
platform: state
to: 'Off'
condition: []
action:
- data:
entity_id: ' cover.master_bed_headboard'
service: cover.stop_cover