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

I’d suggest tweaking the debug part to be more like this.

If you find a wire that looks like it’s sending uart messages you may need to tweak the after: a bit. Or try my other commented out variants.

If you get messages but they are complete rubbish you may need find the gnd and share it with the ESP.

  debug:
    direction: BOTH
    dummy_receiver: true
    after:
      # timeout: 5ms
      # delimiter: "\n"
      delimiter: [0x7E]
    sequence:
      - lambda: |-
          UARTDebug::log_int(direction, bytes, ',');                // Log the message as int. Good for height message checks.
          UARTDebug::log_hex(direction, bytes, ',');                // Log the message in hex. Good for checking against protocol documentation.
          ESP_LOGD("custom", "Bytes size: %d", bytes.size());       // Logs how many bytes in the message, useful for protocol and message identification.

Thanks for the tip.

If I understand it correctly you’re suggesting to put external power on the ESP rather using the one from the bus? I thought that would also work, but could be I misunderstood.
As to finding those pins I’m a complete novice, except for basic knowledge about a multi meter. I found this video, but any other tips are welcome.

You can try powering it either way. I’m no guru at it either. I’ve done both.

From the bus, you should be able to find gnd as when you have your black multimeter lead on it and touch another wire with red lead then you will probably see similar voltage on each of the other wires.

Once you’ve found ground, you might notice that one of the other three wires has slightly higher voltage in reference to the gnd wire. That is probably the power wire.

Then you just have to guess which is rx and tx (and confirm by finding messages).

That vid looks helpful…

Hi, were you able to figure anything out with the mitzzon controller?
I would be interested in any news.

hi @Mahko_Mahko and @GeneralPILK

I’ve been trying to get something out of the Mittzon, but I feel I didn’t get any luck so far.

I did get something when connecting GND to 2 and RX to 6 of the RJ12 @ 9600.

Received input

[22:17:40][D][uart_debug:176]: <<< 0
[22:17:40][D][uart_debug:114]: <<< 00
[22:17:40][D][custom:070]: Bytes size: 1
[22:17:42][D][uart_debug:176]: <<< 0,0,0,0,192,12,161,212,69,160,56,48,64,0,0,116,62,32,1,52,30,32,225,234
[22:17:42][D][uart_debug:114]: <<< 00,00,00,00,C0,0C,A1,D4,45,A0,38,30,40,00,00,74,3E,20,01,34,1E,20,E1,EA
[22:17:42][D][custom:070]: Bytes size: 24
[22:17:44][D][uart_debug:176]: <<< 252,0,1,0,0,128,48,128,4,1,64,0,0,0,0,5,64,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
[22:17:44][D][uart_debug:114]: <<< FC,00,01,00,00,80,30,80,04,01,40,00,00,00,00,05,40,04,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00,00
[22:17:44][D][custom:070]: Bytes size: 41
[22:17:50][D][uart_debug:176]: <<< 0,0,0,0,0,0,0,0,0,0,0,208,0,146
[22:17:50][D][uart_debug:114]: <<< 00,00,00,00,00,00,00,00,00,00,00,D0,00,92
[22:17:50][D][custom:070]: Bytes size: 14
[22:19:25][D][uart_debug:176]: <<< 0
[22:19:25][D][uart_debug:114]: <<< 00
[22:19:25][D][custom:070]: Bytes size: 1
[22:19:25][D][uart_debug:176]: <<< 130,4,224,2,0,0,0,1,0,0,0,0,0,0,1,248,0
[22:19:25][D][uart_debug:114]: <<< 82,04,E0,02,00,00,00,01,00,00,00,00,00,00,01,F8,00
[22:19:25][D][custom:070]: Bytes size: 17
[22:19:29][D][uart_debug:176]: <<< 0,0,0,0,0,0,0,0,0,0,0,64,152,0,128,32,146,174
[22:19:29][D][uart_debug:114]: <<< 00,00,00,00,00,00,00,00,00,00,00,40,98,00,80,20,92,AE
[22:19:29][D][custom:070]: Bytes size: 18

But I think that might just be gibberish. It also doesn’t give me any input when I change height or select a preset.

I also tried the jiecang_desk_controller at GitHub - Rocka84/esphome_components but that also doesn’t allow me to control or read it.

1 Like

Hi,
I also have an Ikea Mittzon desk, and I have analyzed the RJ12 jack with a multimeter.
Pins from the left to the right with the click side up:
Explanation: 1+ means the red/positive cable of the multimeter was connected to the first pin. 1- means the black/negative cable was connected to the first pin.

1 + 2 + 3 + 4 + 5 + 6 +
1 - X 3.05 0 0 3.04 3.28
2 - -2.94 X 0.04 -2,7 0 0
3 - -0.02 0 X -0.02 0 0
4 - 0 2.7 0.03 X 2.8 3
5 - -3.04 0 0.03 -2.75 X 0
6 - -3.28 0 0.02 -2.95 0 X

