POSTS

Multi-Protocol Remote Hub (in progress June 2024)

Description

I have five different remotes to control the things in my living room. They transmit using infrared (TV+speakers), Bluetooth (Chromecast), and RF on/off keying (OOK) (fan+lights)

I misplace at least one of these remotes almost every day.

The goal of this project is to unify all of them into one central hub, which can then be controlled by any of several identical simpler remotes scattered throughout the house.

The advantages of this type of setup include:

  • Harder to lose the remotes (they’re interchangeable and plentiful)
  • IR control (for speakers especially) no longer requires line of sight to the remote
  • OOK and BLE control have a consistent RF propagation path regardless of where the remote is used from
  • User inteface can be customized
  • Future devices can be added without needing even more remotes

Development

All of my previous major embedded projects have relied either on Arduinos or bare AVR microcontrollers. For this project I wanted to branch out to a different platform. I initially chose to use an ESP32-WROOM dev module as the basis for the project, but quickly found it cumbersome how many pins were reserved for special functions and would cause the module to malfunction if used as an input.

I was able to get it to transmit and receive NEC IR codes, but once I needed more pins for the PLL chip I chose to switch platforms.

Part of the point of the project was to experiment outside the Arduino ecosystem, so I chose the STM32F103C8T6-based “Blue Pill” board as a starting point for the project, and programmed it using STM32CubeIDE, which I had no prior experience with.

The first hurdle I encountered here was that STM32CubeIDE has a fairly steep initial learning curve. Searching for “STM32 blue pill blink example” on Youtube yields a lot of tutorials in the range of 20-30 minutes long. It’s a much more complex environment than Arduino or using avr-gcc with a makefile. However once I got a blink project working and learned to configure the basic GPIO and timer peripherals on STM32CubeIDE, things went a lot more smoothly.

On-Off Keying - for fan and lights

The first part I worked on was the RF on-off keying transmitter. I have two remotes for this, one which transmits at 303.95 MHz for my fan, and one that transmits at 433.8MHz for my lights. The fan uses a simple two-level on-off keying scheme, but the lights use a slightly more complex 3-level scheme.

To capture the codes I used a hackRF one with SDRSharp to capture raw I/Q traces, then used GNU Octave to analyze the signals and extract the codes. (similar to my NRF24L01 troubleshooting post)

Here you can see an example capture from the fan, showing a code that is repeated 8 times.

Zooming in closer on one of these bursts, it’s possible to directly read off the code being transmitted.

In this modulation scheme, a 0 consists of a long (~710us) “off” period followed by a short (~340us) “on” pulse, and a 1 consists of a short “off” delay followed by a long “on” pulse.

Based on this decoding, the above code is 0100001000001. Each of the codes on this remote is 13 bits long, with a symbol period of 1.08 milliseconds and each symbol divided evenly into 3 sections: off, on/off(depending on the bit value), and on.

To transmit the code I’m using an ADF4350 PLL breakout board with two cheap telescoping monopole antennas attached to the balanced RF output ports.

The ADF4350 can be programmed to keep the PLL running while switching the RF output on and off, but it can only be controlled by a register via serial command and not by a pin.

The ADF4350 communication interface works essentially like a shift register. Data is shifted in continuously on a clock, but not loaded into the registers until a rising edge is sent to the LE pin.

In order to get consistent timing, a timer interrupt is used on the STM32 which first strobes the LE pin, then sends the data to configure the PLL on/off for the next pulse.

It is done in this order so that if calculation of the next pulse delay takes an inconsistent amount of time to execute, it will not affect the RF transmission timing.

Getting this process right took a lot of trial and error, comparing the output of PLL to the output of the remote using the hackRF and Octave, but eventually it was able to control the fan and its built-in light.

The outlets that power my lights were much more difficult to get working.

In addition to operating at a different frequency of 433.95 MHz, these use a 3-level code (1, 2, or 3), with timing that is not an even division of the symbol period, and codes that are 27 bits long. I also learned later after about a week of troubleshooting, that they are extremely sensitive to the delay between retransmissions. Even being 100us off was enough to cause it to work only very sporadically, and usually only once immediately after uploading the program to the STM32.

The outlet remote also transmits a very short pulse before the transmission, but having or omitting this did not seem to make any difference. I confirmed this by using Universal Radio Hacker to trim it out and do a replay attack while troubleshooting why my PLL-based solution wasn’t working.

Below is an example of an outlet code which is repeated 7 times.

Zooming in, we can see the code.

It’s a bit difficult to tell because the code is so long, but the 3 symbols are as follows:

Symbol Time on (us) Time off (us)
1 321 757
2 539 539
3 745 327

The symbol period is 1.084 ms and retransmit delay is 5.38 ms (as measured using the hackRF)

The remote has 8 buttons with the following codes:

Button Code
ALL ON 223111133113313111111111132
1 ON 223111133113313111111311132
2 ON 223111133113313111113111132
3 ON 223111133113313111113311132
3 OFF 223111133113313111113311112
2 OFF 223111133113313111113111112
1 OFF 223111133113313111111311112
ALL OFF 223111133113313111111111112

From this you can see the pictured code is for the “ALL ON” button.

I did the fan first, so getting it to work with this new scheme required a near complete rewrite of the on-off keying logic.

After probably 20+ hours over a week and a half of troubleshooting, I got both OOK transmission schemes to work consistently on the STM32 with the PLL board.

In parallel with troubleshooting the outlets, I started work on the remote.

I wanted the remote to have a nice comfortable user experience, so for the switches I chose to use low-profile mechanical keyboard switches. Specifically, Gateron KS-33 brown switches. I really enjoy the feel of brown switches, but I wanted something low profile so the remote wouldn’t be too thick.

