Turn your Desktronic standing desk into a smart desk!

I decoded the messages again, but this time with a baudrate of 9600:

baudrate hex-values meaning
9600 0xA5 0x00 0x00 0xFF 0xFF idle
9600 0xA5 0x00 0x20 0xDF 0xFF up
9600 0xA5 0x00 0x40 0xBF 0xFF down
9600 0xA5 0x00 0x02 0xFD 0xFF Preset 1
9600 0xA5 0x00 0x04 0xFB 0xFF Preset 2
9600 0xA5 0x00 0x08 0xF7 0xFF Preset 3
9600 0xA5 0x00 0x01 0xFE 0xFF M

This values seem to be more accurate and precise than using a baudrate of 5600.
I checked the values with the already mentioned Logic Software and also with the rx-pin on my chip.

1 Like

Nice. They seem to be cleaning up?

Now you could maybe try sending one of them (say a preset), with the UART write?

1 Like

Actually I tried some stuff this weekend and now I can read out the correct height of the desk.
I read out the tx-uart-messages with the logic-analyzer and saw some dependencies in the value. The desk height is between 72.0cm and 119cm. From 72.0 to 99.9cm there is one decimal-digit. From 100 to 119 there only integers.

Mian-Learnings

Tx sends 6 bytes and heres what they mean:

Byte Meaning
1 constant value: 0x5A
2 tens digit of the height
3 units digit of the height
4 1st decimal digit of the height
5 constant value: 0x01
6 propably a kind of an id (actually the sum of all the other bytes divided by a specific number I don’t know) → I measured every value for every height (round about 280) and collected them in another table, which I won’t upload here, as it is too big und for this progression not needed.

Here is an example of the height 75.7:

I compared some values and found out:

Tens

Value Byte
7#.# 0x07
8#.# 0x7F
9#.# 0x6F
1## 0x06

Units

Value Byte
#0.# 0xBF
#1.# 0x86
#2.# 0xDB
#3.# 0xCF
#4.# 0xE6
#5.# 0xED
#6.# 0xFD
#7.# 0x87
#8.# 0xFF
#9.# 0xEF

1st decimal

Value Byte
##.0 0x3F
##.1 0x06
##.2 0x5B
##.3 0x4F
##.4 0x66
##.5 0x6D
##.6 0x7D
##.7 0x07
##.8 0x7F
##.9 0x6F

So I can read out every byte that is send via uart.
For this I wrote my own esphome-custom-component. Here you can find the repository. The currently active branch is feat/desktronic. (I had some inspiration from @ssieb here) Here I will just mention the important part of the component:

Main-Loop for getting the current-height

void Desktronic::loop()
{
    static int bytePositionInUARTMessage = 0;
    static double height = 0.0;
    bool beginning_skipping_garbage_bytes = true;

    while (esphome::uart::UARTDevice::available())
    {
        uint8_t byte;
        esphome::uart::UARTDevice::read_byte(&byte);

        // the uart-messages just get read. So they also can start in the middle of a message
        // in this case, these "garbage-bytes" have to be skipped
        if (beginning_skipping_garbage_bytes)
        {
            if (is_skipping_garbage_byte(byte))
            {
                continue;
            }
            else
            {
                beginning_skipping_garbage_bytes = false;
                bytePositionInUARTMessage = 0;
                height = 0.0;
            }
        }

        handle_byte(byte, bytePositionInUARTMessage, height);
    }
}

Managing how the bytes of the uart-message is parsed to a valid height.

void Desktronic::handle_byte(const uint8_t byte, int& bytePosition, double& height)
{
    switch (bytePosition)
    {
    case 0: // everytime 0x5A
        bytePosition = 1;
        break;
    case 1:
        // 70, 80, 90, 100
        height += get_tens_digit(byte);
        bytePosition = 2;
        break;
    case 2:
        // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
        height += get_units_digit(byte);
        bytePosition = 3;
        break;
    case 3:
        // 0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9
        height += get_first_decimal_digit(byte);
        bytePosition = 4;
        break;
    case 4: // everytime 0x01
        bytePosition = 5;
        break;
    case 5: // the uart-message is complete
        current_pos_ = height;

        // update the height-sensor
        if (height_sensor_)
        {
            // accuracy is set in __init__.py
            height_sensor_->publish_state(height);
        }

        // reset the height and position
        bytePosition = 0;
        height = 0.0;

        break;
    default:
        break;
    }
}

I unfortunately had to do this with those static-variables because of the problematic of the garbage_bytes at the beginning. Because of this I cannot read out 6 bytes at once.

My Yaml-File looks like this:

...

################################################
# Ready for production (this should word as expected)
################################################

external_components:
  - source:
      type: git
      url: https://github.com/MhouneyLH/esphome_custom_components
      # ref: feat/desktronic
      # ref: feat/test
      ref: feat/old_test
      # ref: feat/without_map
    refresh: 10s
    components: [ desktronic ]

