Skip to content

Commit 2db9704

Browse files
committed
Created OTA lib for packet encoding
I realised we can't easily test code in tr/rx_main, so started refactoring new code into an OTA (over the air) library. So far this just has code for encoding the new 10bit analog/3 pos switches packets in OTA.cpp. I moved the packet header definitions into OTA.hpp since they make sense there, and are easier to access for testing. test_switches.cpp contains some initial tests for the new encoding scheme. Still lots of tests to write for unpacking and the non-hybrid packet encoding. Signed-off-by: JBKingdon <[email protected]>
1 parent 1b13d02 commit 2db9704

7 files changed

Lines changed: 218 additions & 49 deletions

File tree

src/lib/OTA/OTA.cpp

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "CRSF.h"
2+
#include "LoRaRadioLib.h"
3+
#include "OTA.h"
4+
5+
#ifdef HYBRID_SWITCHES_8
6+
7+
/**
8+
* Hybrid switches packet
9+
* Replaces Generate4ChannelData_11bit
10+
* Analog channels are reduced to 10 bits to allow for switch encoding
11+
* Switch[0] is sent on every packet.
12+
* A 3 bit switch index and 2 bit value is used to send the remaining switches
13+
* in a round-robin fashion.
14+
* If any of the round-robin switches have changed
15+
* we take the lowest indexed one and send that, hence lower indexed switches have
16+
* higher priority in the event that several are changed at once.
17+
*
18+
* Inputs: crsf.ChannelDataIn, crsf.currentSwitches
19+
* Outputs: Radio.TXdataBuffer, side-effects the sentSwitch value
20+
*/
21+
void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8(SX127xDriver *Radio, CRSF *crsf, uint8_t addr)
22+
{
23+
uint8_t PacketHeaderAddr;
24+
PacketHeaderAddr = (addr << 2) + RC_DATA_PACKET;
25+
Radio->TXdataBuffer[0] = PacketHeaderAddr;
26+
Radio->TXdataBuffer[1] = ((crsf->ChannelDataIn[0]) >> 3);
27+
Radio->TXdataBuffer[2] = ((crsf->ChannelDataIn[1]) >> 3);
28+
Radio->TXdataBuffer[3] = ((crsf->ChannelDataIn[2]) >> 3);
29+
Radio->TXdataBuffer[4] = ((crsf->ChannelDataIn[3]) >> 3);
30+
Radio->TXdataBuffer[5] = ((crsf->ChannelDataIn[0] & 0b110) << 5) +
31+
((crsf->ChannelDataIn[1] & 0b110) << 3) +
32+
((crsf->ChannelDataIn[2] & 0b110) << 1) +
33+
((crsf->ChannelDataIn[3] & 0b110) >> 1);
34+
35+
// switch 0 is sent on every packet - intended for low latency arm/disarm
36+
Radio->TXdataBuffer[6] = (crsf->currentSwitches[0] & 0b11) << 5; // note this leaves the top bit of byte 6 unused
37+
38+
// find the next switch to send
39+
int i = crsf->getNextSwitchIndex() & 0b111; // mask for paranoia
40+
uint8_t value = crsf->currentSwitches[i] & 0b11; // mask for paranoia
41+
42+
// put the bits into buf[6]-> i is in the range 1 through 7 so takes 3 bits
43+
// currentSwitches[i] is in the range 0 through 2, takes 2 bits->
44+
Radio->TXdataBuffer[6] += (i << 2) + value;
45+
46+
// update the sent value
47+
crsf->setSentSwitch(i, value);
48+
}
49+
#endif

src/lib/OTA/OTA.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#ifndef H_OTA
2+
#define H_OTA
3+
4+
#include "CRSF.h"
5+
#include "LoRaRadioLib.h"
6+
7+
// expresslrs packet header types
8+
// 00 -> standard 4 channel data packet
9+
// 01 -> switch data packet
10+
// 11 -> tlm packet
11+
// 10 -> sync packet with hop data
12+
#define RC_DATA_PACKET 0b00
13+
#define SWITCH_DATA_PACKET 0b01
14+
#define TLM_PACKET 0b11
15+
#define SYNC_PACKET 0b10
16+
17+
#ifdef HYBRID_SWITCHES_8
18+
void GenerateChannelDataHybridSwitch8(SX127xDriver *Radio, CRSF *crsf, uint8_t addr);
19+
#endif
20+
21+
#endif

src/src/common.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,3 @@ int16_t MeasureRSSI(int FHSSindex); //--todo, move this to radio lib
9292

9393
uint8_t TLMratioEnumToValue(expresslrs_tlm_ratio_e enumval);
9494

95-
// expresslrs packet header types
96-
// 00 -> standard 4 channel data packet
97-
// 01 -> switch data packet
98-
// 11 -> tlm packet
99-
// 10 -> sync packet with hop data
100-
#define RC_DATA_PACKET 0b00
101-
#define SWITCH_DATA_PACKET 0b01
102-
#define TLM_PACKET 0b11
103-
#define SYNC_PACKET 0b10

src/src/rx_main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
// #include "Debug.h"
1010
#include "rx_LinkQuality.h"
1111
#include "errata.h"
12+
#include "OTA.h"
1213

