Desky Standing Desk (ESPHome) [Works with Desky, Uplift, Jiecang, Assmann & others]

To get started:

  1. Take a look at the Option #1: ESPHome pass-through dongle solution
  2. Then check out the Option #2 Custom control panel (for more advanced set-up and features).

This project could void your warranty or cause safety risks. Proceed at your own risk (I did;).

Possible compatibility with other desks:

  • Lots of desks use the same controllers. There’s a decent chance that if your desk uses a ‘Jiecang controller’ then this solution may work with no/minimal further changes.
  • It likely won’t work if your desk doesn’t use a Jiecang controller (many don’t)
  • To confirm if your desk uses a Jiecang controller you could:
    • Google around the internet, especially checking whether your control box looks similar to ones on the Jiecang site (linked in this thread)
    • Crack open the controller or control panel and look at the labelling on the boards. (peruse this thread for details)

Here is a rough list (may not be well maintained, or accurate)

Jiecang OEM based desks:

If you error out your desk:

  • Don’t panic too much if you error out your desk during testing (flashing ASr message). It will probably be ok.
  • Turn the power off and on at the wall and find reset instructions in your manual or online.
  • A common cause of error is commanding the desk to go lower than its programmed minimum height, so try avoid that. The desk may forget it’s own height and needs to do a special sequence of moves to re-baseline it’s own height.

Triggering the memory preset buttons (which have “soft start/stop”).


The rest of this first post is legacy which I’ve left here for completeness…


I’ve started work on trying to add an ESPHome based controller to my Desky Dual standing desk (Australian Company).

I’m consolidating my progress (or lack thereof😊) here. I expect I’ll be drawing on the LoctekMotion Hacking Discord group a lot but wanted to keep my stuff consolidated so it doesn’t get lost in big threads.

Resources:

My Desk: Desky Dual Sit Stand Desk Frame

Similar project I’m hoping to modify/build on:

Other similar projects:

The Discord Channel for LoctekMotion where there is a lot of active discussion and findings: LoctekMotion Hacking

The ESPHome thread I’m getting lots of help on:

Looks like Jiecang is the control box OEM:

RJ45 adapters:
https://www.aliexpress.com/item/1005001493014437.html?spm=a2g0o.cart.0.0.422c3c00oSBFOb&mp=1

Goals:
Whilst ideally I would achieve a all singing, all dancing integration, my more humble initial goals are:

  1. Trigger the memory height presets
  2. Automate a sit/stand schedule (for example, 40min sitting followed by 20min standing)
  3. I guess knowing the current state (height) would be good.
8 Likes

Photo’s of the controller.

Controller power in and Motor 1.

HS goes to the touch panel.
M2 is motor 2 controller.
Tech support confirmed F is for a non-existent bluetooth dongle (Rj12 with 6 pins?)

Touch panel front
JCHT35K9-003-v4

https://en.jiecang.com/product/122.html

Touch panel back

Board



JCB35N2-MKE02-44pin-V1


Some relevant Google results of the controller:
https://en.jiecang.com/product/119.html

This is probably the bluetooth dongle:
https://en.jiecang.com/product/131.html
https://fccid.io/2ANKDJCP35NBLT/Internal-Photos/Internal-Photos-3727739

Similar Project:
https://hackaday.io/project/4173/logs

Have you made any progress on this over the last month?
I also have this desk so I’m very interested in such a project.

Not yet.

I was waiting on some parts arriving and will start working on it in the next few weeks.

I don’t have much of a clue what I’m doing, so it may be slow going.

I’ll post progress though…

2 Likes

Wiring Diagram of a ‘Pass Through’ approach

1 Like

It’s looking likely that 5v logic is used. A multi-meter measures ~ 5v across the keypad pins.

There’s a bit of debate online about whether ESP32’s are 5V tolerant on the logic pins.

I’m going to start prototyping assuming they are, and move to 5V fi required or maybe for production solution.

https://www.ridiculously-simple.com/2021/05/19/are-the-esp32-and-esp8266-5v-tolerant-yes-they-officially-are/

Connecting 1 (Green) and ground raises desk.
Connecting 2 (Yellow) and ground lowers desk.

Working progess pinout:

Edit: Updated 2022-04-25

All pins meaure 5v across them with respect to ground. I think this means 5V logic?

