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.
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.
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
I could create a diagram. But this weekend Iām busy with other stuff. I could create one next week.
Hey, I wanted to give a short update.
@Mahko_Mahko I did not have forgotten the diagram, but I did not had the time since a few weeks.
Thats a not completely finished diagram, if you still need it:
Currently I am working on a project to control the desk via app on a smartphone. The progress you can see in this repository. A friend of mine also made a nicer connection to the chip with properly soldered cables.
Nice. Thanks for getting back to me. I figured it out with some help from some people on Discord though.
My āMan in the middleā wiring looks like this.
The green wire both intercepts and injects uart messages. For the yellow wire I only need to read the messages.
I need the max3232 convertors to convert the 12v logic to 5v and back again.
Thank you for the great project base!
I have a similar odd desk.
I am embarrassed to be still very confused about the wiring to the ESP dongle between desk and the controller, and how the UART wires should be.
The cable itself has 10 pins, which i counted after ordering the expanders from Aliexpress with 8 pins But i discarded the outermost ones that control the 12 Volts, and it almost works, it fits to the base nicely and i can use the inner 8.
But here are the problems:
void move_to_from(const float height_in_cm, const float current_height);
void move_to(const float height_in_cm);
void stop();
void move_up();
void move_down();
void move_to_memory_1();
void move_to_memory_2();
So about wiring:
- id: remote_bus
tx_pin: 1 # when WHITE - allows to control the desk
rx_pin: 3 # when WHITE - reads sensor data from controller
# together they break
- id: desk_bus
rx_pin: 17 # no combo does anything
tx_pin: 16 # no combo does anything
Iām ashamed to have spent too much time on this, and now I just want to finish it out of spite.
Tried the uart_mitm component for no avail.
So I almost hacked out the height from desk, logging and reading the hex data, parsing it to numbers (without proper wiring):
debug:
direction: BOTH
dummy_receiver: true
after:
delimiter: "\xA5"
sequence:
- lambda: |-
UARTDebug::log_hex(direction , bytes, ','); //Still log the data
auto uart_direction = "IDK";
if (direction = uart::UART_DIRECTION_TX ){
uart_direction = "TX: ";
}
else if (direction = uart::UART_DIRECTION_RX ){
uart_direction = "RX_ ";
}
auto bytes_size = bytes.size();
std::string log_message = uart_direction;
for(int i = 0; i < bytes_size; ++i){
char buffer[4];
sprintf(buffer, "%02x ", bytes[i + bytes_size - 1 ]);
log_message += buffer;
}
if ( bytes_size >= 5){
ESP_LOGW("desk_bus", "%s", log_message.c_str());
}
else {
ESP_LOGI("desk_bus", "%s", log_message.c_str());
}
even got this far as to read the height value out of the uart buffer, somewhat semi-consistently:
script:
- id: get_debug_height
mode: single
then:
- lambda: |-
// provided map gives error on "4" // data[2] is data2 = EDGE CASE if contain 4 (74, 84, 94, 104, 114, 124)
// static const int SEGMENT_MAP[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x67, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
// 0 1 2 3 4 5 6 7 8 9
static const int SEGMENT_MAP[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x67, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
//0x66
uint8_t data[5];
while (id(desk_uart).available() >= 5) {
id(desk_uart).read_array(data, 5);
if (data[0] != 0x5A) {
ESP_LOGE("seisuk", "[0] %02x must be 0x5A, [3] is %02x", data[1], data[3]);
//break;
continue;
}
if ((data[1] | data[2] | data[3]) == 0x00) {
ESP_LOGD("seisuk", "null height");
//break;
continue;
}
int data0 = -1, data1 = -1, data2 = -1;
for (int i = 0; i < 10; i++) {
if (data[1] == SEGMENT_MAP[i]) data0 = i;
if (data[2] == SEGMENT_MAP[i]) data1 = i;
if (data[3] == SEGMENT_MAP[i]) data2 = i;
}
float got_height = 0.0;
if (data0 < 0 || data1 < 0 || data2 < 0) {
if (data0 < 0 ) {
ESP_LOGE("data", "data0: %02x", data[1] ); // if ( data[1] != 0x00 ) { }
}
if (data1 < 0 ) {
// true for values above 100, false for below 100
ESP_LOGW("data", "data1: %02x", data[2] ); // if ( data[2] != 0x00 ) { }
}
if (data2 < 0 ) {
ESP_LOGW("seisuk", "data2: %02x", data[3] );
}
//break;
continue;
}
// get decimal
int decimal = ((~data[4] + 256) % 256) / 10;
// sum if up
got_height = data0 * 100 + data1 * 10 + data2 + decimal * 0.1;
add decimal
//if (got_height >= 100.0){
// got_height += decimal * 0.1;
//}
// If belo 3 digis, e.g 100
// INVERT HEIGHT FOR BELOW ZERO
if (data[2] & 0x80 && data1 < 0) {
got_height = got_height / 10 + 10;
float under_99 = ( ( ~data[4] + 256 ) % 256 );
if (under_99 > 100 ){
//under_99 /= 10;
}
float inverted = ( ( data[4] + 256 ) % 256); // + 100;
if (inverted > 100 ){
//inverted /= 10;
}
float reinverted = ( ( data[4] ) % 256) / 2; // + 100;
if (reinverted < 10 ){
//reinverted *= 10;
}
if (under_99 < 101 && under_99 > 65 ){ // && under_99 != 68.00
ESP_LOGI("under_99", "%.2f", under_99 );
}
if (inverted < 101 && inverted > 65 ){
ESP_LOGW("inverted", "%.2f", inverted );
}
if (reinverted < 101 && reinverted > 65 ){ // && reinverted != 100.00
ESP_LOGE("reinverted", "%.2f", reinverted );
}
if (data1 < 0 ) {
// ESP_LOGE("data1", "%02x", data[2] ); // if ( data[2] != 0x00 ) { }
}
if (under_99 <= 100 && under_99 > 66 ){
//got_height = under_99;
//ESP_LOGI("got_height", "%.2f", under_99);
}
if (got_height <= 100 ){
ESP_LOGI("lt100", " %.1f", got_height );
}
}
// IF BELOW 100, divide by 10, get decimal place
ESP_LOGW("height", "data[2]: %02x, data1: %d", data[2], data1);
// IF ALL IS CORRECT, PUBLISH VALUES, DO STUFF
if (got_height <= id(max_height) && got_height >= id(min_height) ){
ESP_LOGI("height", "%.1f ", got_height );
id(uart_height).publish_state(got_height);
id(height) = got_height;
}
if (got_height >= id(max_height) || got_height <= id(min_height) ){
id(seisuk).stop();
}
}
I havenāt been following this in detail, but based on my experiences with another project, here is how I believe uart wiring needs to be for two controlling devices to be able to issue commandsā¦
If you only need to āread signals on a wireā (listen to them, let them āpass throughā, but not inject your own commands), then you can just āHopā the wire. See yellow wire.
If you need to both read signals on the wire and inject your own (listen for keypad presses and insert your own from the ESP), then the wire must only pass through the ESP via a āuart forwardā. This is where I got stuck for a long time with my project. I believe this is key to a āMan-In-The-Middleā (MITM) set-up. See green wire. If you try to hop a signal you need to both read and write from, it wonāt work when both devices are connected.
In my diagram, you can pretend the convertors arenāt there (wires go straight through). Pendant is control panel. Robot is desk control box.
Iām not 100% if this applies to this project, but it took me a while to figure this out so sharing in case it is of use.
I also found for my project that the MITM may introduce a small lag as it recieves and forwards uart messages. This may or may not create issues if communication is two way (there is a request that waits for a response and may timeout).
As wire colors are different, I only have black and white to play with, and red for control takeover. I use green/purple to power the ESP. Yellow and Brown are ignored because I messed up buying the 8-pin over 10-pin cable expander This means I cant get USB power from the control panel, but thatās okay for nowā¦
I can do without knowing the binary sensors for if controller button is pressed, i just would like to get the desk height, so i can move it to set height. Currently using my modified code i can move it up and down with switches, but since i dont know the height, i cant set the positionā¦
P ā Key1 # RED = P (Panel?)
R ā D-TX # BLACK = Rx
T ā D-RX # WHITE = TX
5 ā +5V # GREEN = 5g
G ā G # PURPLE = GND
YELLOW = DC + 12v
BROWN = DC_G - 12v
Are the height messages the same protocol as the OP? I assume they are on the black wire?
Good find, they are not exactly the same. As when using the original external component, I didnāt get anything as height, but with my custom lambda script at least something did come outā¦
// provided map gives error on "4" // data[2] is data2 = EDGE CASE if contain 4 (74, 84, 94, 104, 114, 124)
// static const int SEGMENT_MAP[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x67, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
// 0 1 2 3 4 5 6 7 8 9
static const int SEGMENT_MAP[10] = {0x3f, 0x06, 0x5b, 0x4f, 0x67, 0x6d, 0x7d, 0x07, 0x7f, 0x6f};
using the original map i logged some weird jumps, and missing numbers (the component provided sensor didnāt work still):
5A,07,FF,7F,85 -> 138.0
5A,07,FF,7F,85 -> 138.0
5A,07,FF,6F,75 -> 136.0
5A,07,FF,6F,75 -> 136.0
5A,07,EF,3F,35 -> 113.0
5A,07,EF,3F,35 -> 113.0
5A,07,EF,06,FC -> 110.0
5A,07,EF,06,FC -> 110.0
5A,07,EF,5B,51 -> 112.0
5A,07,EF,4F,45 -> 113.0
5A,07,EF,4F,45 -> 113.0
5A,07,EF,6D,63 -> 114.0
5A,07,EF,6D,63 -> 114.0
5A,07,EF,07,FD -> 110.0
5A,07,EF,07,FD -> 110.0
5A,07,EF,6F,65 -> 115.0
5A,07,EF,6F,65 -> 115.0
5A,7D,FD,4F,C9 -> 64,9
5A,7D,FD,5B,D5 -> 62,5
5A,7D,FD,3F,B9 -> 60,9
5A,7D,ED,6F,D9 -> 79,9
5A,7D,ED,07,71 -> 77,1
5A,7D,ED,6D,D7 -> 75,7
5A,7D,ED,66,D0 -> 74,0
5A,7D,ED,4F,B9 -> 73,9
5A,7D,ED,5B,C5 -> 72,5
5A,7D,ED,06,70 -> 71,0
5A,7D,ED,3F,A9 -> 70,9
5A,7D,FD,6D,E7 -> 65,4
5A,7D,FD,07,81 -> 67,1
5A,7D,FD,7F,F9 -> 68,9
5A,7D,FD,6F,E9 -> 69,9
5A,7D,FD,4F,C9
5A,7D,FD,5B,D5 -> 62,5
5A,7D,FD,3F,B9 -> 60,9
5A,7D,ED,6F,D9 -> 79,9
5A,7D,ED,07,71 -> 77,1
5A,7D,ED,6D,D7 -> 75,7
5A,7D,ED,66,D0 -> 74,0
5A,7D,ED,4F,B9 -> 73,9
5A,7D,ED,5B,C5 -> 72,5
5A,7D,ED,06,70 -> 71,0
5A,7D,ED,3F,A9 -> 70,9
5A,7D,FD,6D,E7 -> 65,4
5A,7D,FD,07,81 -> 67,1
5A,7D,FD,7F,F9 -> 68,9
5A,7D,FD,6F,E9 -> 69,9 - bottom target reached
Got it working through some ugly hacks Forgot to post, hope this will help someone else. Buttons on keypad and the screen is not working though, maybe iāll deal with it in the future. Maybe even add some fun things, since its possible to write random stuff on the 7-segment-display Ā· GitHub Topics Ā· GitHub
substitutions:
friendly_name: 'Seisuk'
device_name: 'seisuk'
node_name: 'seisuk'
device_description: 'Seisuk AOKE WP-H01 WP-CB01-001 Desktronic/JSDRIVE DCU_G-PRT5G'
project_base: 'AOKE' # https://aoke-europe.com/troubleshooting
project_name: 'WP-H01' # panel
project_version: 'WP-CB01-001' # control box # https://aoke-europe.com/mwdownloads/download/link/id/88/
# https://profeqprofessional.nl/media/productattach//e/r/error_codes_wp-cb01-001_2.pdf
# https://motionwise-products.com/wp-content/uploads/2018/11/MotionWise_sf_Manual_081318_E_Rev-USB.pdf
# https://profeqprofessional.nl/media/productattach//e/r/error_codes_wp-cb01-001_2.pdf
min_height: "65.5" # real 65 # Min height + 0.5
max_height: "129.5" # real 130 # Max height - 0.5
initial_value: "90"
globals:
- id: height
type: float
restore_value: yes
initial_value: '100.0'
- id: min_height
type: float
restore_value: yes
initial_value: '65.5'
- id: max_height
type: float
restore_value: yes
initial_value: '129.5'
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
name_add_mac_suffix: false
comment: ${device_description}
project:
name: ${project_base}.${project_name}
version: $project_version
on_boot:
- priority: 600.0
then:
- button.press: stop
- priority: -200.0
then:
- lambda: |-
id(uart_height).publish_state( id(height) );
esp8266:
board: nodemcu
early_pin_init: false
restore_from_flash: true
packages:
device_base: !include common/device.base.yaml
ota:
on_begin:
then:
- button.press: stop
logger:
esp8266_store_log_strings_in_flash: false
level: DEBUG
baud_rate: 0
uart:
- id: remote_bus
tx_pin: 1 # WHITE (2nd pin)
rx_pin: 3 # N/A (3rd pin)
baud_rate: 9600
- id: desk_bus
tx_pin: 4 # N/A (D2)
rx_pin: 5 # BLACK (D1)
baud_rate: 9600
rx_buffer_size: 256
external_components:
- source: components
components: [ desktronic ]
desktronic:
id: seisuk
desk_uart: desk_bus
remote_uart: remote_bus
height:
name: Wnotworkign desktronic Height
id: desk_height_sensor
icon: mdi:human-male-height
device_class: distance
unit_of_measurement: cm
accuracy_decimals: 1
internal: true
move_pin:
number: 14 # (D5)
binary_sensor:
- id: moving
name: Moving
platform: template
device_class: moving
icon: mdi:hand-back-right-off
lambda: return id(seisuk).current_operation != desktronic::DESKTRONIC_OPERATION_IDLE;
on_state:
- while:
condition:
binary_sensor.is_on: moving
then:
- script.execute: get_debug_height
- delay: 50ms
- id: going_up
name: Moving up
platform: template
device_class: moving
icon: mdi:transfer-up
entity_category: diagnostic
lambda: return (id(seisuk).current_operation == desktronic::DESKTRONIC_OPERATION_RAISING) || (id(seisuk).current_operation == desktronic::DESKTRONIC_OPERATION_UP);
- id: going_down
name: Moving down
platform: template
device_class: moving
icon: mdi:transfer-down
entity_category: diagnostic
lambda: return (id(seisuk).current_operation == desktronic::DESKTRONIC_OPERATION_LOWERING) || (id(seisuk).current_operation == desktronic::DESKTRONIC_OPERATION_DOWN);
sensor:
- id: uart_height
platform: template
name: Height
icon: mdi:human-male-height
device_class: distance
unit_of_measurement: cm
accuracy_decimals: 1
update_interval: never
lambda: |-
float current = id(height);
return current;
number:
- platform: template
id: desk_height
name: Height
icon: mdi:human-male-height-variant
min_value: $min_height
max_value: $max_height
lambda: |-
return id(height);
device_class: distance
unit_of_measurement: cm
update_interval: never
mode: box
step: 0.1
set_action:
then:
- while:
condition:
- lambda: |-
float to = std::round(x * 10) / 10;
float from = std::round(id(height) * 10) / 10;
if ( to != from ) {
return true;
}
else{
return false;
}
then:
- script.execute:
id: move_it
to: !lambda 'return x;'
- delay: 150ms
cover:
- platform: template
name: None
optimistic: true
device_class: damper
icon: mdi:desk
assumed_state: true
has_position: true
lambda: |-
auto value = (int)id(height);
float percentage = (float)(value - id(min_height)) / (id(max_height) - id(min_height));
return percentage;
open_action:
- number.set:
id: desk_height
value: $max_height
close_action:
- number.set:
id: desk_height
value: $min_height
stop_action:
- button.press: stop
position_action:
- number.set:
id: desk_height
value: !lambda |-
int value = id(min_height) + (int)( ( pos ) * (id(max_height) - id(min_height)) );
return value;
button:
- platform: template
name: Stop
icon: mdi:stop
entity_category: config
id: stop
on_press:
then:
- switch.turn_off: takeover
- script.stop: move_it
- switch.turn_off: switch_up
- switch.turn_off: switch_down
- lambda: |-
id(seisuk).stop();
- number.set:
id: desk_height
value: !lambda |-
float x = id(height);
return x;
- component.update: desk_height
switch:
- platform: gpio
name: Takeover
icon: mdi:gesture-tap
id: takeover
pin:
number: 14 # (D5)
mode: OUTPUT
restore_mode: ALWAYS_OFF
entity_category: diagnostic
disabled_by_default: true
on_turn_on:
then:
- delay: 50ms
- switch.turn_off: takeover
- platform: template
optimistic: true
name: Move Up
entity_category: config
restore_mode: ALWAYS_OFF
icon: mdi:arrow-up-bold-box
id: switch_up
on_turn_on:
then:
- switch.turn_on: takeover
- switch.turn_off: switch_down
- lambda: |-
id(seisuk).move_up();
on_turn_off:
then:
button.press: stop
- platform: template
name: Move Down
entity_category: config
restore_mode: ALWAYS_OFF
optimistic: true
icon: mdi:arrow-down-bold-box
id: switch_down
on_turn_on:
then:
- switch.turn_on: takeover
- switch.turn_off: switch_up # interlock: [up]
- lambda: |-
id(seisuk).move_down();
on_turn_off:
then:
button.press: stop
script:
- id: move_it
parameters:
to: float
mode: restart
then:
- if:
condition:
- binary_sensor.is_off: moving
then:
- lambda: |-
float target = std::round(to * 10) / 10;
float from = std::round(id(height) * 10) / 10;
ESP_LOGI("seisuk", "target %.1f from %.1f", target, from);
if ( target != from ) {
if ( target < from ) {
id(switch_down).turn_on();
}
else if ( target > from ) {
id(switch_up).turn_on();
}
else if ( target == from ) {
id(stop).press();
ESP_LOGI("seisuk", "target == from");
}
else {
id(stop).press();
ESP_LOGI("seisuk", "else height same");
}
}
else{
id(stop).press();
ESP_LOGI("seisuk", "height not not same");
}
else:
- if:
condition:
- binary_sensor.is_on: going_up
then:
- lambda: |-
float target = std::round(to * 10) / 10;
float from = std::round(id(uart_height).state * 10) / 10;
ESP_LOGI("seisuk", "moving to %.1f from %.1f", target, from);
if ( target <= from ) {
id(stop).press();
ESP_LOGE("seisuk", "height more");
auto call = id(desk_height).make_call();
call.set_value(id(uart_height).state);
call.perform();
}
- if:
condition:
- binary_sensor.is_on: going_down
then:
- lambda: |-
float target = std::round(to * 10) / 10;
float from = std::round(id(uart_height).state * 10) / 10;
ESP_LOGI("seisuk", "moving to %.1f from %.1f", target, from);
if ( target >= from ) {
id(stop).press();
ESP_LOGE("seisuk", "height less");
auto call = id(desk_height).make_call();
call.set_value(id(uart_height).state);
call.perform();
}
- delay: 100ms
- id: get_debug_height
mode: single
then:
- lambda: |-
// before continuing tell its not really moving
// id(moving).publish_state(false);
// id(going_up).publish_state(false);
// id(going_down).publish_state(false);
uint8_t data[5];
while (id(desk_bus).available() >= 5) {
id(desk_bus).read_array(data, 5);
if (data[0] != 0x5A) {
break;
}
if ((data[1] | data[2] | data[3]) == 0x00) {
break;
}
enum Segment : uint8_t
{
SEGMENT_INVALID = 0x00,
SEGMENT_0 = 0x3f,
SEGMENT_1 = 0x06,
SEGMENT_2 = 0x5b,
SEGMENT_3 = 0x4f,
SEGMENT_4 = 0x67,
SEGMENT_5 = 0x6d,
SEGMENT_6 = 0x7d,
SEGMENT_7 = 0x07,
SEGMENT_8 = 0x7f,
SEGMENT_9 = 0x6f,
};
auto segment_to_number = [](const uint8_t segment) {
switch (segment & 0x7f)
{
case SEGMENT_0:
return 0;
case SEGMENT_1:
return 1;
case SEGMENT_2:
return 2;
case SEGMENT_3:
return 3;
case SEGMENT_4:
return 4;
case SEGMENT_5:
return 5;
case SEGMENT_6:
return 6;
case SEGMENT_7:
return 7;
case SEGMENT_8:
return 8;
case SEGMENT_9:
return 9;
default:
ESP_LOGD("desktronic", "idk");
}
return -1;
};
int data0 = segment_to_number(data[1]);
int data1 = segment_to_number(data[2]);
int data2 = segment_to_number(data[3]);
float got_height = 0.0;
if (data0 < 0x00 || data1 < 0x00 || data2 < 0x00) {
break;
}
got_height = data0 * 100 + data1 * 10 + data2; // + decimal * 0.1;
// flip height for below 100
if (data[2] & 0x80 ) {
got_height /= 10.0;
}
// If all correct, publish values
if (got_height <= id(max_height) - 0.5 && got_height >= id(min_height) + 0.5 ){
id(height) = got_height;
id(uart_height).publish_state(got_height);
}
if (got_height >= id(max_height) || got_height <= id(min_height) ){
// id(stop).press();
ESP_LOGE("seisuk","stopped, out of bounds");
}
// now its moving
// id(moving).publish_state(true);
// id(going_up).publish_state(true);
// id(going_down).publish_state(true);
}
- delay: 50ms
Converting it to template cover is really nice, and allows exposing to to Google Home for voice control
Thanks again for the code @MhouneyLH!
Hey @veli, the interface for controlling the desk in HomeAssistant looks pretty good! I will have a look at it, as this was my first project using HomeAssistant
I havenāt been working on the desk project for the last few months, so I havenāt checked the forum at all. Sorry about that. But Iām glad to see itās working for you now. It took me a few weeks to figure out how to modify the desk. You should not feel bad about the time you invested. The result (and of course the adventure) is what counts
I am currently working on an app that will allow you to control the desk with your smartphone. For the app I will have to update the component a bit. I will try to keep this thread updated. I donāt know if anyone would use it, but theoretically you could test the app on your desk.
An app separate from HA/ESPHome? Happy to test it, but personally I use HA to get rid of extra apps
I had a small business plan (too lazy to execute it, feel free to steal or collaborate with me) of creating the dongles to sell to our local standing desk manufacturer. An app would for sure be a necessity for that since their customers woult not use HA or like to connect to the ESP AP to control itā¦
I rewrote bits of the component, hence using it as this - public functions, more binary sensors, etc:
- source: components
components: [ desktronic ]
A friend of mine and I also had this business plan. But at the moment (when the app is ready) it is in the first place just a fun gadget to use. Maybe the business plan will be executed in the future, but currently Iām too lazy too.
Of course, people who use HomeAssistant, probably wonāt use the app the as you explained.
Technically, JS already has a BT adapter on the market: Standing Desk Bluetooth from China manufacturer - JS Technology
No WiFi adapter yet.
I have the same jsm-2.1-0.3w controller in my desk, however the control panel pinout seems to be quite different (as well as the colours for the pins).
The RJ50 end has colours in order of brown, black, white, orange, red, blue, white, red, green and yellow.
The controller on the other hand, uses blue, green, white, black, red, brown and yellow (with a not connected pin between red and brown).
The labels are, same order as the colours: āGā, ā5ā, āTā, āRā, āPā, NC, āU_Gā, āDCā
The cabling actually seems to line up with @MhouneyLHās from the first post, with the main difference limited to a colour change - purple, aka āGā is blue on mine, purple on theirs.
Iāve ordered an RJ50 male to female short cable (30cm) that Iām going to cannibalize and embed an ESP32 in a small through-package.
Oh and I also managed to coax out a bit of documentation from the seller of my desk, EYOJYA - theyāve sent over the documentation for a BLUETOOTH adapter they donāt even have on sale! Nonetheless hereās the PDF, it seems to use the same, or at the very least, incredibly similar command structure, even if itās kinda full of typos.
@veli would you mind sharing your changes to the desktronic component?
Oh, and also, JS also has some quite much fancier versions of the controllers, with (apparently controllable?) RGB lighting, motorised knob, and a proper OLED display:
In the desktronic.h, just moved things from private to public so i can lambda call id(desk).move_up()
and down. Will check for any not needed test comments in files, fork the OP code on github and perhaps we can make it work without hacks
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/uart/uart.h"
#include "esphome/components/binary_sensor/binary_sensor.h"
#include "esphome/components/number/number.h"
namespace esphome
{
namespace desktronic
{
enum DesktronicOperation : uint8_t
{
DESKTRONIC_OPERATION_IDLE = 0U,
DESKTRONIC_OPERATION_RAISING,
DESKTRONIC_OPERATION_LOWERING,
DESKTRONIC_OPERATION_UP,
DESKTRONIC_OPERATION_DOWN,
DESKTRONIC_OPERATION_MEMORY_1,
DESKTRONIC_OPERATION_MEMORY_2,
};
enum Segment : uint8_t
{
SEGMENT_INVALID = 0x00,
SEGMENT_0 = 0x3f,
SEGMENT_1 = 0x06,
SEGMENT_2 = 0x5b,
SEGMENT_3 = 0x4f,
SEGMENT_4 = 0x67,
SEGMENT_5 = 0x6d,
SEGMENT_6 = 0x7d,
SEGMENT_7 = 0x07,
SEGMENT_8 = 0x7f,
SEGMENT_9 = 0x6f,
};
enum MovingIdentifier : uint8_t
{
MOVING_IDENTIFIER_UP = 0x20,
MOVING_IDENTIFIER_DOWN = 0x40,
MOVING_IDENTIFIER_MEMORY_1 = 0x02,
MOVING_IDENTIFIER_MEMORY_2 = 0x04,
MOVING_IDENTIFIER_MEMORY_3 = 0x08,
MOVING_IDENTIFIER_MEMORY_4 = 0x10,
};
static const char* desktronic_operation_to_string(const DesktronicOperation operation);
static int segment_to_number(const uint8_t segment);
class Desktronic : public Component
{
public:
float get_setup_priority() const override { return setup_priority::LATE; }
void setup() override;
void loop() override;
void dump_config() override;
void set_remote_uart(uart::UARTComponent* uart) { remote_uart_ = uart; }
void set_desk_uart(uart::UARTComponent* uart) { desk_uart_ = uart; }
void set_move_pin(GPIOPin* pin) { move_pin_ = pin; }
void set_height_sensor(sensor::Sensor* sensor) { height_sensor_ = sensor; }
void set_up_bsensor(binary_sensor::BinarySensor* sensor) { up_bsensor_ = sensor; }
void set_down_bsensor(binary_sensor::BinarySensor* sensor) { down_bsensor_ = sensor; }
void set_memory1_bsensor(binary_sensor::BinarySensor* sensor) { memory1_bsensor_ = sensor; }
void set_memory2_bsensor(binary_sensor::BinarySensor* sensor) { memory2_bsensor_ = sensor; }
void set_memory3_bsensor(binary_sensor::BinarySensor* sensor) { memory3_bsensor_ = sensor; }
void move_to_from(const float height_in_cm, const float current_height);
void move_to(const float height_in_cm);
void stop();
void move_up();
void move_down();
void move_to_memory_1();
void move_to_memory_2();
public:
DesktronicOperation current_operation{DesktronicOperation::DESKTRONIC_OPERATION_IDLE};
private:
void read_remote_uart();
void read_desk_uart();
void publish_remote_states(const uint8_t data);
void reset_remote_buffer();
void reset_desk_buffer();
bool must_move_up(const float height_in_cm) const;
void move_to_target_height();
bool isCurrentHeightValid() const;
bool isCurrentHeightInTargetBoundaries() const;
protected:
uart::UARTComponent* remote_uart_{nullptr};
uart::UARTComponent* desk_uart_{nullptr};
GPIOPin* move_pin_{nullptr};
sensor::Sensor* height_sensor_{nullptr};
binary_sensor::BinarySensor* up_bsensor_{nullptr};
binary_sensor::BinarySensor* down_bsensor_{nullptr};
binary_sensor::BinarySensor* memory1_bsensor_{nullptr};
binary_sensor::BinarySensor* memory2_bsensor_{nullptr};
binary_sensor::BinarySensor* memory3_bsensor_{nullptr};
std::vector<uint8_t> remote_buffer_;
std::vector<uint8_t> desk_buffer_;
bool is_remote_rx_uart_message_start_found{false};
bool is_desk_rx_uart_message_start_found{false};
float current_height_{0.0};
float target_height_{-1.0};
};
} // namespace desktronic
} // namespace esphome
Iāve done some further investigation.
The BT protocol I was sent? It uses a secondary UART port on the 12V lines - B-RX, B-TX and B-C1 pins on the MC side.
Hereās the FCCID page for the BT dongle
Iāve also grabbed the close-up shots of the board:
With some image editing wizardry the PCB layout should be somewhat salvageable. However the logic is pretty straightforward, it takes the RJ50 incoming port, splits it out to 10 pins, puts 5V, GND, KEY1, KEY2 and the UART pins forward, and breaks out B-RX, B-TX, and B-C1 for the Bluetooth MCU:
The orange path, B-TX, is pretty straightforward, it goes onot an UART pin of the MCU, with a voltage divider in the way.
B-RX on the other handā¦ It runs into a resistor that has B-C1 on the other end, then these two run into a diode that is hooked up to a separate pin on the MCU, and finally the output goes through another resistor before heading into the MCUās other UART pin (transmit side of MCU).
Then finally purpleā¦ Now thatās a total mystery for me, it doesnāt seem to go anywhere or do anything.
According to the BLE chipās spec sheet, Orange and Blue pins are an UART port, whereas Yellow and Purple are an I2C port. However I see no I2C component there, or anywhere on the boardā¦
Nonetheless, this puts us a step closer to having a proper, non-injection control solution for these desks, as we have the documentation for the B-bus messages, as well as the pins to use.
Fair warning, one CAN NOT hook up an ESP32 or similar to the B-RX and B-TX pins, as these operate at 12V - there needs to be some level of voltage conversion, via optocouplers or similar!
Iāve also discovered a secret menu on my controller. By pressing the REMINDER/TIMER button first, then the M button, youāll be presented with a 7-entry menu.
Iām yet to figure out all the menu options, but hereās what iāve found so far:
1 - boolean, 0 or 1 as values, defaults to 1
2 - numeric, 0 to 8 as possible values, defaults to 3 on my controller
3 - numeric, 0 to 7 as possible values, defaults to 6 on my controller
4 - numeric, 0 to 7 as possible values, defaults to 6 on my controller
5 - numeric, 0 to 9 as possible values, defaults to 5 on my controller
6 - numeric, menu option is not displayed, defaults to 60 on my controller (I believe this is the minimum height of the desk)
7 - boolean, 0 or 1 as values, defaults to 1
Oh and one more thing, the DC
and U_G
pins on my HC seem to be 35V instead of 12V - can anyone else confirm what their desk reads at?
@veli this looks nice and almost works for me! Did you change any code in the cpp file? I get messages saying the checksum did not match, and I wondered if you changed that.