Ikea Bekant table control over MQTT with Megadesk and ESP32

Ever since I own an Ikea Bekant table with an electric motor I wanted to automate it. Now, after many years, I was able to finish this project.

At the beginning of the idea I thought I can just add a relay to move the table up or down, but soon after opening the case where the buttons are I figured out that there is a microprocessor controlling the table. I then added some cables to the buttons and a distance sensor to a Raspberry Pi and tried to solve it that way, but that didn't really work out, and the project went on-hold.

Along came Megadesk - an Open Source custom controller which replaces the original controller. At first, it "just" had memory positions, but later serial control was added. With an ESP32 attached to this serial interface, my Ikea table is now fully IoT ready, connected to my MQTT broker at home.

Preparing Megadesk

How to install Megadesk is already well documented on the linked GitHub page. To upgrade the firmware to the latest one, I ordered a Sparkfun Pocket AVR Programmer and flashed the serial-enabled version v2021.09 (v2021.09-serial.hex) to Megadesk.

In order to successfully upgrade, I downloaded ATTinyCore directly in the Arduino IDE and installed avrdude from the Arch Linux community repo.

The commands I used for flashing are as follows (I found them in flash.bat, which obviously doesn't work on Linux):

avrdude -C /home/tobru/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/avrdude.conf -c usbtiny -p t841 -U lfuse:w:0xe2:m
avrdude -C /home/tobru/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/avrdude.conf -c usbtiny -p t841 -U hfuse:w:0xd6:m
avrdude -C /home/tobru/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/avrdude.conf -c usbtiny -p t841 -U efuse:w:0xfe:m
avrdude -C /home/tobru/.arduino15/packages/ATTinyCore/hardware/avr/1.5.2/avrdude.conf -v -pattiny841 -cusbtiny -Uflash:w:v2021.09-serial.hex:i

The connection between the programmer and Megadesk is straight-forward: I just connected the pins which have the same name, as described in the DYI page.

Connecting ESP32

The connection from Megadesk to an ESP32 microcontroller (I'm using the Wemos ESP32MiniKit) looks like that:

MISO -> RX
SCK -> TX
GND -> GND

Programming the ESP32

As I'm a lazy guy, I'm using ESPHome to configure and flash the ESP32. It just needs some YAML:

esphome:
  name: megadesk
  comment: Megadesk IKEA Bekant Controller
  platform: ESP32
  board: wemos_d1_mini32

uart:
  id: uart_bus
  baud_rate: 115200
  rx_pin: 16
  tx_pin: 17
  debug:
    direction: RX
    dummy_receiver: false
    after:
      delimiter: "\n"
    sequence:
      - lambda: UARTDebug::log_string(direction, bytes);

switch:
  - platform: uart
    name: "Desk position up"
    data: '<L0,3.'
  - platform: uart
    name: "Desk position down"
    data: '<L0,2.'
  - platform: uart
    name: "Get desk position"
    data: '<C0,0.'
  - platform: uart
    name: "Go up a bit"
    data: '<+500..'

Of course if you want it to connect to your Wifi and your MQTT broker, you have to add that accordingly. The ESPHome documentation helps.

With this configuration, I can now move my desk to the stored position 3 by simply publishing a message on MQTT:

mosquitto_pub -t "megadesk/switch/desk_position_up/command" -h mqtt.example.com -m "ON"

And as ESPHome works perfectly fine with Home Assistant, integration there just works.

Serial Commands

The serial commands are only documented in a GitHub comment, that's why I'm replicating them here:

After the marker byte '<', there's the Command byte, then two fields of ascii integer digits. Terminate each field with any non-digit character eg !,./ or a line-break. Missing digits are interpreted as a 0 so <L.. is valid and passes two 0s to the L command. First field (position) can be any number up to a maximum of 65535, the second field (slot) has a maximum of 255.

  data start (first byte)
    char   meaning
  -----------------
    <      start Rx Marker

  command (second byte)
    cmd    meaning
  -----------------
    +      increase
    -      decrease
    =      absolute
    C      Ask for current location
    S      Save location to (UpButton) EEPROM
    s      Save location to Downbutton EEPROM
    L      Load (Upbutton) location from EEPROM and move
    l      load Downbutton location from EEPROM and move
    W      Write arbitrary data to EEPROM
    R      Read arbitrary data from EEPROM
    T      play tone

  position (third/fourth bytes or 1st digit field. max 65535)
    cmd    meaning
  -----------------
    +-     relative to current position
    =SsW   absolute position
    T      tone frequency
    CRLl   (ignore)

  push_addr (fifth byte or 2nd digit field. max 255)
    cmd    meaning
  -----------------
    SLlWwR EEPROM pushCount/slot
    T      tone duration/4 ms. (250 == 1s)
    +-=C   (ignore)
    
Examples:

<W3000,3.     # writes  location 3 with height 3000.
<T3000,255.   # plays a 3000Hz tone for 1sec
<C0.0.        # reports current height
<+300..       # nudge desk up by 300
<L,5.         # recall position from slot 2
<l,5.         # recall position from down-button slot 2
<R.0/         # read eeprom slot 0
You've successfully subscribed to Tobias Brunner aka tobru
Great! Next, complete checkout to get full access to all premium content.
Error! Could not sign up. invalid link.
Welcome back! You've successfully signed in.
Error! Could not sign in. Please try again.
Success! Your account is fully activated, you now have access to all content.
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.