Handset Order Wire Colour Function Comment
1 Green Up Grounding this simulates up press
2 Yellow Down Grounding this simulates down press
3 Purple Memory presets recall ? Desk moves to memory preset #2 when grounded. Access to other presets is currently unkown.
4 Transparent/copper Simulates M button press? Screen shows [S-] when grounded
5 Brown TX UART / height messages are sent from the controller to the control panel on this wire.
6 White Wake? Wakes control panel and makes controller send height UART messages when grounded
7 Black Ground
8 Red +5v
1 Like

Why not just put optocouplers in between and control them from the GPIO and be sure noting breaks.

That way nothing that possibly could happen on either side won’t destroy the other.

Ta will look into that. I have no clue what I’m doing, so all suggestions welcome;)

Ohhh recieved a bit of data with the Custom UART Text Sensor. That’s a bit excitement;)

[11:22:02][V][text_sensor:016]: 'uart_readline': Received new state \xff\xff\xff\xff\xff\xfe\xff\x80\xff\xffa\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xfc\xfc\xf8\xfc\xfc\xfc\xf8\xfc\xfc\xfc\xfc\xc0\xf8\xfc\xf8\xf8\xf8\xfc\xfc\xf0\xfc\xf8\xfc\xf0\xfc\xf8\xfc\xf8\xf8\xfc\xfc\xf8\xfc\xfc
[11:22:02][D][text_sensor:067]: 'uart_readline': Sending state '\xff\xff\xff\xff\xff\xfe\xff\x80\xff\xffa\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\xfc\xfc\xf8\xfc\xfc\xfc\xf8\xfc\xfc\xfc\xfc\xc0\xf8\xfc\xf8\xf8\xf8\xfc\xfc\xf0\xfc\xf8\xfc\xf0\xfc\xf8\xfc\xf8\xf8\xfc\xfc\xf8\xfc\xfc'
[11:22:04][V][text_sensor:016]: 'uart_readline': Received new state \xf8@\xf8\x80\xf0@\xc0\xf8E\xf8\xf8 \xfc?\xf8K\xf8\xff\xfc\xff\xf8\xf8\xfc\xfa\xff\xff@\xff@\xff\xff\x80\x80\xa0\xdf \xf74\xf8\xf8\xf8@\xfc\xf8@\xf8\xf8\xf0\xf8=\xf8\xff\xf8\xf0\xf8\xf8\xd8\xf0\xfe@
[11:22:04][D][text_sensor:067]: 'uart_readline': Sending state '\xf8@\xf8\x80\xf0@\xc0\xf8E\xf8\xf8 \xfc?\xf8K\xf8\xff\xfc\xff\xf8\xf8\xfc\xfa\xff\xff@\xff@\xff\xff\x80\x80\xa0\xdf \xf74\xf8\xf8\xf8@\xfc\xf8@\xf8\xf8\xf0\xf8=\xf8\xff\xf8\xf0\xf8\xf8\xd8\xf0\xfe@'

Not sure about how valid these values are yet. First time I’ve done this. Not sure if it’s wiring or how I somehow need to configure the custom UART Text sensor?

[12:12:46][D][text_sensor:067]: 'uart_readline': Sending state '!\xfe!!\xde\xdf!!\xe5\xffK\xa1)\xcb\xffK!\xedK!!\xed!!\xed\xffK!!\x92\xff\xcb!%\xa5\xfbO\xa1i\xdaZ\xcb\xa1N\xfb'

Getting lots of help from ssieb over on ESPHome Discord now…

1 Like

On this, there does exist ESP32 Relay Modules (up to 16 channels) with optocouplers that can be used to control higher voltage circuits.

There’s a working solution to this coming in the next few weeks…

2 Likes

Interested in finding out more too - it looks like most of these units use the same / compatible controllers / keypads - would be great to have the pin-outs fully documented so we can develop our own accessories.

Would be really interested in the alternate port, which appears to be for wifi/bluetooth modules…

Option #1: Pass-Through Dongle (Simpler)

Update note 2022-07-03: Might be worth looking over Option #2 config as I’ve made some enhancments/refinements there.


I’m going to keep refining/editing this post as the main ‘how to’.

The majority of credit for the solution so far goes to @ssieb . He generously donated his time and knowledge to decode the UART protocol and write the ESPHome custom component (I don’t know any C++, or much about UART either). He doesn’t have one of these desks. He may be generous enough to add some more features or otherwise collaborate on it’s development if we are nice😉.

Key links:

Hardware: We build ourselves a Dongle from the RJ45 port which “passes-through” 8 of 8 wires between the controller and the control panel and fork-off 6 of 8 to a d1 mini (or esp32 etc)

Parts list:

Soldering

If you are allergic to soldering (I used to be, but worked through it;) you could do this solder free if you pick an esp with pins already on it and buy some dupont cables.

Software:

  • Use Sseib’s custom component and some of my config (optional).
  • You might want to start with just using ssieb’s less bloated config (but make sure you invert the pins). Then maybe read through my config and use what you want.
  • Also Sseib’s config works in mm, whereas I’ve adjust mine to generally be in cm.

Edit: 2022-06-05 Some of the config is a little dated since ssieb deployed some new features l requested. Look at his base config first.

####################################################################################################
##Notes/Info
####################################################################################################

## Home Assistant Thread: https://community.home-assistant.io/t/desky-standing-desk/383790/3
## ssieb who generously donated his time and knowledge to developing the custom component may offer some support on the Discord thread (please read both threads first).
## https://discord.com/channels/429907082951524364/952464939480645642

#Notes:
# 1. Use this component at own risk. It has not had extensive testing. May void warranty or cause desk faults etc.
# 2. This controller may bypass desk safety features like collision detection.
# 3. This component doesn't know about the values stored in your desk. Values in this config are (and must be) set independently.
# 4. ssieb's solution may be a lot simpler and cleaner for most people. I've cobbled together some more features in this config because I can't programn in C++.


#Troubleshooting Tips: 
#If you ever get a flashing ASr message, you might need to reset your desk.
#https://desky.com.au/blogs/news/reset-standing-desk-control-panel#:~:text=When%20ready%2C%20press%20and%20hold,Hooray!


#TODO 
#Ask ssieb to make change requests (move logic to component code)
  # Invert pins
  # Get Desk Height On Boot
  # Interlock Up/Down Pins (If not done already)
  # Interrupt/stop movement function (Stop desk motion)

#other
  # Test collision detection
  # Limit external component fetch to just desky? 
  # Fix Desk Is moving on Boot.

esphome:
  name: desky-d1mini-v2
  platform: ESP8266
  board: d1_mini #pinout: https://i0.wp.com/randomnerdtutorials.com/wp-content/uploads/2019/05/ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png?quality=100&strip=all&ssl=1
  on_boot:
    priority: -100.0
    then:
    #Request a desk height update after boot.
      - delay: 5s
      - switch.turn_on: wake_desk_and_get_height
    
wifi:
  ssid: "NotTellingYouThat"
  password: "NopeItsMySecret"

api:
ota:

logger:
  # level: VERY_VERBOSE ##Uncomment to see UART debug messages 

external_components:
#Fetch ssieb's custom component# https://github.com/ssieb/custom_components/tree/master/components/desky
  - source:
      type: git
      url: https://github.com/ssieb/custom_components
    components: [ desky ]

uart:
  - id: desk_uart
    baud_rate: 9600
    rx_pin: 1 #Labelled TX on my D1mini clone (mislabelled?)
    ##You can uncomment the debug section below to see UART messages.
    # debug:
      # direction: RX
      # dummy_receiver: true
      # after:
        # bytes: 4
      # sequence:     
        # - lambda: UARTDebug::log_int(direction, bytes, ',');
 
desky:
  id: my_desky
  ####################################################################################
  ##Uncomment this block to use Ssieb's move_to componet function.
  # up:    
    # number: 4 #D2
    # inverted: true 
  # down:  
    # number: 5 #D1
    # inverted: true
  # stopping_distance: 15  # optional distance from target to turn off moving, default 15  
  ####################################################################################
  height:  # Sensor publishing the current height
    name: Desky Height
    id: desky_height
    accuracy_decimals: 1
    unit_of_measurement: cm
    #any other sensor options
    filters:
    - delta: 0.05 #Only send values to HA if they change
    - throttle: 200ms #Limit values sent to Ha to 5 per sec.
    - multiply: 0.1 #convert from mm to cm
    on_value:
      then:
          #If the value changes, then the desk is moving
        - binary_sensor.template.publish:
            id: desky_is_moving
            state: ON
        - delay: 300ms
          #Assume it's stopped moving if no height changes after a short time.
        - binary_sensor.template.publish:
            id: desky_is_moving
            state: Off
            