1314
#ifdef PLATFORM_ESP8266
1415
#include "ESP8266_WebUpdate.h"

src/src/tx_main.cpp

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "POWERMGNT.h"
1212
#include "msp.h"
1313
#include "msptypes.h"
14+
#include <OTA.h>
1415

1516
#ifdef TARGET_EXPRESSLRS_PCB_TX_V3
1617
#include "soc/soc.h"
@@ -241,45 +242,6 @@ void ICACHE_RAM_ATTR GenerateChannelDataSeqSwitch()
241242
}
242243
#endif
243244

244-
#ifdef HYBRID_SWITCHES_8
245-
/**
246-
* Hybrid switches packet
247-
* Replaces Generate4ChannelData_11bit
248-
* Analog channels are reduced to 10 bits to allow for switch encoding
249-
* Switch[0] is sent on every packet.
250-
* A 3 bit switch index and 2 bit value is used to send the remaining switches
251-
* in a round-robin fashion.
252-
* If any of the round-robin switches have changed
253-
* we take the lowest indexed one and send that, hence lower indexed switches have
254-
* higher priority in the event that several are changed at once.
255-
*/
256-
void ICACHE_RAM_ATTR GenerateChannelDataHybridSwitch8()
257-
{
258-
uint8_t PacketHeaderAddr;
259-
PacketHeaderAddr = (DeviceAddr << 2) + RC_DATA_PACKET;
260-
Radio.TXdataBuffer[0] = PacketHeaderAddr;
261-
Radio.TXdataBuffer[1] = ((crsf.ChannelDataIn[0]) >> 3);
262-
Radio.TXdataBuffer[2] = ((crsf.ChannelDataIn[1]) >> 3);
263-
Radio.TXdataBuffer[3] = ((crsf.ChannelDataIn[2]) >> 3);
264-
Radio.TXdataBuffer[4] = ((crsf.ChannelDataIn[3]) >> 3);
265-
Radio.TXdataBuffer[5] = ((crsf.ChannelDataIn[0] & 0b110) << 5) + ((crsf.ChannelDataIn[1] & 0b110) << 3) +
266-
((crsf.ChannelDataIn[2] & 0b110) << 1) + ((crsf.ChannelDataIn[3] & 0b110) >> 1);
267-
268-
// switch 0 is sent on every packet - intended for low latency arm/disarm
269-
Radio.TXdataBuffer[6] = (crsf.currentSwitches[0] & 0b11) << 5; // note this leaves the top bit of byte 6 unused
270-
271-
// find the next switch to send
272-
int i = crsf.getNextSwitchIndex() & 0b111; // mask for paranoia
273-
uint8_t value = crsf.currentSwitches[i] & 0b11; // mask for paranoia
274-
275-
// put the bits into buf[6]. i is in the range 1 through 7 so takes 3 bits
276-
// currentSwitches[i] is in the range 0 through 2, takes 2 bits.
277-
Radio.TXdataBuffer[6] += (i << 2) + value;
278-
279-
// update the sent value
280-
crsf.setSentSwitch(i, value);
281-
}
282-
#endif
283245

