Files
edgetx-gx12/radio/src/telemetry/frsky_d.cpp
T
Arne SchwabeandBertrand Songis 06b0694f03 Schwabe/multi telemetry (#4151)
* Add FY805 protocol

* Fix stray space

* Fix multi sending 120% instead of 100%.

* Implement a custom telemetry for the multi module

The custom telemetry allows the module to tell the telemetry protocol to OpenTX and also allows signaling the status of the Module (e.g. invalid protocol) to OpenTX

This should also close #3979.

* Cosmetics and a few small bugfixes

* remove empty if

* Move status line directly below the mode to be always visible when selecting a protocol

* warn if internal RF module is on

* Replace sprint with small helper function to build on AVR ARM (AR9X, 9XPRO, …)

* TR in wrong order, RF_PROTO_OFF inverted

* Refactor definition of Multimodule protocols to have one central place to define all multi protocol properties

* Also update protocols in companion

* Update multi version number to display as a.b.c.d
2016-12-24 00:04:11 +01:00

334 lines
11 KiB
C++

/*
* Copyright (C) OpenTX
*
* Based on code named
* th9x - http://code.google.com/p/th9x
* er9x - http://code.google.com/p/er9x
* gruvin9x - http://code.google.com/p/gruvin9x
*
* License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "opentx.h"
#if (defined(FRSKY_HUB) || defined(WS_HOW_HIGH))
void checkMinMaxAltitude()
{
if (TELEMETRY_RELATIVE_BARO_ALT_BP > telemetryData.hub.maxAltitude)
telemetryData.hub.maxAltitude = TELEMETRY_RELATIVE_BARO_ALT_BP;
if (TELEMETRY_RELATIVE_BARO_ALT_BP < telemetryData.hub.minAltitude)
telemetryData.hub.minAltitude = TELEMETRY_RELATIVE_BARO_ALT_BP;
}
#endif
#if defined(FRSKY_HUB)
void parseTelemHubByte(uint8_t byte)
{
static int8_t structPos;
static uint8_t lowByte;
static TS_STATE state = TS_IDLE;
if (byte == 0x5e) {
state = TS_DATA_ID;
return;
}
if (state == TS_IDLE) {
return;
}
if (state & TS_XOR) {
byte = byte ^ 0x60;
state = (TS_STATE)(state - TS_XOR);
}
else if (byte == 0x5d) {
state = (TS_STATE)(state | TS_XOR);
return;
}
if (state == TS_DATA_ID) {
if (byte > 0x3f) {
state = TS_IDLE;
}
else {
structPos = byte * 2;
state = TS_DATA_LOW;
}
return;
}
if (state == TS_DATA_LOW) {
lowByte = byte;
state = TS_DATA_HIGH;
return;
}
state = TS_IDLE;
#if defined(GPS)
if ((uint8_t)structPos == offsetof(FrskyTelemetryData, gpsLatitude_bp)) {
if (lowByte || byte)
telemetryData.hub.gpsFix = 1;
else if (telemetryData.hub.gpsFix > 0 && telemetryData.hub.gpsLatitude_bp > 1)
telemetryData.hub.gpsFix = 0;
}
else if ((uint8_t)structPos == offsetof(FrskyTelemetryData, gpsLongitude_bp)) {
if (lowByte || byte)
telemetryData.hub.gpsFix = 1;
else if (telemetryData.hub.gpsFix > 0 && telemetryData.hub.gpsLongitude_bp > 1)
telemetryData.hub.gpsFix = 0;
}
if ((uint8_t)structPos == offsetof(FrskyTelemetryData, gpsAltitude_bp) ||
((uint8_t)structPos >= offsetof(FrskyTelemetryData, gpsAltitude_ap) && (uint8_t)structPos <= offsetof(FrskyTelemetryData, gpsLatitudeNS) && (uint8_t)structPos != offsetof(FrskyTelemetryData, baroAltitude_bp) && (uint8_t)structPos != offsetof(FrskyTelemetryData, baroAltitude_ap))) {
// If we don't have a fix, we may discard the value
if (telemetryData.hub.gpsFix <= 0)
return;
}
#endif // #if defined(GPS)
((uint8_t*)&telemetryData.hub)[structPos] = lowByte;
((uint8_t*)&telemetryData.hub)[structPos+1] = byte;
switch ((uint8_t)structPos) {
case offsetof(FrskyTelemetryData, rpm):
telemetryData.hub.rpm *= (uint8_t)60/(g_model.frsky.blades+2);
if (telemetryData.hub.rpm > telemetryData.hub.maxRpm)
telemetryData.hub.maxRpm = telemetryData.hub.rpm;
break;
case offsetof(FrskyTelemetryData, temperature1):
if (telemetryData.hub.temperature1 > telemetryData.hub.maxTemperature1)
telemetryData.hub.maxTemperature1 = telemetryData.hub.temperature1;
break;
case offsetof(FrskyTelemetryData, temperature2):
if (telemetryData.hub.temperature2 > telemetryData.hub.maxTemperature2)
telemetryData.hub.maxTemperature2 = telemetryData.hub.temperature2;
break;
case offsetof(FrskyTelemetryData, current):
#if defined(FAS_OFFSET) || !defined(CPUM64)
if ((int16_t)telemetryData.hub.current > 0 && ((int16_t)telemetryData.hub.current + g_model.frsky.fasOffset) > 0)
telemetryData.hub.current += g_model.frsky.fasOffset;
else
telemetryData.hub.current = 0;
#endif
if (telemetryData.hub.current > telemetryData.hub.maxCurrent)
telemetryData.hub.maxCurrent = telemetryData.hub.current;
break;
case offsetof(FrskyTelemetryData, currentConsumption):
// we receive data from openXsensor. stops the calculation of consumption and power
telemetryData.hub.openXsensor = 1;
break;
case offsetof(FrskyTelemetryData, volts_ap):
#if defined(FAS_PROTOTYPE)
telemetryData.hub.vfas = (telemetryData.hub.volts_bp * 10 + telemetryData.hub.volts_ap);
#else
telemetryData.hub.vfas = ((telemetryData.hub.volts_bp * 10 + telemetryData.hub.volts_ap) * 21) / 11;
#endif
/* TODO later if (!telemetryData.hub.minVfas || telemetryData.hub.minVfas > telemetryData.hub.vfas)
telemetryData.hub.minVfas = telemetryData.hub.vfas; */
break;
case offsetof(FrskyTelemetryData, baroAltitude_bp):
// First received barometer altitude => Altitude offset
if (!telemetryData.hub.baroAltitudeOffset)
telemetryData.hub.baroAltitudeOffset = -telemetryData.hub.baroAltitude_bp;
telemetryData.hub.baroAltitude_bp += telemetryData.hub.baroAltitudeOffset;
checkMinMaxAltitude();
break;
#if defined(GPS)
case offsetof(FrskyTelemetryData, gpsAltitude_ap):
if (!telemetryData.hub.gpsAltitudeOffset)
telemetryData.hub.gpsAltitudeOffset = -telemetryData.hub.gpsAltitude_bp;
telemetryData.hub.gpsAltitude_bp += telemetryData.hub.gpsAltitudeOffset;
if (!telemetryData.hub.baroAltitudeOffset) {
if (telemetryData.hub.gpsAltitude_bp > telemetryData.hub.maxAltitude)
telemetryData.hub.maxAltitude = telemetryData.hub.gpsAltitude_bp;
if (telemetryData.hub.gpsAltitude_bp < telemetryData.hub.minAltitude)
telemetryData.hub.minAltitude = telemetryData.hub.gpsAltitude_bp;
}
if (!telemetryData.hub.pilotLatitude && !telemetryData.hub.pilotLongitude) {
// First received GPS position => Pilot GPS position
getGpsPilotPosition();
}
else if (telemetryData.hub.gpsDistNeeded || menuHandlers[menuLevel] == menuViewTelemetryFrsky) {
getGpsDistance();
}
break;
case offsetof(FrskyTelemetryData, gpsSpeed_bp):
// Speed => Max speed
if (telemetryData.hub.gpsSpeed_bp > telemetryData.hub.maxGpsSpeed)
telemetryData.hub.maxGpsSpeed = telemetryData.hub.gpsSpeed_bp;
break;
#endif
case offsetof(FrskyTelemetryData, volts):
frskyUpdateCells();
break;
case offsetof(FrskyTelemetryData, accelX):
case offsetof(FrskyTelemetryData, accelY):
case offsetof(FrskyTelemetryData, accelZ):
*(int16_t*)(&((uint8_t*)&telemetryData.hub)[structPos]) /= 10;
break;
#if 0
case offsetof(FrskyTelemetryData, gpsAltitude_bp):
case offsetof(FrskyTelemetryData, fuelLevel):
case offsetof(FrskyTelemetryData, gpsLongitude_bp):
case offsetof(FrskyTelemetryData, gpsLatitude_bp):
case offsetof(FrskyTelemetryData, gpsCourse_bp):
case offsetof(FrskyTelemetryData, day):
case offsetof(FrskyTelemetryData, year):
case offsetof(FrskyTelemetryData, sec):
case offsetof(FrskyTelemetryData, gpsSpeed_ap):
case offsetof(FrskyTelemetryData, gpsLongitude_ap):
case offsetof(FrskyTelemetryData, gpsLatitude_ap):
case offsetof(FrskyTelemetryData, gpsCourse_ap):
case offsetof(FrskyTelemetryData, gpsLongitudeEW):
case offsetof(FrskyTelemetryData, gpsLatitudeNS):
case offsetof(FrskyTelemetryData, varioSpeed):
case offsetof(FrskyTelemetryData, power): /* because sent by openXsensor */
case offsetof(FrskyTelemetryData, vfas):
case offsetof(FrskyTelemetryData, volts_bp):
break;
default:
*((uint16_t *)(((uint8_t*)&telemetryData.hub) + structPos)) = previousValue;
break;
#endif
}
}
#endif // #if defined(FRSKY_HUB)
#if defined(WS_HOW_HIGH)
void parseTelemWSHowHighByte(uint8_t byte)
{
if (wshhStreaming < (WSHH_TIMEOUT10ms - 10)) {
((uint8_t*)&telemetryData.hub)[offsetof(FrskyTelemetryData, baroAltitude_bp)] = byte;
checkMinMaxAltitude();
}
else {
// At least 100mS passed since last data received
((uint8_t*)&telemetryData.hub)[offsetof(FrskyTelemetryData, baroAltitude_bp)+1] = byte;
}
// baroAltitude_bp unit here is feet!
wshhStreaming = WSHH_TIMEOUT10ms; // reset counter
}
#endif
void frskyDProcessPacket(const uint8_t *packet)
{
// What type of packet?
switch (packet[0])
{
case LINKPKT: // A1/A2/RSSI values
{
telemetryData.analog[TELEM_ANA_A1].set(packet[1], g_model.frsky.channels[TELEM_ANA_A1].type);
telemetryData.analog[TELEM_ANA_A2].set(packet[2], g_model.frsky.channels[TELEM_ANA_A2].type);
telemetryData.rssi[0].set(packet[3]);
telemetryData.rssi[1].set(packet[4] / 2);
telemetryStreaming = TELEMETRY_TIMEOUT10ms; // reset counter only if valid packets are being detected
link_counter += 256 / FRSKY_D_AVERAGING;
#if defined(VARIO)
uint8_t varioSource = g_model.frsky.varioSource - VARIO_SOURCE_A1;
if (varioSource < 2) {
telemetryData.hub.varioSpeed = applyChannelRatio(varioSource, telemetryData.analog[varioSource].value);
}
#endif
break;
}
#if defined(FRSKY_HUB) || defined (WS_HOW_HIGH)
case USRPKT: // User Data packet
uint8_t numBytes = 3 + (packet[1] & 0x07); // sanitize in case of data corruption leading to buffer overflow
for (uint8_t i=3; i<numBytes; i++) {
#if defined(FRSKY_HUB)
if (IS_USR_PROTO_FRSKY_HUB()) {
parseTelemHubByte(packet[i]);
}
#endif
#if defined(WS_HOW_HIGH)
if (IS_USR_PROTO_WS_HOW_HIGH()) {
parseTelemWSHowHighByte(packet[i]);
}
#endif
}
break;
#endif // #if defined(FRSKY_HUB) || defined (WS_HOW_HIGH)
}
}
// Alarms level sent to the FrSky module
uint8_t frskyAlarmsSendState = 0 ;
void frskyPushValue(uint8_t *&ptr, uint8_t value)
{
// byte stuff the only byte than might need it
bool bytestuff = false;
if (value == START_STOP) {
bytestuff = true;
value = 0x5e;
}
else if (value == BYTESTUFF) {
bytestuff = true;
value = 0x5d;
}
*ptr++ = value;
if (bytestuff)
*ptr = BYTESTUFF;
}
void frskySendPacket(uint8_t type, uint8_t value, uint8_t p1, uint8_t p2)
{
uint8_t *ptr = &frskyTxBuffer[0];
*ptr++ = START_STOP; // End of packet
*ptr++ = 0x00;
*ptr++ = 0x00;
*ptr++ = 0x00;
*ptr++ = 0x00;
*ptr++ = 0x00;
*ptr++ = (IS_SOUND_OFF() ? alarm_off : p2);
*ptr++ = p1;
frskyPushValue(ptr, value);
*ptr++ = type;
*ptr++ = START_STOP; // Start of packet
frskyTxBufferCount = ptr - &frskyTxBuffer[0];
#if !defined(SIMU)
telemetryTransmitBuffer();
#endif
}
inline void frskyDSendNextAlarm(void)
{
if (frskyTxBufferCount)
return; // we only have one buffer. If it's in use, then we can't send yet.
// Now send a packet
frskyAlarmsSendState -= 1;
uint8_t alarm = 1 - (frskyAlarmsSendState % 2);
if (frskyAlarmsSendState < SEND_MODEL_ALARMS) {
uint8_t channel = 1 - (frskyAlarmsSendState / 2);
frskySendPacket(A22PKT + frskyAlarmsSendState, g_model.frsky.channels[channel].alarms_value[alarm], ALARM_GREATER(channel, alarm), ALARM_LEVEL(channel, alarm));
}
else {
frskySendPacket(RSSI1PKT-alarm, getRssiAlarmValue(alarm), 0, (2+alarm+g_model.frsky.rssiAlarms[alarm].level) % 4);
}
}