/* * Authors (alphabetical order) * - Andre Bernet * - Andreas Weitl * - Bertrand Songis * - Bryan J. Rentoul (Gruvin) * - Cameron Weeks * - Erez Raviv * - Gabriel Birkus * - Jean-Pierre Parisy * - Karl Szmutny * - Michael Blandford * - Michal Hlavinka * - Pat Mackenzie * - Philip Moss * - Rob Thomson * - Romolo Manfredini * - Thomas Husterer * * opentx is based on code named * gruvin9x by Bryan J. Rentoul: http://code.google.com/p/gruvin9x/, * er9x by Erez Raviv: http://code.google.com/p/er9x/, * and the original (and ongoing) project by * Thomas Husterer, th9x: http://code.google.com/p/th9x/ * * 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" // Enumerate FrSky packet codes #define LINKPKT 0xfe #define USRPKT 0xfd #define A11PKT 0xfc #define A12PKT 0xfb #define A21PKT 0xfa #define A22PKT 0xf9 #define ALRM_REQUEST 0xf8 #define RSSI1PKT 0xf7 #define RSSI2PKT 0xf6 #define RSSI_REQUEST 0xf1 #if !defined(CPUARM) && (defined(FRSKY_HUB) || defined(WS_HOW_HIGH)) void checkMinMaxAltitude() { if (TELEMETRY_RELATIVE_BARO_ALT_BP > frskyData.hub.maxAltitude) frskyData.hub.maxAltitude = TELEMETRY_RELATIVE_BARO_ALT_BP; if (TELEMETRY_RELATIVE_BARO_ALT_BP < frskyData.hub.minAltitude) frskyData.hub.minAltitude = TELEMETRY_RELATIVE_BARO_ALT_BP; } #endif #if defined(FRSKY_HUB) typedef enum { TS_IDLE = 0, // waiting for 0x5e frame marker TS_DATA_ID, // waiting for dataID TS_DATA_LOW, // waiting for data low byte TS_DATA_HIGH, // waiting for data high byte TS_XOR = 0x80 // decode stuffed byte } TS_STATE; 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); } if (byte == 0x5d) { state = (TS_STATE)(state | TS_XOR); return; } if (state == TS_DATA_ID) { if (byte > 0x3f) { state = TS_IDLE; } else { #if defined(CPUARM) structPos = byte; #else structPos = byte * 2; #endif state = TS_DATA_LOW; } return; } if (state == TS_DATA_LOW) { lowByte = byte; state = TS_DATA_HIGH; return; } state = TS_IDLE; #if defined(CPUARM) processHubPacket(structPos, (byte << 8) + lowByte); #else #if defined(GPS) if ((uint8_t)structPos == offsetof(FrskySerialData, gpsLatitude_bp)) { if (lowByte || byte) frskyData.hub.gpsFix = 1; else if (frskyData.hub.gpsFix > 0 && frskyData.hub.gpsLatitude_bp > 1) frskyData.hub.gpsFix = 0; } else if ((uint8_t)structPos == offsetof(FrskySerialData, gpsLongitude_bp)) { if (lowByte || byte) frskyData.hub.gpsFix = 1; else if (frskyData.hub.gpsFix > 0 && frskyData.hub.gpsLongitude_bp > 1) frskyData.hub.gpsFix = 0; } if ((uint8_t)structPos == offsetof(FrskySerialData, gpsAltitude_bp) || ((uint8_t)structPos >= offsetof(FrskySerialData, gpsAltitude_ap) && (uint8_t)structPos <= offsetof(FrskySerialData, gpsLatitudeNS) && (uint8_t)structPos != offsetof(FrskySerialData, baroAltitude_bp) && (uint8_t)structPos != offsetof(FrskySerialData, baroAltitude_ap))) { // If we don't have a fix, we may discard the value if (frskyData.hub.gpsFix <= 0) return; } #endif #if 0 uint16_t previousValue = *((uint16_t *)(((uint8_t*)&frskyData.hub) + structPos)); #endif ((uint8_t*)&frskyData.hub)[structPos] = lowByte; ((uint8_t*)&frskyData.hub)[structPos+1] = byte; switch ((uint8_t)structPos) { case offsetof(FrskySerialData, rpm): frskyData.hub.rpm *= (uint8_t)60/(g_model.frsky.blades+2); if (frskyData.hub.rpm > frskyData.hub.maxRpm) frskyData.hub.maxRpm = frskyData.hub.rpm; break; case offsetof(FrskySerialData, temperature1): if (frskyData.hub.temperature1 > frskyData.hub.maxTemperature1) frskyData.hub.maxTemperature1 = frskyData.hub.temperature1; break; case offsetof(FrskySerialData, temperature2): if (frskyData.hub.temperature2 > frskyData.hub.maxTemperature2) frskyData.hub.maxTemperature2 = frskyData.hub.temperature2; break; case offsetof(FrskySerialData, current): #if defined(FAS_OFFSET) || !defined(CPUM64) if ((int16_t)frskyData.hub.current > 0 && ((int16_t)frskyData.hub.current + g_model.frsky.fasOffset) > 0) frskyData.hub.current += g_model.frsky.fasOffset; else frskyData.hub.current = 0; #endif if (frskyData.hub.current > frskyData.hub.maxCurrent) frskyData.hub.maxCurrent = frskyData.hub.current; break; case offsetof(FrskySerialData, currentConsumption): // we receive data from openXsensor. stops the calculation of consumption and power frskyData.hub.openXsensor = 1; break; case offsetof(FrskySerialData, volts_ap): #if defined(FAS_BSS) frskyData.hub.vfas = (frskyData.hub.volts_bp * 10 + frskyData.hub.volts_ap); #else frskyData.hub.vfas = ((frskyData.hub.volts_bp * 100 + frskyData.hub.volts_ap * 10) * 21) / 110; #endif /* TODO later if (!frskyData.hub.minVfas || frskyData.hub.minVfas > frskyData.hub.vfas) frskyData.hub.minVfas = frskyData.hub.vfas; */ break; case offsetof(FrskySerialData, baroAltitude_bp): // First received barometer altitude => Altitude offset if (!frskyData.hub.baroAltitudeOffset) frskyData.hub.baroAltitudeOffset = -frskyData.hub.baroAltitude_bp; frskyData.hub.baroAltitude_bp += frskyData.hub.baroAltitudeOffset; checkMinMaxAltitude(); break; #if defined(GPS) case offsetof(FrskySerialData, gpsAltitude_ap): if (!frskyData.hub.gpsAltitudeOffset) frskyData.hub.gpsAltitudeOffset = -frskyData.hub.gpsAltitude_bp; frskyData.hub.gpsAltitude_bp += frskyData.hub.gpsAltitudeOffset; if (!frskyData.hub.baroAltitudeOffset) { if (frskyData.hub.gpsAltitude_bp > frskyData.hub.maxAltitude) frskyData.hub.maxAltitude = frskyData.hub.gpsAltitude_bp; if (frskyData.hub.gpsAltitude_bp < frskyData.hub.minAltitude) frskyData.hub.minAltitude = frskyData.hub.gpsAltitude_bp; } if (!frskyData.hub.pilotLatitude && !frskyData.hub.pilotLongitude) { // First received GPS position => Pilot GPS position getGpsPilotPosition(); } else if (frskyData.hub.gpsDistNeeded || g_menuStack[g_menuStackPtr] == menuTelemetryFrsky) { getGpsDistance(); } break; case offsetof(FrskySerialData, gpsSpeed_bp): // Speed => Max speed if (frskyData.hub.gpsSpeed_bp > frskyData.hub.maxGpsSpeed) frskyData.hub.maxGpsSpeed = frskyData.hub.gpsSpeed_bp; break; #endif case offsetof(FrskySerialData, volts): frskyUpdateCells(); break; #if defined(GPS) case offsetof(FrskySerialData, hour): frskyData.hub.hour = ((uint8_t)(frskyData.hub.hour + g_eeGeneral.timezone + 24)) % 24; break; #endif case offsetof(FrskySerialData, accelX): case offsetof(FrskySerialData, accelY): case offsetof(FrskySerialData, accelZ): *(int16_t*)(&((uint8_t*)&frskyData.hub)[structPos]) /= 10; break; #if 0 case offsetof(FrskySerialData, gpsAltitude_bp): case offsetof(FrskySerialData, fuelLevel): case offsetof(FrskySerialData, gpsLongitude_bp): case offsetof(FrskySerialData, gpsLatitude_bp): case offsetof(FrskySerialData, gpsCourse_bp): case offsetof(FrskySerialData, day): case offsetof(FrskySerialData, year): case offsetof(FrskySerialData, sec): case offsetof(FrskySerialData, gpsSpeed_ap): case offsetof(FrskySerialData, gpsLongitude_ap): case offsetof(FrskySerialData, gpsLatitude_ap): case offsetof(FrskySerialData, gpsCourse_ap): case offsetof(FrskySerialData, gpsLongitudeEW): case offsetof(FrskySerialData, gpsLatitudeNS): case offsetof(FrskySerialData, varioSpeed): case offsetof(FrskySerialData, power): /* because sent by openXsensor */ case offsetof(FrskySerialData, vfas): case offsetof(FrskySerialData, volts_bp): break; default: *((uint16_t *)(((uint8_t*)&frskyData.hub) + structPos)) = previousValue; break; #endif } #endif } #endif #if defined(WS_HOW_HIGH) && !defined(CPUARM) void parseTelemWSHowHighByte(uint8_t byte) { if (frskyUsrStreaming < (WSHH_TIMEOUT10ms - 10)) { ((uint8_t*)&frskyData.hub)[offsetof(FrskySerialData, baroAltitude_bp)] = byte; checkMinMaxAltitude(); } else { // At least 100mS passed since last data received ((uint8_t*)&frskyData.hub)[offsetof(FrskySerialData, baroAltitude_bp)+1] = byte; } // baroAltitude_bp unit here is feet! frskyUsrStreaming = WSHH_TIMEOUT10ms; // reset counter } #endif #if defined(CPUARM) void frskyDProcessPacket(uint8_t *packet) { // What type of packet? switch (packet[0]) { case LINKPKT: // A1/A2/RSSI values { setTelemetryValue(TELEM_PROTO_FRSKY_D, D_A1_ID, 0, packet[1], UNIT_VOLTS, 0); setTelemetryValue(TELEM_PROTO_FRSKY_D, D_A2_ID, 0, packet[2], UNIT_VOLTS, 0); setTelemetryValue(TELEM_PROTO_FRSKY_D, D_RSSI_ID, 0, packet[3], UNIT_RAW, 0); frskyData.rssi.set(packet[3]); frskyStreaming = FRSKY_TIMEOUT10ms; // reset counter only if valid frsky packets are being detected break; } 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 g_model.frsky.channels[idx].alarms_value[i]) return true; } else { if (frskyData.analog[idx].value < g_model.frsky.channels[idx].alarms_value[i]) return true; } } } return false; } #endif #if defined(CPUARM) struct FrSkyDSensor { const uint8_t id; const char * name; const TelemetryUnit unit; const uint8_t prec; }; const FrSkyDSensor frskyDSensors[] = { { D_RSSI_ID, ZSTR_RSSI, UNIT_RAW, 0 }, { D_A1_ID, ZSTR_A1, UNIT_VOLTS, 0 }, { D_A2_ID, ZSTR_A2, UNIT_VOLTS, 0 }, { RPM_ID, ZSTR_RPM, UNIT_RAW, 0 }, { FUEL_ID, ZSTR_FUEL, UNIT_PERCENT, 0 }, { TEMP1_ID, ZSTR_TEMP, UNIT_CELSIUS, 0 }, { TEMP2_ID, ZSTR_TEMP, UNIT_CELSIUS, 0 }, { CURRENT_ID, ZSTR_CURR, UNIT_AMPS, 1 }, { ACCEL_X_ID, ZSTR_ACCX, UNIT_G, 3 }, { ACCEL_Y_ID, ZSTR_ACCY, UNIT_G, 3 }, { ACCEL_Z_ID, ZSTR_ACCZ, UNIT_G, 3 }, { VARIO_ID, ZSTR_VSPD, UNIT_METERS_PER_SECOND, 2 }, { VFAS_ID, ZSTR_VFAS, UNIT_VOLTS, 2 }, { BARO_ALT_AP_ID, ZSTR_ALT, UNIT_METERS, 2 }, { GPS_SPEED_BP_ID, ZSTR_GSPD, UNIT_KTS, 0 }, { GPS_COURS_BP_ID, ZSTR_HDG, UNIT_DEGREE, 0 }, { VOLTS_ID, ZSTR_CELLS, UNIT_CELLS, 2 }, { GPS_ALT_BP_ID, ZSTR_GPSALT, UNIT_METERS, 0 }, { GPS_HOUR_MIN_ID, ZSTR_GPSDATETIME, UNIT_DATETIME, 0 }, { GPS_LAT_AP_ID, ZSTR_GPS, UNIT_GPS, 0 }, { 0, NULL, UNIT_RAW, 0 } // sentinel }; const FrSkyDSensor * getFrSkyDSensor(uint8_t id) { const FrSkyDSensor * result = NULL; for (const FrSkyDSensor * sensor = frskyDSensors; sensor->id; sensor++) { if (id == sensor->id) { result = sensor; break; } } return result; } void processHubPacket(uint8_t id, int16_t value) { static uint8_t lastId = 0; static uint16_t lastValue = 0; TelemetryUnit unit = UNIT_RAW; uint8_t precision = 0; int32_t data = value; if (id > FRSKY_LAST_ID || id == GPS_SPEED_AP_ID || id == GPS_ALT_AP_ID || id == GPS_COURS_AP_ID) { return; } if (id == GPS_LAT_BP_ID || id == GPS_LONG_BP_ID || id == GPS_ALT_BP_ID || id == BARO_ALT_BP_ID || id == VOLTS_BP_ID) { lastId = id; lastValue = value; return; } if (id == GPS_LAT_AP_ID) { if (lastId == GPS_LAT_BP_ID) { data += lastValue << 16; unit = UNIT_GPS_LATITUDE; } else { return; } } else if (id == GPS_LONG_AP_ID) { if (lastId == GPS_LONG_BP_ID) { data += lastValue << 16; id = GPS_LAT_AP_ID; unit = UNIT_GPS_LONGITUDE; } else { return; } } else if (id == GPS_LAT_NS_ID) { id = GPS_LAT_AP_ID; unit = UNIT_GPS_LATITUDE_NS; } else if (id == GPS_LONG_EW_ID) { id = GPS_LAT_AP_ID; unit = UNIT_GPS_LONGITUDE_EW; } else if (id == BARO_ALT_AP_ID) { if (lastId == BARO_ALT_BP_ID) { data += lastValue * 100; unit = UNIT_METERS; precision = 2; } else { return; } } else if (id == VOLTS_ID) { unit = UNIT_CELLS; uint32_t cellData = (uint32_t)data; data = ((cellData & 0x00F0) << 12) + (((((cellData & 0xFF00) >> 8) + ((cellData & 0x000F) << 8))) / 5); } else if (id == GPS_DAY_MONTH_ID) { id = GPS_HOUR_MIN_ID; unit = UNIT_DATETIME_DAY_MONTH; } else if (id == GPS_HOUR_MIN_ID) { unit = UNIT_DATETIME_HOUR_MIN; } else if (id == GPS_SEC_ID) { id = GPS_HOUR_MIN_ID; unit = UNIT_DATETIME_SEC; } else if (id == GPS_YEAR_ID) { id = GPS_HOUR_MIN_ID; unit = UNIT_DATETIME_YEAR; } else { const FrSkyDSensor * sensor = getFrSkyDSensor(id); if (sensor) { unit = sensor->unit; precision = sensor->prec; } } setTelemetryValue(TELEM_PROTO_FRSKY_D, id, 0, data, unit, precision); } void frskyDSetDefault(int index, uint16_t id) { TelemetrySensor & telemetrySensor = g_model.telemetrySensors[index]; telemetrySensor.id = id; telemetrySensor.instance = 0; const FrSkyDSensor * sensor = getFrSkyDSensor(id); if (sensor) { TelemetryUnit unit = sensor->unit; uint8_t prec = min(2, sensor->prec); telemetrySensor.init(sensor->name, unit, prec); if (id >= D_A1_ID && id <= D_A2_ID) { telemetrySensor.prec = 1; telemetrySensor.custom.ratio = 132; telemetrySensor.filter = 1; } else if (id == D_RSSI_ID) { telemetrySensor.filter = 1; } } else { telemetrySensor.init(id); } eeDirty(EE_MODEL); } #endif