uart:
  - id: desk_uart
    rx_pin: 1
    baud_rate: 9600

desktronic:
  id: my_desktronic
  height:
    name: DeskHeight
  up:
    number: 14
  down:
    number: 12
  request:
    number: 4
  stopping_distance: 0
  timeout: 5s

Thats the result for now that makes me pretty happy for now :slight_smile: (its on less fps; on my pc it is realtime to the moving-desk):
height_sensor

Problems

I came across 2 problems:

  1. The flash-size is of my esp8266 is not enough to story the current program + the generated copy of it as I already use round about 65% of the space. So I have to clear out the whole esp in order to have the newest software on the device. I guess the easiert solution for that is to buy another esp with more flash-size.
  2. Sometimes I have problems to be connected to the esp. (When I am installing the new software on the device) The error message after waiting about 30secs:

    After that message it automatically connects. Maybe somebody had to deal with something like this in the past and know what to do about it.

Next Step

Now where I have this working state I am trying to move the desk via software.

1 Like

Great progress!

I would get an esp32 for sure.

Sounds like byte 6 is a checksum.

You can try using a static IP in improve connection time.

1 Like

Esp32 looks quite good. 4 Mb of Flash-Memory and 530 Kb SRAM.

image

I already tried using a static IP. This does not speed up the process for me.

1 Like

You can also look at this thread for recommendations.

Do you have the static IP set both in your router and your esp config?

You can also try fast_connect.

Yes, but unfortunately this also does not work. But thats not a big problem for now.

I had already made a component for this type of desk. The height bytes are actually the 7-segment display output values. If you put an ESP32 between the display and the desk, my component can read the height value and the buttons you press. I’m currently working on adding the capability for moving the desk.

external_components:
  - source:
      type: git
      url: https://github.com/ssieb/custom_components
      ref: jsdrive
    components: [ jsdrive ]
2 Likes

Wow, im currently on the same state. Then I will try to work with your code and try to figure out how to move the desk by software.

I faced some questions using your solution @ssieb. What is meant by desk_uart and remote_uart? I don’t know what this is supposed to do. For now I created an uart-object in yaml. But for this solution I need 2 I guess?

It should be as you guess I’m relatively sure. You need to define two uarts.

One is from the perspective of what is being sent/received by the control box, the other from the perspective of the control panel.

1 Like

But why do we need 2 uarts? Thats what I don’t really get.

I probably can’t explain so well.

But I think it’s because that it’s “two way communication”, you need to be able to both read AND write to each of the tx and rx.

My guess/ understanding…
.

1 Like

I also understand, that these are the segment-values. But this works only for the first decimal-position and the tens-digits, like you can see in this screenshot:
image
Interesting is, that at the Units, the last 4 bits are the same, but the first 4 ones are different. (0xBF0x3F; 0x860x06; …)

1 Like

Nevermind. I got the example working in my own repo. I made a new branch for using your code-snippts: GitHub - MhouneyLH/esphome_custom_components at feat/using_ssieb_code

I had to fix some things. This weekend I will try to get the desk moving with this new (and better + lightweight) version from you @ssieb. Thanks for this amazing hint on your repo! Until now I had always overlooked the different branches of your repo.

1 Like

Great News - The desk is movable by software!

Summary of what I did the last few days

I already updated my repository on GitHub: GitHub - MhouneyLH/esphome_custom_components: A collection of custom components for esphome.


The desk can move up and down. Finally. I used the G-Pin (the second one) for showing the controller-box that something has to move. I augmented my adapter-construction with another white-cable for the touchpad-tx (in the code this is remote-uart). With that one I can write the corresponding messages via uart.


But how does the desk know, if it should go up or down?
I have defined a function called move_to(target_height). It checks the boundaries, sets the new target-height and updates the current-operation based on the current-height:

void Desktronic::move_to(const float height_in_cm)
{
    if (height_in_cm < MIN_HEIGHT || height_in_cm > MAX_HEIGHT)
    {
        ESP_LOGE(TAG, "Moving: Height must be between 72.0 and 119.0 cm");

        return;
    }

    target_height_ = height_in_cm;
    current_operation = must_move_up(height_in_cm) ? DESKTRONIC_OPERATION_RAISING : DESKTRONIC_OPERATION_LOWERING;
}

The main-loop looks like this (no need to move, if we are idling anyway):

void Desktronic::loop()
{
    read_desk_uart();

    if (current_operation == DesktronicOperation::DESKTRONIC_OPERATION_IDLE)
    {
        read_remote_uart();
        return;
    }

    move_to_target_height();
}

The read_desk_uart()-method updates the current_height_. That’s pretty important for the later check, if the target-height is reached or if the desk should move further more. This check you will see in the move_to_target_height()-method.

The interesting part is the move_to_target_height()-method. Summarized, I do some checks at the beginning, check wether I have to move up or down and do that until I reach the wished height (inside of the target-boundaries). The target boundaries are defined with a deviation from the target height of 0.6cm. The move_pin_ is the G-Pin in this case, which has to be high when moving:

void Desktronic::move_to_target_height()
{
    if (!move_pin_)
    {
        ESP_LOGE(TAG, "Moving: Move pin is not configured");
        return;
    }

    if (!remote_uart_)
    {
        ESP_LOGE(TAG, "Moving: Remote UART is not configured");
        return;
    }

    if (!isCurrentHeightValid())
    {
        ESP_LOGE(TAG, "Moving: Height must be between 72.0 and 119.0 cm");
        move_pin_->digital_write(false);
        current_operation = DesktronicOperation::DESKTRONIC_OPERATION_IDLE;

        return;
    }

    move_pin_->digital_write(true);
    switch (current_operation)
    {
    case DesktronicOperation::DESKTRONIC_OPERATION_RAISING:
        ESP_LOGE(TAG, "Moving: Up");
        for (int i = 0; i < REMOTE_UART_SEND_MESSAGE_COUNT; i++)
        {
            remote_uart_->write_array(REMOTE_UART_MESSAGE_MOVE_UP, REMOTE_UART_MESSAGE_LENGTH);
        }

        break;
    case DesktronicOperation::DESKTRONIC_OPERATION_LOWERING:
        ESP_LOGE(TAG, "Moving: Down");
        for (int i = 0; i < REMOTE_UART_SEND_MESSAGE_COUNT; i++)
        {
            remote_uart_->write_array(REMOTE_UART_MESSAGE_MOVE_DOWN, REMOTE_UART_MESSAGE_LENGTH);
        }

        break;
    default:
        return;
    }

    if (isCurrentHeightInTargetBoundaries())
    {
        ESP_LOGE(TAG, "Moving: Finished");
        move_pin_->digital_write(false);
        current_operation = DesktronicOperation::DESKTRONIC_OPERATION_IDLE;
    }
}

I was also able to reduce the flash memory to 44%. I removed unneeded header files as well as yaml configurations that are useless now. My yaml file now looks like this:

esphome:
  name: esphome-web-755eb2

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: ...

ota:

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

  manual_ip:
    static_ip: ...
    gateway: ...
    subnet: ...

external_components:
  - source:
      type: git
      url: https://github.com/MhouneyLH/esphome_custom_components
      ref: develop
    refresh: 4s
    components: [ desktronic ]

uart:
  - id: desk_bus
    tx_pin: 5 # D1
    rx_pin: 4 # D2
    baud_rate: 9600
  - id: remote_bus
    tx_pin: 3 # RX
    rx_pin: 1 # TX
    baud_rate: 9600

desktronic:
  id: my_desktronic
  desk_uart: desk_bus
  remote_uart: remote_bus
  height:
    name: Desk Height
  move_pin:
    number: 14 # D5
  up:
    name: Up Button
  down: 
    name: Down Button
  memory1:
    name: Memory1 Button
  memory2:
    name: Memory2 Button
  memory3:
    name: Memory3 Button

switch:
  - id: move_switch
    name: ↑↓
    platform: gpio
    pin:
      number: 2 # D4
      inverted: true
    on_turn_on:
      then:
       - lambda: id(my_desktronic).move_to(80.0);
      #  - lambda: id(my_desktronic).stop();

binary_sensor:
  - id: is_moving_bsensor 
    name: Desk is moving
    platform: template
    lambda: return id(my_desktronic).current_operation != desktronic::DESKTRONIC_OPERATION_IDLE;

For more information about the code, etc. you can check the mentioned repository. With this state of the project, I accomplished a major (and the most difficult intermediate step) of the planned project. :slight_smile: Thank you, especially @Mahko_Mahko and @ssieb!

The next steps and goals

  • currently it is not possible to see the current height or other stuff on the touchpad, when the remote-tx is connected to the chip.
    → potential solution: use a third uart-bus for sending the remote-tx-data separately.
  • I want to create a more beautiful ui in HomeAssistant.
  • I want to connect a weight-sensor to the home-assistant eco-system. The weight-sensor lays under my chair and if I sit down, the desk automatically goes down.
  • add 2 methods with which I can adjust the height of the desk one by one. (so I dont have to give it a fixed height everytime)
  • improving the hardware circumstances: At the moment, I have soldered the chip on improperly. I should now do this again properly because I noticed in between that there were some loose contacts.
2 Likes

Nice one!

For chair occupancy take a look at items 1 and 13 here. You might find a battery operated device works better for a chair.

1 Like

Do you have a wiring diagram you could share please?

I’m actually working on a similar project that uses a pass through set-up and I’m not sure how to wire for reading and writing to both the rx and tx. I understand you had to do something like this for your set-up. Sorry if I’ve missed it.

https://community.home-assistant.io/t/i-scored-a-scorbot-er-iii/540324/2?u=mahko_mahko

1 Like

I could create a diagram. But this weekend I’m busy with other stuff. I could create one next week. :slight_smile:

1 Like