My assumption:
1 → GND
2 → TX?
3 → Not connected?
4 → RX?
5 → TX?
6 → 3V3

@peetje How did you come to the conclusion that the second pin is GND?

P.S. I am currently waiting to get an RJ45 female-to-female part, so I can analyze the traffic between the controller and the buttons unit. On the RJ45 I have the 5V but unfortunately on different pins as https://www.tindie.com/products/tjhorner/upsy-desky/

2 Likes

@edenhaus, mostly because those gave me some data. I counted the pins from right to left initially though, so I meant pins 1 & 5. I might have swapped them around, because I also measure 3.3V between 1- and 5+.

Looks like a decent guess to me.

I think that’s a decent start for some further uart debug inspections.

These are quite handy for this kind of thing too.

Just found this amazing item on AliExpress. Check it out!

AU$11.39 | USB Logic Analyzer 24MHz 8 Channel 24M/seconds Logic Analyzer Debugger For ARM FPGA Logic Analyzer Logic 24M 8CH
https://a.aliexpress.com/_mNQQEyU

I’ve been using this tool with a couple of standing desks, one from Uplift and one from Deskhaus and it works pretty well, the only problem is that the Uplift occasionally seems to lose the tilt value, and it floods my logs (and eventually kills HA) with entries like this:

[09:02:42][D][cover:170]: 'Detached Desk' - Publishing:
[09:02:42][D][cover:178]:   State: CLOSED
[09:02:42][D][cover:184]:   Tilt: nan%
[09:02:42][D][cover:186]:   Current Operation: IDLE

Is there any way to set the tilt to some default integrer, or rate limit the state reporting when it doesn’t get an initial value from the desk controller? If I manually adjust the height the desk correctly reports, and the log flood stops, but the desk is in another building, and routinely loses the tilt value so not sustainable to manually move the height all the time.

What config are you using?

Is the esp perhaps rebooting? You can add an uptime sensor to check.

Here’s my config file. Oddly it’s exactly the same (aside from obvious things like name and measurements) as the Deskhaus, which doesn’t exhibit this behavior. Whenever I update via OTA I get the above condition where the logs are flooded.

I’m using the same ESP boards and same wiring in each as well.

esphome:
  name: detached-desk
  friendly_name: Detached Desk

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "secret"

ota:
  - platform: esphome
    password: "secret"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Detached-Desk Fallback Hotspot"
    password: "secret"

captive_portal:

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: GPIO1 #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, ',');

substitutions:
  # Required (replace these with your own values!)
  desky_request_height_pin: GPIO14 #Request desk height | white wire  
  desky_purple_pin: GPIO12 #purple wire  
  desky_down_pin: GPIO5 #Move desk down | yellow wire  
  desky_up_pin: GPIO4  #Move desk up | green wire  
  descriptor: 'Detached Desk'
  unit_of_measurement: 'in'
  min_height: '25.3'
  max_height: '50.9'
  default_standing_height: '29.2'
  stopping_distance: '1'

desky:
  id: desk_controller
  up:    
    number: ${desky_up_pin} #D2
    inverted: true 
  down:  
    number: ${desky_down_pin} #D1
    inverted: true
  request:
    number: ${desky_request_height_pin} #D6
    inverted: true
  stopping_distance: ${stopping_distance}
  timeout: 15s
  height:
    id: desk_height
    name: ${descriptor} Height
    accuracy_decimals: 1
    unit_of_measurement: ${unit_of_measurement}
    filters:
    - delta: 0.05
    - throttle: 200ms
    - multiply: 0.1

cover:
  - platform: template
    name: ${descriptor}
    device_class: blind
    icon: mdi:desk
    lambda: |-
      if (id(desk_height).state >= id(desk_standing_height).state) {
        return COVER_OPEN;
      } else {
        return COVER_CLOSED;
      }
    open_action:
      - lambda: id(desk_controller).move_to(id(desk_standing_height).state * 10);  
    close_action:
      - lambda: id(desk_controller).move_to(id(desk_sitting_height).state * 10);  
    stop_action:
      - lambda: id(desk_controller).stop();
    tilt_lambda: |-
        return (id(desk_height).state - ${min_height}) / (${max_height} - ${min_height});
    tilt_action:
      - lambda: id(desk_controller).move_to(((tilt * (${max_height} - ${min_height})) + ${min_height}) * 10);