binary_sensor:
  - platform: template
    id: desky_is_moving
    name: "Desky Is Moving"
    filters:
      - delayed_off: 400ms
    #If the desk isn't moving for a bit we better turn off attempts at movement. It's like poor man's collision detection? 
    on_release:
      then:
        - button.press: desky_stop_desk

button: 
#Stop movement 
  - platform: template
    name: Stop Desk
    id: desky_stop_desk
    on_press:
      then:
        - switch.turn_off: raise_desk
        - switch.turn_off: lower_desk

#Move to function
  - platform: template
    name: Go To Desky Height x
    id: go_to_desky_preset_height_x
    on_press:
      then:
      ##Option 1: Uncomment to use Ssieb's move_to componet functions
        # - lambda: id(my_desky).move_to(id(desky_target_height).state*10);  

        
      ##Option 2: Uncomment to use Mahko's lambda alternative 
      #Check if we need to move desk up or down from current position      
        if:
          condition:
          #Current height is more than target height, then move desk down
            lambda: |-
              return id(desky_target_height).state < id(desky_height).state;
          then:
            - switch.turn_on: lower_desk
            - wait_until:
              #Run until the difference between current and target state is < stopping distance 
                condition:
                  lambda: return abs((id(desky_height).state - (id(desky_target_height).state)))<(id(stopping_distance_cm).state);
            - switch.turn_off: lower_desk
          else:
          #Current height is less than target height, move desk up
            - switch.turn_on: raise_desk
            - wait_until:
                condition:
                  lambda: return abs((id(desky_height).state - (id(desky_target_height).state)))<(id(stopping_distance_cm).state);
                  #Run until the difference between current and target state is <0.3cm
            - switch.turn_off: raise_desk


number:
#Target Height ("Move desk to height x").
    #You should probably limit the range you can move the desk to to within the limits you've set via the control panel, perhaps offset a little within the range.
    #Sending commands higher/lower than this may cause error messages and require desk reset (or worse).
  - platform: template
    id: desky_target_height
    name: "Desky Target Height"
    optimistic: true
    unit_of_measurement: cm
    min_value: 80
    max_value: 130.0
    step: 0.1
    
#Offset correction - Adjust until you get the best accuracy. 
#The desk keeps moving for a little while after the up/down pins are released and we try to account for this.
#1.5cm was about right on mine
  - platform: template
    name: "Desky Stopping Distance cm"
    id: stopping_distance_cm
    unit_of_measurement: cm
    optimistic: true
    min_value: 0
    max_value: 2
    step: 0.1
    restore_value: true
    initial_value: 1.5
    
###############################################
#Define some preset heights.
###############################################
#You can freely define as many adjustable presets as you like.
#These are all seperate/independant of what you've set via the control panel (we can't retrieve them currently).
 
#Standing Height #1 - Set a standing height. 
  - platform: template
    id: desky_standing_height_1
    name: "Desky Standing Height 1"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 120
    max_value: 130
    step: 0.1
    restore_value: true
    initial_value: 124
    
#Sitting Height #1 - Set a sitting height. This is independant of what you've set via the control panel.
  - platform: template
    id: desky_sitting_height_1
    name: "Desky Sitting Height 1"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 79.7
    max_value: 82
    step: 0.1
    restore_value: true
    initial_value: 79.9


      
switch:
#wake up ther desk and request it sends its height 
  - platform: gpio
    id: wake_desk_and_get_height
    name: "Request Desk Height"
    pin:
      number: D5
      inverted: true
    on_turn_on:
    - delay: 100ms
    - switch.turn_off: wake_desk_and_get_height

#Raise the desk 
  - platform: gpio
    id: raise_desk
    name: "Raise Desk"
    pin:
      number: D2
      # mode: INPUT_PULLUP
      inverted: true
    interlock: lower_desk
    on_turn_on:
    #Auto off after 15s just in case
    - delay: 15s
    - switch.turn_off: raise_desk
#Lower the desk 
  - platform: gpio
    id: lower_desk
    name: "Lower Desk" 
    pin:
      number: D1
      # mode: INPUT_PULLUP
      inverted: true
    interlock: raise_desk
    on_turn_on:
   #Auto off after 15s just in case
    - delay: 15s
    - switch.turn_off: lower_desk
  


   







7 Likes

I thought I’d put up what I’ve got so far so that others can start using/contributing too…

This is looking awesome!
I can’t personally contribute to it, unfortunately, but thanks for having the determination to follow through with this. The Home Assistant and smart home community in general are so inspiring.
Will be sure to give thanks to ssieb!

2 Likes