For the keycaps, I used Kubic backlit re-legendable keycaps from ebay (link)

For volume control, it was also a requirement for me that the remote use a rotary encoder knob.

I had been using my remote from the audio visualizer project (which is still running on the same set of AA batteries from 2018 !!) to control my speaker volume through an ESP32 with an IR transmitter, and I think it’s a much better user experience than using buttons.

For backlighting, I used orange thru-hole 3mm LEDs, and for the PCB I wanted to try fabricating one myself. I bought a very cheap (~$160) Genmitsu 3018 CNC machine and designed a one-layer PCB in KiCAD, then used FlatCAM and Candle to send it to the CNC.

For holding down the PCB I just used a sandwich of masking tape and double sided tape on top of a piece of 1/4” plywood clamped to the CNC work table.

The first version of the PCB turned out ok, but with the design I had to do a lot of the routing for the button matrix with hand-soldered wires. I also forgot to add a header to connect it more easily to the rest of the project, and I didn’t add backlighting. It also had some bridged traces due to an insufficient cut depth on the CNC.

I decided to abandon the first one before I finished wiring it and redesign a new one with a pin header, header for an OLED display, and backlighting.

This one took much longer to machine at about 2 hours total, but it turned out much better, especially with the backlighting.

I used a second stm32 blue pill board to handle control of the individual key backlighting and polling of the keypad and encoder. I used a timer interrupt to handle multiplexing of the 3 columns to avoid flickering of the backlight LEDs during time consuming computations.

Once this was working I ported my NRF24L01 library from the audio visualizer project to STM32CubeIDE and used it to send a 1-byte packet with the characters “0123456789AB” for the 12 buttons (including the one built-in to the encoder). I had it send ‘+’ and ‘-’ when the encoder is turned clockwise or counter clockwise, respectively, to mimic the behavior of the audio visualizer remote for easy testing with the existing ESP32-based IR hub system.

For this testing I had the board on an extremely long chain of USB extension cables, which I think is why the nRF24 module didn’t work initially. I soldered a large (1000uF) electrolytic capacitor to the VCC and GND pins and it worked after that.

Next I updated the base station code to also have an NRF24L01 using the same library, and to listen for NRF packets. I changed the station address on both to avoid conflict with my existing volume control setup, then added code to translate the button presses into OOK transmissions and was able to control the fan and lights.

As of 3/7/24, the system is able to control my TV, speakers, ceiling fan light, and outlet lights. The latter two are controlled by 303MHz and 433MHz OOK and the previous are controlled by NEC IR.

The remote can switch between controlling IR devices or fan/lights (due to lack of buttons to do everything at once), indicating the selected mode on a 128x32 SSD1306 OLED display.

The next steps are to:

  • Get BLE control working - I’m planning to have the ESP32 connected to the STM32 via UART for this purpose only
  • Improve usability of the remote - right now the cables are a little fragile and inconveniently placed.

Update 6/23/24

The revised PCB pictured above worked really well and I found myself using it a lot. For the next step, I wanted to make it more self-contained so it could be moved without risk of unplugging wires or shorting things out.

I wanted the redesign to consist of two layers: a top layer with the buttons, display, and rotary encoder, and a bottom layer with the STM32 and supporting electronics.

I started with the top layer. I used the same Gateron low-profile brown switches as before but replaced the encoder with a horizontal scroll wheel based on the Radiomaster TX16S. I also added cutouts to allow for small OLED displays to be placed inside the keys at a later point. It also includes WS2812 reverse-mount addressable “Neopixel” LEDs in addition to pads for mounting 3mm LEDs as used in the CNC milled version.

The board was designed with headers so it could sit directly on a breadboard.

Below is the result:

After adding labels:

The breadboards are adhered to laser-cut plastic with bumpers on the bottom and a 18650 battery on the top to provide some tilt and allow the remote to be powered off battery for up to 2 days (this requires the USB port to be unplugged)

The base station was also upgraded. Originally I had just two telescopic monopole antennas coming off the differential output of the ADF4350 board, but I replaced this with a balun and SMA magnetic stick-on antenna with a 30dB amplifier to boost the signal strength. Before adding these the system was very unreliable when controlling the lights.

I also replaced the infrared LED with 3 infrared emitters which could be attached near the TV and speaker receivers with velcro. This also improved the reliability of the system and made it much more difficult to accidentally block the IR signal.

For monitoring the battery status when it’s connected, I’ve been using a MAX17303EVKIT board with the modelgauge GUI. This provides useful info including charge/discharge rate, time to charge, and an accurate state of charge for the battery. Currently the power draw is too high to make the 18650 practical (roughly 60mA draw, battery lasts roughly 48 hours), but I think there is potential to make it last longer.

The base station is also set up to emulate a serial port, so the lights, volume, and TV input selection can be controlled via a virtual serial port in PuTTY. So far this is the feature I’ve used the most, both because the keyboard is often closer to me than the remote, and because it can be controlled through a remote desktop connection.

Next steps for the project are:

  • Better integrate the base station (currently has 3 PCBs, 2 breadboards, and the amplifier needs to be powered externally with a second USB port)
  • Integrate the bottom half of the remote, at a bare minimum it should have an NRF24L01 radio, STM32, and battery charger + fuel gauge to be practical.
  • Decrease power consumption - goal is for it to last at least 14 days on one charge, ideally 60+ days
  • Add WS2812 functionality to the remote - currently I’ve only activated them using an external arduino and not the internal STM32.