number:
  - platform: template
    id: desk_sitting_height
    name: ${descriptor} Sitting Height
    optimistic: true
    unit_of_measurement: ${unit_of_measurement}
    min_value: ${min_height}
    max_value: ${default_standing_height}
    step: 0.1
    restore_value: true
    initial_value: ${min_height}
    mode: box
    icon: mdi:arrow-down
    
  - platform: template
    id: desk_standing_height
    name: ${descriptor} Standing Height
    optimistic: true
    unit_of_measurement: ${unit_of_measurement}
    min_value: ${default_standing_height}
    max_value: ${max_height}
    step: 0.1
    restore_value: true
    initial_value: ${default_standing_height}
    mode: box
    icon: mdi:arrow-up

Does the desk not report it’s height after boot or OTA by chance… Until you move it?

You can probably test if the request height command is working properly with the below using your gpio14. May need to allow_other_uses to use the pin twice.

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

It’s probably better to try to address the underlying problem, but you can probably check for and workaround nans using something like this in your tilt lamda.

Does the desk not report it’s height after boot or OTA by chance… Until you move it?

Yes, that’s exactly what happens!

I added the height request switch and also added the lambda (THANK YOU as that solved the log flooding). However, activating the switch results in no height returned, and basically the desk acts as if it were “dead” until it is moved at the physical controller, and then all works as expected. This state seems to occur after an OTA. I haven’t tried after a power failure or similar, but I recall this happening outside of just OTA so any reboot of the ESP may trigger this state although I can confirm next time I’m out there.

You should double check your wiring / connections on GPIO14. And then try moving it to another GPIO if that doesn’t resolve it.

I had a cheap D1 mini kind of die slowly. Possibly just due to it being a crappy board. Or possibly because we are using 5V logic on the 3V pins (they are 5v tolerant but may not last in the long run).

Give the above a go…

Do you know if the configuration from @ssieb has been used or tested on ESP32 boards? I’ve only seen examples of the D1 mini’s here for that config.

I’m currently trying the same cover config that Slappy a few posts above is using. I’m running an esp32s (esp32-wroom-32), trying to control an Uplift V2 desk. Pins have been changed per open pins from here.
I’ve tested all used GPIO pins to ground w/ a multimeter, and they are at a constant 3.3V when inactive, showing that the invert is working, however when I try to give any commands, I don’t get a low level signal at all. I wrote some test code with a few switches to fire the same exact GPIO pins, and it works without issues.

All other aspects, such as height fetching work as intended

Any thoughts?
Current config with issues:

substitutions:
  name: "office-desk"
  friendly_name: Office Desk
  # Required (replace these with your own values!)
  desky_request_height_pin: GPIO18 #Request desk height | white wire  
  desky_purple_pin: GPIO19 #purple wire  
  desky_down_pin: GPIO21 #Move desk down | yellow wire  
  desky_up_pin: GPIO22  #Move desk up | green wire 
  descriptor: "Office Desk"
  unit_of_measurement: 'in'
  min_height: '25.3'
  max_height: '50.9'
  default_standing_height: '42.0'
  stopping_distance: '1'

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: dev

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

# Allow Over-The-Air updates
ota:
- platform: esphome

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: 192.168.12.102
    gateway: 192.168.12.1
    subnet: 255.255.255.0

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Office Desk Fallback"
    password: "redacted"


# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/example-configs/esphome-web/esp32.yaml@main
  import_full_config: true

# Sets up Bluetooth LE (Only on ESP32) to allow the user
# to provision wifi credentials to the device.
esp32_improv:
  authorizer: none

# To have a "next url" for improv serial
web_server:

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: GPIO1 #To TX0.
    ##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: desk_controller
  up:    
    number: ${desky_up_pin} #D22
    inverted: true
  down:  
    number: ${desky_down_pin} #D21
    inverted: true
  request:
    number: ${desky_request_height_pin} #D18
    allow_other_uses: true
    inverted: true
  stopping_distance: ${stopping_distance}
  timeout: 15s
  height:
    id: desk_height
    name: ${descriptor} Height
    accuracy_decimals: 1
    unit_of_measurement: ${unit_of_measurement}
    filters:
    - delta: 0.05
    - throttle: 200ms
    - multiply: 0.1

cover:
  - platform: template
    name: ${descriptor}
    device_class: blind
    icon: mdi:desk
    lambda: |-
      if (id(desk_height).state >= id(desk_standing_height).state) {
        return COVER_OPEN;
      } else {
        return COVER_CLOSED;
      }
    open_action:
      - lambda: id(desk_controller).move_to(id(desk_standing_height).state * 10);  
    close_action:
      - lambda: id(desk_controller).move_to(id(desk_sitting_height).state * 10);  
    stop_action:
      - lambda: id(desk_controller).stop();
    tilt_lambda: |-
        return (id(desk_height).state - ${min_height}) / (${max_height} - ${min_height});
    tilt_action:
      - lambda: id(desk_controller).move_to(((tilt * (${max_height} - ${min_height})) + ${min_height}) * 10);



