Line data Source code
1 : /*
2 : MIT License
3 :
4 : Copyright (c) 2019 Horizon Hobby, LLC
5 :
6 : Permission is hereby granted, free of charge, to any person obtaining a copy
7 : of this software and associated documentation files (the "Software"), to deal
8 : in the Software without restriction, including without limitation the rights
9 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 : copies of the Software, and to permit persons to whom the Software is
11 : furnished to do so, subject to the following conditions:
12 :
13 : The above copyright notice and this permission notice shall be included in all
14 : copies or substantial portions of the Software.
15 :
16 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 : SOFTWARE.
23 : */
24 :
25 : #include <string.h>
26 : #include <stdint.h>
27 :
28 : #include "spm_srxl.h"
29 :
30 : /// LOCAL TYPES AND CONSTANTS ///
31 :
32 : #if(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_SPEED)
33 : const uint16_t srxlCRCTable[] =
34 : {
35 : 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
36 : 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
37 : 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
38 : 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
39 : 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
40 : 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
41 : 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
42 : 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
43 :
44 : 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
45 : 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
46 : 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
47 : 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
48 : 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
49 : 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
50 : 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
51 : 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
52 :
53 : 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
54 : 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
55 : 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
56 : 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
57 : 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
58 : 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
59 : 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
60 : 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
61 :
62 : 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
63 : 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
64 : 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
65 : 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
66 : 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
67 : 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
68 : 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
69 : 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0
70 : };
71 : #endif
72 :
73 : #define SRXL_TELEM_SUPPRESS_MAX (100)
74 :
75 : /// PUBLIC VARIABLES ///
76 :
77 : SrxlChannelData srxlChData;
78 : SrxlTelemetryData srxlTelemData;
79 : SrxlVtxData srxlVtxData = {0, 0, 1, 0, 0, 1};
80 :
81 : /// LOCAL VARIABLES ///
82 :
83 : static SrxlDevice srxlThisDev;
84 : static SrxlBus srxlBus[SRXL_NUM_OF_BUSES];
85 : static bool srxlChDataIsFailsafe = false;
86 : static bool srxlTelemetryPhase = false;
87 : #ifdef SRXL_INCLUDE_MASTER_CODE
88 : static uint32_t srxlFailsafeChMask; // Tracks all active channels for use during failsafe transmission
89 : #endif
90 : static SrxlBindData srxlBindInfo;
91 : static SrxlReceiverStats srxlRx;
92 : static uint16_t srxlTelemSuppressCount;
93 :
94 : #ifdef SRXL_INCLUDE_FWD_PGM_CODE
95 : static SrxlFullID srxlFwdPgmDevice; // Device that should accept Forward Programming connection by default
96 : static uint8_t srxlFwdPgmBuffer[FWD_PGM_MAX_DATA_SIZE];
97 : static uint8_t srxlFwdPgmBufferLength;
98 : #endif
99 :
100 : // Include additional header and externs if using STM32 hardware acceleration
101 : #if(SRXL_CRC_OPTIMIZE_MODE > SRXL_CRC_OPTIMIZE_SIZE)
102 : #ifdef __cplusplus
103 : extern "C"
104 : {
105 : #endif
106 : #if(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_STM_HAL)
107 : #if(SRXL_STM_TARGET_FAMILY == SRXL_STM_TARGET_F7)
108 : #include "stm32f7xx_hal.h"
109 : #else
110 : #include "stm32f3xx_hal.h" // Default to F3 if not given
111 : #endif
112 : extern CRC_HandleTypeDef hcrc;
113 : #else
114 : #if(SRXL_STM_TARGET_FAMILY == SRXL_STM_TARGET_F7)
115 : #error "STM32F7 targets not yet supported for register-based STM HW optimization"
116 : #else
117 : #include "stm32f30x.h" // Default to F3 if not given
118 : #endif
119 : #endif
120 : #ifdef __cplusplus
121 : }
122 : #endif
123 : #endif
124 :
125 : /// LOCAL HELPER FUNCTIONS ///
126 :
127 : // Compute SRXL CRC over packet buffer (assumes length is correctly set)
128 0 : static uint16_t srxlCrc16(uint8_t* packet)
129 : {
130 0 : uint16_t crc = 0; // Seed with 0
131 0 : uint8_t length = packet[2] - 2; // Exclude 2 CRC bytes at end of packet from the length
132 :
133 0 : if(length <= SRXL_MAX_BUFFER_SIZE - 2)
134 : {
135 : #if(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_SIZE)
136 : // Use bitwise method
137 : for(uint8_t i = 0; i < length; ++i)
138 : {
139 : crc = crc ^ ((uint16_t)packet[i] << 8);
140 : for(int b = 0; b < 8; b++)
141 : {
142 : if(crc & 0x8000)
143 : crc = (crc << 1) ^ 0x1021;
144 : else
145 : crc = crc << 1;
146 : }
147 : }
148 : #elif(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_STM_HW)
149 : // Use direct STM32 HW CRC register access
150 : uint8_t* pEnd = &packet[length];
151 : srxlEnterCriticalSection();
152 : #ifdef SRXL_SAVE_HW_CRC_CONTEXT
153 : uint32 savedPOL = CRC->POL;
154 : uint32 savedINIT = CRC->INIT;
155 : uint32 savedCR = CRC->CR;
156 : uint32 savedDR = CRC->DR;
157 : #endif
158 : CRC->POL = 0x1021;
159 : CRC->INIT = 0;
160 : CRC->CR = 0x09; // 16-bit polynomial, no input or output reversal, reset to init of 0
161 : while(packet < pEnd)
162 : *(uint8_t*)(CRC_BASE) = *(packet++);
163 : crc = (uint16_t)CRC->DR;
164 : #ifdef SRXL_SAVE_HW_CRC_CONTEXT
165 : // We have to use this convoluted method to restore things because writing INIT sets DR too
166 : CRC->CR = 0;
167 : CRC->POL = 0x04C11DB7;
168 : CRC->INIT = savedINIT;
169 : CRC->DR = savedINIT;
170 : CRC->POL = savedDR;
171 : CRC->DR = 1;
172 : CRC->CR = savedCR;
173 : CRC->POL = savedPOL;
174 : #endif
175 : srxlExitCriticalSection();
176 : #elif(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_STM_HAL)
177 : // STM32f3/f7 hardware optimization using the STM32Cube libraries in HAL mode requires the following
178 : // configuration, set in the STM32CubeMX "Pinout & Configuration" tab under "Computing > CRC":
179 : // Basic Parameters
180 : // Default Polynomial State = Disable
181 : // CRC Length = 16-bit
182 : // CRC Generating Polynomial = X12+X5+X0
183 : // Default Init Value State = Disable
184 : // Init Value For CRC Computation = 0
185 : // Advanced Parameters
186 : // Input Data Inversion Mode = None
187 : // Output Data Inversion Mode = Disable
188 : // Input Data Format = Bytes
189 : crc = (uint16_t)HAL_CRC_Calculate(&hcrc, (uint32_t*)packet, length);
190 : #elif(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_EXTERNAL)
191 0 : crc = SRXL_CRC_CALCULATE(packet, length, crc);
192 : #else
193 : // Default to table-lookup method
194 : uint8_t i;
195 : for(i = 0; i < length; ++i)
196 : {
197 : // Get indexed position in lookup table using XOR of current CRC hi byte
198 : uint8_t pos = (uint8_t)((crc >> 8) ^ packet[i]);
199 : // Shift LSB up and XOR with the resulting lookup table entry
200 : crc = (uint16_t)((crc << 8) ^ (uint16_t)(srxlCRCTable[pos]));
201 : }
202 : #endif
203 : }
204 0 : return crc;
205 : }
206 :
207 : // Get the receiver entry for the requested bus and device ID
208 0 : static inline SrxlRcvrEntry* srxlGetReceiverEntry(uint8_t busIndex, uint8_t deviceID)
209 : {
210 0 : SrxlRcvrEntry* pRcvr = 0;
211 : uint8_t i;
212 0 : for(i = 0; i < srxlRx.rcvrCount; ++i)
213 : {
214 0 : if((srxlRx.rcvr[i].busBits & (1u << busIndex)) && (srxlRx.rcvr[i].deviceID == deviceID))
215 : {
216 0 : pRcvr = &srxlRx.rcvr[i];
217 0 : break;
218 : }
219 : }
220 0 : return pRcvr;
221 : }
222 :
223 : // Add a new receiver entry for the given device
224 0 : static inline SrxlRcvrEntry* srxlAddReceiverEntry(SrxlBus* pBus, SrxlDevEntry devEntry)
225 : {
226 : // Only allow receivers (or flight controllers in certain circumstances) to be added
227 0 : if(!pBus || devEntry.deviceID < 0x10 || devEntry.deviceID >= 0x40)
228 0 : return 0;
229 :
230 : // If we didn't previously add this receiver, add it now if we have room
231 0 : SrxlRcvrEntry* pRcvr = srxlGetReceiverEntry(pBus->fullID.busIndex, devEntry.deviceID);
232 0 : if(!pRcvr)
233 : {
234 0 : if(srxlRx.rcvrCount >= SRXL_MAX_RCVRS)
235 0 : return 0;
236 :
237 0 : uint8_t i = srxlRx.rcvrCount++;
238 0 : pRcvr = &srxlRx.rcvr[i];
239 0 : pRcvr->deviceID = devEntry.deviceID;
240 0 : pRcvr->busBits = (1u << pBus->fullID.busIndex);
241 0 : pRcvr->info = devEntry.info;
242 :
243 : // If this receiver is full-range, insert into our sorted list after the other full-range telemetry receivers
244 0 : if(pRcvr->info & SRXL_DEVINFO_TELEM_FULL_RANGE)
245 : {
246 : uint8_t n;
247 0 : for(n = i; n > srxlRx.rcvrSortInsert; --n)
248 0 : srxlRx.rcvrSorted[n] = srxlRx.rcvrSorted[n - 1];
249 0 : srxlRx.rcvrSorted[(srxlRx.rcvrSortInsert)++] = pRcvr;
250 : }
251 : // Else just tack onto the end
252 : else
253 : {
254 0 : srxlRx.rcvrSorted[i] = pRcvr;
255 : }
256 : }
257 :
258 : // If this new receiver is a base receiver that supports telemetry or we haven't set a default active telemetry receiver, set it
259 0 : if(!srxlRx.pTelemRcvr || (pRcvr->deviceID >= 0x20 && pRcvr->deviceID < 0x30 && (pRcvr->info & SRXL_DEVINFO_TELEM_TX_ENABLED)))
260 : {
261 0 : srxlRx.pTelemRcvr = pRcvr;
262 : }
263 :
264 0 : return pRcvr;
265 : }
266 :
267 : // Pick the best receiver to send telemetry on
268 0 : static inline SrxlRcvrEntry* srxlChooseTelemRcvr(void)
269 : {
270 : // If we only know about one receiver, set it to that
271 0 : if(srxlRx.rcvrCount == 1)
272 0 : return srxlRx.rcvrSorted[0];
273 :
274 : // If we were previously sending telemetry
275 0 : if(srxlRx.pTelemRcvr && srxlRx.pTelemRcvr->channelMask)
276 : {
277 : // If the current choice is not full-range
278 0 : if((srxlRx.pTelemRcvr->info & SRXL_DEVINFO_TELEM_FULL_RANGE) == 0)
279 : {
280 : // Then see if there is a full-range choice that received channel data to switch to
281 : uint8_t i;
282 0 : for(i = 0; i < srxlRx.rcvrSortInsert; ++i)
283 : {
284 0 : if(srxlRx.rcvrSorted[i]->channelMask)
285 0 : return srxlRx.rcvrSorted[i];
286 : }
287 : }
288 :
289 : // Else keep using the current receiver
290 0 : return srxlRx.pTelemRcvr;
291 : }
292 : // Else just pick the first one that got channel data this past frame
293 : else
294 : {
295 : uint8_t i;
296 0 : for(i = 0; i < srxlRx.rcvrCount; ++i)
297 : {
298 0 : if(srxlRx.rcvrSorted[i]->channelMask)
299 0 : return srxlRx.rcvrSorted[i];
300 : }
301 : }
302 :
303 0 : return 0;
304 : }
305 :
306 : // Return pointer to device entry matching the given ID, or NULL if not found
307 0 : static SrxlDevEntry* srxlGetDeviceEntry(SrxlBus* pBus, uint8_t deviceID)
308 : {
309 0 : if(pBus)
310 : {
311 : uint8_t i;
312 0 : for(i = 0; i < pBus->rxDevCount; ++i)
313 : {
314 0 : if(pBus->rxDev[i].deviceID == deviceID)
315 0 : return &(pBus->rxDev[i]);
316 : }
317 : }
318 0 : return 0;
319 : }
320 :
321 : // Add an entry to our list of devices found on the SRXL bus (or update an entry if it already exists)
322 0 : static SrxlDevEntry* srxlAddDeviceEntry(SrxlBus* pBus, SrxlDevEntry devEntry)
323 : {
324 : // Don't allow broadcast or unknown device types to be added
325 0 : if(!pBus || devEntry.deviceID < 0x10 || devEntry.deviceID > 0xEF)
326 0 : return 0;
327 :
328 : // Limit device priority
329 0 : if(devEntry.priority > 100)
330 0 : devEntry.priority = 100;
331 :
332 : // Update device entry if it already exists
333 0 : SrxlDevEntry* retVal = srxlGetDeviceEntry(pBus, devEntry.deviceID);
334 0 : if(retVal)
335 : {
336 0 : pBus->rxDevPrioritySum -= retVal->priority;
337 0 : *retVal = devEntry;
338 0 : pBus->rxDevPrioritySum += retVal->priority;
339 : }
340 : // Else add to the list if we have room
341 0 : else if(pBus->rxDevCount < SRXL_MAX_DEVICES)
342 : {
343 0 : retVal = &(pBus->rxDev[pBus->rxDevCount++]);
344 0 : *retVal = devEntry;
345 0 : pBus->rxDevPrioritySum += retVal->priority;
346 :
347 : #ifdef SRXL_INCLUDE_FWD_PGM_CODE
348 : // If the new device supports Forward Programming and is a base receiver or flight controller, choose as the default
349 : if(devEntry.info & SRXL_DEVINFO_FWD_PROG_SUPPORT)
350 : {
351 : uint8_t devType = devEntry.deviceID >> 4;
352 : if(devType > (srxlFwdPgmDevice.deviceID >> 4) && devType < SrxlDevType_ESC)
353 : {
354 : srxlFwdPgmDevice.deviceID = devEntry.deviceID;
355 : srxlFwdPgmDevice.busIndex = pBus->fullID.busIndex;
356 : }
357 : }
358 : #endif
359 :
360 : // If the new device is a receiver, add to our receiver list
361 0 : if(retVal->deviceID < 0x30)
362 : {
363 0 : srxlAddReceiverEntry(pBus, *retVal);
364 : }
365 : }
366 :
367 0 : return retVal;
368 : }
369 :
370 : /// PUBLIC FUNCTIONS ///
371 :
372 : /**
373 : @brief Initialize common SRXL info for this device
374 :
375 : @param deviceID: SRXL Device ID (see section 7.1.1 of SRXL2 Spec)
376 : @param priority: Requested telemetry priority (1-100; typical is 10 per unique message type)
377 : @param info: Device info bits (see SRXL_DEVINFO_XXX bits in spm_srxl.h)
378 : @param uid: Unique 32-bit id to avoid device ID collision during handshake
379 : @return bool: True if device info was successfully initialized
380 : */
381 0 : bool srxlInitDevice(uint8_t deviceID, uint8_t priority, uint8_t info, uint32_t uid)
382 : {
383 0 : if(deviceID < 0x10 || deviceID > 0xEF)
384 0 : return false;
385 :
386 0 : srxlThisDev.devEntry.deviceID = deviceID;
387 0 : srxlThisDev.devEntry.info = info;
388 0 : srxlThisDev.devEntry.priority = priority;
389 0 : srxlThisDev.devEntry.rfu = 0;
390 0 : srxlThisDev.uid = uid;
391 0 : srxlThisDev.vtxProxy = false;
392 :
393 : #ifdef SRXL_INCLUDE_MASTER_CODE
394 : // If this device is a receiver, add to our receiver info
395 : if(deviceID < 0x30)
396 : {
397 : srxlInitReceiver(deviceID, info);
398 : }
399 : #endif
400 :
401 : #ifdef SRXL_INCLUDE_FWD_PGM_CODE
402 : // If this device is a receiver or flight controller that supports Forward Programming, set as default
403 : if((info & SRXL_DEVINFO_FWD_PROG_SUPPORT) && deviceID < 0x40)
404 : {
405 : srxlFwdPgmDevice.deviceID = deviceID;
406 : srxlFwdPgmDevice.busIndex = 0;
407 : }
408 : #endif
409 :
410 : #if(SRXL_CRC_OPTIMIZE_MODE == SRXL_CRC_OPTIMIZE_STM_HW)
411 : // Enable the peripheral clock for the HW CRC engine
412 : RCC->AHBENR |= RCC_AHBENR_CRCEN;
413 : #endif
414 :
415 0 : return true;
416 : }
417 :
418 : /**
419 : @brief Initialize bus settings for the given SRXL bus
420 :
421 : @param busIndex: Index into srxlBus array of bus entries
422 : @param uart: Number to identify UART to which this SRXL bus should be connected
423 : @param baudSupported: 0 = 115200 baud, 1 = 400000 baud
424 : @return bool: True if SRXL bus was successfully initialized
425 : */
426 0 : bool srxlInitBus(uint8_t busIndex, uint8_t uart, uint8_t baudSupported)
427 : {
428 0 : if(busIndex >= SRXL_NUM_OF_BUSES || !srxlThisDev.devEntry.deviceID)
429 0 : return false;
430 :
431 0 : SrxlBus* pBus = &srxlBus[busIndex];
432 0 : pBus->state = SrxlState_ListenOnStartup;
433 0 : pBus->fullID.deviceID = srxlThisDev.devEntry.deviceID;
434 0 : pBus->fullID.busIndex = busIndex;
435 0 : pBus->rxDevCount = 0;
436 0 : pBus->rxDevPrioritySum = 0;
437 0 : pBus->requestID = (srxlThisDev.devEntry.deviceID == 0x10) ? 0x11 : 0;
438 0 : pBus->baudSupported = baudSupported;
439 0 : pBus->baudRate = SRXL_BAUD_115200;
440 0 : pBus->frameErrCount = 0;
441 0 : pBus->uart = uart;
442 : // Default remote receiver is automatically master -- everyone else figures it out during handshake
443 0 : pBus->master = (srxlThisDev.devEntry.deviceID == 0x10);
444 0 : pBus->pMasterRcvr = (srxlThisDev.devEntry.deviceID == 0x10) ? &srxlRx.rcvr[0] : 0;
445 0 : pBus->initialized = true;
446 :
447 0 : return true;
448 : }
449 :
450 : /**
451 : @brief See if this device is the bus master on the given bus
452 :
453 : @param busIndex: Index into srxlBus array for the desired SRXL bus
454 : @return bool: True if this device is the bus master on the given SRXL bus
455 : */
456 0 : bool srxlIsBusMaster(uint8_t busIndex)
457 : {
458 0 : return (busIndex < SRXL_NUM_OF_BUSES && srxlBus[busIndex].master);
459 : }
460 :
461 : /**
462 : @brief Get the current SRXL state machine timeout count for the given bus
463 :
464 : @param busIndex: Index into srxlBus array for the desired SRXL bus
465 : @return uint16_t: Timeout count in ms for the given SRXL bus
466 : */
467 0 : uint16_t srxlGetTimeoutCount_ms(uint8_t busIndex)
468 : {
469 0 : return (busIndex < SRXL_NUM_OF_BUSES) ? srxlBus[busIndex].timeoutCount_ms : 0;
470 : }
471 :
472 : /**
473 : @brief Get the Device ID of this device on the given bus
474 :
475 : @param busIndex: Index into srxlBus array for the desired SRXL bus
476 : @return uint8_t: Device ID of this device on the given SRXL bus
477 : */
478 0 : uint8_t srxlGetDeviceID(uint8_t busIndex)
479 : {
480 0 : return (busIndex < SRXL_NUM_OF_BUSES) ? srxlBus[busIndex].fullID.deviceID : 0;
481 : }
482 :
483 : /**
484 : @brief Internal send function called by srxlRun() -- do not call in user code
485 :
486 : @param pBus: Pointer to SRXL bus entry for the desired SRXL bus
487 : @param srxlCmd: Specific type of packet to send
488 : @param replyID: Device ID of the device this Send command is targeting
489 : */
490 0 : static void srxlSend(SrxlBus* pBus, SRXL_CMD srxlCmd, uint8_t replyID)
491 : {
492 0 : if(!pBus || !pBus->initialized)
493 0 : return;
494 :
495 0 : memset(pBus->srxlOut.raw, 0, SRXL_MAX_BUFFER_SIZE);
496 0 : pBus->srxlOut.header.srxlID = SPEKTRUM_SRXL_ID;
497 :
498 : // VTX Data
499 0 : if(srxlCmd == SRXL_CMD_VTX)
500 : {
501 0 : pBus->srxlOut.header.packetType = SRXL_CTRL_ID;
502 0 : pBus->srxlOut.header.length = SRXL_CTRL_BASE_LENGTH + sizeof(SrxlVtxData);
503 0 : pBus->srxlOut.control.payload.cmd = SRXL_CTRL_CMD_VTX;
504 0 : pBus->srxlOut.control.payload.replyID = replyID;
505 0 : pBus->srxlOut.control.payload.vtxData = srxlVtxData;
506 : }
507 : #ifdef SRXL_INCLUDE_MASTER_CODE
508 : // Channel Data
509 : else if(srxlCmd == SRXL_CMD_CHANNEL || srxlCmd == SRXL_CMD_CHANNEL_FS)
510 : {
511 : pBus->srxlOut.header.packetType = SRXL_CTRL_ID;
512 : uint32_t channelMask;
513 : if(srxlCmd == SRXL_CMD_CHANNEL)
514 : {
515 : pBus->srxlOut.control.payload.cmd = SRXL_CTRL_CMD_CHANNEL;
516 : pBus->srxlOut.control.payload.replyID = replyID;
517 :
518 : channelMask = srxlChData.mask;
519 : }
520 : else // == SRXL_CMD_CHANNEL_FS
521 : {
522 : // In failsafe mode, we dont want a telemetry reply
523 : pBus->srxlOut.control.payload.cmd = SRXL_CTRL_CMD_CHANNEL_FS;
524 : pBus->srxlOut.control.payload.replyID = 0;
525 :
526 : channelMask = srxlFailsafeChMask;
527 : }
528 :
529 : // Set signal quality info (only a bus master sends this, so assume srxlChData contains the latest values)
530 : pBus->srxlOut.control.payload.channelData.rssi = srxlChData.rssi;
531 : #ifdef SRXL_IS_HUB
532 : pBus->srxlOut.control.payload.channelData.frameLosses = srxlRx.frameLosses;
533 : #else
534 : pBus->srxlOut.control.payload.channelData.frameLosses = srxlRx.rcvr[0].fades;
535 : #endif
536 :
537 : uint8_t channelIndex = 0;
538 : uint32_t channelMaskBit = 1;
539 : for(uint8_t i = 0; i < 32; ++i, channelMaskBit <<= 1)
540 : {
541 : if(channelMask & channelMaskBit)
542 : {
543 : pBus->srxlOut.control.payload.channelData.values[channelIndex++] = srxlChData.values[i];
544 : }
545 : }
546 :
547 : // Set bits in packet for channels we populated, and clear those mask bits if it was part of a normal channel data command
548 : pBus->srxlOut.control.payload.channelData.mask = channelMask;
549 : if(srxlCmd == SRXL_CMD_CHANNEL)
550 : srxlChData.mask &= ~channelMask;
551 :
552 : pBus->srxlOut.header.length = SRXL_CTRL_BASE_LENGTH + 7 + (2 * channelIndex);
553 : }
554 : #ifdef SRXL_INCLUDE_FWD_PGM_CODE
555 : // Forward Programming Pass-thru
556 : else if(srxlCmd == SRXL_CMD_FWDPGM)
557 : {
558 : pBus->srxlOut.header.packetType = SRXL_CTRL_ID;
559 : pBus->srxlOut.header.length = SRXL_CTRL_BASE_LENGTH + 3 + srxlFwdPgmBufferLength;
560 : pBus->srxlOut.control.payload.cmd = SRXL_CTRL_CMD_FWDPGM;
561 : pBus->srxlOut.control.payload.replyID = replyID;
562 : memcpy(pBus->srxlOut.control.payload.fpData.data, srxlFwdPgmBuffer, srxlFwdPgmBufferLength);
563 : }
564 : #endif // SRXL_INCLUDE_FWD_PGM_CODE
565 : #endif // SRXL_INCLUDE_MASTER_CODE
566 : // RSSI Data
567 0 : else if(srxlCmd == SRXL_CMD_RSSI)
568 : {
569 0 : pBus->srxlOut.header.packetType = SRXL_RSSI_ID;
570 0 : pBus->srxlOut.header.length = sizeof(SrxlRssiPacket);
571 0 : pBus->srxlOut.rssi.request = SRXL_RSSI_REQ_REQUEST; // TODO: Needs to handle both directions!
572 0 : pBus->srxlOut.rssi.antennaA = 0; // TODO: Fill in actual data later
573 0 : pBus->srxlOut.rssi.antennaB = 0;
574 0 : pBus->srxlOut.rssi.antennaC = 0;
575 0 : pBus->srxlOut.rssi.antennaD = 0;
576 : }
577 0 : else if(srxlCmd == SRXL_CMD_HANDSHAKE)
578 : {
579 0 : pBus->srxlOut.header.packetType = SRXL_HANDSHAKE_ID;
580 0 : pBus->srxlOut.header.length = sizeof(SrxlHandshakePacket);
581 0 : pBus->srxlOut.handshake.payload.srcDevID = srxlThisDev.devEntry.deviceID;
582 0 : pBus->srxlOut.handshake.payload.destDevID = replyID;
583 0 : pBus->srxlOut.handshake.payload.priority = srxlThisDev.devEntry.priority;
584 0 : pBus->srxlOut.handshake.payload.baudSupported = pBus->baudSupported;
585 0 : pBus->srxlOut.handshake.payload.info = srxlThisDev.devEntry.info;
586 0 : pBus->srxlOut.handshake.payload.uid = srxlThisDev.uid;
587 : }
588 0 : else if(srxlCmd == SRXL_CMD_TELEMETRY)
589 : {
590 0 : srxlFillTelemetry(&pBus->srxlOut.telemetry.payload);
591 0 : pBus->srxlOut.header.packetType = SRXL_TELEM_ID;
592 0 : pBus->srxlOut.header.length = sizeof(SrxlTelemetryPacket);
593 : // If we successfully received a handshake from the bus master
594 0 : if(pBus->pMasterRcvr)
595 : {
596 : // If we know that a device on this bus should send it, then target that device
597 0 : if(srxlRx.pTelemRcvr && (srxlRx.pTelemRcvr->busBits & (1u << pBus->fullID.busIndex)))
598 0 : pBus->srxlOut.telemetry.destDevID = srxlRx.pTelemRcvr->deviceID;
599 : else
600 0 : pBus->srxlOut.telemetry.destDevID = 0;
601 : }
602 : else
603 : {
604 : // Send 0xFF to tell bus master to re-send the handshake so we know where to direct telemetry in the future
605 0 : pBus->srxlOut.telemetry.destDevID = 0xFF;
606 : }
607 :
608 : #ifdef SRXL_INCLUDE_MASTER_CODE
609 : if(srxlRx.pTelemRcvr && (pBus->srxlOut.telemetry.destDevID == srxlRx.pTelemRcvr->deviceID))
610 : {
611 : // Don't mark telemetry as having been sent if we are sending it ourself over RF
612 : if(pBus->srxlOut.telemetry.destDevID != srxlThisDev.pRcvr->deviceID)
613 : {
614 : srxlTelemetrySent();
615 : // Clear telemetry buffer after sending so we don't repeatedly display old data
616 : // srxlTelemData.sensorID = srxlTelemData.secondaryID = 0;
617 : }
618 : }
619 : #endif
620 : }
621 0 : else if(srxlCmd == SRXL_CMD_ENTER_BIND)
622 : {
623 0 : pBus->srxlOut.header.packetType = SRXL_BIND_ID;
624 0 : pBus->srxlOut.header.length = sizeof(SrxlBindPacket);
625 0 : pBus->srxlOut.bind.request = SRXL_BIND_REQ_ENTER;
626 0 : pBus->srxlOut.bind.deviceID = replyID;
627 0 : pBus->srxlOut.bind.data.type = DSMX_11MS;
628 0 : pBus->srxlOut.bind.data.options = (replyID != 0xFF) ? SRXL_BIND_OPT_TELEM_TX_ENABLE | SRXL_BIND_OPT_BIND_TX_ENABLE : 0;
629 0 : pBus->srxlOut.bind.data.guid = 0;
630 0 : pBus->srxlOut.bind.data.uid = 0;
631 : }
632 0 : else if(srxlCmd == SRXL_CMD_REQ_BIND)
633 : {
634 0 : pBus->srxlOut.header.packetType = SRXL_BIND_ID;
635 0 : pBus->srxlOut.header.length = sizeof(SrxlBindPacket);
636 0 : pBus->srxlOut.bind.request = SRXL_BIND_REQ_STATUS;
637 0 : pBus->srxlOut.bind.deviceID = replyID;
638 0 : memset(&(pBus->srxlOut.bind.data), 0, sizeof(SrxlBindData));
639 : }
640 0 : else if(srxlCmd == SRXL_CMD_SET_BIND)
641 : {
642 0 : pBus->srxlOut.header.packetType = SRXL_BIND_ID;
643 0 : pBus->srxlOut.header.length = sizeof(SrxlBindPacket);
644 0 : pBus->srxlOut.bind.request = SRXL_BIND_REQ_SET_BIND;
645 0 : pBus->srxlOut.bind.deviceID = replyID;
646 0 : pBus->srxlOut.bind.data = srxlBindInfo;
647 : }
648 0 : else if(srxlCmd == SRXL_CMD_BIND_INFO)
649 : {
650 0 : pBus->srxlOut.header.packetType = SRXL_BIND_ID;
651 0 : pBus->srxlOut.header.length = sizeof(SrxlBindPacket);
652 0 : pBus->srxlOut.bind.request = SRXL_BIND_REQ_BOUND_DATA;
653 0 : pBus->srxlOut.bind.deviceID = replyID;
654 0 : pBus->srxlOut.bind.data = srxlBindInfo;
655 : }
656 :
657 : // Compute CRC over entire SRXL packet (excluding the 2 CRC bytes at the end)
658 0 : uint16_t crc = srxlCrc16(pBus->srxlOut.raw);
659 :
660 : // Add CRC to packet in big-endian byte order
661 0 : pBus->srxlOut.raw[pBus->srxlOut.header.length - 2] = (crc >> 8) & 0xFF;
662 0 : pBus->srxlOut.raw[pBus->srxlOut.header.length - 1] = crc & 0xFF;
663 :
664 : // Send the packet out over the assigned UART
665 0 : srxlSendOnUart(pBus->uart, pBus->srxlOut.raw, pBus->srxlOut.header.length);
666 : }
667 :
668 : /**
669 : @brief Parse an SRXL packet received on the given SRXL bus UART
670 :
671 : @param busIndex: Index of SRXL bus state information entry in the srxlBus array
672 : @param packet: Pointer to received packet data
673 : @param length: Length in bytes of received packet data
674 : @return bool: True if a valid packet was received, else false
675 : */
676 0 : bool srxlParsePacket(uint8_t busIndex, uint8_t* packet, uint8_t length)
677 : {
678 : // Validate parameters
679 0 : if(busIndex >= SRXL_NUM_OF_BUSES || !packet || length < 5 || length > SRXL_MAX_BUFFER_SIZE)
680 0 : return false;
681 :
682 : // Validate SRXL ID and length
683 0 : if(packet[0] != SPEKTRUM_SRXL_ID || packet[2] != length)
684 0 : return false;
685 :
686 : // Validate checksum
687 0 : uint16_t crc = srxlCrc16(packet);
688 0 : if((((uint16_t)packet[length - 2] << 8) | packet[length - 1]) != crc)
689 0 : return false;
690 :
691 : // Copy packet into our unioned buffer to avoid "strict aliasing" violations
692 0 : SrxlBus* pBus = &srxlBus[busIndex];
693 0 : SrxlPacket* pRx = &(pBus->srxlIn);
694 0 : memcpy(pRx, packet, length);
695 :
696 : // Handle restart with ongoing communications -- bump to run state
697 0 : pBus->timeoutCount_ms = 0; // TODO: Should we clear this even if packet isn't valid?
698 0 : if(pBus->state < SrxlState_Running && pRx->header.packetType != SRXL_HANDSHAKE_ID)
699 0 : pBus->state = SrxlState_Running;
700 :
701 : // Parse the specific data
702 0 : switch(pRx->header.packetType)
703 : {
704 0 : case SRXL_CTRL_ID: // 0xCD
705 : {
706 0 : SrxlControlData* pCtrlData = &(pRx->control.payload);
707 :
708 : // Validate command
709 0 : if(pCtrlData->cmd > SRXL_CTRL_CMD_FWDPGM)
710 0 : break;
711 :
712 : // VTX
713 0 : if(pCtrlData->cmd == SRXL_CTRL_CMD_VTX)
714 : {
715 0 : if(srxlSetVtxData(&pCtrlData->vtxData))
716 0 : srxlOnVtx(&srxlVtxData);
717 0 : if(pCtrlData->replyID == pBus->fullID.deviceID || pCtrlData->replyID == 0xFF || srxlThisDev.vtxProxy)
718 : {
719 : // TODO: Should we ack this somehow
720 : }
721 : }
722 : #ifdef SRXL_INCLUDE_FWD_PGM_CODE
723 : // Forward Programming
724 : else if(pCtrlData->cmd == SRXL_CTRL_CMD_FWDPGM)
725 : {
726 : if(pCtrlData->replyID == pBus->fullID.deviceID)
727 : {
728 : memcpy(srxlFwdPgmBuffer, pCtrlData->fpData.data, pRx->header.length - 3 - SRXL_CTRL_BASE_LENGTH);
729 : srxlFwdPgmBufferLength = length;
730 : if(pCtrlData->replyID == srxlFwdPgmDevice.deviceID)
731 : {
732 : // Handle Forward Programming command locally
733 : srxlOnFwdPgm(srxlFwdPgmBuffer, srxlFwdPgmBufferLength);
734 : }
735 : else if(srxlFwdPgmDevice.deviceID && srxlBus[srxlFwdPgmDevice.busIndex].master)
736 : {
737 : // Pass it on through to the next target
738 : srxlBus[srxlFwdPgmDevice.busIndex].txFlags.sendFwdPgmData = 1;
739 : }
740 : }
741 : }
742 : #endif
743 : // Channel Data or Failsafe Data
744 : else
745 : {
746 0 : bool isFailsafe = (pCtrlData->cmd == SRXL_CTRL_CMD_CHANNEL_FS);
747 0 : srxlChData.rssi = pCtrlData->channelData.rssi;
748 0 : srxlChData.frameLosses = pCtrlData->channelData.frameLosses;
749 0 : if(pBus->pMasterRcvr)
750 : {
751 0 : if(pCtrlData->channelData.rssi < 0)
752 : {
753 0 : pBus->pMasterRcvr->rssi_dBm = pCtrlData->channelData.rssi;
754 0 : pBus->pMasterRcvr->rssiRcvd |= RSSI_RCVD_DBM;
755 : }
756 : else
757 : {
758 0 : pBus->pMasterRcvr->rssi_Pct = pCtrlData->channelData.rssi;
759 0 : pBus->pMasterRcvr->rssiRcvd |= RSSI_RCVD_PCT;
760 : }
761 : // If the receiver is sending alternating dBm/%, then use that as phase
762 0 : if(pBus->pMasterRcvr->rssiRcvd == RSSI_RCVD_BOTH)
763 : {
764 0 : srxlTelemetryPhase = pCtrlData->channelData.rssi >= 0;
765 : }
766 0 : pBus->pMasterRcvr->fades = pCtrlData->channelData.frameLosses;
767 0 : pBus->pMasterRcvr->channelMask = isFailsafe ? 0 : pCtrlData->channelData.mask;
768 0 : srxlRx.rxBusBits |= pBus->pMasterRcvr->busBits;
769 : }
770 :
771 : // Only save received channel values to srxlChData if it's normal channel data or we're in a hold condition
772 0 : if(!isFailsafe || !srxlRx.lossCountdown)
773 : {
774 0 : uint8_t channelIndex = 0;
775 0 : uint32_t channelMaskBit = 1;
776 : uint8_t i;
777 0 : for(i = 0; i < 32 && channelMaskBit <= pCtrlData->channelData.mask; ++i, channelMaskBit <<= 1)
778 : {
779 0 : if(pCtrlData->channelData.mask & channelMaskBit)
780 : {
781 0 : srxlChData.values[i] = pCtrlData->channelData.values[channelIndex++];
782 0 : srxlChData.mask |= channelMaskBit;
783 : }
784 : }
785 : }
786 :
787 0 : srxlChDataIsFailsafe = isFailsafe; // TODO: Can we still assume this???
788 0 : srxlReceivedChannelData(&(pCtrlData->channelData), isFailsafe);
789 :
790 : // Figure out what type of reply packet to send, if any
791 0 : if(pCtrlData->replyID == 0)
792 : {
793 0 : if(pBus->txFlags.enterBind)
794 : {
795 0 : pBus->state = SrxlState_SendEnterBind;
796 0 : pBus->txFlags.enterBind = 0;
797 : }
798 0 : else if(pBus->txFlags.setBindInfo)
799 : {
800 0 : if(srxlRx.pBindRcvr) // TODO: Double-check this logic
801 : {
802 0 : pBus->requestID = srxlRx.pBindRcvr->deviceID;
803 0 : pBus->state = SrxlState_SendSetBindInfo;
804 : }
805 0 : pBus->txFlags.setBindInfo = 0;
806 : }
807 0 : else if(pBus->txFlags.broadcastBindInfo)
808 : {
809 0 : pBus->requestID = 0xFF;
810 0 : pBus->state = SrxlState_SendSetBindInfo;
811 0 : pBus->txFlags.broadcastBindInfo = 0;
812 : }
813 : }
814 0 : else if(pCtrlData->replyID == pBus->fullID.deviceID)
815 : {
816 0 : pBus->state = SrxlState_SendTelemetry;
817 : }
818 : }
819 0 : break;
820 : }
821 0 : case SRXL_HANDSHAKE_ID: // 0x21
822 : {
823 0 : if(length < sizeof(SrxlHandshakePacket))
824 0 : return false;
825 :
826 : // If this is an unprompted handshake (dest == 0) from a higher device ID, then we're the master
827 0 : SrxlHandshakeData* pHandshake = &(pRx->handshake.payload);
828 0 : if((pHandshake->destDevID == 0) && (pHandshake->srcDevID > pBus->fullID.deviceID))
829 : {
830 : // Send a reply immediately to get the slave to shut up
831 0 : pBus->state = SrxlState_SendHandshake;
832 0 : pBus->requestID = pHandshake->srcDevID;
833 0 : pBus->baudSupported = SRXL_SUPPORTED_BAUD_RATES;
834 0 : srxlRun(busIndex, 0);
835 0 : pBus->requestID = pBus->fullID.deviceID;
836 0 : pBus->state = SrxlState_SendHandshake;
837 0 : pBus->master = true;
838 0 : pBus->pMasterRcvr = &srxlRx.rcvr[0];
839 : }
840 :
841 : // Add this device to our list of discovered devices
842 0 : SrxlDevEntry newDev = {.deviceID = pHandshake->srcDevID, .priority = pHandshake->priority, .info = pHandshake->info};
843 0 : srxlAddDeviceEntry(pBus, newDev);
844 :
845 : // Bus master needs to track responses and poll next device
846 0 : if(pBus->master)
847 : {
848 : // Keep track of baud rates supported
849 0 : pBus->baudSupported &= pHandshake->baudSupported;
850 :
851 : // Make sure the state machine is set to poll -- this will eventually reach broadcast address (0xFF)
852 0 : pBus->state = SrxlState_SendHandshake;
853 : }
854 : // Broadcast handshake sets the agreed upon baud rate for this bus
855 0 : else if(pHandshake->destDevID == 0xFF)
856 : {
857 : // Get bus master receiver entry (if it's a flight controller, add it now as a receiver on this bus)
858 0 : if(newDev.deviceID >= 0x30 && newDev.deviceID < 0x40)
859 : {
860 0 : pBus->pMasterRcvr = srxlAddReceiverEntry(pBus, newDev);
861 : }
862 : else
863 : {
864 0 : pBus->pMasterRcvr = srxlGetReceiverEntry(busIndex, newDev.deviceID);
865 : }
866 :
867 : // Set baud rate and advance to run state
868 0 : pBus->baudRate = pHandshake->baudSupported;
869 0 : if(pBus->baudRate & SRXL_BAUD_400000)
870 : {
871 0 : srxlChangeBaudRate(pBus->uart, 400000); // Only alternate rate supported for now...
872 : }
873 0 : pBus->state = SrxlState_Running;
874 : }
875 : // Normal Handshake destined for this device should be replied to -- else ignore
876 : else
877 : {
878 0 : if(pHandshake->destDevID == pBus->fullID.deviceID && pBus->state != SrxlState_SendHandshake)
879 : {
880 0 : pBus->requestID = pHandshake->srcDevID;
881 0 : pBus->state = SrxlState_SendHandshake;
882 : }
883 : else
884 : {
885 0 : pBus->state = SrxlState_ListenForHandshake;
886 : }
887 : }
888 :
889 0 : break;
890 : }
891 0 : case SRXL_PARAM_ID: // 0x50
892 : {
893 : // TODO: Add later
894 0 : break;
895 : }
896 0 : case SRXL_RSSI_ID: // 0x55
897 : {
898 : // TODO: Add later
899 0 : break;
900 : }
901 0 : case SRXL_BIND_ID: // 0x41
902 : {
903 0 : if(length < sizeof(SrxlBindPacket))
904 0 : return false;
905 :
906 0 : SrxlBindPacket* pBindInfo = &(pRx->bind);
907 :
908 : // If this is a bound data report
909 0 : if(pBindInfo->request == SRXL_BIND_REQ_BOUND_DATA)
910 : {
911 : // Call the user-defined callback -- if returns true, bind all other receivers
912 : SrxlFullID boundID;
913 0 : boundID.deviceID = pBindInfo->deviceID;
914 0 : boundID.busIndex = busIndex;
915 0 : if(srxlOnBind(boundID, pBindInfo->data))
916 : {
917 : // Update the bind info
918 0 : srxlBindInfo.type = pBindInfo->data.type;
919 0 : if(pBindInfo->data.options & SRXL_BIND_OPT_TELEM_TX_ENABLE)
920 : {
921 0 : SrxlRcvrEntry* pNewTelem = srxlGetReceiverEntry(busIndex, pBindInfo->deviceID);
922 0 : if(pNewTelem || !srxlRx.pTelemRcvr)
923 0 : srxlRx.pTelemRcvr = pNewTelem;
924 : }
925 0 : srxlBindInfo.options = 0; // Disable telemetry and Discovery reply when setting other receivers
926 0 : srxlBindInfo.guid = pBindInfo->data.guid;
927 0 : srxlBindInfo.uid = pBindInfo->data.uid;
928 :
929 : // Try to set bind info for all other receivers on other buses to match it
930 : uint8_t b;
931 0 : for(b = 0; b < SRXL_NUM_OF_BUSES; ++b)
932 : {
933 0 : if(b == busIndex)
934 0 : continue;
935 0 : srxlBus[b].txFlags.broadcastBindInfo = 1;
936 : }
937 : }
938 : }
939 : // If this bind packet is directed at us
940 0 : else if(pBindInfo->deviceID == pBus->fullID.deviceID || pBindInfo->deviceID == 0xFF)
941 : {
942 : // Check for Enter Bind Mode (only valid if sent to a specific receiver)
943 0 : if(pBindInfo->request == SRXL_BIND_REQ_ENTER)
944 : {
945 : #ifdef SRXL_INCLUDE_MASTER_CODE
946 : srxlBindInfo.type = pBindInfo->data.type;
947 : srxlBindInfo.options = pBindInfo->data.options;
948 : srxlBindInfo.guid = 0;
949 : srxlBindInfo.uid = 0;
950 : srxlTryToBind(srxlBindInfo);
951 : #endif
952 : }
953 0 : else if(pBindInfo->request == SRXL_BIND_REQ_STATUS && srxlThisDev.pRcvr)
954 : {
955 : // TODO: Fill in data if we didn't just bind?
956 0 : pBus->txFlags.reportBindInfo = 1;
957 : }
958 : // Handle set bind info request
959 0 : else if(pBindInfo->request == SRXL_BIND_REQ_SET_BIND)
960 : {
961 0 : srxlBindInfo = pBindInfo->data;
962 : #ifdef SRXL_INCLUDE_MASTER_CODE
963 : if(pBus->fullID.deviceID < 0x30)
964 : srxlTryToBind(srxlBindInfo);
965 : #endif
966 : }
967 : }
968 :
969 0 : break;
970 : }
971 0 : case SRXL_TELEM_ID: // 0x80
972 : {
973 0 : if(length < sizeof(SrxlTelemetryPacket))
974 0 : return false;
975 :
976 : // NOTE: This data should be sent by exactly one telemetry device in response to a bus master request,
977 : // so it is safe to update the global pTelemRcvr here even though this is a bus-specific function.
978 :
979 0 : SrxlTelemetryPacket* pTelem = &(pRx->telemetry);
980 0 : memcpy(&srxlTelemData, &pTelem->payload, sizeof(srxlTelemData));
981 : // If the telemetry destination is set to broadcast, that indicates a request to re-handshake
982 0 : if(pBus->master && pTelem->destDevID == 0xFF)
983 : {
984 : // If the master only found one device, don't poll again -- just tell the requesting device who we are
985 0 : pBus->requestID = pBus->rxDevCount > 1 ? pBus->fullID.deviceID : 0xFF;
986 0 : pBus->state = SrxlState_SendHandshake;
987 : }
988 : // If the incoming telemetry is destined for us, then we need to figure out who should send it over RF
989 0 : else if(pTelem->destDevID == pBus->fullID.deviceID)
990 : {
991 : // This needs different logic for hubs versus endpoints
992 : #ifdef SRXL_IS_HUB
993 : if(srxlRx.pTelemRcvr == 0)
994 : {
995 : srxlRx.pTelemRcvr = srxlChooseTelemRcvr();
996 : }
997 : #else
998 0 : srxlRx.pTelemRcvr = srxlThisDev.pRcvr;
999 : #endif
1000 : // Enable this device's telemetry tx based on whether we are the chosen telemetry receiver
1001 : #ifdef SRXL_INCLUDE_MASTER_CODE
1002 : srxlSetTelemetryTxEnable(srxlRx.pTelemRcvr && (srxlRx.pTelemRcvr == srxlThisDev.pRcvr));
1003 : #endif
1004 : }
1005 : // Else turn off our telemetry and that of any receivers we might reply to via our own telemetry
1006 : else
1007 : {
1008 0 : srxlRx.pTelemRcvr = 0;
1009 : #ifdef SRXL_INCLUDE_MASTER_CODE
1010 : srxlSetTelemetryTxEnable(false);
1011 : #endif
1012 : }
1013 :
1014 0 : srxlTelemSuppressCount = 0;
1015 : #ifdef SRXL_INCLUDE_MASTER_CODE
1016 : srxlSuppressInternalTelemetry(&pTelem->payload);
1017 : #endif
1018 0 : break;
1019 : }
1020 0 : default:
1021 : {
1022 0 : break;
1023 : }
1024 : }
1025 :
1026 : // Run state machine for slave devices after each received packet
1027 0 : if(!pBus->master)
1028 : {
1029 0 : srxlRun(busIndex, 0);
1030 : }
1031 :
1032 0 : return true;
1033 : }
1034 :
1035 : /**
1036 : @brief Run the SRXL state machine after each receive or rx timeout
1037 :
1038 : @param busIndex: Index of SRXL bus state information entry in the srxlBus array
1039 : @param timeoutDelta_ms: Number of milliseconds to increment receive timeout if a timeout
1040 : occured, or <= 0 to clear timeout count upon packet receive.
1041 : */
1042 0 : void srxlRun(uint8_t busIndex, int16_t timeoutDelta_ms)
1043 : {
1044 0 : SrxlBus* pBus = &srxlBus[busIndex];
1045 0 : if(busIndex >= SRXL_NUM_OF_BUSES || !pBus->initialized || pBus->state == SrxlState_Disabled)
1046 0 : return;
1047 :
1048 : // Check receive timeout and advance state if needed
1049 0 : if(timeoutDelta_ms > 0)
1050 : {
1051 0 : pBus->timeoutCount_ms += timeoutDelta_ms;
1052 0 : if(pBus->timeoutCount_ms >= 30000)
1053 0 : pBus->timeoutCount_ms = 30000;
1054 :
1055 0 : if(pBus->timeoutCount_ms >= 50)
1056 : {
1057 : // After startup delay of 50ms, switch to handshake send or listen based on device unit ID
1058 0 : if(pBus->state == SrxlState_ListenOnStartup)
1059 : {
1060 0 : pBus->state = (pBus->fullID.deviceID & 0x0F) ? SrxlState_ListenForHandshake : SrxlState_SendHandshake;
1061 : }
1062 : // Reset non-master device back to startup conditions if 50ms elapses with no communications
1063 0 : else if(!pBus->master && pBus->state >= SrxlState_Running)
1064 : {
1065 0 : srxlChangeBaudRate(pBus->uart, 115200);
1066 0 : pBus->baudRate = SRXL_BAUD_115200;
1067 0 : pBus->timeoutCount_ms = 0;
1068 0 : pBus->requestID = 0; // Change back to 0 to indicate unprompted handshake from slave device
1069 0 : if(pBus->pMasterRcvr)
1070 : {
1071 0 : pBus->pMasterRcvr->channelMask = 0;
1072 0 : pBus->pMasterRcvr->fades = 0xFFFF;
1073 0 : pBus->pMasterRcvr->rssi_Pct = 0;
1074 0 : pBus->pMasterRcvr->rssi_dBm = -1;
1075 : }
1076 0 : pBus->state = SrxlState_ListenOnStartup;
1077 : }
1078 : }
1079 : }
1080 : else
1081 : {
1082 0 : pBus->timeoutCount_ms = 0;
1083 : }
1084 :
1085 0 : if(!pBus->master)
1086 : {
1087 : // Non-master actions for the given state
1088 0 : switch(pBus->state)
1089 : {
1090 0 : case SrxlState_SendHandshake:
1091 : {
1092 0 : srxlSend(pBus, SRXL_CMD_HANDSHAKE, pBus->requestID);
1093 0 : break;
1094 : }
1095 0 : case SrxlState_SendTelemetry:
1096 : {
1097 0 : srxlSend(pBus, SRXL_CMD_TELEMETRY, pBus->requestID);
1098 0 : pBus->state = SrxlState_Running;
1099 0 : break;
1100 : }
1101 0 : case SrxlState_SendVTX:
1102 : {
1103 0 : srxlSend(pBus, SRXL_CMD_VTX, pBus->requestID);
1104 0 : pBus->state = SrxlState_Running;
1105 0 : break;
1106 : }
1107 0 : case SrxlState_SendEnterBind:
1108 : {
1109 0 : if(srxlRx.pBindRcvr && (srxlRx.pBindRcvr != srxlThisDev.pRcvr))
1110 : {
1111 0 : srxlSend(pBus, SRXL_CMD_ENTER_BIND, srxlRx.pBindRcvr->deviceID);
1112 : }
1113 : else
1114 : {
1115 0 : srxlBindInfo.options = 0;
1116 0 : srxlSend(pBus, SRXL_CMD_ENTER_BIND, 0xFF);
1117 : }
1118 0 : pBus->state = SrxlState_Running;
1119 0 : break;
1120 : }
1121 0 : case SrxlState_SendSetBindInfo:
1122 : {
1123 0 : srxlSend(pBus, SRXL_CMD_SET_BIND, pBus->requestID);
1124 0 : pBus->state = SrxlState_Running;
1125 0 : break;
1126 : }
1127 0 : case SrxlState_SendBoundDataReport:
1128 : {
1129 0 : srxlSend(pBus, SRXL_CMD_BIND_INFO, pBus->fullID.deviceID);
1130 0 : pBus->state = SrxlState_Running;
1131 0 : break;
1132 : }
1133 0 : case SrxlState_Running:
1134 : default:
1135 : {
1136 0 : return;
1137 : }
1138 : }
1139 : }
1140 : #ifdef SRXL_INCLUDE_MASTER_CODE
1141 : else
1142 : {
1143 : srxlRunMaster(pBus);
1144 : }
1145 : #endif // SRXL_INCLUDE_MASTER_CODE
1146 : }
1147 :
1148 : /**
1149 : @brief Tell the "best" receiver to enter bind mode, either locally or via SRXL command
1150 :
1151 : @param bindType: One of the possible bind status types to use when binding -- NOTE: The transmitter may ignore this
1152 : @param broadcast: True if this is a local request that should tell all connected receivers to enter bind
1153 : @return bool: True if a receiver was told to enter bind mode; else false
1154 : */
1155 0 : bool srxlEnterBind(uint8_t bindType, bool broadcast)
1156 : {
1157 0 : srxlRx.pBindRcvr = 0;
1158 0 : if(broadcast && srxlThisDev.pRcvr)
1159 : {
1160 0 : srxlRx.pBindRcvr = srxlThisDev.pRcvr;
1161 : }
1162 0 : else if(srxlRx.pTelemRcvr)
1163 : {
1164 0 : srxlRx.pBindRcvr = srxlRx.pTelemRcvr;
1165 : }
1166 0 : else if(srxlRx.rcvrCount > 0 && srxlRx.rcvrSorted[0]->deviceID < 0x30)
1167 : {
1168 0 : srxlRx.pBindRcvr = srxlRx.rcvrSorted[0];
1169 : }
1170 :
1171 0 : if(srxlRx.pBindRcvr)
1172 : {
1173 : #ifdef SRXL_INCLUDE_MASTER_CODE
1174 : // Local bind
1175 : if(srxlRx.pBindRcvr == srxlThisDev.pRcvr)
1176 : {
1177 : srxlBindInfo.type = bindType;
1178 : srxlBindInfo.options = SRXL_BIND_OPT_BIND_TX_ENABLE;
1179 : srxlBindInfo.guid = 0;
1180 : srxlBindInfo.uid = 0;
1181 : if(srxlRx.pBindRcvr == srxlRx.pTelemRcvr)
1182 : srxlBindInfo.options |= SRXL_BIND_OPT_TELEM_TX_ENABLE;
1183 : srxlTryToBind(srxlBindInfo);
1184 : if(broadcast)
1185 : {
1186 : uint8_t b;
1187 : for(b = 0; b < SRXL_NUM_OF_BUSES; ++b)
1188 : {
1189 : srxlBus[b].txFlags.enterBind = 1;
1190 : }
1191 : }
1192 : return true;
1193 : }
1194 : #endif // SRXL_INCLUDE_MASTER_CODE
1195 :
1196 : // Remote bind
1197 : uint8_t i;
1198 0 : for(i = 0; i < SRXL_NUM_OF_BUSES; ++i)
1199 : {
1200 0 : if((1u << i) & srxlRx.pBindRcvr->busBits)
1201 : {
1202 0 : srxlBus[i].txFlags.enterBind = 1;
1203 0 : return true;
1204 : }
1205 : }
1206 : }
1207 :
1208 0 : return false;
1209 : }
1210 :
1211 : /**
1212 : @brief Public function to set bind info for the system, either locally or via SRXL commands
1213 :
1214 : @param bindType: Type of bind requested for this receiver or all receivers
1215 : @param guid: Transmitter GUID to bind the receiver to
1216 : @param uid: Unique ID provided by transmitter upon initial bind (can be 0 if unknown)
1217 : @return bool: True if bind info was successfully set for the destination device; else false
1218 : */
1219 0 : bool srxlSetBindInfo(uint8_t bindType, uint64_t guid, uint32_t uid)
1220 : {
1221 0 : if(guid == 0)
1222 0 : return false;
1223 :
1224 : // Set bind info, with options defaulted to 0
1225 0 : srxlBindInfo.type = bindType;
1226 0 : srxlBindInfo.options = SRXL_BIND_OPT_US_POWER; // Request US power, with no guarantee it's supported
1227 0 : srxlBindInfo.guid = guid;
1228 0 : srxlBindInfo.uid = uid ? uid : srxlThisDev.uid;
1229 :
1230 : #ifdef SRXL_INCLUDE_MASTER_CODE
1231 : // If we are a receiver
1232 : if(srxlThisDev.pRcvr)
1233 : {
1234 : // Bind locally, which will result in no further packets since options == 0
1235 : srxlTryToBind(srxlBindInfo);
1236 : }
1237 : #endif
1238 :
1239 : // Broadcast this bind info on all SRXL buses
1240 : uint8_t b;
1241 0 : for(b = 0; b < SRXL_NUM_OF_BUSES; ++b)
1242 : {
1243 0 : srxlBus[b].txFlags.broadcastBindInfo = 1;
1244 : }
1245 :
1246 0 : return true;
1247 : }
1248 :
1249 : /**
1250 : @brief Public function to call from the user UART code when a UART frame error occurs
1251 :
1252 : @param busIndex: Index of SRXL bus state information entry in the srxlBus array
1253 : */
1254 0 : void srxlOnFrameError(uint8_t busIndex)
1255 : {
1256 0 : SrxlBus* pBus = &srxlBus[busIndex];
1257 0 : if(busIndex >= SRXL_NUM_OF_BUSES || !pBus->initialized)
1258 0 : return;
1259 :
1260 0 : ++(pBus->frameErrCount);
1261 0 : if(pBus->master)
1262 : {
1263 : // TODO: If master (i.e. remote receiver 0x10), cause break condition to force reset?
1264 0 : return;
1265 : }
1266 :
1267 0 : if(pBus->state == SrxlState_ListenOnStartup || pBus->state == SrxlState_ListenForHandshake)
1268 : {
1269 : // Wait for multiple frame breaks before trying to change baud rate
1270 0 : if(pBus->frameErrCount < 3)
1271 0 : return;
1272 :
1273 : // Try the next higher baud rate
1274 0 : switch(pBus->baudRate)
1275 : {
1276 0 : case SRXL_BAUD_115200:
1277 : {
1278 0 : if(pBus->baudSupported & SRXL_BAUD_400000)
1279 : {
1280 0 : srxlChangeBaudRate(pBus->uart, 400000);
1281 0 : pBus->baudRate = SRXL_BAUD_400000;
1282 0 : break;
1283 : }
1284 : FALLTHROUGH;
1285 : // else fall thru...
1286 : }
1287 : case SRXL_BAUD_400000:
1288 : default:
1289 : {
1290 : // TODO: Cause break condition to force reset of everyone? Or just keep cycling?
1291 :
1292 0 : srxlChangeBaudRate(pBus->uart, 115200);
1293 0 : pBus->baudRate = SRXL_BAUD_115200;
1294 0 : break;
1295 : }
1296 : }
1297 0 : pBus->frameErrCount = 0;
1298 : }
1299 : else
1300 : {
1301 : // TODO: Handle frame error during normal comm -- probably caused by collision on bus?
1302 : }
1303 : }
1304 :
1305 0 : SrxlFullID srxlGetTelemetryEndpoint(void)
1306 : {
1307 0 : SrxlFullID retVal = {{0}};
1308 0 : if(srxlRx.pTelemRcvr)
1309 : {
1310 0 : retVal.deviceID = srxlRx.pTelemRcvr->deviceID;
1311 0 : retVal.busIndex = srxlRx.pTelemRcvr->busBits;
1312 : }
1313 0 : return retVal;
1314 : }
1315 :
1316 0 : bool srxlSetVtxData(SrxlVtxData* pVtxData)
1317 : {
1318 0 : if(!pVtxData)
1319 0 : return false;
1320 :
1321 : // Update VTX data, ignoring values marked as unchanged
1322 0 : if(pVtxData->band != 0xFF)
1323 0 : srxlVtxData.band = pVtxData->band;
1324 0 : if(pVtxData->channel != 0xFF)
1325 0 : srxlVtxData.channel = pVtxData->channel;
1326 0 : if(pVtxData->pit != 0xFF)
1327 0 : srxlVtxData.pit = pVtxData->pit;
1328 0 : if(pVtxData->power != 0xFF || pVtxData->powerDec != 0xFFFF)
1329 0 : srxlVtxData.power = pVtxData->power;
1330 0 : if(pVtxData->powerDec != 0xFFFF)
1331 0 : srxlVtxData.powerDec = pVtxData->powerDec;
1332 0 : if(pVtxData->region != 0xFF)
1333 0 : srxlVtxData.region = pVtxData->region;
1334 :
1335 : uint8_t b;
1336 0 : for(b = 0; b < SRXL_NUM_OF_BUSES; ++b)
1337 : {
1338 0 : srxlBus[b].txFlags.sendVtxData = 1;
1339 : }
1340 :
1341 0 : return true;
1342 : }
1343 :
1344 0 : void srxlSetHoldThreshold(uint8_t countdownReset)
1345 : {
1346 0 : srxlRx.lossHoldCount = (countdownReset > 1) ? countdownReset : 45;
1347 0 : }
1348 :
1349 0 : void srxlClearCommStats(void)
1350 : {
1351 0 : srxlRx.holds = 0;
1352 0 : srxlRx.frameLosses = 0;
1353 0 : srxlRx.lossCountdown = srxlRx.lossHoldCount + 1;
1354 0 : }
1355 :
1356 : // Return true on failsafe hold
1357 0 : bool srxlUpdateCommStats(bool isFade)
1358 : {
1359 0 : srxlRx.rxBusBits = 0;
1360 0 : if(srxlTelemetryPhase)
1361 : {
1362 0 : srxlRx.bestRssi_dBm = -128;
1363 0 : srxlRx.bestRssi_Pct = 0;
1364 : }
1365 :
1366 : uint8_t i;
1367 0 : for(i = 0; i < srxlRx.rcvrCount; ++i)
1368 : {
1369 0 : if(srxlRx.rcvr[i].channelMask)
1370 : {
1371 0 : srxlRx.lossCountdown = srxlRx.lossHoldCount + 1;
1372 :
1373 0 : if((srxlRx.rcvr[i].rssiRcvd & RSSI_RCVD_DBM) && srxlRx.bestRssi_dBm < srxlRx.rcvr[i].rssi_dBm)
1374 0 : srxlRx.bestRssi_dBm = srxlRx.rcvr[i].rssi_dBm;
1375 0 : if((srxlRx.rcvr[i].rssiRcvd & RSSI_RCVD_PCT) && srxlRx.bestRssi_dBm < srxlRx.rcvr[i].rssi_Pct)
1376 0 : srxlRx.bestRssi_Pct = srxlRx.rcvr[i].rssi_Pct;
1377 : }
1378 : }
1379 :
1380 : // Set RSSI based on telemetry phase and type of telemetry received
1381 0 : srxlChData.rssi = (srxlTelemetryPhase || srxlChDataIsFailsafe) ? srxlRx.bestRssi_Pct : srxlRx.bestRssi_dBm;
1382 :
1383 : // Update flight log frame losses and holds
1384 0 : if(isFade && srxlRx.lossCountdown)
1385 : {
1386 0 : if(--srxlRx.lossCountdown == 0)
1387 : {
1388 0 : ++srxlRx.holds;
1389 0 : srxlRx.frameLosses -= srxlRx.lossHoldCount;
1390 : }
1391 : else
1392 : {
1393 0 : ++srxlRx.frameLosses;
1394 : }
1395 : }
1396 :
1397 : static uint8_t telemFadeCount = 0;
1398 : // If we are allowed to send telemetry by the device downstream (i.e. any slave device)
1399 0 : if(srxlRx.pTelemRcvr)
1400 : {
1401 0 : if(srxlRx.pTelemRcvr->channelMask == 0 || (srxlRx.pTelemRcvr->info & SRXL_DEVINFO_TELEM_FULL_RANGE) == 0)
1402 : {
1403 : // If our telemetry receiver missed channel data 3 frames in a row, switch
1404 0 : if(++telemFadeCount > 3)
1405 : {
1406 0 : srxlRx.pTelemRcvr = srxlChooseTelemRcvr();
1407 : #ifdef SRXL_INCLUDE_MASTER_CODE
1408 : srxlSetTelemetryTxEnable(srxlRx.pTelemRcvr && (srxlRx.pTelemRcvr == srxlThisDev.pRcvr));
1409 : #endif
1410 0 : telemFadeCount = 0;
1411 : }
1412 : }
1413 : else
1414 : {
1415 0 : telemFadeCount = 0;
1416 : }
1417 : }
1418 : #ifdef SRXL_INCLUDE_MASTER_CODE
1419 : // Else check to make sure we're still supposed to suppress telemetry (reset countdown when slave tells us not to send again)
1420 : else if(++srxlTelemSuppressCount > SRXL_TELEM_SUPPRESS_MAX)
1421 : {
1422 : // Enable this device's telemetry tx since we stopped being told not to
1423 : srxlRx.pTelemRcvr = srxlThisDev.pRcvr;
1424 : srxlSetTelemetryTxEnable(srxlRx.pTelemRcvr);
1425 : }
1426 : #endif
1427 :
1428 : // Return true while we're in hold condition (failsafe)
1429 0 : return srxlRx.lossCountdown == 0;
1430 : }
|