284246
void ICACHE_RAM_ATTR GenerateSwitchChannelData()
285247
{
@@ -416,7 +378,7 @@ void ICACHE_RAM_ATTR SendRCdataToRF()
416378
else
417379
{
418380
#if defined HYBRID_SWITCHES_8
419-
GenerateChannelDataHybridSwitch8();
381+
GenerateChannelDataHybridSwitch8(&Radio, &crsf, DeviceAddr);
420382
#elif defined SEQ_SWITCHES
421383
GenerateChannelDataSeqSwitch();
422384
#else
@@ -702,6 +664,7 @@ void loop()
702664
button.handle();
703665
#endif
704666

667+
<<<<<<< 1b13d02ffb71cc3cadb6d80e9de4f51f4ff71dda
705668
<<<<<<< 017e1958d86feddb32c14cc2d5c67cd93acd9400
706669
#ifdef PLATFORM_ESP32
707670
if (Serial2.available()) {
@@ -719,6 +682,10 @@ void loop()
719682
}
720683
=======
721684
>>>>>>> Sequential and hybrid switch modes
685+
=======
686+
vTaskDelay(2);
687+
688+
>>>>>>> Created OTA lib for packet encoding
722689
}
723690

724691
void ICACHE_RAM_ATTR TimerExpired()

src/test/test_switches.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#include <Arduino.h>
2+
#include <unity.h>
3+
4+
#include "CRSF.h"
5+
#include "LoRaRadioLib.h"
6+
#include "targets.h"
7+
#include "common.h"
8+
#include <OTA.h>
9+
10+
CRSF crsf; // need an instance to provide the fields and code to be tested
11+
SX127xDriver Radio; // needed for Radio.TXdataBuffer
12+
13+
14+
/* Check that the round robin works
15+
* First call should return 0 for seq switches or 1 for hybrid
16+
* Successive calls should increment the next index until wrap
17+
* around from 7 to either 0 or 1 depending on mode.
18+
*/
19+
void test_round_robin(void) {
20+
21+
uint8_t expectedIndex=crsf.nextSwitchIndex;
22+
23+
for(uint8_t i=0; i<10; i++) {
24+
uint8_t nsi = crsf.getNextSwitchIndex();
25+
TEST_ASSERT_EQUAL(expectedIndex, nsi);
26+
expectedIndex++;
27+
if (expectedIndex == 8) {
28+
#ifdef HYBRID_SWITCHES_8
29+
expectedIndex = 1;
30+
#else
31+
expectedIndex = 0;
32+
#endif
33+
}
34+
}
35+
}
36+
37+
/* Check that a changed switch gets priority
38+
*/
39+
void test_priority(void) {
40+
41+
uint8_t nsi;
42+
43+
crsf.nextSwitchIndex=0; // this would be the next switch if nothing changed
44+
45+
// set all switches and sent values to be equal
46+
for(uint8_t i=0; i<N_SWITCHES; i++) {
47+
crsf.sentSwitches[i] = 0;
48+
crsf.currentSwitches[i] = 0;
49+
}
50+
51+
// set two switches' current value to be different
52+
crsf.currentSwitches[4] = 1;
53+
crsf.currentSwitches[6] = 1;
54+
55+
// we expect to get the lowest changed switch
56+
nsi = crsf.getNextSwitchIndex();
57+
TEST_ASSERT_EQUAL(4, nsi);
58+
59+
// The sending code would then change the sent value to match:
60+
crsf.sentSwitches[4] = 1;
61+
62+
// so now we expect to get 6 (the other changed switch we set above)
63+
nsi = crsf.getNextSwitchIndex();
64+
TEST_ASSERT_EQUAL(6, nsi);
65+
66+
// The sending code would then change the sent value to match:
67+
crsf.sentSwitches[6] = 1;
68+
69+
// Now all sent values should match the current values, and we expect
70+
// to get the last returned value +1
71+
nsi = crsf.getNextSwitchIndex();
72+
TEST_ASSERT_EQUAL(7, nsi);
73+
74+
}
75+
76+
/* Check the encoding of a packet for OTA tx
77+
*/
78+
void test_encoding()
79+
{
80+
uint8_t UID[6] = {MY_UID};
81+
uint8_t DeviceAddr = UID[5] & 0b111111;
82+
uint8_t expected;
83+
84+
// Define the input data
85+
// 4 channels of analog data
86+
crsf.ChannelDataIn[0] = 0x0123;
87+
crsf.ChannelDataIn[1] = 0x4567;
88+
crsf.ChannelDataIn[2] = 0x89AB;
89+
crsf.ChannelDataIn[3] = 0xCDEF;
90+
91+
// 8 switches
92+
for(int i=0; i<N_SWITCHES; i++) {
93+
crsf.currentSwitches[i] = i % 3;
94+
crsf.sentSwitches[i] = i % 3; // make all the sent values match
95+
}
96+
97+
// set the nextSwitchIndex so we know which switch to expect in the packet
98+
crsf.nextSwitchIndex=3;
99+
100+
// encode it
101+
GenerateChannelDataHybridSwitch8(&Radio, &crsf, DeviceAddr);
102+
103+
// check it looks right
104+
// 1st byte is header & packet type
105+
uint8_t header = (DeviceAddr << 2) + RC_DATA_PACKET;
106+
TEST_ASSERT_EQUAL(header, Radio.TXdataBuffer[0]);
107+
108+
// bytes 1 through 5 are 10 bit packed analog channels
109+
for(int i=0; i<4; i++) {
110+
expected = crsf.ChannelDataIn[i] >> 3; // most significant 8 bits
111+
TEST_ASSERT_EQUAL(expected, Radio.TXdataBuffer[i+1]);
112+
}
113+
114+
// byte 5 is bits 1 and 2 of each analog channel
115+
expected = 0;
116+
for(int i=0; i<4; i++) {
117+
expected = (expected <<2) | ((crsf.ChannelDataIn[i] >> 1) & 0b11);
118+
}
119+
TEST_ASSERT_EQUAL(expected, Radio.TXdataBuffer[5]);
120+
121+
// byte 6 is the switch encoding
122+
// expect switch 0 in bits 5 and 6, index in 2-4 and value in 0,1
123+
// top bit is undefined
124+
TEST_ASSERT_EQUAL(crsf.currentSwitches[0], (Radio.TXdataBuffer[6] & 0b01100000)>>5);
125+
TEST_ASSERT_EQUAL(3, (Radio.TXdataBuffer[6] & 0b11100)>>2);
126+
TEST_ASSERT_EQUAL(crsf.currentSwitches[3], Radio.TXdataBuffer[6] & 0b11);
127+
}
128+
129+
/* Check the decoding of a packet after rx
130+
*/
131+
132+
void setup_switches()
133+
{
134+
RUN_TEST(test_round_robin);
135+
RUN_TEST(test_priority);
136+
RUN_TEST(test_encoding);
137+
}

src/test/test_switches.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Declare the entry point provided by test_switches
2+
3+
void setup_switches(void);

0 commit comments

Comments
 (0)