number:
  - platform: template
    id: desk_sitting_height
    name: ${descriptor} Sitting Height
    optimistic: true
    unit_of_measurement: ${unit_of_measurement}
    min_value: ${min_height}
    max_value: ${default_standing_height}
    step: 0.1
    restore_value: true
    initial_value: ${min_height}
    mode: box
    icon: mdi:arrow-down
    
  - platform: template
    id: desk_standing_height
    name: ${descriptor} Standing Height
    optimistic: true
    unit_of_measurement: ${unit_of_measurement}
    min_value: ${default_standing_height}
    max_value: ${max_height}
    step: 0.1
    restore_value: true
    initial_value: ${default_standing_height}
    mode: box
    icon: mdi:arrow-up

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: GPIO18
      inverted: true
      allow_other_uses: true
    on_turn_on:
    - delay: 100ms
    - switch.turn_off: wake_desk_and_get_height

Yes I’ve tested it personally. No issues. Just use different pins. However that was an earlier version.

There were some changes to it a while back that possibly caused issues. I’m not sure if they were ever worked through. So could be that.

Does a basic “move_to” command work?

Say with a hardcoded value using a virtual button (making sure you’re in the right units)?

on_...: 
  then:
    - lambda: id(my_desky).move_to(150); # https://github.com/ssieb/esphome_components/tree/master/components/desky

We’re on the same train of thought. After giving it a break last night, I wanted to come back and try to drive it through the desky configuration. Basic “move_to” and “stop” commands are working fine (units set to ‘in’, sending hardcoded ints of inches*10). Only anomaly with the code I tacked onto the end is that the switch “turn_off_action” doesn’t actually send a stop command. I need to hit the button for that, but that’s easy to build a workaround.

Looks like it’s now narrowed down to the cover entity setup from my original config, which I can certainly dig into. That’s what I get for copy-pasting I guess.

Test code additions:

switch:
  - platform: template
    name: Move Up
    id: desky_move_up
    turn_on_action:
      then:
        - lambda: id(desk_controller).move_to(509);
    turn_off_action: 
      then:
        - lambda: id(desk_controller).stop();

  - platform: template
    name: Move Down
    id: desky_move_down
    turn_on_action:
      then:
        - lambda: id(desk_controller).move_to(253);
    turn_off_action: 
      then:
        - lambda: id(desk_controller).stop();

button: 
#Move to test function
  - platform: template
    name: Go To Desky Height x
    id: go_to_desky_preset_height_x
    on_press:
      then:
        - lambda: id(desk_controller).move_to(420); # Check your desk for a suitable value to use.
#Stop movement 
  - platform: template
    name: Stop Desk
    id: desky_stop_desk
    on_press:
      then:
        - lambda: id(desk_controller).stop();
        
binary_sensor:
  - platform: template
    name: Desky moving
    lambda: return id(desk_controller).current_operation != desky::DESKY_OPERATION_IDLE;
1 Like

I was mistaken. It’s not narrowed down to the cover, but rather it looks like something in the firmware flashing causes issues. Tried to re-build bits and pieces of the cover entity and flash again. Nothing worked as before, so I reverted back to the original working code. Still there was no output, so I think I’m moving on from the external components from github.

Has anyone gotten this working with older, non-V2 Uplift desks?

I don’t have much info about the model and am not seeing any identifying info on the control box, but I did buy it ten years ago so its pretty old and definitely not the V2.

Where I’m at now is I’ve flashed an ESP32 with a few different configs, with @Mahko_Mahko’s more complex config I get the following:

With the rx pin disconnected:

  • basic up/down/stop functionality does work
  • getting the desk height does not work, HA shows “unknown” value
  • telling the desk to go to a specific height will set the desk in motion but it does not stop at the correct height or even move in the correct direction (obviously HA/ESPhome won’t know which direction to go in or where to stop if it doesn’t know the height)

With the rx pin connected:

  • the desk will go up and down at seemingly random intervals/durations regardless of whether or not any action has been requested from HA

With @ssieb’s simpler config the result is similar but when the rx pin is connected there is no random movement, rather the controls on the desk’s control panel stop working, but work again if you disconnect the rx pin.

Next step would be going through and debugging but as I do that I was hoping to get confirmation of whether or not I should even expect this to work off the shelf given the age of my control box.

I would continue trying. There is some hope I think.

You need to focus on :

  1. Confirming you can see some uart messages when you move the desk with the desks buttons. Enable/ uncomment uart debug and set logger level to verbose.
  2. If/when you see consistent uart messages, post them so we can take a look.

It would also help debugging if you post some pictures of your control box, wiring, and post your best config. Could be your wiring or you may need to switch rx/tx etc.

Does your control box just have a rj45 port or does it have an rj12 too?