{"id":28561216,"url":"https://github.com/zaxbux/loctekmotion-reng","last_synced_at":"2025-08-02T11:08:00.982Z","repository":{"id":197506676,"uuid":"698769480","full_name":"zaxbux/LoctekMotion-reng","owner":"zaxbux","description":null,"archived":false,"fork":false,"pushed_at":"2023-10-01T20:45:38.000Z","size":351,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-10T10:53:01.573Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zaxbux.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-09-30T23:01:14.000Z","updated_at":"2025-01-02T01:24:16.000Z","dependencies_parsed_at":null,"dependency_job_id":"d9f7e3c0-de23-43d7-8819-c969ccfce3a3","html_url":"https://github.com/zaxbux/LoctekMotion-reng","commit_stats":null,"previous_names":["zaxbux/loctekmotion-reng"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/zaxbux/LoctekMotion-reng","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaxbux%2FLoctekMotion-reng","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaxbux%2FLoctekMotion-reng/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaxbux%2FLoctekMotion-reng/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaxbux%2FLoctekMotion-reng/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zaxbux","download_url":"https://codeload.github.com/zaxbux/LoctekMotion-reng/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaxbux%2FLoctekMotion-reng/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268378806,"owners_count":24240896,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-10T10:41:14.472Z","updated_at":"2025-08-02T11:08:00.974Z","avatar_url":"https://github.com/zaxbux.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# LoctekMotion Desk (FlexiSpot)\n\nI purchased new desk from FlexiSpot recently and was happy to discover there were some projects for integrating the controls with HomeAssistant. However, my control box only has a single RJ45 port for the hand set. I didn't want to give up manual control of the desk and rely solely on control over the network, so I wrote an Arduino program to sit between the handset and control box. This repository documents my progress.\n\n- Desk: `EC5B-CA`\n  - `EC5` = Model\n  - `B` = Black / `W` = White / `S` = Grey\n  - `CA` = Canada?\n  - Specs:\n    - Colour: Black\n    - Height: `25.4` - `51.0` (`24.4` - `50.0` is printed in the manual)\n    - Power supply: 29 VDC, 1.8 A (52.2 W)\n- Control Box: `CB38M2H(PB)-2`\n  - `CB` = Control Box\n  - `38` = ??\n  - `M` = ??\n  - `2` = ??\n  - `H` = ??\n  - `(PB)` = ??\n  - `-2` = ??\n  - Specs:\n    - Input: 18-33VDC, 10A\n    - Output M1/M2: maximum 33VDC, maximum 4A\n    - Output DC: maximum 1.1A\n    - Duty cycle: 2 min on, 18 min off\n    - Ports:\n      - DC in\n      - RJ45 - handset control\n      - M1, M2 - motors\n      - 2x DC out - handset USB\n- Hand Set: `HS11B-1`\n  - `HS` = Hand set\n  - `11` = ??\n  - `B` = USB port\n  - `-1` = ??\n  - Specs:\n    - Digital display\n    - Up/Down buttons\n    - 3 presets\n    - Time reminder\n    - USB charging port\n\n## Parts\n\n- Arduino Mega - (for the multiple hardware serial ports)\n- RJ45 QuickPort/Keystone\n- RJ45 connector\n- Wire (I had some Cat6 cable lying around)\n\n## Wiring\n\n![Schematic](docs/images/basic.png)\n\n[Schematic (PDF)](docs/images/basic.pdf)\n\nI am terminating the RJ45 port/connectors according to the T568B standard.\n\n| Hand Set |           | RJ45   | Arduino | Arduino | RJ45   | Control Box |\n| -------- | --------- | ------ | ------- | ------- | ------ | ----------- |\n| RESET    | 🟤 Brown  | 🟠⚪ 1 | —       | —       | 🟠⚪ 1 | RESET       |\n| SWIM     | ⚪ White  | 🟠🟠 2 | —       | —       | 🟠🟠 2 | SWIM        |\n| empty    | 🟣 Purple | 🟢⚪ 3 | —       | —       | 🟢⚪ 3 | empty       |\n| PIN 20   | 🔴 Red    | 🔵🔵 4 | 4       | 4       | 🔵🔵 4 | PIN 20      |\n| RX       | 🟢 Green  | 🔵⚪ 5 | TX1     | RX2     | 🔵⚪ 5 | TX          |\n| TX       | ⚫ Black  | 🟢🟢 6 | RX1     | TX2     | 🟢🟢 6 | RX          |\n| GND      | 🔵 Blue   | 🟤⚪ 7 | GND     | GND     | 🟤⚪ 7 | GND         |\n| +5V      | 🟡 Yellow | 🟤🟤 8 | +5V     | +5V     | 🟤🟤 8 | +5V         |\n\n## 7-segment display\n\n\u003e \u003chttps://alselectro.wordpress.com/2015/03/03/8051-tutorials-3-interfacing-7-segment-display/\u003e\n\n```\n     ╱‾‾‾‾‾╲               ╱‾‾‾‾‾╲               ╱‾‾‾‾‾╲\n    |   A   |             |   A   |             |   A   |\n ╱‾╲ ╲_____╱ ╱‾╲       ╱‾╲ ╲_____╱ ╱‾╲       ╱‾╲ ╲_____╱ ╱‾╲\n|   |       |   |     |   |       |   |     |   |       |   |\n| F |       | B |     | F |       | B |     | F |       | B |\n|   |       |   |     |   |       |   |     |   |       |   |\n ╲_╱ ╱‾‾‾‾‾╲ ╲_╱       ╲_╱ ╱‾‾‾‾‾╲ ╲_╱       ╲_╱ ╱‾‾‾‾‾╲ ╲_╱\n    |   G   |             |   G   |             |   G   |\n ╱‾╲ ╲_____╱ ╱‾╲       ╱‾╲ ╲_____╱ ╱‾╲       ╱‾╲ ╲_____╱ ╱‾╲\n|   |       |   |     |   |       |   |     |   |       |   |\n| E |       | C |     | E |       | C |     | E |       | C |\n|   |       |   |     |   |       |   |     |   |       |   |\n ╲_╱ ╱‾‾‾‾‾╲ ╲_╱       ╲_╱ ╱‾‾‾‾‾╲ ╲_╱       ╲_╱ ╱‾‾‾‾‾╲ ╲_╱\n    |   D   |             |   D   |             |   D   |\n     ╲_____╱  [.]          ╲_____╱  [.]          ╲_____╱  [.]\n```\n\n| [.] | G   | F   | E   | D   | C   | B   | A   | value  | character |\n| --- | --- | --- | --- | --- | --- | --- | --- | ------ | --------- |\n| `0` | `0` | `0` | `0` | `0` | `0` | `0` | `0` | `0x00` | (blank)   |\n| `0` | `0` | `1` | `1` | `1` | `1` | `1` | `1` | `0x3F` | 🯰         |\n| `0` | `0` | `0` | `0` | `0` | `1` | `1` | `0` | `0x06` | 🯱         |\n| `0` | `1` | `0` | `1` | `1` | `1` | `1` | `1` | `0x5B` | 🯲         |\n| `0` | `1` | `0` | `0` | `1` | `1` | `1` | `1` | `0x4F` | 🯳         |\n| `0` | `1` | `1` | `0` | `0` | `1` | `1` | `0` | `0x66` | 🯴         |\n| `0` | `1` | `1` | `0` | `1` | `1` | `0` | `1` | `0x6D` | 🯵         |\n| `0` | `1` | `1` | `1` | `1` | `1` | `0` | `0` | `0x7C` | 🯶         |\n| `0` | `0` | `0` | `0` | `0` | `1` | `1` | `1` | `0x07` | 🯷         |\n| `0` | `1` | `1` | `1` | `1` | `1` | `1` | `1` | `0x7F` | 🯸         |\n| `0` | `1` | `1` | `0` | `1` | `1` | `1` | `1` | `0x6F` | 🯹         |\n| `0` | `0` | `0` | `0` | `1` | `0` | `0` | `1` | `0x09` | `🮀`       |\n| `0` | `0` | `1` | `1` | `0` | `0` | `0` | `1` | `0x31` | `R`       |\n| `0` | `1` | `0` | `0` | `0` | `0` | `0` | `0` | `0x40` | `-`       |\n| `0` | `1` | `0` | `1` | `0` | `0` | `0` | `0` | `0x50` | `r`       |\n| `0` | `1` | `0` | `1` | `1` | `1` | `0` | `0` | `0x5C` | `o`       |\n| `0` | `1` | `0` | `1` | `0` | `1` | `0` | `0` | `0x54` | `n`       |\n| `0` | `1` | `1` | `1` | `0` | `0` | `0` | `1` | `0x71` | `F`       |\n| `0` | `1` | `1` | `1` | `0` | `1` | `1` | `1` | `0x77` | `A`       |\n| `0` | `1` | `1` | `1` | `1` | `0` | `0` | `0` | `0x78` | `t`       |\n| `0` | `1` | `1` | `1` | `1` | `0` | `0` | `1` | `0x79` | `E`       |\n| `1` | `0` | `0` | `0` | `0` | `0` | `0` | `0` | `0x80` | `.`       |\n\n### Height\n\nThe height of the desk is transmitted as a series of bytes (see table) that tell the display which segments of the three 7-segment modules to turn on.\n\n**Example**\n\nThe bytes `66 FD 3F` would display `46.0`.\n\n- 4th byte `0x66` = `4`\n- 5th byte `0xFD` = `6.`\n  - `0x7d` = `6`\n  - `0x80` = `.`\n- 6th byte `0x3F` = `0`\n\n### Reminder alarm\n\nDuring setup/countdown: `0x09` (top and bottom segments), and values corresponding to digits 00-99\n\n**on**\n\n`0x00` `0x5C` `0x54`\n\nSend command `0x14` to activate alarm sound.\n\n**oFF**\n\n`0x5C` `0x71` `0x71`\n\nSend command `0x15` to deactivate alarm sound.\n\n### Other Messages\n\n**A-1** / **A-2** / **A-3**\n\n- `0x77` `0x40` `0x06` (A-1)\n- `0x77` `0x40` `0x5B` (A-2)\n- `0x77` `0x40` `0x4F` (A-3)\n\nAnti-collision sensitivity. (0=off; 1 = low; 2 = medium; 3 = high)\n\nPress `▲` and `▼` at the same time to adjust.\n\n**S-1** / **S-2** / **S-3**\n\n- `0x6D` `0x40` `0x06` (S-1)\n- `0x6D` `0x40` `0x5B` (S-2)\n- `0x6D` `0x40` `0x4F` (S-3)\n\nSaved height setting.\n\nSet height, press `M`, then press `1`, `2`, or `3` to save.\n\n**E01** / **E02**\n\n- `0x79` `0x3F` `0x06` (E01)\n- `0x79` `0x3F` `0x5B` (E02)\n\nProblem with the motors. This could be due to overheating, collision, etc.\n\n- E22 - When motor 2 is unplugged before operating.\n- E23 - When motor 1 in unplugged before operating.\n\n**rSt** / **ASr**\n\n- `0x50` `0x6d` `0x78` (rSt) - actually displayed\n- `0x77` `0x6d` `0x50` (ASr) - instruction manual\n\nResolution: Hold the down button until desk rebounds and display returns to normal.\n\n\u003e I triggered this message accidentally by pulling PIN20 low while the desk was moving.\n\u003e\n\u003e Example:\n\u003e\n\u003e 1.  Set a preset while the desk is in a low position.\n\u003e 2.  Raise the desk a few inches.\n\u003e 3.  Using Arduino code, pull PIN20 high.\n\u003e 4.  Send the command to trigger the preset\n\u003e 5.  Before the desk stops, pull PIN20 low.\n\n## Protocol\n\n| Begin  | Length | Type | Payload | CRC16 (modbus) | End    |\n| ------ | ------ | ---- | ------- | -------------- | ------ |\n| `0x9B` | u8     | u8   | u8...   | u16            | `0x9D` |\n\nPackets:\n\n- begin with `0x9B`\n- length is number of bytes between `0x9B` and `0x9D` (exclusive).\n- type\n- payload\n- CRC16 (modbus), calculated using `length` + `type` + `payload`\n- end with `0x9D`\n\n### Hand set (HS)\n\nThese packets are received from the hand set.\n\n#### `0x02` - State\n\n| Begin  | Length | Type   | Payload | CRC16 (modbus) | End    |\n| ------ | ------ | ------ | ------- | -------------- | ------ |\n| `0x9B` | `0x06` | `0x02` | u16     | `0x` `0x`      | `0x9D` |\n\n**HS** sends this about every 40ms (25 Hz).\n\n| Value    | Button         |\n| -------- | -------------- |\n| `0x0000` | None           |\n| `0x0100` | `▲` - Raise    |\n| `0x0200` | `▼` - Lower    |\n| `0x0400` | `1` - Preset 1 |\n| `0x0800` | `2` - Preset 2 |\n| `0x1000` | `3` - Preset 3 |\n|          | `4` - Preset 4 |\n| `0x2000` | `M` - Save     |\n| `0x4000` | `A` - Remind   |\n\n\u003e The `0x0000` value can be used to tell the **CB** that the **HS** is \"active\". Useful to obtain the height of the desk.\n\nThese values can be added together to indicate multiple buttons pressed at the same time.\nE.G. `0x0100` \u0026 `0x0200` = `▲` and `▼` at the same time.\n\n### Control Box (CB)\n\nThese packets are received from the control box.\n\n#### `0x11` - Unknown\n\n| Begin  | Length | Type   | CRC16 (modbus) | End    |\n| ------ | ------ | ------ | -------------- | ------ |\n| `0x9B` | `0x07` | `0x11` | `0x7C` `0xC3`  | `0x9D` |\n\n**CB** sends this about every 40 ms (25 Hz).\n\nNot sure what this means. Could be a heartbeat signal?\n\n#### `0x12` - Display\n\n| Begin  | Length | Type   | Payload                         | CRC16 (modbus) | End    |\n| ------ | ------ | ------ | ------------------------------- | -------------- | ------ |\n| `0x9B` | `0x07` | `0x12` | u8, u8, u8 (tens, ones, tenths) | `0x??` `0x??`  | `0x9D` |\n\n**CB** sends this about every 60 ms (16.66 Hz).\n\n\u003e When all three bytes are `0x00`, display is off.\n\n#### `0x14` - Beep On\n\n| Begin  | Length | Type   | CRC16 (modbus) | End    |\n| ------ | ------ | ------ | -------------- | ------ |\n| `0x9B` | `0x07` | `0x14` | `0x7F` `0x03`  | `0x9D` |\n\n**CB** sends this about every XX ms (XX Hz).\n\nCauses HS to start beeping.\n\n#### `0x15` - Beep Off\n\n| Begin  | Length | Type   | CRC16 (modbus) | End    |\n| ------ | ------ | ------ | -------------- | ------ |\n| `0x9B` | `0x07` | `0x15` | `0xBF` `0xC2`  | `0x9D` |\n\n**CB** sends this about every 8 ms (125 Hz).\n\nIf `0x14` was sent previously, this causes HS to stop beeping.\n\n## Credits\n\n- [iMicknl/LoctekMotion_IoT](https://github.com/iMicknl/LoctekMotion_IoT) - Wiring and initial Arduino code.\n- [notiflux/arduinoSerialTap](https://github.com/notiflux/arduinoSerialTap) - Arduino code for UART/serial tap.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaxbux%2Floctekmotion-reng","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzaxbux%2Floctekmotion-reng","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaxbux%2Floctekmotion-reng/lists"}