2 ******************************************************************************
3 * @addtogroup OpenPilotModules OpenPilot Modules
5 * @addtogroup MKSerialModule MK Serial Control Module
6 * @brief Connect to MK module
10 * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
11 * @brief Interfacing with MK via serial port
12 * @see The GNU Public License (GPL) Version 3
14 *****************************************************************************/
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 3 of the License, or
19 * (at your option) any later version.
21 * This program is distributed in the hope that it will be useful, but
22 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
26 * You should have received a copy of the GNU General Public License along
27 * with this program; if not, write to the Free Software Foundation, Inc.,
28 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 #include "openpilot.h"
34 #include "attitudestate.h" // object that will be updated by the module
35 #include "positionstate.h"
36 #include "flightbatterystate.h"
41 #define PORT PIOS_COM_AUX
42 #define DEBUG_PORT PIOS_COM_GPS
43 #define STACK_SIZE 1024
44 #define TASK_PRIORITY (tskIDLE_PRIORITY + 3)
45 #define MAX_NB_PARS 100
46 // #define ENABLE_DEBUG_MSG
47 // #define GENERATE_BATTERY_INFO // The MK can report battery voltage, but normally the current sensor will be used, so this module should not report battery state
49 #if PORT == PIOS_COM_AUX
50 #ifndef PIOS_ENABLE_AUX_UART
51 #error "This module cannot be included without the AUX UART enabled"
58 #define MSGCMD_GET_DEBUG 'd'
59 #define MSGCMD_DEBUG 'D'
60 #define MSGCMD_GET_VERSION 'v'
61 #define MSGCMD_VERSION 'V'
62 #define MSGCMD_GET_OSD 'o'
63 #define MSGCMD_OSD 'O'
65 #define DEBUG_MSG_NICK_IDX (2 + 2 * 2)
66 #define DEBUG_MSG_ROLL_IDX (2 + 3 * 2)
68 #define OSD_MSG_CURRPOS_IDX 1
69 #define OSD_MSG_NB_SATS_IDX 50
70 #define OSD_MSG_BATT_IDX 57
71 #define OSD_MSG_GNDSPEED_IDX 58
72 #define OSD_MSG_COMPHEADING_IDX 62
73 #define OSD_MSG_NICK_IDX 64
74 #define OSD_MSG_ROLL_IDX 65
76 #ifdef ENABLE_DEBUG_MSG
77 #define DEBUG_MSG(format, ...) PIOS_COM_SendFormattedString(DEBUG_PORT, format,##__VA_ARGS__)
79 #define DEBUG_MSG(format, ...)
89 uint8_t pars
[MAX_NB_PARS
];
113 static void OnError(int line
);
114 // static void PrintMsg(const MkMsg_t* msg);
115 static int16_t Par2Int16(const MkMsg_t
*msg
, uint8_t index
);
116 static int32_t Par2Int32(const MkMsg_t
*msg
, uint8_t index
);
117 static int8_t Par2Int8(const MkMsg_t
*msg
, uint8_t index
);
118 static void GetGpsPos(const MkMsg_t
*msg
, uint8_t index
, GpsPosition_t
*pos
);
119 static uint8_t WaitForBytes(uint8_t *buf
, uint8_t nbBytes
, portTickType xTicksToWait
);
120 static bool WaitForMsg(uint8_t cmd
, MkMsg_t
*msg
, portTickType xTicksToWait
);
121 static void SendMsg(const MkMsg_t
*msg
);
122 static void SendMsgParNone(uint8_t address
, uint8_t cmd
);
123 static void SendMsgPar8(uint8_t address
, uint8_t cmd
, uint8_t par0
);
124 static void MkSerialTask(void *parameters
);
126 static void OnError(int line
)
128 DEBUG_MSG("MKProcol error %d\n\r", line
);
132 static void PrintMsg(const MkMsg_t
*msg
)
134 switch (msg
->address
) {
152 DEBUG_MSG("%c ", msg
->cmd
);
154 for (int i
= 0; i
< msg
->nbPars
; i
++) {
155 DEBUG_MSG("%02x ", msg
->pars
[i
]);
161 static int16_t Par2Int16(const MkMsg_t
*msg
, uint8_t index
)
165 res
= (int)(msg
->pars
[index
+ 1]) * 256 + msg
->pars
[index
];
166 if (res
> 0xFFFF / 2) {
172 static int32_t Par2Int32(const MkMsg_t
*msg
, uint8_t index
)
176 val
= (((int)msg
->pars
[index
]) << 0) + (((int)msg
->pars
[index
+ 1]) << 8);
177 val
+= (((int)msg
->pars
[index
+ 2]) << 16) + ((int)msg
->pars
[index
+ 3] << 24);
178 if (val
> 0xFFFFFFFF / 2) {
184 static int8_t Par2Int8(const MkMsg_t
*msg
, uint8_t index
)
186 if (msg
->pars
[index
] > 127) {
187 return msg
->pars
[index
] - 256;
189 return msg
->pars
[index
];
193 static void GetGpsPos(const MkMsg_t
*msg
, uint8_t index
, GpsPosition_t
*pos
)
195 pos
->longitute
= (float)Par2Int32(msg
, index
) * (float)1e-7;
196 pos
->latitude
= (float)Par2Int32(msg
, index
+ 4) * (float)1e-7;
197 pos
->altitude
= (float)Par2Int32(msg
, index
+ 8) * (float)1e-3;
198 pos
->status
= msg
->pars
[index
+ 12];
201 static uint8_t WaitForBytes(uint8_t *buf
, uint8_t nbBytes
, portTickType xTicksToWait
)
203 uint8_t nbBytesLeft
= nbBytes
;
204 xTimeOutType xTimeOut
;
206 vTaskSetTimeOutState(&xTimeOut
);
209 // - all bytes are received
213 // Check if timeout occured
214 if (xTaskCheckForTimeOut(&xTimeOut
, &xTicksToWait
)) {
218 // Check if there are some bytes
219 if (PIOS_COM_ReceiveBufferUsed(PORT
)) {
220 *buf
= PIOS_COM_ReceiveBuffer(PORT
);
223 if (buf
[0] == '\r') {
229 // FIXME: should be blocking
232 } while (nbBytesLeft
);
234 return nbBytes
- nbBytesLeft
;
237 static bool WaitForMsg(uint8_t cmd
, MkMsg_t
*msg
, portTickType xTicksToWait
)
243 unsigned int checkVal
;
244 xTimeOutType xTimeOut
;
246 vTaskSetTimeOutState(&xTimeOut
);
248 while (!done
&& !error
) {
249 // When we are here, it means we did not encounter the message we are waiting for
250 // Check if we did not timeout yet.
255 if (xTaskCheckForTimeOut(&xTimeOut
, &xTicksToWait
)) {
258 WaitForBytes(buf
, 1, 100 / portTICK_RATE_MS
);
259 } while (buf
[0] != '#');
261 // Wait for cmd and address
262 if (WaitForBytes(buf
, 2, 10 / portTICK_RATE_MS
) != 2) {
266 // Is this the command we are waiting for?
267 if (cmd
== 0 || cmd
== buf
[1]) {
268 // OK follow this message to the end
269 msg
->address
= buf
[0] - 'a';
272 checkVal
= '#' + buf
[0] + buf
[1];
276 while (!done
&& !error
) {
277 n
= WaitForBytes(buf
, 4, 10 / portTICK_RATE_MS
);
278 if (n
> 0 && buf
[n
- 1] == '\r') {
280 // This is the end of the message
283 unsigned int msgCeckVal
;
284 msgCeckVal
= (buf
[n
- 1] - '=') + (buf
[n
- 2] - '=') * 64;
285 // printf("%x %x\n", msgCeckVal, checkVal&0xFFF);
288 if (msgCeckVal
== (checkVal
& 0xFFF)) {
301 for (i
= 0; i
< 4; i
++) {
305 if (msg
->nbPars
< MAX_NB_PARS
) {
306 msg
->pars
[msg
->nbPars
] = (((buf
[0] << 2) & 0xFF) | ((buf
[1] >> 4)));
309 if (msg
->nbPars
< MAX_NB_PARS
) {
310 msg
->pars
[msg
->nbPars
] = ((buf
[1] & 0x0F) << 4 | (buf
[2] >> 2));
313 if (msg
->nbPars
< MAX_NB_PARS
) {
314 msg
->pars
[msg
->nbPars
] = ((buf
[2] & 0x03) << 6 | buf
[3]);
325 return done
&& !error
;
328 static void SendMsg(const MkMsg_t
*msg
)
332 uint8_t nbParsRemaining
;
337 buf
[1] = msg
->address
+ 'a';
339 PIOS_COM_SendBuffer(PORT
, buf
, 3);
340 checkVal
= (unsigned int)'#' + buf
[1] + buf
[2];
343 nbParsRemaining
= msg
->nbPars
;
345 while (nbParsRemaining
) {
354 if (nbParsRemaining
) {
358 if (nbParsRemaining
) {
365 buf
[0] = (a
>> 2) + '=';
366 buf
[1] = (((a
& 0x03) << 4) | ((b
& 0xf0) >> 4)) + '=';
367 buf
[2] = (((b
& 0x0f) << 2) | ((c
& 0xc0) >> 6)) + '=';
368 buf
[3] = (c
& 0x3f) + '=';
374 PIOS_COM_SendBuffer(PORT
, buf
, 4);
378 buf
[0] = (checkVal
/ 64) + '=';
379 buf
[1] = (checkVal
% 64) + '=';
381 PIOS_COM_SendBuffer(PORT
, buf
, 3);
384 static void SendMsgParNone(uint8_t address
, uint8_t cmd
)
388 msg
.address
= address
;
395 static void SendMsgPar8(uint8_t address
, uint8_t cmd
, uint8_t par0
)
399 msg
.address
= address
;
407 static uint16_t VersionMsg_GetVersion(const MkMsg_t
*msg
)
409 return msg
->pars
[0] * 100 + msg
->pars
[1];
412 static void DoConnectedToFC(void)
414 AttitudeStateData attitudeData
;
419 memset(&attitudeData
, 0, sizeof(attitudeData
));
421 // Configure FC for fast reporting of the debug-message
422 SendMsgPar8(MK_ADDR_ALL
, MSGCMD_GET_DEBUG
, 10);
425 if (WaitForMsg(MSGCMD_DEBUG
, &msg
, 500 / portTICK_RATE_MS
)) {
430 nick
= Par2Int16(&msg
, DEBUG_MSG_NICK_IDX
);
431 roll
= Par2Int16(&msg
, DEBUG_MSG_ROLL_IDX
);
433 DEBUG_MSG("Att: Nick=%5d Roll=%5d\n\r", nick
, roll
);
435 attitudeData
.Pitch
= -(float)nick
/ 10;
436 attitudeData
.Roll
= -(float)roll
/ 10;
437 AttitudeStateSet(&attitudeData
);
445 static void DoConnectedToNC(void)
449 AttitudeStateData attitudeData
;
450 PositionStateData positionData
;
451 FlightBatteryStateData flightBatteryData
;
453 #ifdef GENERATE_BATTERY_INFO
454 uint8_t battStateCnt
= 0;
459 memset(&attitudeData
, 0, sizeof(attitudeData
));
460 memset(&positionData
, 0, sizeof(positionData
));
461 memset(&flightBatteryData
, 0, sizeof(flightBatteryData
));
463 // Configure NC for fast reporting of the osd-message
464 SendMsgPar8(MK_ADDR_ALL
, MSGCMD_GET_OSD
, 10);
467 if (WaitForMsg(MSGCMD_OSD
, &msg
, 500 / portTICK_RATE_MS
)) {
469 GetGpsPos(&msg
, OSD_MSG_CURRPOS_IDX
, &pos
);
472 DEBUG_MSG("Bat=%d\n\r", msg
.pars
[OSD_MSG_BATT_IDX
]);
473 DEBUG_MSG("Nick=%d Roll=%d\n\r", Par2Int8(&msg
, OSD_MSG_NICK_IDX
), Par2Int8(&msg
, OSD_MSG_ROLL_IDX
));
474 DEBUG_MSG("POS #Sats=%d stat=%d lat=%d lon=%d alt=%d\n\r", msg
.pars
[OSD_MSG_NB_SATS_IDX
], pos
.status
, (int)pos
.latitude
,
475 (int)pos
.longitute
, (int)pos
.altitude
);
479 attitudeData
.Pitch
= -Par2Int8(&msg
, OSD_MSG_NICK_IDX
);
480 attitudeData
.Roll
= -Par2Int8(&msg
, OSD_MSG_ROLL_IDX
);
481 AttitudeStateSet(&attitudeData
);
483 positionData
.Longitude
= pos
.longitute
;
484 positionData
.Latitude
= pos
.latitude
;
485 positionData
.Altitude
= pos
.altitude
;
486 positionData
.Satellites
= msg
.pars
[OSD_MSG_NB_SATS_IDX
];
487 positionData
.Heading
= Par2Int16(&msg
, OSD_MSG_COMPHEADING_IDX
);
488 positionData
.Groundspeed
= ((float)Par2Int16(&msg
, OSD_MSG_GNDSPEED_IDX
)) / 100 /* cm/s => m/s */;
489 if (positionData
.Satellites
< 5) {
490 positionData
.Status
= POSITIONACTUAL_STATUS_NOFIX
;
492 positionData
.Status
= POSITIONACTUAL_STATUS_FIX3D
;
494 PositionStateSet(&positionData
);
496 #if GENERATE_BATTERY_INFO
497 if (++battStateCnt
> 2) {
498 flightBatteryData
.Voltage
= (float)msg
.pars
[OSD_MSG_BATT_IDX
] / 10;
499 FlightBatteryStateSet(&flightBatteryData
);
510 static void MkSerialTask(__attribute__((unused
)) void *parameters
)
514 bool connectionOk
= FALSE
;
516 PIOS_COM_ChangeBaud(PORT
, 57600);
517 PIOS_COM_ChangeBaud(DEBUG_PORT
, 57600);
519 DEBUG_MSG("MKSerial Started\n\r");
522 // Wait until we get version from MK
523 while (!connectionOk
) {
524 SendMsgParNone(MK_ADDR_ALL
, MSGCMD_GET_VERSION
);
525 DEBUG_MSG("Version... ");
526 if (WaitForMsg(MSGCMD_VERSION
, &msg
, 250 / portTICK_RATE_MS
)) {
527 version
= VersionMsg_GetVersion(&msg
);
528 DEBUG_MSG("%d\n\r", version
);
535 // Dependent on version, decide it we are connected to NC or FC
536 // TODO: use slave-addr to distinguish FC/NC -> much safer
538 DoConnectedToNC(); // Will only return after an error
540 DoConnectedToFC(); // Will only return after an error
543 connectionOk
= FALSE
;
545 vTaskDelay(250 / portTICK_RATE_MS
);
550 * Initialise the module
551 * \return -1 if initialisation failed
552 * \return 0 on success
554 int32_t MKSerialInitialize(void)
557 xTaskCreate(MkSerialTask
, (signed char *)"MkSerial", STACK_SIZE
, NULL
, TASK_PRIORITY
, NULL
);