5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
23 uint8_t telemetryStreaming
= 0;
24 uint8_t telemetryRxBuffer
[TELEMETRY_RX_PACKET_SIZE
]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1)
25 uint8_t telemetryRxBufferCount
= 0;
27 #if defined(WS_HOW_HIGH)
28 uint8_t wshhStreaming
= 0;
31 uint8_t link_counter
= 0;
34 uint8_t telemetryState
= TELEMETRY_INIT
;
37 TelemetryData telemetryData
;
40 uint8_t telemetryProtocol
= 255;
43 #if defined(PCBSKY9X) && defined(REVX)
44 uint8_t serialInversion
= 0;
48 uint16_t getChannelRatio(source_t channel
)
50 return (uint16_t)g_model
.frsky
.channels
[channel
].ratio
<< g_model
.frsky
.channels
[channel
].multiplier
;
53 lcdint_t
applyChannelRatio(source_t channel
, lcdint_t val
)
55 return ((int32_t)val
+g_model
.frsky
.channels
[channel
].offset
) * getChannelRatio(channel
) * 2 / 51;
59 void processTelemetryData(uint8_t data
)
61 #if defined(CROSSFIRE)
62 if (telemetryProtocol
== PROTOCOL_PULSES_CROSSFIRE
) {
63 processCrossfireTelemetryData(data
);
67 #if defined(MULTIMODULE)
68 if (telemetryProtocol
== PROTOCOL_SPEKTRUM
) {
69 processSpektrumTelemetryData(data
);
72 if (telemetryProtocol
== PROTOCOL_FLYSKY_IBUS
) {
73 processFlySkyTelemetryData(data
);
76 if (telemetryProtocol
== PROTOCOL_MULTIMODULE
) {
77 processMultiTelemetryData(data
);
81 processFrskyTelemetryData(data
);
84 void telemetryWakeup()
87 uint8_t requiredTelemetryProtocol
= modelTelemetryProtocol();
89 uint8_t requiredSerialInversion
= g_model
.moduleData
[EXTERNAL_MODULE
].invertedSerial
;
90 if (telemetryProtocol
!= requiredTelemetryProtocol
|| serialInversion
!= requiredSerialInversion
) {
91 serialInversion
= requiredSerialInversion
;
93 if (telemetryProtocol
!= requiredTelemetryProtocol
) {
95 telemetryInit(requiredTelemetryProtocol
);
101 if (telemetryGetByte(&data
)) {
102 LOG_TELEMETRY_WRITE_START();
104 processTelemetryData(data
);
105 LOG_TELEMETRY_WRITE_BYTE(data
);
106 } while (telemetryGetByte(&data
));
108 #elif defined(PCBSKY9X)
109 if (telemetryProtocol
== PROTOCOL_FRSKY_D_SECONDARY
) {
111 while (telemetrySecondPortReceive(data
)) {
112 processTelemetryData(data
);
116 // Receive serial data here
117 rxPdcUsart(processTelemetryData
);
122 if (IS_FRSKY_D_PROTOCOL()) {
123 // Attempt to transmit any waiting Fr-Sky alarm set packets every 50ms (subject to packet buffer availability)
124 static uint8_t frskyTxDelay
= 5;
125 if (frskyAlarmsSendState
&& (--frskyTxDelay
== 0)) {
126 frskyTxDelay
= 5; // 50ms
128 frskyDSendNextAlarm();
135 for (int i
=0; i
<MAX_TELEMETRY_SENSORS
; i
++) {
136 const TelemetrySensor
& sensor
= g_model
.telemetrySensors
[i
];
137 if (sensor
.type
== TELEM_TYPE_CALCULATED
) {
138 telemetryItems
[i
].eval(sensor
);
144 if (TELEMETRY_STREAMING() && !IS_FAI_ENABLED()) {
149 #define FRSKY_BAD_ANTENNA() (IS_SWR_VALUE_VALID() && telemetryData.swr.value > 0x33)
152 static tmr10ms_t alarmsCheckTime
= 0;
153 #define SCHEDULE_NEXT_ALARMS_CHECK(seconds) alarmsCheckTime = get_tmr10ms() + (100*(seconds))
154 if (int32_t(get_tmr10ms() - alarmsCheckTime
) > 0) {
156 SCHEDULE_NEXT_ALARMS_CHECK(1/*second*/);
158 bool sensor_lost
= false;
159 for (int i
=0; i
<MAX_TELEMETRY_SENSORS
; i
++) {
160 if (isTelemetryFieldAvailable(i
)) {
161 TelemetryItem
& item
= telemetryItems
[i
];
162 if (item
.hasReceiveTime() && item
.getDelaySinceLastValue() > TELEMETRY_VALUE_OLD_THRESHOLD
) {
163 TelemetrySensor
* sensor
= & g_model
.telemetrySensors
[i
];
164 if (sensor
->unit
!= UNIT_DATETIME
) {
171 if (sensor_lost
&& TELEMETRY_STREAMING() && !g_model
.rssiAlarms
.disabled
) {
172 audioEvent(AU_SENSOR_LOST
);
175 #if defined(PCBTARANIS) || defined(PCBHORUS)
176 if ((IS_MODULE_PXX(INTERNAL_MODULE
) || IS_MODULE_PXX(EXTERNAL_MODULE
)) && FRSKY_BAD_ANTENNA()) {
178 POPUP_WARNING(STR_WARNING
);
179 const char * w
= STR_ANTENNAPROBLEM
;
180 SET_WARNING_INFO(w
, strlen(w
), 0);
181 SCHEDULE_NEXT_ALARMS_CHECK(10/*seconds*/);
185 if (!g_model
.rssiAlarms
.disabled
) {
186 if (TELEMETRY_STREAMING()) {
187 if (TELEMETRY_RSSI() < g_model
.rssiAlarms
.getCriticalRssi() ) {
189 SCHEDULE_NEXT_ALARMS_CHECK(10/*seconds*/);
191 else if (TELEMETRY_RSSI() < g_model
.rssiAlarms
.getWarningRssi() ) {
193 SCHEDULE_NEXT_ALARMS_CHECK(10/*seconds*/);
197 if (TELEMETRY_STREAMING()) {
198 if (telemetryState
== TELEMETRY_KO
) {
199 AUDIO_TELEMETRY_BACK();
201 telemetryState
= TELEMETRY_OK
;
203 else if (telemetryState
== TELEMETRY_OK
) {
204 telemetryState
= TELEMETRY_KO
;
205 AUDIO_TELEMETRY_LOST();
212 void telemetryInterrupt10ms()
214 #if defined(FRSKY_HUB) && !defined(CPUARM)
215 uint16_t voltage
= 0; /* unit: 1/10 volts */
216 for (uint8_t i
=0; i
<telemetryData
.hub
.cellsCount
; i
++)
217 voltage
+= telemetryData
.hub
.cellVolts
[i
];
218 voltage
/= (10 / TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
);
219 telemetryData
.hub
.cellsSum
= voltage
;
220 if (telemetryData
.hub
.cellsSum
< telemetryData
.hub
.minCells
) {
221 telemetryData
.hub
.minCells
= telemetryData
.hub
.cellsSum
;
225 if (TELEMETRY_STREAMING()) {
226 if (!TELEMETRY_OPENXSENSOR()) {
228 for (int i
=0; i
<MAX_TELEMETRY_SENSORS
; i
++) {
229 const TelemetrySensor
& sensor
= g_model
.telemetrySensors
[i
];
230 if (sensor
.type
== TELEM_TYPE_CALCULATED
) {
231 telemetryItems
[i
].per10ms(sensor
);
236 uint8_t channel
= g_model
.frsky
.voltsSource
;
237 if (channel
<= FRSKY_VOLTS_SOURCE_A2
) {
238 voltage
= applyChannelRatio(channel
, telemetryData
.analog
[channel
].value
) / 10;
241 #if defined(FRSKY_HUB)
242 else if (channel
== FRSKY_VOLTS_SOURCE_FAS
) {
243 voltage
= telemetryData
.hub
.vfas
;
247 #if defined(FRSKY_HUB)
248 uint16_t current
= telemetryData
.hub
.current
; /* unit: 1/10 amps */
250 uint16_t current
= 0;
253 channel
= g_model
.frsky
.currentSource
- FRSKY_CURRENT_SOURCE_A1
;
254 if (channel
< MAX_FRSKY_A_CHANNELS
) {
255 current
= applyChannelRatio(channel
, telemetryData
.analog
[channel
].value
) / 10;
258 telemetryData
.hub
.power
= ((current
>>1) * (voltage
>>1)) / 25;
260 telemetryData
.hub
.currentPrescale
+= current
;
261 if (telemetryData
.hub
.currentPrescale
>= 3600) {
262 telemetryData
.hub
.currentConsumption
+= 1;
263 telemetryData
.hub
.currentPrescale
-= 3600;
269 if (telemetryData
.hub
.power
> telemetryData
.hub
.maxPower
) {
270 telemetryData
.hub
.maxPower
= telemetryData
.hub
.power
;
275 #if defined(WS_HOW_HIGH)
276 if (wshhStreaming
> 0) {
281 if (telemetryStreaming
> 0) {
282 telemetryStreaming
--;
287 telemetryData
.rssi
.reset();
289 telemetryData
.rssi
[0].set(0);
290 telemetryData
.rssi
[1].set(0);
296 void telemetryReset()
298 memclear(&telemetryData
, sizeof(telemetryData
));
301 for (int index
=0; index
<MAX_TELEMETRY_SENSORS
; index
++) {
302 telemetryItems
[index
].clear();
306 telemetryStreaming
= 0; // reset counter only if valid frsky packets are being detected
310 telemetryState
= TELEMETRY_INIT
;
313 #if defined(FRSKY_HUB) && !defined(CPUARM)
314 telemetryData
.hub
.gpsLatitude_bp
= 2;
315 telemetryData
.hub
.gpsLongitude_bp
= 2;
316 telemetryData
.hub
.gpsFix
= -1;
319 #if defined(SIMU) && !defined(CPUARM)
320 telemetryData
.rssi
[0].value
= 75;
321 telemetryData
.rssi
[1].value
= 75;
322 telemetryData
.analog
[TELEM_ANA_A1
].set(120, UNIT_VOLTS
);
323 telemetryData
.analog
[TELEM_ANA_A2
].set(240, UNIT_VOLTS
);
325 telemetryData
.hub
.fuelLevel
= 75;
326 telemetryData
.hub
.rpm
= 12000;
327 telemetryData
.hub
.vfas
= 100;
328 telemetryData
.hub
.minVfas
= 90;
331 telemetryData
.hub
.gpsFix
= 1;
332 telemetryData
.hub
.gpsLatitude_bp
= 4401;
333 telemetryData
.hub
.gpsLatitude_ap
= 7710;
334 telemetryData
.hub
.gpsLongitude_bp
= 1006;
335 telemetryData
.hub
.gpsLongitude_ap
= 8872;
336 telemetryData
.hub
.gpsSpeed_bp
= 200; //in knots
337 telemetryData
.hub
.gpsSpeed_ap
= 0;
338 getGpsPilotPosition();
340 telemetryData
.hub
.gpsLatitude_bp
= 4401;
341 telemetryData
.hub
.gpsLatitude_ap
= 7455;
342 telemetryData
.hub
.gpsLongitude_bp
= 1006;
343 telemetryData
.hub
.gpsLongitude_ap
= 9533;
347 telemetryData
.hub
.airSpeed
= 1000; // 185.1 km/h
349 telemetryData
.hub
.cellsCount
= 6;
350 telemetryData
.hub
.cellVolts
[0] = 410/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
351 telemetryData
.hub
.cellVolts
[1] = 420/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
352 telemetryData
.hub
.cellVolts
[2] = 430/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
353 telemetryData
.hub
.cellVolts
[3] = 440/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
354 telemetryData
.hub
.cellVolts
[4] = 450/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
355 telemetryData
.hub
.cellVolts
[5] = 460/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
356 telemetryData
.hub
.minCellVolts
= 250/TELEMETRY_CELL_VOLTAGE_MUTLIPLIER
;
357 telemetryData
.hub
.minCell
= 300; //unit 10mV
358 telemetryData
.hub
.minCells
= 220; //unit 100mV
359 //telemetryData.hub.cellsSum = 261; //calculated from cellVolts[]
361 telemetryData
.hub
.gpsAltitude_bp
= 50;
362 telemetryData
.hub
.baroAltitude_bp
= 50;
363 telemetryData
.hub
.minAltitude
= 10;
364 telemetryData
.hub
.maxAltitude
= 500;
366 telemetryData
.hub
.accelY
= 100;
367 telemetryData
.hub
.temperature1
= -30;
368 telemetryData
.hub
.maxTemperature1
= 100;
370 telemetryData
.hub
.current
= 55;
371 telemetryData
.hub
.maxCurrent
= 65;
376 // we don't reset the telemetry here as we would also reset the consumption after model load
377 void telemetryInit(uint8_t protocol
)
379 telemetryProtocol
= protocol
;
381 if (protocol
== PROTOCOL_FRSKY_D
) {
382 telemetryPortInit(FRSKY_D_BAUDRATE
, TELEMETRY_SERIAL_DEFAULT
);
385 #if defined(MULTIMODULE)
386 else if (protocol
== PROTOCOL_MULTIMODULE
|| protocol
== PROTOCOL_FLYSKY_IBUS
) {
387 // The DIY Multi module always speaks 100000 baud regardless of the telemetry protocol in use
388 telemetryPortInit(MULTIMODULE_BAUDRATE
, TELEMETRY_SERIAL_8E2
);
390 outputTelemetryBufferSize
= 0;
391 outputTelemetryBufferTrigger
= 0x7E;
394 else if (protocol
== PROTOCOL_SPEKTRUM
) {
395 // Spektrum's own small race RX (SPM4648) uses 125000 8N1, use the same since there is no real standard
396 telemetryPortInit(125000, TELEMETRY_SERIAL_DEFAULT
);
400 #if defined(CROSSFIRE)
401 else if (protocol
== PROTOCOL_PULSES_CROSSFIRE
) {
402 telemetryPortInit(CROSSFIRE_BAUDRATE
, TELEMETRY_SERIAL_DEFAULT
);
404 outputTelemetryBufferSize
= 0;
405 outputTelemetryBufferTrigger
= 0;
407 telemetryPortSetDirectionOutput();
411 #if defined(SERIAL2) || defined(PCBSKY9X)
412 else if (protocol
== PROTOCOL_FRSKY_D_SECONDARY
) {
413 telemetryPortInit(0, TELEMETRY_SERIAL_DEFAULT
);
414 serial2TelemetryInit(PROTOCOL_FRSKY_D_SECONDARY
);
419 telemetryPortInit(FRSKY_SPORT_BAUDRATE
, TELEMETRY_SERIAL_WITHOUT_DMA
);
421 outputTelemetryBufferSize
= 0;
422 outputTelemetryBufferTrigger
= 0x7E;
426 #if defined(REVX) && !defined(SIMU)
427 if (serialInversion
) {
443 NOINLINE
uint8_t getRssiAlarmValue(uint8_t alarm
)
445 return (45 - 3*alarm
+ g_model
.frsky
.rssiAlarms
[alarm
].value
);
449 #if defined(LOG_TELEMETRY) && !defined(SIMU)
450 extern FIL g_telemetryFile
;
451 void logTelemetryWriteStart()
453 static tmr10ms_t lastTime
= 0;
454 tmr10ms_t newTime
= get_tmr10ms();
455 if (lastTime
!= newTime
) {
458 f_printf(&g_telemetryFile
, "\r\n%4d-%02d-%02d,%02d:%02d:%02d.%02d0:", utm
.tm_year
+TM_YEAR_BASE
, utm
.tm_mon
+1, utm
.tm_mday
, utm
.tm_hour
, utm
.tm_min
, utm
.tm_sec
, g_ms100
);
463 void logTelemetryWriteByte(uint8_t data
)
465 f_printf(&g_telemetryFile
, " %02X", data
);
469 uint8_t outputTelemetryBuffer
[TELEMETRY_OUTPUT_FIFO_SIZE
] __DMA
;
470 uint8_t outputTelemetryBufferSize
= 0;
471 uint8_t outputTelemetryBufferTrigger
= 0;
474 Fifo
<uint8_t, LUA_TELEMETRY_INPUT_FIFO_SIZE
> * luaInputTelemetryFifo
= NULL
;