Skip to content

Commit 048e32b

Browse files
committed
Changes to 32 proportional channel transmission (CRSF specifications PR#28 tbs-fpv/tbs-crsf-spec#28 ).
To be paired with EdgeTX PR#6504 EdgeTX/edgetx#6504
1 parent b629daf commit 048e32b

File tree

12 files changed

+126
-90
lines changed

12 files changed

+126
-90
lines changed

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,15 @@ A brief video showing the code in action:
1414

1515
You can find the C++ source code to flash numerous internal and external ExpressLRS modules under [transmitterFW](https://github.com/rotorman/CyberBrick_ESPNOW/tree/main/transmitterFW) subfolder and the MicroPython scripts for the CyberBrick Core modules under the [receiverPY](https://github.com/rotorman/CyberBrick_ESPNOW/tree/main/receiverPY) subfolder. [transmitterLua](https://github.com/rotorman/CyberBrick_ESPNOW/tree/main/transmitterLua) subfolder holds the Lua script for EdgeTX radios in order to bind/pair the radio transmitter module with CyberBrick models.
1616

17-
The firmware can be built and flashed into the hardware using [Visual Studio Code](https://code.visualstudio.com/) and [PlatformIO](https://platformio.org/) extension paired with an [Arduino framework](https://docs.platformio.org/en/latest/frameworks/arduino.html) for [Espressif ESP32](https://docs.platformio.org/en/latest/platforms/espressif32.html#platform-espressif32) platform, similar to the development of the [ExpressLRS firmware](https://www.expresslrs.org/software/toolchain-install/).
18-
1917
The MicroPython scripts can be copied to the CyberBrick Core with any REPL capable editor, such as [Arduino Lab for MicroPython](https://labs.arduino.cc/en/labs/micropython) or [Thonny](https://thonny.org/).
2018

2119
## Compatible radios
22-
The code should work with any (internal or external) [ExpressLRS transmitter module](https://www.expresslrs.org/hardware/hardware-selection/#transmitter-selection) with an ESP32 or ESP32-S3 microcontroller paired with an arbitrary [EdgeTX](https://edgetx.org/) handset able to talk with ExpressLRS module(s).
20+
The code should work with any (internal or external) [ExpressLRS transmitter module](https://www.expresslrs.org/hardware/hardware-selection/#transmitter-selection) with an ESP32 or ESP32-S3 microcontroller paired with an arbitrary [EdgeTX](https://edgetx.org/) handset able to talk with ExpressLRS module(s). To use the latest code, that uses internally 32 channel transmission, you should flash EdgeTX firmware from the [EdgeTX pull request #6504](https://github.com/EdgeTX/edgetx/pull/6504).
2321

2422
I have tested the code with RadioMaster TX16s mkII with internal ExpressLRS module, RadioMaster MT12 with internal 2.4 GHz ExpressLRS module, Jumper T15 with internal 2.4 GHz ExpressLRS module, external HappyModel ES24TX Pro module, external RadioMaster Ranger module and also with an ESP32 development kit (ESP32DevKitCv4).
2523

26-
## Flashing
27-
Flashing the internal ExpressLRS module(s) is possible via EdgeTX passthrough (triggered in the background while uploading firmware either from [ExpressLRS Configurator](https://github.com/ExpressLRS/ExpressLRS-Configurator/releases/latest) or from VSCode and PlatformIO by [Python script](https://github.com/rotorman/CyberBrick_ESPNOW/transmitterFW/python/EdgeTXpassthrough.py)). External modules can be flashed via UART. Some (external) modules come with an integrated USB-to-serial adapter, others might need an external adapter, like [FTDI](https://www.sparkfun.com/ftdi-cable-5v-vcc-3-3v-i-o.html) or [Silabs CP2102](https://betafpv.com/collections/expresslrs-series-accessories/products/expresslrs-recovery-dongle), to flash them.
24+
## Flashing & code customization
25+
The firmware can be built and flashed into the ExpressLRS hardware via EdgeTX passthrough (triggered in the background while uploading firmware either from [ExpressLRS Configurator](https://github.com/ExpressLRS/ExpressLRS-Configurator/releases/latest) or from VSCode and PlatformIO by [Python script](https://github.com/rotorman/CyberBrick_ESPNOW/transmitterFW/python/EdgeTXpassthrough.py)). External modules can be flashed via UART. Some (external) modules come with an integrated USB-to-serial adapter, others might need an external adapter, like [FTDI](https://www.sparkfun.com/ftdi-cable-5v-vcc-3-3v-i-o.html) or [Silabs CP2102](https://betafpv.com/collections/expresslrs-series-accessories/products/expresslrs-recovery-dongle), to flash them. You can use [Visual Studio Code](https://code.visualstudio.com/) and [PlatformIO](https://platformio.org/) to customize the code. The firmware code is using [Arduino framework](https://docs.platformio.org/en/latest/frameworks/arduino.html) for [Espressif ESP32](https://docs.platformio.org/en/latest/platforms/espressif32.html#platform-espressif32) platform, similar to the development of the [ExpressLRS firmware](https://www.expresslrs.org/software/toolchain-install/).
2826

2927
## Credits
3028
Large parts of the code used in this repository stem from the wonderful [ExpressLRS project](https://github.com/ExpressLRS/ExpressLRS/).

receiverPY/bulldozer/bulldozer.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
https://makerworld.com/de/models/1461532-bulldozer-cyberbrick-rc
2525
2626
The handset, running EdgeTX firmware, sends, via custom ESP-NOW flashed
27-
ExpressLRS transmitter module channel data according to CRSF specifications:
28-
16 proportional channels in slightly lower than 11-bit resolution.
27+
ExpressLRS transmitter module channel data according to CRSF specifications
28+
PR#28 (https://github.com/tbs-fpv/tbs-crsf-spec/pull/28) 32 proportional
29+
channels in slightly lower than 11-bit resolution.
2930
The channel order, range, mixing and further parameters can be adjusted
3031
in the EdgeTX radio.
3132
@@ -209,9 +210,9 @@ def mapchannel(chvalue, minmapvalue, maxmapvalue):
209210
enow_reset()
210211

211212
else:
212-
if len(msg) > 31:
213-
ch = struct.unpack('<HHHHHHHHHHHHHHHH', msg)
214-
if len(ch) == 16:
213+
if len(msg) > 63:
214+
ch = struct.unpack('<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH', msg)
215+
if len(ch) == 32:
215216
# Received expected CRSF telegram channel count from the handset
216217
# Blink green
217218
if ((utime.ticks_ms() % blinkertime_ms) > (blinkertime_ms / 2)):

receiverPY/debug/debug.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
Debug script, that prints incoming data onto MicroPython REPL.
2424
2525
The handset, running EdgeTX firmware, sends, via custom ESP-NOW flashed
26-
ExpressLRS transmitter module channel data according to CRSF specifications:
27-
16 proportional channels in slightly lower than 11-bit resolution.
26+
ExpressLRS transmitter module channel data according to CRSF specifications
27+
PR#28 (https://github.com/tbs-fpv/tbs-crsf-spec/pull/28) 32 proportional
28+
channels in slightly lower than 11-bit resolution.
2829
The channel order, range, mixing and further parameters can be adjusted
2930
in the EdgeTX radio.
3031
@@ -135,11 +136,11 @@ def send_bind():
135136
enow_reset()
136137

137138
else:
138-
if len(msg) > 31:
139-
ch = struct.unpack('<HHHHHHHHHHHHHHHH', msg)
140-
if len(ch) == 16:
139+
if len(msg) > 63:
140+
ch = struct.unpack('<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH', msg)
141+
if len(ch) == 32:
141142
# Received expected CRSF telegram channel count from the handset
142-
print('%-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i|%-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i' % (ch[0:16]))
143+
print('%-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i|%-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i|%-5i%-5i%-5i%-5i| %-5i%-5i%-5i%-5i' % (ch[0:32]))
143144
# Blink Core LED green
144145
if ((utime.ticks_ms() % blinkertime_ms) > (blinkertime_ms / 2)):
145146
np[0] = (0, 0, 0) # Dark phase

receiverPY/forklift/forklift.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
https://makerworld.com/de/models/1395994-cyberbrick-official-forklift
2525
2626
The handset, running EdgeTX firmware, sends, via custom ESP-NOW flashed
27-
ExpressLRS transmitter module channel data according to CRSF specifications:
28-
16 proportional channels in slightly lower than 11-bit resolution.
27+
ExpressLRS transmitter module channel data according to CRSF specifications
28+
PR#28 (https://github.com/tbs-fpv/tbs-crsf-spec/pull/28) 32 proportional
29+
channels in slightly lower than 11-bit resolution.
2930
The channel order, range, mixing and further parameters can be adjusted
3031
in the EdgeTX radio.
3132
@@ -192,9 +193,9 @@ def mapchannel(chvalue, minmapvalue, maxmapvalue):
192193
enow_reset()
193194

194195
else:
195-
if len(msg) > 31:
196-
ch = struct.unpack('<HHHHHHHHHHHHHHHH', msg)
197-
if len(ch) == 16:
196+
if len(msg) > 63:
197+
ch = struct.unpack('<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH', msg)
198+
if len(ch) == 32:
198199
# Received expected CRSF telegram channel count from the handset
199200
# Blink green
200201
if ((utime.ticks_ms() % blinkertime_ms) > (blinkertime_ms / 2)):

receiverPY/generic/generic.py

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,43 @@
2727
ch2 - brushed motor2
2828
ch3 - servo1 (0.5ms to 2.5ms range)
2929
ch4 - servo2 (0.5ms to 2.5ms range)
30-
ch7 - Neopixel string1 LED1
31-
ch8 - Neopixel string1 LED2
32-
ch9 - Neopixel string1 LED3
33-
ch10 - Neopixel string1 LED4
34-
ch11 - Neopixel string2 LED1
35-
ch12 - Neopixel string2 LED2
36-
ch13 - Neopixel string2 LED3
37-
ch14 - Neopixel string2 LED4
30+
31+
ch9 - Neopixel string1 LED1 Red
32+
ch10 - Neopixel string1 LED1 Green
33+
ch11 - Neopixel string1 LED1 Blue
34+
35+
ch12 - Neopixel string1 LED2 Red
36+
ch13 - Neopixel string1 LED2 Green
37+
ch14 - Neopixel string1 LED2 Blue
38+
39+
ch15 - Neopixel string1 LED3 Red
40+
ch16 - Neopixel string1 LED3 Green
41+
ch17 - Neopixel string1 LED3 Blue
42+
43+
ch18 - Neopixel string1 LED4 Red
44+
ch19 - Neopixel string1 LED4 Green
45+
ch20 - Neopixel string1 LED4 Blue
46+
47+
ch21 - Neopixel string2 LED1 Red
48+
ch22 - Neopixel string2 LED1 Green
49+
ch23 - Neopixel string2 LED1 Blue
50+
51+
ch24 - Neopixel string2 LED2 Red
52+
ch25 - Neopixel string2 LED2 Green
53+
ch26 - Neopixel string2 LED2 Blue
54+
55+
ch27 - Neopixel string2 LED3 Red
56+
ch28 - Neopixel string2 LED3 Green
57+
ch29 - Neopixel string2 LED3 Blue
58+
59+
ch30 - Neopixel string2 LED4 Red
60+
ch31 - Neopixel string2 LED4 Green
61+
ch32 - Neopixel string2 LED4 Blue
3862
3963
The handset, running EdgeTX firmware, sends, via custom ESP-NOW flashed
40-
ExpressLRS transmitter module channel data according to CRSF specifications:
41-
16 proportional channels in slightly lower than 11-bit resolution.
64+
ExpressLRS transmitter module channel data according to CRSF specifications
65+
PR#28 (https://github.com/tbs-fpv/tbs-crsf-spec/pull/28) 32 proportional
66+
channels in slightly lower than 11-bit resolution.
4267
The channel order, range, mixing and further parameters can be adjusted
4368
in the EdgeTX radio.
4469
@@ -48,26 +73,8 @@
4873
EdgeTX, the value range increases form 0 to 1984, with 992 still being
4974
the middle position.
5075
51-
The LEDs data is interpreted as RGB343 (3 bits for red, 4 for green and
52-
3 for blue) with an offset of 173 (value 173 -> R=0, G=0, B=0).
53-
The input is converted into RGB888 output. The undefined bits are intepreted
54-
as 1, except if the value is 0, then as 0.
55-
Examples:
56-
57-
value | binary (-173) | red | green | blue
58-
173 | b000 0000 000 | 0 | 0 | 0 (<- dark/black)
59-
174 | b000 0000 001 | 0 | 0 | b0011 1111 = 63
60-
175 | b000 0000 010 | 0 | 0 | b0101 1111 = 95
61-
180 | b000 0000 111 | 0 | 0 | b1111 1111 = 255
62-
181 | b000 0001 000 | 0 | 31 | 0
63-
182 | b000 0001 001 | 0 | 31 | 63
64-
300 | b000 1111 111 | 0 | 255 | 255 (<- cyan)
65-
301 | b001 0000 000 | 63 | 0 | 0
66-
302 | b001 0000 001 | 63 | 0 | 63
67-
773 | b100 1011 000 | 159 | 191 | 0
68-
1196 | b111 1111 111 | 255 | 255 | 255 (<- white)
69-
70-
Any value above 173+1023=1196 is intepreted as white.
76+
The LEDs data is interpreted as 0% (173) to 100% (1811).
77+
Any value below 173 is interpreted as 0% and any value above 1811 as 100%.
7178
"""
7279

7380
from machine import Pin, PWM
@@ -176,7 +183,6 @@ def send_bind():
176183
FULLSCALE16BIT = 65535
177184
PWMGAINCOEFFICIENTPOS = const(80) # FULLSCALE16BIT/(CRSF_CHANNEL_VALUE_MAX - CRSF_CHANNEL_VALUE_MID)
178185
PWMGAINCOEFFICIENTNEG = const(80) # FULLSCALE16BIT/(CRSF_CHANNEL_VALUE_MID - CRSF_CHANNEL_VALUE_MIN)
179-
MAXRGBVALUE = const(1023) # max of 11-bit value range
180186

181187
def BrushedMotorControl(channel):
182188
#deadzone check
@@ -186,21 +192,11 @@ def BrushedMotorControl(channel):
186192
else:
187193
if channel < CRSF_CHANNEL_VALUE_MID:
188194
# First direction
189-
return (int)(max(min(PWMGAINCOEFFICIENTNEG*(CRSF_CHANNEL_VALUE_MID-channel), FULLSCALE16BIT)), 0), 0
195+
return (int)(max(min(PWMGAINCOEFFICIENTNEG*(CRSF_CHANNEL_VALUE_MID-channel), FULLSCALE16BIT), 0)), 0
190196
else:
191197
# Rotate in the other direction
192-
return 0, (int)(max(min(PWMGAINCOEFFICIENTPOS*(channel-CRSF_CHANNEL_VALUE_MID), FULLSCALE16BIT)), 0)
198+
return 0, (int)(max(min(PWMGAINCOEFFICIENTPOS*(channel-CRSF_CHANNEL_VALUE_MID), FULLSCALE16BIT), 0))
193199

194-
def rgb343(val):
195-
adjusted = min(val - CRSF_CHANNEL_VALUE_MIN, MAXRGBVALUE)
196-
if adjusted == 0:
197-
return 0,0,0
198-
else:
199-
r = min(((adjusted & 0b1110000000) >> 2) + 0b11111, 255)
200-
g = min(((adjusted & 0b0001111000) << 1) + 0b1111, 255)
201-
b = min(((adjusted & 0b0000000111) << 5) + 0b11111, 255)
202-
return r,g,b
203-
204200
def mapchannel(chvalue, minmapvalue, maxmapvalue):
205201
if minmapvalue > SERVORAWmidpoint:
206202
minmapvalue = SERVORAWmidpoint
@@ -216,6 +212,18 @@ def mapchannel(chvalue, minmapvalue, maxmapvalue):
216212
return ((chvalue-CRSF_CHANNEL_VALUE_MID)*(maxmapvalue-SERVORAWmidpoint)/(CRSF_CHANNEL_VALUE_MAX-CRSF_CHANNEL_VALUE_MID)) + SERVORAWmidpoint
217213
else:
218214
return SERVORAWmidpoint - ((CRSF_CHANNEL_VALUE_MID-chvalue)*(SERVORAWmidpoint-minmapvalue)/(CRSF_CHANNEL_VALUE_MID-CRSF_CHANNEL_VALUE_MIN))
215+
216+
def mapLED(chvalue):
217+
if chvalue < CRSF_CHANNEL_VALUE_MIN:
218+
chvalue = CRSF_CHANNEL_VALUE_MIN
219+
if chvalue > CRSF_CHANNEL_VALUE_MAX:
220+
chvalue = CRSF_CHANNEL_VALUE_MAX
221+
mapped = round((chvalue - CRSF_CHANNEL_VALUE_MIN) * 255 / (CRSF_CHANNEL_VALUE_MAX - CRSF_CHANNEL_VALUE_MIN))
222+
if mapped < 0:
223+
return 0
224+
if mapped > 255:
225+
return 255
226+
return (int)(mapped)
219227

220228
while True:
221229
if button.value() == 0:
@@ -255,9 +263,9 @@ def mapchannel(chvalue, minmapvalue, maxmapvalue):
255263
enow_reset()
256264

257265
else:
258-
if len(msg) > 31:
259-
ch = struct.unpack('<HHHHHHHHHHHHHHHH', msg)
260-
if len(ch) == 16:
266+
if len(msg) > 63:
267+
ch = struct.unpack('<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH', msg)
268+
if len(ch) == 32:
261269
# Received expected CRSF telegram channel count from the handset
262270

263271
# 0.5 to 2.5ms range
@@ -271,17 +279,18 @@ def mapchannel(chvalue, minmapvalue, maxmapvalue):
271279
d1,d2 = BrushedMotorControl(ch[1]) # Motor2
272280
M2A.duty_u16(d1)
273281
M2B.duty_u16(d2)
274-
282+
275283
# NeoPixel LEDs
276-
LEDstring1[0] = rgb343(ch[6])
277-
LEDstring1[1] = rgb343(ch[7])
278-
LEDstring1[2] = rgb343(ch[8])
279-
LEDstring1[3] = rgb343(ch[9])
284+
LEDstring1[0] = (mapLED(ch[8]), mapLED(ch[9]), mapLED(ch[10]))
285+
LEDstring1[1] = (mapLED(ch[11]), mapLED(ch[12]), mapLED(ch[13]))
286+
LEDstring1[2] = (mapLED(ch[14]), mapLED(ch[15]), mapLED(ch[16]))
287+
LEDstring1[3] = (mapLED(ch[17]), mapLED(ch[18]), mapLED(ch[19]))
280288
LEDstring1.write()
281-
LEDstring2[0] = rgb343(ch[10])
282-
LEDstring2[1] = rgb343(ch[11])
283-
LEDstring2[2] = rgb343(ch[12])
284-
LEDstring2[3] = rgb343(ch[13])
289+
290+
LEDstring2[0] = (mapLED(ch[20]), mapLED(ch[21]), mapLED(ch[22]))
291+
LEDstring2[1] = (mapLED(ch[23]), mapLED(ch[24]), mapLED(ch[25]))
292+
LEDstring2[2] = (mapLED(ch[26]), mapLED(ch[27]), mapLED(ch[28]))
293+
LEDstring2[3] = (mapLED(ch[29]), mapLED(ch[30]), mapLED(ch[31]))
285294
LEDstring2.write()
286295

287296
# Blink Core LED green

receiverPY/truck/truck.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@
2424
https://makerworld.com/de/models/1396031-cyberbrick-official-truck
2525
2626
The handset, running EdgeTX firmware, sends, via custom ESP-NOW flashed
27-
ExpressLRS transmitter module channel data according to CRSF specifications:
28-
16 proportional channels in slightly lower than 11-bit resolution.
27+
ExpressLRS transmitter module channel data according to CRSF specifications
28+
PR#28 (https://github.com/tbs-fpv/tbs-crsf-spec/pull/28) 32 proportional
29+
channels in slightly lower than 11-bit resolution.
2930
The channel order, range, mixing and further parameters can be adjusted
3031
in the EdgeTX radio.
3132
@@ -184,9 +185,9 @@ def mapchannel(chvalue, minmapvalue, maxmapvalue):
184185
enow_reset()
185186

186187
else:
187-
if len(msg) > 31:
188-
ch = struct.unpack('<HHHHHHHHHHHHHHHH', msg)
189-
if len(ch) == 16:
188+
if len(msg) > 63:
189+
ch = struct.unpack('<HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH', msg)
190+
if len(ch) == 32:
190191
# Received expected CRSF telegram channel count from the handset
191192
# Blink green
192193
if ((utime.ticks_ms() % blinkertime_ms) > (blinkertime_ms / 2)):

0 commit comments

Comments
 (0)