Add cell voltage warning in Hott protocol
[openXsensor.git] / openXsensor / oXs_out_hott.cpp
blobc9b0089ea0a8495dd850c1e172ce95cac9e368d4
1 // File for Hott
2 #include "oXs_out_hott.h"
3 #if defined(PROTOCOL) && (PROTOCOL == HOTT)
5 #ifdef DEBUG_BLINK
6 #define DEBUG_BLINK_UPLOAD_HOTT_DATA
7 #endif
9 #ifdef DEBUG
10 // ************************* here Several parameters to help debugging
11 //#define DEBUGHOTT
12 #endif
14 extern OXS_MS5611 oXs_MS5611 ;
15 extern OXS_VOLTAGE oXs_Voltage ;
16 extern OXS_CURRENT oXs_Current ;
17 extern OXS_4525 oXs_4525 ;
20 extern unsigned long micros( void ) ;
21 extern unsigned long millis( void ) ;
22 extern void delay(unsigned long ms) ;
24 //volatile uint8_t sendStatus ;
25 volatile uint8_t flagUpdateHottBuffer ; // When a polling for oXs is received, it says that main loop must update the buffer (within the 4mes)
26 volatile uint8_t debugStatus ;
28 // Transmit buffer
29 volatile static union { // union is a easy way to access the data in several ways (name of field + index of byte)
30 volatile HOTT_GAM_MSG gamMsg ; // structured general air module
31 volatile HOTT_GPS_MSG gpsMsg ;
32 volatile uint8_t txBuffer[TXHOTTDATA_BUFFERSIZE] ;
33 } TxHottData;
37 volatile uint8_t state ; //!< Holds the state of the UART.
38 static volatile unsigned char SwUartTXData ; //!< Data to be transmitted.
39 static volatile unsigned char SwUartTXBitCount ; //!< TX bit counter.
40 static volatile uint8_t SwUartRXData ; //!< Storage for received bits.
41 static volatile uint8_t SwUartRXBitCount ; //!< RX bit counter.
42 static volatile uint8_t TxCount ;
43 static volatile uint8_t LastRx ; // last byte received (allows to check on the second byte received for polling)
44 volatile uint8_t debugUartRx ;
45 static uint8_t delayTxPendingCount ; // Used to register the number of extra delay(each 1msec) to wait in TxPending (so before replying)
46 volatile uint8_t ppmInterrupted ; // This flag is activated at the end of handling interrupt on Timer 1 Compare A if during this interrupt handling an interrupt on pin change (INT0 or INT1) occurs
47 // in this case, ppm will be wrong and has to be discarded
49 static uint8_t convertGpsFix[5] = {0x2d , 0x2d , 0x32 , 0x33 , 0x44 } ;
51 #ifdef DEBUG
52 OXS_OUT::OXS_OUT(uint8_t pinTx,HardwareSerial &print)
53 #else
54 OXS_OUT::OXS_OUT(uint8_t pinTx)
55 #endif
57 _pinTx = pinTx ;
58 #ifdef DEBUG
59 printer = &print; //operate on the address of print
60 #endif
61 } // end constructor
64 // **************** Setup the OutputLib *********************
65 void OXS_OUT::setup() {
66 // flagUpdateHottBuffer = 0 ; // does not allow to fill directly the buffer with data to transmit; in fact, filling occurs only after a polling
67 // fill the buffer with 0
68 // for ( uint8_t i = 0 ; i <= TXHOTTDATA_BUFFERSIZE ; i++ ) { // first fill the buffer with 0
69 // TxHottData.txBuffer[i] = 0 ;
70 // }
71 // fill fixed values in the buffer
72 // TxHottData.gamMsg.start_byte = 0x7c ;
73 // TxHottData.gamMsg.gam_sensor_id = 0x8d ; //GENRAL AIR MODULE = HOTT_TELEMETRY_GAM_SENSOR_ID
74 // TxHottData.gamMsg.sensor_id = 0xd0 ;
75 // TxHottData.gamMsg.stop_byte = 0x7D ;
77 //initilalise PORT
78 TRXDDR &= ~( 1 << PIN_SERIALTX ) ; // PIN is input, tri-stated.
79 TRXPORT &= ~( 1 << PIN_SERIALTX ) ; // PIN is input, tri-stated.
81 // Activate pin change interupt on Tx pin
82 #if PIN_SERIALTX == 4
83 PCMSK2 |= 0x10 ; // IO4 (PD4) on Arduini mini
84 #elif PIN_SERIALTX == 2
85 PCMSK2 |= 0x04 ; // IO2 (PD2) on Arduini mini
86 #else
87 #error "This PIN is not supported"
88 #endif
89 PCIFR = (1<<PCIF2) ; // clear pending interrupt
90 initHottUart( ) ; // initialise UART
92 #ifdef DEBUGHOTT
93 printer->print(F("Hott Output Module: TX Pin="));
94 printer->println(_pinTx);
95 #endif
98 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS > 0) && defined(CELL_UNDERVOLTAGE_WARNING)
100 byte OXS_OUT::warning_beeps_Hott(void) { // Determine whether a warning has to be sent to the transmitter.
101 // Define the state variables for the state machine controlling the warning generation.
102 // W_IDLE is the IDLE state but that name cannot be used because IDLE is already defined in oXs_out_hott.h.
103 static enum {W_IDLE = 0, WARNING, DEADTIME} state = W_IDLE;
104 static unsigned long warning_start_time;
105 static uint8_t current_warning;
106 // In order not to flood the transmitter with warnings, we transmit our warnings only every 3 seconds.
107 // If the dead time is over, fall back into the idle state.
108 if (state == DEADTIME && millis() - warning_start_time > 3000) state = W_IDLE;
109 // State WARNING indicates that we just started to transmit a warning.
110 // Repeat it for 500ms to make sure the transmitter can receive it.
111 // After 500ms we stop transmitting the warning and disable new warnings for a while.
112 if (state == WARNING && millis() - warning_start_time > 500) {
113 state = DEADTIME;
114 current_warning = 0;
116 // In the idle state, we are ready to accept new warnings.
117 if (state == W_IDLE) {
118 if (voltageData->mVoltCellMin < CELL_UNDERVOLTAGE_WARNING) {
119 warning_start_time = millis();
120 state = WARNING;
121 current_warning = 17; /* Cell undervoltage warning */
124 return current_warning;
126 #endif
128 void OXS_OUT::sendData() {
129 #ifdef DEBUGHOTT
130 // printer->print(F("F= ")); printer->print(flagUpdateHottBuffer);
131 // printer->print(F(" S=")); printer->print(state);
132 // printer->print(F(" LR=")); printer->print(LastRx);
133 // printer->print(F(" Tc=")); printer->println(TxCount);
134 #endif
135 if ( flagUpdateHottBuffer ) { // this flag is set to true when UART get a polling of the device. Then measurement must be filled in the buffer
136 #ifdef DEBUG_BLINK_UPLOAD_HOTT_DATA
137 blinkLed(5) ; // blink every 500 ms at least
138 #endif
139 // fill the buffer with 0
140 for ( uint8_t i = 0 ; i < TXHOTTDATA_BUFFERSIZE ; i++ ) { // first fill the buffer with 0
141 TxHottData.txBuffer[i] = 0 ;
143 if ( flagUpdateHottBuffer == 0x8d ) { // this flag is set to true when UART get a polling of the GAM device. Then measurement must be filled in the buffer
144 TxHottData.gamMsg.start_byte = 0x7c ;
145 TxHottData.gamMsg.gam_sensor_id = 0x8d ; //GENRAL AIR MODULE = HOTT_TELEMETRY_GAM_SENSOR_ID
146 TxHottData.gamMsg.sensor_id = 0xd0 ;
147 TxHottData.gamMsg.stop_byte = 0x7D ;
149 // in general air module data to fill are:
150 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 1)
151 TxHottData.gamMsg.cell[0] = voltageData->mVoltCell[0] /20 ; // Volt Cell 1 (in 2 mV increments, 210 == 4.20 V)
152 #endif
153 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 2)
154 TxHottData.gamMsg.cell[1] = voltageData->mVoltCell[1] /20 ; // Volt Cell 2 (in 2 mV increments, 210 == 4.20 V)
155 #endif
156 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 3)
157 TxHottData.gamMsg.cell[2] = voltageData->mVoltCell[2] /20 ; // Volt Cell 3 (in 2 mV increments, 210 == 4.20 V)
158 #endif
159 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 4)
160 TxHottData.gamMsg.cell[3] = voltageData->mVoltCell[3] /20 ; // Volt Cell 4 (in 2 mV increments, 210 == 4.20 V)
161 #endif
162 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 5)
163 TxHottData.gamMsg.cell[4] = voltageData->mVoltCell[4] /20 ; // Volt Cell 5 (in 2 mV increments, 210 == 4.20 V)
164 #endif
165 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 6)
166 TxHottData.gamMsg.cell[5] = voltageData->mVoltCell[5] /20 ; // Volt Cell 6 (in 2 mV increments, 210 == 4.20 V)
167 #endif
168 #if defined(BATTERY_1_SOURCE) && ( (BATTERY_1_SOURCE == VOLT_1) || (BATTERY_1_SOURCE == VOLT_2) || (BATTERY_1_SOURCE == VOLT_3) || (BATTERY_1_SOURCE == VOLT_4) || (BATTERY_1_SOURCE == VOLT_5) || (BATTERY_1_SOURCE == VOLT_6) ) && defined(PIN_VOLTAGE)
169 TxHottData.gamMsg.Battery1 = voltageData->mVolt[BATTERY_1_SOURCE - VOLT_1].value / 100; //battery 1 voltage 0.1V steps. 55 = 5.5V only pos. voltages
170 #endif
171 #if defined(BATTERY_1_SOURCE) && ( (BATTERY_1_SOURCE == ADS_VOLT_1) || (BATTERY_1_SOURCE == ADS_VOLT_2) || (BATTERY_1_SOURCE == ADS_VOLT_3) || (BATTERY_1_SOURCE == ADS_VOLT_4) ) && defined(ADS_MEASURE)
172 TxHottData.gamMsg.Battery1 = ads_Conv[BATTERY_1_SOURCE - ADS_VOLT_1].value / 100; //battery 1 voltage 0.1V steps. 55 = 5.5V only pos. voltages
173 #endif
174 #if defined(BATTERY_2_SOURCE) && ( (BATTERY_2_SOURCE == VOLT_1) || (BATTERY_2_SOURCE == VOLT_2) || (BATTERY_2_SOURCE == VOLT_3) || (BATTERY_2_SOURCE == VOLT_4) || (BATTERY_2_SOURCE == VOLT_5) || (BATTERY_2_SOURCE == VOLT_6) ) && defined(PIN_VOLTAGE)
175 TxHottData.gamMsg.Battery2 = voltageData->mVolt[BATTERY_2_SOURCE - VOLT_1].value / 100; //battery 1 voltage 0.1V steps. 55 = 5.5V only pos. voltages
176 #endif
177 #if defined(BATTERY_2_SOURCE) && ( (BATTERY_2_SOURCE == ADS_VOLT_1) || (BATTERY_2_SOURCE == ADS_VOLT_2) || (BATTERY_2_SOURCE == ADS_VOLT_3) || (BATTERY_2_SOURCE == ADS_VOLT_4) ) && defined(ADS_MEASURE)
178 TxHottData.gamMsg.Battery1 = ads_Conv[BATTERY_2_SOURCE - ADS_VOLT_1].value / 100; //battery 1 voltage 0.1V steps. 55 = 5.5V only pos. voltages
179 #endif
181 #if defined(TEMPERATURE_1_SOURCE) && (TEMPERATURE_1_SOURCE == TEST_1 )
182 TxHottData.gamMsg.temperature1 = test1.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
183 #elif defined(TEMPERATURE_1_SOURCE) && (TEMPERATURE_1_SOURCE == TEST_2 )
184 TxHottData.gamMsg.temperature1 = test2.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
185 #elif defined(TEMPERATURE_1_SOURCE) && (TEMPERATURE_1_SOURCE == TEST_3 )
186 TxHottData.gamMsg.temperature1 = test3.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
187 #elif defined(TEMPERATURE_1_SOURCE) && (TEMPERATURE_1_SOURCE == GLIDER_RATIO ) && defined(GLIDER_RATIO_CALCULATED_AFTER_X_SEC)
188 TxHottData.gamMsg.temperature1 = gliderRatio.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
189 #elif defined(TEMPERATURE_1_SOURCE) && (TEMPERATURE_1_SOURCE == SENSITIVITY ) && defined(VARIO)
190 TxHottData.gamMsg.temperature1 = oXs_MS5611.varioData.sensitivity.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
191 #elif defined(TEMPERATURE_1_SOURCE) && (TEMPERATURE_1_SOURCE == PPM ) && defined(PIN_PPM)
192 TxHottData.gamMsg.temperature1 = ppm.value + 120 ; // Hott applies an offset of 20. A value of 20 = 0°C
193 #else
194 TxHottData.gamMsg.temperature1 = 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
195 #endif
197 #if defined(TEMPERATURE_2_SOURCE) && (TEMPERATURE_2_SOURCE == TEST_1 )
198 TxHottData.gamMsg.temperature2 = test1.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
199 #elif defined(TEMPERATURE_2_SOURCE) && (TEMPERATURE_2_SOURCE == TEST_2 )
200 TxHottData.gamMsg.temperature2 = test2.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
201 #elif defined(TEMPERATURE_2_SOURCE) && (TEMPERATURE_2_SOURCE == TEST_3 )
202 TxHottData.gamMsg.temperature2 = test3.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
203 #elif defined(TEMPERATURE_2_SOURCE) && (TEMPERATURE_2_SOURCE == GLIDER_RATIO ) && defined(GLIDER_RATIO_CALCULATED_AFTER_X_SEC)
204 TxHottData.gamMsg.temperature2 = gliderRatio.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
205 #elif defined(TEMPERATURE_2_SOURCE) && (TEMPERATURE_2_SOURCE == SENSITIVITY ) && defined(VARIO)
206 TxHottData.gamMsg.temperature2 = oXs_MS5611.varioData.sensitivity.value + 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
207 #elif defined(TEMPERATURE_2_SOURCE) && (TEMPERATURE_2_SOURCE == PPM ) && defined(PIN_PPM)
208 TxHottData.gamMsg.temperature2 = ppm.value + 120 ; // Hott applies an offset of 20. A value of 20 = 0°C
209 #else
210 TxHottData.gamMsg.temperature2 = 20 ; // Hott applies an offset of 20. A value of 20 = 0°C
211 #endif
213 TxHottData.gamMsg.rpm++ ;
214 if ( TxHottData.gamMsg.rpm > 1000) TxHottData.gamMsg.rpm = 1 ;
215 #ifdef MEASURE_RPM
216 TxHottData.gamMsg.rpm = RpmValue /10 ; //#22 RPM in 10 RPM steps. 300 = 3000rpm
217 #endif
218 #ifdef VARIO
219 TxHottData.gamMsg.altitude = ((varioData->relativeAlt.value ) / 100 ) + 500 ; //altitude in meters. offset of 500, 500 = 0m
220 TxHottData.gamMsg.climbrate_L = mainVspeed.value + 30000 ; //climb rate in 0.01m/s. Value of 30000 = 0.00 m/s
221 #else
222 TxHottData.gamMsg.altitude = 500 ; //altitude in meters. offset of 500, 500 = 0m
223 TxHottData.gamMsg.climbrate_L = 30000 ; //climb rate in 0.01m/s. Value of 30000 = 0.00 m/s
224 #endif
225 TxHottData.gamMsg.climbrate3s = 120 ; //#28 climb rate in m/3sec. Value of 120 = 0m/3sec
226 #if defined(PIN_CURRENTSENSOR)
227 TxHottData.gamMsg.current = currentData->milliAmps.value /100; //current in 0.1A steps 100 == 10,0A
228 #endif
229 #if defined(MAIN_BATTERY_SOURCE) && ( (MAIN_BATTERY_SOURCE == VOLT_1) || (MAIN_BATTERY_SOURCE == VOLT_2) || (MAIN_BATTERY_SOURCE == VOLT_3) || (MAIN_BATTERY_SOURCE == VOLT_4) || (MAIN_BATTERY_SOURCE == VOLT_5) || (MAIN_BATTERY_SOURCE == VOLT_6) ) && defined(PIN_VOLTAGE)
230 TxHottData.gamMsg.main_voltage = voltageData->mVolt[MAIN_BATTERY_SOURCE - VOLT_1].value / 100; //Main power voltage using 0.1V steps 100 == 10,0V] / 100
231 #endif
232 #if defined(PIN_CURRENTSENSOR)
233 TxHottData.gamMsg.batt_cap = currentData->consumedMilliAmps.value / 10 ; // used battery capacity in 10mAh steps
234 #endif
235 #ifdef AIRSPEED
236 TxHottData.gamMsg.speed = airSpeedData->airSpeed.value ; // Km/h
237 #endif
238 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS >= 0)
239 TxHottData.gamMsg.min_cell_volt = voltageData->mVoltCellMin /20 ; // minimum cell voltage in 2mV steps. 124 = 2,48V
240 #endif
241 #if defined(NUMBEROFCELLS) && (NUMBEROFCELLS > 0) && defined(CELL_UNDERVOLTAGE_WARNING)
242 TxHottData.gamMsg.warning_beeps = warning_beeps_Hott(); // Transmitter warning message
243 #endif
245 // field of msg not implemented
246 // byte climbrate3s; //#28 climb rate in m/3sec. Value of 120 = 0m/3sec
247 // byte min_cell_volt_num; //#38 number of the cell with the lowest voltage
248 // uint16_t rpm2; //#39 LSB 2nd RPM in 10 RPM steps. 100 == 1000rpm
249 // byte general_error_number; //#41 General Error Number (Voice Error == 12) TODO: more documentation
250 // byte pressure; //#42 High pressure up to 16bar. 0,1bar scale. 20 == 2.0bar
253 #ifdef GPS_INSTALLED
254 else {
255 // here the code for GPS
256 TxHottData.gpsMsg.startByte = 0x7c ;
257 TxHottData.gpsMsg.sensorID = HOTT_TELEMETRY_GPS_SENSOR_ID ; //0x8A
258 TxHottData.gpsMsg.sensorTextID = HOTTV4_GPS_SENSOR_TEXT_ID ; // 0xA0
259 TxHottData.gpsMsg.endByte = 0x7D ;
260 uint16_t altitudeHott = 500 ; // Hott uses an offset of 500 (m)
261 if ( (GPS_fix_type == 3 ) || (GPS_fix_type == 4 ) ) {
262 // if( GPS_latAvailable ) { // test if data are available (GPS fix) ; if available, fill the buffer
263 // GPS_latAvailable = false ; // reset the flag
264 TxHottData.gpsMsg.flightDirection = GPS_ground_course / 200000 ; // convert from degre * 100000 to 1/2 degree; Flightdir./dir. 1 = 2°; 0° (North), 90° (East), 180° (South), 270° (West)
265 static uint16_t speedHott ;
266 #ifdef GPS_SPEED_3D
267 speedHott = ((uint32_t) GPS_speed_3d) * 36 /1000 ; // convert from cm/sec to km/h
268 #else
269 speedHott = ((uint32_t) GPS_speed_2d) * 36 /1000 ; // convert from cm/sec to km/h
270 #endif
271 TxHottData.gpsMsg.GPSSpeedLow = speedHott ;
272 TxHottData.gpsMsg.GPSSpeedHigh = speedHott >> 8 ;
273 uint16_t degMin ;
274 uint16_t decimalMin ;
275 TxHottData.gpsMsg.LatitudeNS = (GPS_lat < 0) ; // Byte 10: 000 = N = 48°39’0988
276 convertLonLat_Hott(GPS_lat, & degMin , & decimalMin ) ; // convert to 2 fields (one beging deg*100+min, the other being the decimal part of min with 4 decimals
277 TxHottData.gpsMsg.LatitudeMinLow = degMin ; // Byte 11: 231 = 0xE7 <= 0x12E7 = 4839
278 TxHottData.gpsMsg.LatitudeMinHigh = degMin >> 8 ; // Byte 12: 018 = 0x12 <= 0x12E7 = 4839
279 TxHottData.gpsMsg.LatitudeSecLow = decimalMin ; // Byte 13: 220 = 0xDC <= 0x03DC = 0988
280 TxHottData.gpsMsg.LatitudeSecHigh = decimalMin >> 8 ; // Byte 14: 003 = 0x03 <= 0x03DC = 0988
282 TxHottData.gpsMsg.longitudeEW = (GPS_lon < 0) ; // Byte 15: 000 = E= 9° 25’9360
283 convertLonLat_Hott(GPS_lon, &degMin , &decimalMin ) ; // convert to 2 fields (one beging deg*100+min, the other being the decimal part of min with 4 decimals
284 TxHottData.gpsMsg.longitudeMinLow = degMin ; // Byte 16: 157 = 0x9D <= 0x039D = 0925
285 TxHottData.gpsMsg.longitudeMinHigh = degMin >> 8 ; // Byte 17: 003 = 0x03 <= 0x039D = 0925
286 TxHottData.gpsMsg.longitudeSecLow = decimalMin ; // Byte 18: 144 = 0x90 <= 0x2490 = 9360
287 TxHottData.gpsMsg.longitudeSecHigh = decimalMin >> 8 ; // Byte 19: 036 = 0x24 <= 0x2490 = 9360
288 TxHottData.gpsMsg.distanceLow = GPS_distance ; // Byte 20: 027 123 = /distance low byte 6 = 6 m
289 TxHottData.gpsMsg.distanceHigh = GPS_distance >> 8 ; // Byte 21: 036 35 = /distance high byte
290 TxHottData.gpsMsg.HomeDirection = GPS_bearing / 2 ; //Byte 29: HomeDirection (direction from starting point to Model position) (1 byte) 2degree = 1
291 altitudeHott += (GPS_altitude / 1000) ; // convert from mm to m (keep the ofsset of 500 m)
294 /* not yet implemented
295 uint8_t resolutionLow; // Byte 24: 48 = Low Byte m/s resolution 0.01m 48 = 30000 = 0.00m/s (1=0.01m/s)
296 uint8_t resolutionHigh; // Byte 25: 117 = High Byte m/s resolution 0.01m
297 uint8_t unknow1; // Byte 26: 120 = 0m/3s
298 uint8_t GPSNumSat; // Byte 27: GPS.Satelites (number of satelites) (1 byte)
299 uint8_t GPSFixChar; // Byte 28: GPS.FixChar. (GPS fix character. display, if DGPS, 2D oder 3D) (1 byte)
300 uint8_t HomeDirection; // Byte 29: HomeDirection (direction from starting point to Model position) (1 byte)
301 uint8_t angleXdirection; // Byte 30: angle x-direction (1 byte)
302 uint8_t angleYdirection; // Byte 31: angle y-direction (1 byte)
303 uint8_t angleZdirection; // Byte 32: angle z-direction (1 byte)
304 uint8_t gyroXLow; // Byte 33: gyro x low byte (2 bytes)
305 uint8_t gyroXHigh; // Byte 34: gyro x high byte
306 uint8_t gyroYLow; // Byte 35: gyro y low byte (2 bytes)
307 uint8_t gyroYHigh; // Byte 36: gyro y high byte
308 uint8_t gyroZLow; // Byte 37: gyro z low byte (2 bytes)
309 uint8_t gyroZHigh; // Byte 38: gyro z high byte
310 uint8_t vibration; // Byte 39: vibration (1 bytes)
311 uint8_t Ascii4; // Byte 40: 00 ASCII Free Character [4]
312 uint8_t Ascii5; // Byte 41: 00 ASCII Free Character [5]
313 uint8_t GPS_fix; // Byte 42: 00 ASCII Free Character [6], we use it for GPS FIX
314 uint8_t version; // Byte 43: 00 version number
315 uint8_t endByte; // Byte 44: 0x7D Ende byte
316 uint8_t chksum; // Byte 45: Parity Byte
318 if (GPS_fix_type > 4 ) GPS_fix_type = 0 ;
319 TxHottData.gpsMsg.GPS_fix = TxHottData.gpsMsg.GPSFixChar = convertGpsFix[GPS_fix_type] ;
320 TxHottData.gpsMsg.GPSNumSat = GPS_numSat;
321 TxHottData.gpsMsg.altitudeLow = altitudeHott ;
322 TxHottData.gpsMsg.altitudeHigh = altitudeHott >> 8 ;
323 uint16_t varioHott = 30000 ;
324 #ifdef VARIO
325 varioHott += mainVspeed.value ; // put vario vertical speed in GPS data
326 #endif
327 TxHottData.gpsMsg.resolutionLow = varioHott ; //climb rate in 0.01m/s. Value of 30000 = 0.00 m/s
328 TxHottData.gpsMsg.resolutionHigh = varioHott >> 8;
329 TxHottData.gpsMsg.unknow1 = 120 ; // Byte 26: 120 = 0m/3s
331 } // end else => flagUpdateHottBuffer == GPS
332 #endif // end of GPS_Installed
333 // calculate the check sum on first bytes
334 TxHottData.txBuffer[TXHOTTDATA_BUFFERSIZE-1] = 0 ;
335 for(uint8_t i = 0; i < TXHOTTDATA_BUFFERSIZE-1; i++){ // one byte less because the last byte is the checksum
336 TxHottData.txBuffer[TXHOTTDATA_BUFFERSIZE-1] += TxHottData.txBuffer[i];
337 } // end for
338 flagUpdateHottBuffer = 0 ; // reset the flag to say that all data have been updated and that UART can transmit the buffer
339 #ifdef DEBUGHOTT
340 // for(uint8_t i = 0; i < TXHOTTDATA_BUFFERSIZE; i++){ // include the last byte (checksum)
341 // printer->print(TxHottData.txBuffer[i], HEX); printer->print(F(" "));
342 // } // end for
343 // printer->print(tempFlag , HEX) ;
344 // printer->println(F(" "));
345 #endif
347 } // end ( flagUpdateHottBuffer )
353 #ifdef GPS_INSTALLED
354 void convertLonLat_Hott( int32_t GPS_LatLon, uint16_t * degMin , uint16_t * decimalMin ) {
355 static uint32_t GPS_LatLonAbs ;
356 static uint16_t degre0decimals ;
357 static uint32_t minute4decimals ;
358 static uint16_t minute0decimals ;
359 GPS_LatLonAbs = ( GPS_LatLon < 0 ? - GPS_LatLon : GPS_LatLon) / 100 ; // remove 2 decimals from original value which contains degre with 7 decimals (so next calculation are smaller)
360 degre0decimals = GPS_LatLonAbs / 100000 ; // extract the degre without decimal
361 minute4decimals = ( GPS_LatLonAbs - ( ((uint32_t) degre0decimals) * 100000l ) ) * 6 ; // keep the decimal of degree and convert them in minutes (*60) and remove 1 decimal (/10) in order to keep 4 decimals
362 minute0decimals = minute4decimals / 10000 ; // extract the minutes (without decimals)
363 *degMin = degre0decimals * 100 + minute0decimals ; // put degree and minutes toegether in a special format
364 *decimalMin = minute4decimals - ( minute0decimals * 10000 ) ; // Extract the decimal part of the minutes (4 decimals)
366 #endif
367 //***************************** here code to modify for GPS
369 #ifdef GPS_INSTALLED
370 //!! shared with Aserial
371 extern uint8_t volatile gpsSendStatus ;
372 extern uint8_t volatile gpsSportDataLock ;
373 extern uint8_t volatile gpsSportData[7] ;
374 #define GPS_DATA_COUNT 5
376 void OXS_OUT::FrSkySportSensorGpsSend(void)
378 // gpsSendStatus can be TO_LOAD, LOADED, SENDING, SEND ; it is managed here and in Aserial
379 // new data is uploaded only when gpsSendStatus == SEND or TO_LOAD
380 // each GPS data is loaded in sequence but only if available (otherwise this data is skipped)
381 static uint8_t gpsDataIdx ;
382 static uint16_t gpsSportId ;
383 static uint32_t gpsSportValue ;
384 static uint32_t gpsLastLoadedMillis ;
385 #ifdef DEBUGSIMULATEGPS
386 static uint8_t gpsSimulateCount ;
387 #endif
388 // Serial.println(F("S gdps"));
390 if ((gpsSendStatus == SEND || gpsSendStatus == TO_LOAD) && (millis() - gpsLastLoadedMillis > 200 ) ){ // send only one data per 200 msec (to test if it help locking found on the Tx log)
391 gpsDataIdx++; // handle next GPS data; if not available, this field will be skipped.
392 if(gpsDataIdx >= GPS_DATA_COUNT) {
393 gpsDataIdx = 0;
395 switch(gpsDataIdx)
397 case 0: //longitude
398 if (!GPS_lonAvailable) return ;
399 GPS_lonAvailable = false ;
400 gpsSportId = GPS_LONG_LATI_FIRST_ID ;
401 #ifdef DEBUGSIMULATEGPS
402 gpsSportValue = ((( (((uint32_t)( GPS_lon < 0 ? -GPS_lon : GPS_lon)) * 6 ) / 100 ) + gpsSimulateCount++ )& 0x3FFFFFFF) | 0x80000000;
403 #else
404 gpsSportValue = (( (((uint32_t)( GPS_lon < 0 ? -GPS_lon : GPS_lon)) * 6 )/ 100 ) & 0x3FFFFFFF) | 0x80000000;
405 #endif
406 if(GPS_lon < 0) gpsSportValue |= 0x40000000;
407 break;
408 case 1: //latitude
409 if (!GPS_latAvailable) return ;
410 GPS_latAvailable = false ;
411 gpsSportId = GPS_LONG_LATI_FIRST_ID ;
412 gpsSportValue = (( (((uint32_t)( GPS_lat < 0 ? -GPS_lat : GPS_lat)) * 6 )/ 100 ) & 0x3FFFFFFF ) ;
413 if(GPS_lat < 0) gpsSportValue |= 0x40000000;
414 break;
415 case 2: // GPS_altitude
416 if (!GPS_altitudeAvailable) return ;
417 GPS_altitudeAvailable = false ;
418 gpsSportId = GPS_ALT_FIRST_ID ;
419 #ifdef DEBUGSIMULATEGPS
420 gpsSportValue = (GPS_altitude / 10) + gpsSimulateCount++; // convert mm in cm
421 #else
422 gpsSportValue = GPS_altitude / 10; // convert mm in cm
423 #endif
424 break;
425 case 3: // GPS_speed_3d // could be 2D
426 #ifdef GPS_SPEED_3D
427 if (!GPS_speed_3dAvailable) return ;
428 GPS_speed_3dAvailable = false ;
429 gpsSportId = GPS_SPEED_FIRST_ID ;
430 #ifdef GPS_SPEED_IN_KMH
431 gpsSportValue = ( ((uint32_t) GPS_speed_3d) * 36 ) ; // convert cm/s in 1/100 of km/h (factor = 3.6)
432 #else
433 gpsSportValue = ( ((uint32_t) GPS_speed_3d) * 700 ) / 36 ; // convert cm/s in 1/100 of knots (factor = 19.44)
434 #endif // end of GPS_SPEED_IN_KMH
435 break;
436 #else // use gps_Speed_2d
437 if (!GPS_speed_2dAvailable) return ;
438 GPS_speed_2dAvailable = false ;
439 gpsSportId = GPS_SPEED_FIRST_ID ;
440 #ifdef GPS_SPEED_IN_KMH
441 gpsSportValue = ( ((uint32_t) GPS_speed_2d) * 36 ) ; // convert cm/s in 1/100 of km/h (factor = 3.6)
442 #else
443 gpsSportValue = ( ((uint32_t) GPS_speed_2d) * 700 ) / 36 ; // convert cm/s in 1/1000 of knots (factor = 19.44)
444 Serial.print(F("2d Knot:"));Serial.println(gpsSportValue);
445 #endif // end of GPS_SPEED_IN_KMH
446 break;
447 #endif // enf of GPS_SPEED_3D
448 case 4: //GPS_ground_courseAvailable
449 if (!GPS_ground_courseAvailable) return ;
450 GPS_ground_courseAvailable = false ;
451 gpsSportId = GPS_COURS_FIRST_ID ;
452 gpsSportValue = GPS_ground_course / 1000; // convert from degree * 100000 to degree * 100
453 break;
454 default:
455 return ;
456 } // end case
457 gpsSportDataLock = 1 ;
458 gpsSportData[0] = 0x10 ;
459 gpsSportData[1] = gpsSportId ; // low byte
460 gpsSportData[2] = gpsSportId >> 8 ; // hight byte
461 gpsSportData[3] = gpsSportValue ;
462 gpsSportData[4] = gpsSportValue >> 8 ;
463 gpsSportData[5] = gpsSportValue >> 16 ;
464 gpsSportData[6] = gpsSportValue >> 24 ;
465 gpsSportDataLock = 0 ;
466 #ifdef DEBUGSENDGPS
467 Serial.print(F("ID: "));
468 Serial.println(gpsSportId , HEX);
469 #endif
471 gpsSendStatus = LOADED ; // from here data can be sent by the interrupt in Aserial
472 } // end test on gpsSendStatus == SEND or TOLOAD
473 } // end function
475 #endif // of of GPS_INSTALLED
482 void convert_to_degrees_minutes_seconds(float val, int *deg_sec, int *degMin){
483 int16_t deg = (int)val;
484 double sec = (val - deg);
485 int8_t min = (int) (sec * 60);
487 *deg_sec = abs((int) (((sec * 60) - min) * 10000.0f));
488 *degMin = abs((int)(deg * 100 + min));
491 /*tinygps FORMAT dd.ddddd
492 void convert_to_degrees_minutes_seconds(float val, int *deg_sec, int *degMin){
493 int16_t deg = (int)val; //extract degrees
494 float sec = (val - deg);
495 int8_t min = (int) (sec * 60);//extract decimal deg
497 *deg_sec = abs((int) (((sec * 60) - min) * 100000.0f));
498 *degMin = abs((int)(deg * 100 + min));
501 static void hottV4GPSUpdate() {
502 //number of Satelites
503 HoTTV4GPSModule.GPSNumSat=MultiHoTTModule.GPS_numSat;
504 if (MultiHoTTModule.GPS_fix > 0) {
505 // GPS fix
506 HoTTV4GPSModule.GPS_fix = 0x33; // Dgps: '0x44' 2D = '0x32' 3D = '0x33' nofix = '0x2d'
508 int16_t deg_sec;
509 int16_t degMin;
511 //latitude
512 convert_to_degrees_minutes_seconds(MultiHoTTModule.GPS_latitude, &deg_sec, &degMin);
513 HoTTV4GPSModule.LatitudeNS=(MultiHoTTModule.GPS_latitude<0);
514 HoTTV4GPSModule.LatitudeMinLow = degMin;
515 HoTTV4GPSModule.LatitudeMinHigh = degMin >> 8;
516 HoTTV4GPSModule.LatitudeSecLow = deg_sec;
517 HoTTV4GPSModule.LatitudeSecHigh = deg_sec >> 8;
520 //longitude
521 convert_to_degrees_minutes_seconds(MultiHoTTModule.GPS_longitude, &deg_sec, &degMin);
522 HoTTV4GPSModule.longitudeEW=(MultiHoTTModule.GPS_longitude<0);
523 HoTTV4GPSModule.longitudeMinLow = degMin;
524 HoTTV4GPSModule.longitudeMinHigh = degMin >> 8;
525 HoTTV4GPSModule.longitudeSecLow = deg_sec;
526 HoTTV4GPSModule.longitudeSecHigh = deg_sec >> 8;
529 // GPS Speed in km/h
530 uint16_t speed = MultiHoTTModule.GPS_speed; // in km/h
531 HoTTV4GPSModule.GPSSpeedLow = speed & 0x00FF;
532 HoTTV4GPSModule.GPSSpeedHigh = speed >> 8;
533 // Distance to home
534 HoTTV4GPSModule.distanceLow = MultiHoTTModule.GPS_distanceToHome & 0x00FF;
535 HoTTV4GPSModule.distanceHigh = MultiHoTTModule.GPS_distanceToHome >> 8;
536 // Altitude
537 HoTTV4GPSModule.altitudeLow = MultiHoTTModule.GPS_altitude & 0x00FF;
538 HoTTV4GPSModule.altitudeHigh = MultiHoTTModule.GPS_altitude >> 8;
539 // Home Direction
540 HoTTV4GPSModule.HomeDirection = MultiHoTTModule.GPS_directionToHome;
541 // Flightdirection
542 HoTTV4GPSModule.flightDirection = MultiHoTTModule.GPS_flightDirection;
544 //VARIO not implemented yet, should be a BMP085
545 //m/s
546 HoTTV4GPSModule.resolutionLow = MultiHoTTModule.GPS_distanceToHome & 0x00FF;
547 HoTTV4GPSModule.resolutionHigh = MultiHoTTModule.GPS_distanceToHome >> 8;
548 //m/3s
549 HoTTV4GPSModule.unknow1 = MultiHoTTModule.GPS_flightDirection;
551 HoTTV4GPSModule.alarmTone = MultiHoTTModule.GPS_alarmTone;
553 } else {
554 HoTTV4GPSModule.GPS_fix = 0x2d; // Displays a ' ' to show nothing or clear the old value
560 // Sends HoTTv4 capable GPS telemetry frame.
561 static void hottV4SendGPS() {
562 // Minimum data set for EAM
563 HoTTV4GPSModule.startByte = 0x7C;
564 HoTTV4GPSModule.sensorID = HOTTV4_GPS_SENSOR_ID;
565 HoTTV4GPSModule.sensorTextID = HOTTV4_GPS_SENSOR_TEXT_ID;
566 HoTTV4GPSModule.endByte = 0x7D;
569 hottV4GPSUpdate();
571 if (is_set_home == 0)
573 HoTTV4GPSModule.alarmTone = 0x08; //alarm tone if no fix
574 toggle_LED(); //Let the led blink
575 }else
577 HoTTV4GPSModule.alarmTone = 0x0;
580 // Clear output buffer
581 memset(&outBuffer, 0, sizeof(outBuffer));
583 // Copy GPS data to output buffer
584 memcpy(&outBuffer, &HoTTV4GPSModule, kHoTTv4BinaryPacketSize);
586 // Send data from output buffer
587 hottV4SendData(outBuffer, kHoTTv4BinaryPacketSize);
591 // **************************** End of code for GPS (still to modify
596 //---------------------------------- Here the code to handle the UART
597 #ifdef DEBUG
598 //#define DEBUGSETNEWDATA
599 //#define DEBUGASERIAL
600 #endif
602 #define FORCE_INDIRECT(ptr) __asm__ __volatile__ ("" : "=e" (ptr) : "0" (ptr))
605 ISR(TIMER1_COMPA_vect)
607 switch (state)
609 case TRANSMIT : // startbit has been sent, it is time to output now 8 bits and 1 stop bit
610 #if DEBUGASERIAL
611 PORTC |= 1 ;
612 #endif
613 if( SwUartTXBitCount < 8 ) { // If not 8 bits have been sent
614 if( SwUartTXData & 0x01 ) { // If the LSB of the TX buffer is 1:
615 SET_TX_PIN_MB() ;
616 } else { // Otherwise:
617 CLEAR_TX_PIN_MB() ; // Send a logic 0 on the TX_PIN
619 SwUartTXData = SwUartTXData >> 1 ; // Bitshift the TX buffer and
620 SwUartTXBitCount += 1 ; // increment TX bit counter.
621 } else { //Send stop bit.
622 SET_TX_PIN_MB() ; // Output a logic 1. (in high impedance) = put stop bit
623 state = TRANSMIT_STOP_BIT;
624 OCR1A += DELAY_2000; // Normally Add 2 msec to the stop bit (required by Hott protocol)
626 OCR1A += TICKS2WAITONEHOTT ; // Count one period into the future.
627 #if DEBUGASERIAL
628 PORTC &= ~1 ;
629 #endif
630 break ;
632 case TRANSMIT_STOP_BIT: //************************************* handling after the stop bit has been sent
633 if ( ++TxCount < TXHOTTDATA_BUFFERSIZE ) {
634 SwUartTXData = TxHottData.txBuffer[TxCount] ;
635 CLEAR_TX_PIN_MB(); // Send a logic 0 on the TX_PIN as start bit
636 OCR1A = TCNT1 + TICKS2WAITONEHOTT - INTERRUPT_BETWEEN_TRANSMIT ; // Count one period into the future.
637 SwUartTXBitCount = 0 ;
638 state = TRANSMIT ;
639 } else { // all bytes have already been sent
640 state = WAITING ;
641 OCR1A += DELAY_2000; // 2mS gap before listening
642 TRXDDR &= ~( 1 << PIN_SERIALTX ) ; // PIN is input
643 TRXPORT &= ~( 1 << PIN_SERIALTX ) ; // PIN is tri-stated.
645 break ;
647 case RECEIVE : // Start bit has been received and we will read bits of data receiving, LSB first.
648 OCR1A += TICKS2WAITONEHOTT ; // Count one period after the falling edge is trigged.
649 uint8_t data ; // Use a temporary local storage (it save some bytes (and perhaps time)
650 data = SwUartRXBitCount ;
651 if( data < 8 ) { //If 8 bits are not yet read
652 SwUartRXBitCount = data + 1 ;
653 data = SwUartRXData ;
654 data >>= 1 ; // Shift due to receiving LSB first.
655 if( !(GET_RX_PIN( ) == 0 )) data |= 0x80 ; // If a logical 1 is read, let the data mirror this.
656 SwUartRXData = data ;
657 } else { //Done receiving = 8 bits are in SwUartRXData
658 if ( ( LastRx == HOTT_BINARY_MODE_REQUEST_ID ) && ( ( SwUartRXData == HOTT_TELEMETRY_GAM_SENSOR_ID) // if the previous byte identifies a polling for a reply in binary format and current is oXs sensor ID
659 #ifdef GPS_INSTALLED
660 || ( SwUartRXData == HOTT_TELEMETRY_GPS_SENSOR_ID )
661 #endif
662 ) ) { // the sensor has to reply (if it has data; here we assume it has always data and the data will be in the Hott buffer)
663 flagUpdateHottBuffer = SwUartRXData ; // flag to say to send function that the buffer must be filled. It is expected that send function is called fast enough (so main loop may not be blocked)
664 state = TxPENDING ;
665 OCR1A += ( DELAY_4000 - TICKS2WAITONEHOTT) ; // 4ms gap before sending; normally Hott protocols says to wait 5 msec but this is too much for timer1
666 delayTxPendingCount = 1 ; // ask for 1 more delay of 1ms in order to reach the total of 5msec
667 } else { // Previous code is not equal to HOTT_BINARY_MODE_REQUEST_ID , enter to iddle mode (so we will accept to read another byte)
668 DISABLE_TIMER_INTERRUPT() ; // Stop the timer interrupts.
669 state = IDLE ; // Go back to idle.
670 PCIFR = ( 1<<PCIF2 ) ; // clear pending interrupt
671 PCICR |= ( 1<<PCIE2 ) ; // pin change interrupt enabled
673 LastRx = SwUartRXData ; // save the byte being received; so next time a byte is received, comparison is possible.
674 } // End receiving 1 byte (8 bits) (and 1 bit)
675 break ;
677 case TxPENDING : //End of delay before sending data has occurs
678 if ( delayTxPendingCount ) { // if additional delay is requested, perform it
679 delayTxPendingCount--;
680 OCR1A += DELAY_1000 ;
681 } else {
682 if ( flagUpdateHottBuffer ) { // it is expected that the main loop will update the buffer and set this flag to true within the delay
683 OCR1A += DELAY_1000 ; // if it is not yet done, stay TxPENDING for 1 msec more
684 // state = WAITING ;
685 } else {
686 CLEAR_TX_PIN_MB() ; // Send a start bit (logic 0 on the TX_PIN).
687 OCR1A = TCNT1 + TICKS2WAITONEHOTT - INTERRUPT_ENTRY_TRANSMIT ; // Count one period into the future (less due to time to enter ISR)
688 SwUartTXBitCount = 0 ;
689 SwUartTXData = TxHottData.txBuffer[0] ;
690 TxCount = 0 ;
691 state = TRANSMIT ;
694 break ;
696 case WAITING : // At the end of wait time, stop timer interrupt but allow pin change interrupt in order to allow to detect incoming data
697 DISABLE_TIMER_INTERRUPT() ; // Stop the timer interrupts.
698 state = IDLE ; // Go back to idle.
699 CLEAR_PIN_CHANGE_INTERRUPT() ; // clear pending pin change interrupt
700 ENABLE_PIN_CHANGE_INTERRUPT() ; // pin change interrupt enabled
701 break ;
703 // Unknown state.
704 default:
705 state = IDLE; // Error, should not occur. Going to a safe state.
706 } // End CASE
707 //#ifdef PPM_INTERRUPT
708 // if ( EIFR & PPM_INT_BIT) ppmInterrupted = 1 ;
709 //#endif
711 } // End of ISR
714 // *********************************** initialise UART for Hott protocol *********************************************
715 // This function will set up pins to transmit and receive on. Control of Timer1 and pin change interrupt 0.
716 void initHottUart( ) //*************** initialise UART for Multiplex
718 //PORT
719 TRXDDR &= ~( 1 << PIN_SERIALTX ) ; // PIN is input.
720 TRXPORT &= ~( 1 << PIN_SERIALTX ) ; // PIN is tri-stated.
722 // External interrupt
724 #if PIN_SERIALTX == 4
725 PCMSK2 |= 0x10 ; // IO4 (PD4) on Arduini mini
726 #elif PIN_SERIALTX == 2
727 PCMSK2 |= 0x04 ; // IO2 (PD2) on Arduini mini
728 #else
729 #error "This PIN is not supported"
730 #endif
731 CLEAR_PIN_CHANGE_INTERRUPT() ;
732 ENABLE_PIN_CHANGE_INTERRUPT() ;
733 state = IDLE ; // Internal State Variable
735 #if DEBUGASERIAL
736 DDRC = 0x01 ; // PC0 as o/p debug = pin A0 !!!!
737 PORTC = 0 ;
738 #endif
742 // ! \brief External interrupt service routine. ********************
743 // Interrupt on Pin Change to detect change on level on Hott signal (= could be a start bit)
745 // The falling edge in the beginning of the start
746 // bit will trig this interrupt. The state will
747 // be changed to RECEIVE, and the timer interrupt
748 // will be set to trig one and a half bit period
749 // from the falling edge. At that instant the
750 // code should sample the first data bit.
752 // note initHottUart( void ) must be called in advance.
754 // This is the pin change interrupt for port D
755 // This assumes it is the only pin change interrupt
756 // on this port
758 ISR(PCINT2_vect) // There is a start bit.
760 if (!( TRXPIN & ( 1 << PIN_SERIALTX ) )) { // Pin is low = start bit
761 DISABLE_PIN_CHANGE_INTERRUPT() ; // pin change interrupt disabled
762 state = RECEIVE ; // Change state
763 DISABLE_TIMER_INTERRUPT() ; // Disable timer to change its registers.
764 OCR1A = TCNT1 + TICKS2WAITONE_HALFHOTT - INTERRUPT_EXEC_CYCL - INTERRUPT_EARLY_BIAS ; // Count one and a half period into the future.
765 #if DEBUGASERIAL
766 PORTC |= 1 ;
767 #endif
768 SwUartRXBitCount = 0 ; // Clear received bit counter.
769 CLEAR_TIMER_INTERRUPT() ; // Clear interrupt bits
770 ENABLE_TIMER_INTERRUPT() ; // Enable timer1 interrupt on again
772 //#ifdef PPM_INTERRUPT
773 // if ( EIFR & PPM_INT_BIT) ppmInterrupted = 1 ;
774 //#endif
776 } // end ISR pin change
779 // -------------------------End of HOTT protocol--------------------------------------------------------------------------------------
781 #endif // END of HOTT