mirror of
https://github.com/betaflight/betaflight.git
synced 2025-07-19 06:15:16 +03:00
Combine ibus serial rx and ibus telemetry on the same fc uart on a single pin.
``` +---------+ | FS-iA6B | | | | Ser RX |---|<---\ +------------+ | | | | FC | | Sensor |---[R]--*-------| Uart TX | +---------+ +------------+ ``` R = 10Kohm, Diode 1N4148 or similar. Both uart tx and rx channels are used so it's not possible to use the spare pin for rx of something else. Configure the serial port like this to get both serial rx and ibus telemetry on the same port: ``` serial 1 4160 115200 57600 115200 115200 ``` It is still possible to run the serial rx and ibus telemetry on two uarts like before, an example: ``` serial 1 64 115200 57600 0 115200 serial 2 4096 115200 57600 115200 115200 ```
This commit is contained in:
parent
355c7cc215
commit
b13d4694bb
12 changed files with 640 additions and 347 deletions
1
Makefile
1
Makefile
|
@ -684,6 +684,7 @@ COMMON_SRC = \
|
||||||
telemetry/ltm.c \
|
telemetry/ltm.c \
|
||||||
telemetry/mavlink.c \
|
telemetry/mavlink.c \
|
||||||
telemetry/ibus.c \
|
telemetry/ibus.c \
|
||||||
|
telemetry/ibus_shared.c \
|
||||||
sensors/esc_sensor.c \
|
sensors/esc_sensor.c \
|
||||||
io/vtx_string.c \
|
io/vtx_string.c \
|
||||||
io/vtx_smartaudio.c \
|
io/vtx_smartaudio.c \
|
||||||
|
|
|
@ -43,6 +43,8 @@
|
||||||
|
|
||||||
#include "rx/rx.h"
|
#include "rx/rx.h"
|
||||||
#include "rx/ibus.h"
|
#include "rx/ibus.h"
|
||||||
|
#include "telemetry/ibus.h"
|
||||||
|
#include "telemetry/ibus_shared.h"
|
||||||
|
|
||||||
#define IBUS_MAX_CHANNEL 14
|
#define IBUS_MAX_CHANNEL 14
|
||||||
#define IBUS_BUFFSIZE 32
|
#define IBUS_BUFFSIZE 32
|
||||||
|
@ -51,11 +53,14 @@
|
||||||
#define IBUS_FRAME_GAP 500
|
#define IBUS_FRAME_GAP 500
|
||||||
|
|
||||||
#define IBUS_BAUDRATE 115200
|
#define IBUS_BAUDRATE 115200
|
||||||
|
#define IBUS_TELEMETRY_PACKET_LENGTH (4)
|
||||||
|
#define IBUS_SERIAL_RX_PACKET_LENGTH (32)
|
||||||
|
|
||||||
static uint8_t ibusModel;
|
static uint8_t ibusModel;
|
||||||
static uint8_t ibusSyncByte;
|
static uint8_t ibusSyncByte;
|
||||||
static uint8_t ibusFrameSize;
|
static uint8_t ibusFrameSize;
|
||||||
static uint8_t ibusChannelOffset;
|
static uint8_t ibusChannelOffset;
|
||||||
|
static uint8_t rxBytesToIgnore;
|
||||||
static uint16_t ibusChecksum;
|
static uint16_t ibusChecksum;
|
||||||
|
|
||||||
static bool ibusFrameDone = false;
|
static bool ibusFrameDone = false;
|
||||||
|
@ -63,6 +68,13 @@ static uint32_t ibusChannelData[IBUS_MAX_CHANNEL];
|
||||||
|
|
||||||
static uint8_t ibus[IBUS_BUFFSIZE] = { 0, };
|
static uint8_t ibus[IBUS_BUFFSIZE] = { 0, };
|
||||||
|
|
||||||
|
|
||||||
|
static bool isValidIa6bIbusPacketLength(uint8_t length)
|
||||||
|
{
|
||||||
|
return (length == IBUS_TELEMETRY_PACKET_LENGTH) || (length == IBUS_SERIAL_RX_PACKET_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Receive ISR callback
|
// Receive ISR callback
|
||||||
static void ibusDataReceive(uint16_t c)
|
static void ibusDataReceive(uint16_t c)
|
||||||
{
|
{
|
||||||
|
@ -72,29 +84,29 @@ static void ibusDataReceive(uint16_t c)
|
||||||
|
|
||||||
ibusTime = micros();
|
ibusTime = micros();
|
||||||
|
|
||||||
if ((ibusTime - ibusTimeLast) > IBUS_FRAME_GAP)
|
if ((ibusTime - ibusTimeLast) > IBUS_FRAME_GAP) {
|
||||||
ibusFramePosition = 0;
|
ibusFramePosition = 0;
|
||||||
|
rxBytesToIgnore = 0;
|
||||||
|
} else if (rxBytesToIgnore) {
|
||||||
|
rxBytesToIgnore--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ibusTimeLast = ibusTime;
|
ibusTimeLast = ibusTime;
|
||||||
|
|
||||||
if (ibusFramePosition == 0) {
|
if (ibusFramePosition == 0) {
|
||||||
if (ibusSyncByte == 0) {
|
if (isValidIa6bIbusPacketLength(c)) {
|
||||||
// detect the frame type based on the STX byte.
|
ibusModel = IBUS_MODEL_IA6B;
|
||||||
if (c == 0x55) {
|
ibusSyncByte = c;
|
||||||
ibusModel = IBUS_MODEL_IA6;
|
ibusFrameSize = c;
|
||||||
ibusSyncByte = 0x55;
|
ibusChannelOffset = 2;
|
||||||
ibusFrameSize = 31;
|
ibusChecksum = 0xFFFF;
|
||||||
ibusChecksum = 0x0000;
|
} else if ((ibusSyncByte == 0) && (c == 0x55)) {
|
||||||
ibusChannelOffset = 1;
|
ibusModel = IBUS_MODEL_IA6;
|
||||||
} else if (c == 0x20) {
|
ibusSyncByte = 0x55;
|
||||||
ibusModel = IBUS_MODEL_IA6B;
|
ibusFrameSize = 31;
|
||||||
ibusSyncByte = 0x20;
|
ibusChecksum = 0x0000;
|
||||||
ibusFrameSize = 32;
|
ibusChannelOffset = 1;
|
||||||
ibusChannelOffset = 2;
|
|
||||||
ibusChecksum = 0xFFFF;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (ibusSyncByte != c) {
|
} else if (ibusSyncByte != c) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,11 +121,42 @@ static void ibusDataReceive(uint16_t c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool isChecksumOkIa6(void)
|
||||||
|
{
|
||||||
|
uint8_t offset;
|
||||||
|
uint8_t i;
|
||||||
|
uint16_t chksum, rxsum;
|
||||||
|
chksum = ibusChecksum;
|
||||||
|
rxsum = ibus[ibusFrameSize - 2] + (ibus[ibusFrameSize - 1] << 8);
|
||||||
|
for (i = 0, offset = ibusChannelOffset; i < IBUS_MAX_CHANNEL; i++, offset += 2) {
|
||||||
|
chksum += ibus[offset] + (ibus[offset + 1] << 8);
|
||||||
|
}
|
||||||
|
return chksum == rxsum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool checksumIsOk(void) {
|
||||||
|
if (ibusModel == IBUS_MODEL_IA6 ) {
|
||||||
|
return isChecksumOkIa6();
|
||||||
|
} else {
|
||||||
|
return isChecksumOkIa6b(ibus, ibusFrameSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void updateChannelData(void) {
|
||||||
|
uint8_t i;
|
||||||
|
uint8_t offset;
|
||||||
|
|
||||||
|
for (i = 0, offset = ibusChannelOffset; i < IBUS_MAX_CHANNEL; i++, offset += 2) {
|
||||||
|
ibusChannelData[i] = ibus[offset] + (ibus[offset + 1] << 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static uint8_t ibusFrameStatus(void)
|
static uint8_t ibusFrameStatus(void)
|
||||||
{
|
{
|
||||||
uint8_t i, offset;
|
|
||||||
uint8_t frameStatus = RX_FRAME_PENDING;
|
uint8_t frameStatus = RX_FRAME_PENDING;
|
||||||
uint16_t chksum, rxsum;
|
|
||||||
|
|
||||||
if (!ibusFrameDone) {
|
if (!ibusFrameDone) {
|
||||||
return frameStatus;
|
return frameStatus;
|
||||||
|
@ -121,32 +164,30 @@ static uint8_t ibusFrameStatus(void)
|
||||||
|
|
||||||
ibusFrameDone = false;
|
ibusFrameDone = false;
|
||||||
|
|
||||||
chksum = ibusChecksum;
|
if (checksumIsOk()) {
|
||||||
rxsum = ibus[ibusFrameSize - 2] + (ibus[ibusFrameSize - 1] << 8);
|
if (ibusModel == IBUS_MODEL_IA6 || ibusSyncByte == 0x20) {
|
||||||
if (ibusModel == IBUS_MODEL_IA6) {
|
updateChannelData();
|
||||||
for (i = 0, offset = ibusChannelOffset; i < IBUS_MAX_CHANNEL; i++, offset += 2)
|
frameStatus = RX_FRAME_COMPLETE;
|
||||||
chksum += ibus[offset] + (ibus[offset + 1] << 8);
|
}
|
||||||
} else {
|
else
|
||||||
for (i = 0; i < 30; i++)
|
{
|
||||||
chksum -= ibus[i];
|
#if defined(TELEMETRY) && defined(TELEMETRY_IBUS)
|
||||||
}
|
rxBytesToIgnore = respondToIbusRequest(ibus);
|
||||||
|
#endif
|
||||||
if (chksum == rxsum) {
|
|
||||||
for (i = 0, offset = ibusChannelOffset; i < IBUS_MAX_CHANNEL; i++, offset += 2) {
|
|
||||||
ibusChannelData[i] = ibus[offset] + (ibus[offset + 1] << 8);
|
|
||||||
}
|
}
|
||||||
frameStatus = RX_FRAME_COMPLETE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return frameStatus;
|
return frameStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static uint16_t ibusReadRawRC(const rxRuntimeConfig_t *rxRuntimeConfig, uint8_t chan)
|
static uint16_t ibusReadRawRC(const rxRuntimeConfig_t *rxRuntimeConfig, uint8_t chan)
|
||||||
{
|
{
|
||||||
UNUSED(rxRuntimeConfig);
|
UNUSED(rxRuntimeConfig);
|
||||||
return ibusChannelData[chan];
|
return ibusChannelData[chan];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool ibusInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
|
bool ibusInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
|
||||||
{
|
{
|
||||||
UNUSED(rxConfig);
|
UNUSED(rxConfig);
|
||||||
|
@ -164,25 +205,29 @@ bool ibusInit(const rxConfig_t *rxConfig, rxRuntimeConfig_t *rxRuntimeConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TELEMETRY
|
#ifdef TELEMETRY
|
||||||
bool portShared = telemetryCheckRxPortShared(portConfig);
|
// bool portShared = telemetryCheckRxPortShared(portConfig);
|
||||||
|
bool portShared = isSerialPortShared(portConfig, FUNCTION_RX_SERIAL, FUNCTION_TELEMETRY_IBUS);
|
||||||
#else
|
#else
|
||||||
bool portShared = false;
|
bool portShared = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
rxBytesToIgnore = 0;
|
||||||
serialPort_t *ibusPort = openSerialPort(portConfig->identifier,
|
serialPort_t *ibusPort = openSerialPort(portConfig->identifier,
|
||||||
FUNCTION_RX_SERIAL,
|
FUNCTION_RX_SERIAL,
|
||||||
ibusDataReceive,
|
ibusDataReceive,
|
||||||
IBUS_BAUDRATE,
|
IBUS_BAUDRATE,
|
||||||
portShared ? MODE_RXTX : MODE_RX,
|
portShared ? MODE_RXTX : MODE_RX,
|
||||||
SERIAL_NOT_INVERTED | (rxConfig->halfDuplex ? SERIAL_BIDIR : 0)
|
SERIAL_NOT_INVERTED | (rxConfig->halfDuplex || portShared ? SERIAL_BIDIR : 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
#ifdef TELEMETRY
|
#if defined(TELEMETRY) && defined(TELEMETRY_IBUS)
|
||||||
if (portShared) {
|
if (portShared) {
|
||||||
telemetrySharedPort = ibusPort;
|
initSharedIbusTelemetry(ibusPort);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ibusPort != NULL;
|
return ibusPort != NULL;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
#endif //SERIAL_RX
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define TELEMETRY_IBUS
|
||||||
|
|
||||||
#define TARGET_CONFIG
|
#define TARGET_CONFIG
|
||||||
#define TARGET_VALIDATECONFIG
|
#define TARGET_VALIDATECONFIG
|
||||||
#define USE_HARDWARE_REVISION_DETECTION
|
#define USE_HARDWARE_REVISION_DETECTION
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#undef TELEMETRY_IBUS //no space left
|
||||||
|
|
||||||
#define TARGET_BOARD_IDENTIFIER "OMNI" // https://en.wikipedia.org/wiki/Omnibus
|
#define TARGET_BOARD_IDENTIFIER "OMNI" // https://en.wikipedia.org/wiki/Omnibus
|
||||||
|
|
||||||
#define CONFIG_FASTLOOP_PREFERRED_ACC ACC_NONE
|
#define CONFIG_FASTLOOP_PREFERRED_ACC ACC_NONE
|
||||||
|
|
|
@ -97,6 +97,7 @@
|
||||||
#define GPS
|
#define GPS
|
||||||
#define CMS
|
#define CMS
|
||||||
#define TELEMETRY_CRSF
|
#define TELEMETRY_CRSF
|
||||||
|
#define TELEMETRY_IBUS
|
||||||
#define TELEMETRY_JETIEXBUS
|
#define TELEMETRY_JETIEXBUS
|
||||||
#define TELEMETRY_MAVLINK
|
#define TELEMETRY_MAVLINK
|
||||||
#define TELEMETRY_SRXL
|
#define TELEMETRY_SRXL
|
||||||
|
|
|
@ -55,114 +55,9 @@
|
||||||
#include "scheduler/scheduler.h"
|
#include "scheduler/scheduler.h"
|
||||||
|
|
||||||
#include "telemetry/ibus.h"
|
#include "telemetry/ibus.h"
|
||||||
|
#include "telemetry/ibus_shared.h"
|
||||||
#include "telemetry/telemetry.h"
|
#include "telemetry/telemetry.h"
|
||||||
|
|
||||||
/*
|
|
||||||
* iBus Telemetry is a half-duplex serial protocol. It shares 1 line for
|
|
||||||
* both TX and RX. It runs at a fixed baud rate of 115200. Queries are sent
|
|
||||||
* every 7ms by the iBus receiver. Multiple sensors can be daisy chained with
|
|
||||||
* ibus but this is implemented but not tested because i don't have one of the
|
|
||||||
* sensors to test with
|
|
||||||
*
|
|
||||||
* _______
|
|
||||||
* / \ /---------\
|
|
||||||
* | STM32 |--UART TX-->[Bi-directional @ 115200 baud]<--| IBUS RX |
|
|
||||||
* | uC |--UART RX--x[not connected] \---------/
|
|
||||||
* \_______/
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The protocol is driven by the iBus receiver, currently either an IA6B or
|
|
||||||
* IA10. All iBus traffic is little endian. It begins with the iBus rx
|
|
||||||
* querying for a sensor on the iBus:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /---------\
|
|
||||||
* | IBUS RX | > Hello sensor at address 1, are you there?
|
|
||||||
* \---------/ [ 0x04, 0x81, 0x7A, 0xFF ]
|
|
||||||
*
|
|
||||||
* 0x04 - Packet Length
|
|
||||||
* 0x81 - bits 7-4 Command (1000 = discover sensor)
|
|
||||||
* bits 3-0 Address (0001 = address 1)
|
|
||||||
* 0x7A, 0xFF - Checksum, 0xFFFF - (0x04 + 0x81)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Due to the daisy-chaining, this hello also serves to inform the sensor
|
|
||||||
* of its address (position in the chain). There are 16 possible addresses
|
|
||||||
* in iBus, however address 0 is reserved for the rx's internally measured
|
|
||||||
* voltage leaving 0x1 to 0xF remaining.
|
|
||||||
*
|
|
||||||
* Having learned it's address, the sensor simply echos the message back:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /--------\
|
|
||||||
* Yes, i'm here, hello! < | Sensor |
|
|
||||||
* [ 0x04, 0x81, 0x7A, 0xFF ] \--------/
|
|
||||||
*
|
|
||||||
* 0x04, 0x81, 0x7A, 0xFF - Echo back received packet
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* On receipt of a response, the iBus rx next requests the sensor's type:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /---------\
|
|
||||||
* | IBUS RX | > Sensor at address 1, what type are you?
|
|
||||||
* \---------/ [ 0x04, 0x91, 0x6A, 0xFF ]
|
|
||||||
*
|
|
||||||
* 0x04 - Packet Length
|
|
||||||
* 0x91 - bits 7-4 Command (1001 = request sensor type)
|
|
||||||
* bits 3-0 Address (0001 = address 1)
|
|
||||||
* 0x6A, 0xFF - Checksum, 0xFFFF - (0x04 + 0x91)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* To which the sensor responds with its details:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /--------\
|
|
||||||
* Yes, i'm here, hello! < | Sensor |
|
|
||||||
* [ 0x06 0x91 0x00 0x02 0x66 0xFF ] \--------/
|
|
||||||
*
|
|
||||||
* 0x06 - Packet Length
|
|
||||||
* 0x91 - bits 7-4 Command (1001 = request sensor type)
|
|
||||||
* bits 3-0 Address (0001 = address 1)
|
|
||||||
* 0x00 - Measurement type (0 = internal voltage)
|
|
||||||
* 0x02 - Unknown, always 0x02
|
|
||||||
* 0x66, 0xFF - Checksum, 0xFFFF - (0x06 + 0x91 + 0x00 + 0x02)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The iBus rx continues the discovery process by querying the next
|
|
||||||
* address. Discovery stops at the first address which does not respond.
|
|
||||||
*
|
|
||||||
* The iBus rx then begins a continual loop, requesting measurements from
|
|
||||||
* each sensor discovered:
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /---------\
|
|
||||||
* | IBUS RX | > Sensor at address 1, please send your measurement
|
|
||||||
* \---------/ [ 0x04, 0xA1, 0x5A, 0xFF ]
|
|
||||||
*
|
|
||||||
* 0x04 - Packet Length
|
|
||||||
* 0xA1 - bits 7-4 Command (1010 = request measurement)
|
|
||||||
* bits 3-0 Address (0001 = address 1)
|
|
||||||
* 0x5A, 0xFF - Checksum, 0xFFFF - (0x04 + 0xA1)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /--------\
|
|
||||||
* I'm reading 0 volts < | Sensor |
|
|
||||||
* [ 0x06 0xA1 0x00 0x00 0x5E 0xFF ] \--------/
|
|
||||||
*
|
|
||||||
* 0x06 - Packet Length
|
|
||||||
* 0xA1 - bits 7-4 Command (1010 = request measurement)
|
|
||||||
* bits 3-0 Address (0001 = address 1)
|
|
||||||
* 0x00, 0x00 - The measurement
|
|
||||||
* 0x58, 0xFF - Checksum, 0xFFFF - (0x06 + 0xA1 + 0x00 + 0x00)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Due to the limited telemetry data types possible with ibus, we
|
|
||||||
* simply send everything which can be represented. Currently this
|
|
||||||
* is voltage and temperature.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#define IBUS_TASK_PERIOD_US (1000)
|
#define IBUS_TASK_PERIOD_US (1000)
|
||||||
|
|
||||||
|
@ -170,37 +65,11 @@
|
||||||
#define IBUS_BAUDRATE (115200)
|
#define IBUS_BAUDRATE (115200)
|
||||||
#define IBUS_CYCLE_TIME_MS (8)
|
#define IBUS_CYCLE_TIME_MS (8)
|
||||||
|
|
||||||
#define IBUS_CHECKSUM_SIZE (2)
|
|
||||||
|
|
||||||
#define IBUS_MIN_LEN (2 + IBUS_CHECKSUM_SIZE)
|
#define IBUS_MIN_LEN (2 + IBUS_CHECKSUM_SIZE)
|
||||||
#define IBUS_MAX_TX_LEN (6)
|
#define IBUS_MAX_TX_LEN (6)
|
||||||
#define IBUS_MAX_RX_LEN (4)
|
#define IBUS_MAX_RX_LEN (4)
|
||||||
#define IBUS_RX_BUF_LEN (IBUS_MAX_RX_LEN)
|
#define IBUS_RX_BUF_LEN (IBUS_MAX_RX_LEN)
|
||||||
|
|
||||||
#define IBUS_TEMPERATURE_OFFSET (400)
|
|
||||||
|
|
||||||
|
|
||||||
typedef uint8_t ibusAddress_t;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IBUS_COMMAND_DISCOVER_SENSOR = 0x80,
|
|
||||||
IBUS_COMMAND_SENSOR_TYPE = 0x90,
|
|
||||||
IBUS_COMMAND_MEASUREMENT = 0xA0
|
|
||||||
} ibusCommand_e;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
IBUS_SENSOR_TYPE_TEMPERATURE = 0x01,
|
|
||||||
IBUS_SENSOR_TYPE_RPM = 0x02,
|
|
||||||
IBUS_SENSOR_TYPE_EXTERNAL_VOLTAGE = 0x03
|
|
||||||
} ibusSensorType_e;
|
|
||||||
|
|
||||||
/* Address lookup relative to the sensor base address which is the lowest address seen by the FC
|
|
||||||
The actual lowest value is likely to change when sensors are daisy chained */
|
|
||||||
static const uint8_t sensorAddressTypeLookup[] = {
|
|
||||||
IBUS_SENSOR_TYPE_EXTERNAL_VOLTAGE,
|
|
||||||
IBUS_SENSOR_TYPE_TEMPERATURE,
|
|
||||||
IBUS_SENSOR_TYPE_RPM
|
|
||||||
};
|
|
||||||
|
|
||||||
static serialPort_t *ibusSerialPort = NULL;
|
static serialPort_t *ibusSerialPort = NULL;
|
||||||
static serialPortConfig_t *ibusSerialPortConfig;
|
static serialPortConfig_t *ibusSerialPortConfig;
|
||||||
|
@ -212,131 +81,9 @@ static uint8_t outboundBytesToIgnoreOnRxCount = 0;
|
||||||
static bool ibusTelemetryEnabled = false;
|
static bool ibusTelemetryEnabled = false;
|
||||||
static portSharing_e ibusPortSharing;
|
static portSharing_e ibusPortSharing;
|
||||||
|
|
||||||
#define INVALID_IBUS_ADDRESS 0
|
|
||||||
static ibusAddress_t ibusBaseAddress = INVALID_IBUS_ADDRESS;
|
|
||||||
|
|
||||||
static uint8_t ibusReceiveBuffer[IBUS_RX_BUF_LEN] = { 0x0 };
|
static uint8_t ibusReceiveBuffer[IBUS_RX_BUF_LEN] = { 0x0 };
|
||||||
|
|
||||||
static uint16_t calculateChecksum(const uint8_t *ibusPacket, size_t packetLength)
|
|
||||||
{
|
|
||||||
uint16_t checksum = 0xFFFF;
|
|
||||||
for (size_t i = 0; i < packetLength - IBUS_CHECKSUM_SIZE; i++) {
|
|
||||||
checksum -= ibusPacket[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isChecksumOk(const uint8_t *ibusPacket)
|
|
||||||
{
|
|
||||||
uint16_t calculatedChecksum = calculateChecksum(ibusReceiveBuffer, IBUS_RX_BUF_LEN);
|
|
||||||
|
|
||||||
// Note that there's a byte order swap to little endian here
|
|
||||||
return (calculatedChecksum >> 8) == ibusPacket[IBUS_RX_BUF_LEN - 1]
|
|
||||||
&& (calculatedChecksum & 0xFF) == ibusPacket[IBUS_RX_BUF_LEN - 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void transmitIbusPacket(uint8_t *ibusPacket, size_t payloadLength)
|
|
||||||
{
|
|
||||||
uint16_t checksum = calculateChecksum(ibusPacket, payloadLength + IBUS_CHECKSUM_SIZE);
|
|
||||||
for (size_t i = 0; i < payloadLength; i++) {
|
|
||||||
serialWrite(ibusSerialPort, ibusPacket[i]);
|
|
||||||
}
|
|
||||||
serialWrite(ibusSerialPort, checksum & 0xFF);
|
|
||||||
serialWrite(ibusSerialPort, checksum >> 8);
|
|
||||||
outboundBytesToIgnoreOnRxCount += payloadLength + IBUS_CHECKSUM_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendIbusDiscoverSensorReply(ibusAddress_t address)
|
|
||||||
{
|
|
||||||
uint8_t sendBuffer[] = { 0x04, IBUS_COMMAND_DISCOVER_SENSOR | address};
|
|
||||||
transmitIbusPacket(sendBuffer, sizeof(sendBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendIbusSensorType(ibusAddress_t address)
|
|
||||||
{
|
|
||||||
uint8_t sendBuffer[] = {0x06,
|
|
||||||
IBUS_COMMAND_SENSOR_TYPE | address,
|
|
||||||
sensorAddressTypeLookup[address - ibusBaseAddress],
|
|
||||||
0x02
|
|
||||||
};
|
|
||||||
transmitIbusPacket(sendBuffer, sizeof(sendBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void sendIbusMeasurement(ibusAddress_t address, uint16_t measurement)
|
|
||||||
{
|
|
||||||
uint8_t sendBuffer[] = { 0x06, IBUS_COMMAND_MEASUREMENT | address, measurement & 0xFF, measurement >> 8};
|
|
||||||
transmitIbusPacket(sendBuffer, sizeof(sendBuffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isCommand(ibusCommand_e expected, const uint8_t *ibusPacket)
|
|
||||||
{
|
|
||||||
return (ibusPacket[1] & 0xF0) == expected;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ibusAddress_t getAddress(const uint8_t *ibusPacket)
|
|
||||||
{
|
|
||||||
return (ibusPacket[1] & 0x0F);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dispatchMeasurementReply(ibusAddress_t address)
|
|
||||||
{
|
|
||||||
int value;
|
|
||||||
|
|
||||||
switch (sensorAddressTypeLookup[address - ibusBaseAddress]) {
|
|
||||||
case IBUS_SENSOR_TYPE_EXTERNAL_VOLTAGE:
|
|
||||||
value = getVbat() * 10;
|
|
||||||
if (telemetryConfig()->report_cell_voltage) {
|
|
||||||
value /= batteryCellCount;
|
|
||||||
}
|
|
||||||
sendIbusMeasurement(address, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IBUS_SENSOR_TYPE_TEMPERATURE:
|
|
||||||
#ifdef BARO
|
|
||||||
value = (baro.baroTemperature + 5) / 10; // +5 to make integer division rounding correct
|
|
||||||
#else
|
|
||||||
value = gyroGetTemperature() * 10;
|
|
||||||
#endif
|
|
||||||
sendIbusMeasurement(address, value + IBUS_TEMPERATURE_OFFSET);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IBUS_SENSOR_TYPE_RPM:
|
|
||||||
sendIbusMeasurement(address, (uint16_t) rcCommand[THROTTLE]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void autodetectFirstReceivedAddressAsBaseAddress(ibusAddress_t returnAddress)
|
|
||||||
{
|
|
||||||
if ((INVALID_IBUS_ADDRESS == ibusBaseAddress) &&
|
|
||||||
(INVALID_IBUS_ADDRESS != returnAddress)) {
|
|
||||||
ibusBaseAddress = returnAddress;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool theAddressIsWithinOurRange(ibusAddress_t returnAddress)
|
|
||||||
{
|
|
||||||
return (returnAddress >= ibusBaseAddress) &&
|
|
||||||
(ibusAddress_t)(returnAddress - ibusBaseAddress) < ARRAYLEN(sensorAddressTypeLookup);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void respondToIbusRequest(uint8_t ibusPacket[static IBUS_RX_BUF_LEN])
|
|
||||||
{
|
|
||||||
ibusAddress_t returnAddress = getAddress(ibusPacket);
|
|
||||||
|
|
||||||
autodetectFirstReceivedAddressAsBaseAddress(returnAddress);
|
|
||||||
|
|
||||||
if (theAddressIsWithinOurRange(returnAddress)) {
|
|
||||||
if (isCommand(IBUS_COMMAND_DISCOVER_SENSOR, ibusPacket)) {
|
|
||||||
sendIbusDiscoverSensorReply(returnAddress);
|
|
||||||
} else if (isCommand(IBUS_COMMAND_SENSOR_TYPE, ibusPacket)) {
|
|
||||||
sendIbusSensorType(returnAddress);
|
|
||||||
} else if (isCommand(IBUS_COMMAND_MEASUREMENT, ibusPacket)) {
|
|
||||||
dispatchMeasurementReply(returnAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pushOntoTail(uint8_t buffer[IBUS_MIN_LEN], size_t bufferLength, uint8_t value)
|
static void pushOntoTail(uint8_t buffer[IBUS_MIN_LEN], size_t bufferLength, uint8_t value)
|
||||||
{
|
{
|
||||||
|
@ -344,13 +91,15 @@ static void pushOntoTail(uint8_t buffer[IBUS_MIN_LEN], size_t bufferLength, uint
|
||||||
ibusReceiveBuffer[bufferLength - 1] = value;
|
ibusReceiveBuffer[bufferLength - 1] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void initIbusTelemetry(void)
|
void initIbusTelemetry(void)
|
||||||
{
|
{
|
||||||
ibusSerialPortConfig = findSerialPortConfig(FUNCTION_TELEMETRY_IBUS);
|
ibusSerialPortConfig = findSerialPortConfig(FUNCTION_TELEMETRY_IBUS);
|
||||||
ibusPortSharing = determinePortSharing(ibusSerialPortConfig, FUNCTION_TELEMETRY_IBUS);
|
ibusPortSharing = determinePortSharing(ibusSerialPortConfig, FUNCTION_TELEMETRY_IBUS);
|
||||||
ibusBaseAddress = INVALID_IBUS_ADDRESS;
|
ibusTelemetryEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void handleIbusTelemetry(void)
|
void handleIbusTelemetry(void)
|
||||||
{
|
{
|
||||||
if (!ibusTelemetryEnabled) {
|
if (!ibusTelemetryEnabled) {
|
||||||
|
@ -367,12 +116,13 @@ void handleIbusTelemetry(void)
|
||||||
|
|
||||||
pushOntoTail(ibusReceiveBuffer, IBUS_RX_BUF_LEN, c);
|
pushOntoTail(ibusReceiveBuffer, IBUS_RX_BUF_LEN, c);
|
||||||
|
|
||||||
if (isChecksumOk(ibusReceiveBuffer)) {
|
if (isChecksumOkIa6b(ibusReceiveBuffer, IBUS_RX_BUF_LEN)) {
|
||||||
respondToIbusRequest(ibusReceiveBuffer);
|
outboundBytesToIgnoreOnRxCount += respondToIbusRequest(ibusReceiveBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool checkIbusTelemetryState(void)
|
bool checkIbusTelemetryState(void)
|
||||||
{
|
{
|
||||||
bool newTelemetryEnabledValue = telemetryDetermineEnabledState(ibusPortSharing);
|
bool newTelemetryEnabledValue = telemetryDetermineEnabledState(ibusPortSharing);
|
||||||
|
@ -391,32 +141,34 @@ bool checkIbusTelemetryState(void)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void configureIbusTelemetryPort(void)
|
void configureIbusTelemetryPort(void)
|
||||||
{
|
{
|
||||||
portOptions_t portOptions;
|
|
||||||
|
|
||||||
if (!ibusSerialPortConfig) {
|
if (!ibusSerialPortConfig) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
portOptions = SERIAL_BIDIR;
|
if (isSerialPortShared(ibusSerialPortConfig, FUNCTION_RX_SERIAL, FUNCTION_TELEMETRY_IBUS)) {
|
||||||
|
// serialRx will open port and handle telemetry
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ibusSerialPort = openSerialPort(ibusSerialPortConfig->identifier, FUNCTION_TELEMETRY_IBUS, NULL, IBUS_BAUDRATE,
|
ibusSerialPort = openSerialPort(ibusSerialPortConfig->identifier, FUNCTION_TELEMETRY_IBUS, NULL, IBUS_BAUDRATE, IBUS_UART_MODE, SERIAL_BIDIR);
|
||||||
IBUS_UART_MODE, portOptions);
|
|
||||||
|
|
||||||
if (!ibusSerialPort) {
|
if (!ibusSerialPort) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initSharedIbusTelemetry(ibusSerialPort);
|
||||||
ibusTelemetryEnabled = true;
|
ibusTelemetryEnabled = true;
|
||||||
outboundBytesToIgnoreOnRxCount = 0;
|
outboundBytesToIgnoreOnRxCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void freeIbusTelemetryPort(void)
|
void freeIbusTelemetryPort(void)
|
||||||
{
|
{
|
||||||
closeSerialPort(ibusSerialPort);
|
closeSerialPort(ibusSerialPort);
|
||||||
ibusSerialPort = NULL;
|
ibusSerialPort = NULL;
|
||||||
|
|
||||||
ibusTelemetryEnabled = false;
|
ibusTelemetryEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
204
src/main/telemetry/ibus_shared.c
Normal file
204
src/main/telemetry/ibus_shared.c
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Cleanflight.
|
||||||
|
*
|
||||||
|
* Cleanflight is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Cleanflight 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* FlySky iBus telemetry implementation by CraigJPerry.
|
||||||
|
* Unit tests and some additions by Unitware
|
||||||
|
*
|
||||||
|
* Many thanks to Dave Borthwick's iBus telemetry dongle converter for
|
||||||
|
* PIC 12F1572 (also distributed under GPLv3) which was referenced to
|
||||||
|
* clarify the protocol.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
// #include <string.h>
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
//#include "common/utils.h"
|
||||||
|
#include "telemetry/telemetry.h"
|
||||||
|
#include "telemetry/ibus_shared.h"
|
||||||
|
|
||||||
|
static uint16_t calculateChecksum(const uint8_t *ibusPacket, size_t packetLength);
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(TELEMETRY) && defined(TELEMETRY_IBUS)
|
||||||
|
|
||||||
|
#include "config/parameter_group.h"
|
||||||
|
#include "config/parameter_group_ids.h"
|
||||||
|
#include "sensors/battery.h"
|
||||||
|
#include "fc/rc_controls.h"
|
||||||
|
#include "sensors/gyro.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define IBUS_TEMPERATURE_OFFSET (400)
|
||||||
|
|
||||||
|
typedef uint8_t ibusAddress_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
IBUS_COMMAND_DISCOVER_SENSOR = 0x80,
|
||||||
|
IBUS_COMMAND_SENSOR_TYPE = 0x90,
|
||||||
|
IBUS_COMMAND_MEASUREMENT = 0xA0
|
||||||
|
} ibusCommand_e;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
IBUS_SENSOR_TYPE_TEMPERATURE = 0x01,
|
||||||
|
IBUS_SENSOR_TYPE_RPM = 0x02,
|
||||||
|
IBUS_SENSOR_TYPE_EXTERNAL_VOLTAGE = 0x03
|
||||||
|
} ibusSensorType_e;
|
||||||
|
|
||||||
|
/* Address lookup relative to the sensor base address which is the lowest address seen by the FC
|
||||||
|
The actual lowest value is likely to change when sensors are daisy chained */
|
||||||
|
static const uint8_t sensorAddressTypeLookup[] = {
|
||||||
|
IBUS_SENSOR_TYPE_EXTERNAL_VOLTAGE,
|
||||||
|
IBUS_SENSOR_TYPE_TEMPERATURE,
|
||||||
|
IBUS_SENSOR_TYPE_RPM
|
||||||
|
};
|
||||||
|
|
||||||
|
static serialPort_t *ibusSerialPort = NULL;
|
||||||
|
|
||||||
|
#define INVALID_IBUS_ADDRESS 0
|
||||||
|
static ibusAddress_t ibusBaseAddress = INVALID_IBUS_ADDRESS;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static uint8_t transmitIbusPacket(uint8_t *ibusPacket, size_t payloadLength)
|
||||||
|
{
|
||||||
|
uint16_t checksum = calculateChecksum(ibusPacket, payloadLength + IBUS_CHECKSUM_SIZE);
|
||||||
|
for (size_t i = 0; i < payloadLength; i++) {
|
||||||
|
serialWrite(ibusSerialPort, ibusPacket[i]);
|
||||||
|
}
|
||||||
|
serialWrite(ibusSerialPort, checksum & 0xFF);
|
||||||
|
serialWrite(ibusSerialPort, checksum >> 8);
|
||||||
|
return payloadLength + IBUS_CHECKSUM_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t sendIbusDiscoverSensorReply(ibusAddress_t address)
|
||||||
|
{
|
||||||
|
uint8_t sendBuffer[] = { 0x04, IBUS_COMMAND_DISCOVER_SENSOR | address};
|
||||||
|
return transmitIbusPacket(sendBuffer, sizeof(sendBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t sendIbusSensorType(ibusAddress_t address)
|
||||||
|
{
|
||||||
|
uint8_t sendBuffer[] = {0x06,
|
||||||
|
IBUS_COMMAND_SENSOR_TYPE | address,
|
||||||
|
sensorAddressTypeLookup[address - ibusBaseAddress],
|
||||||
|
0x02
|
||||||
|
};
|
||||||
|
return transmitIbusPacket(sendBuffer, sizeof(sendBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t sendIbusMeasurement(ibusAddress_t address, uint16_t measurement)
|
||||||
|
{
|
||||||
|
uint8_t sendBuffer[] = { 0x06, IBUS_COMMAND_MEASUREMENT | address, measurement & 0xFF, measurement >> 8};
|
||||||
|
return transmitIbusPacket(sendBuffer, sizeof(sendBuffer));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isCommand(ibusCommand_e expected, const uint8_t *ibusPacket)
|
||||||
|
{
|
||||||
|
return (ibusPacket[1] & 0xF0) == expected;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ibusAddress_t getAddress(const uint8_t *ibusPacket)
|
||||||
|
{
|
||||||
|
return (ibusPacket[1] & 0x0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t dispatchMeasurementReply(ibusAddress_t address)
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
|
||||||
|
switch (sensorAddressTypeLookup[address - ibusBaseAddress]) {
|
||||||
|
case IBUS_SENSOR_TYPE_EXTERNAL_VOLTAGE:
|
||||||
|
value = getVbat() * 10;
|
||||||
|
if (telemetryConfig()->report_cell_voltage) {
|
||||||
|
value /= batteryCellCount;
|
||||||
|
}
|
||||||
|
return sendIbusMeasurement(address, value);
|
||||||
|
|
||||||
|
case IBUS_SENSOR_TYPE_TEMPERATURE:
|
||||||
|
value = gyroGetTemperature() * 10;
|
||||||
|
return sendIbusMeasurement(address, value + IBUS_TEMPERATURE_OFFSET);
|
||||||
|
|
||||||
|
case IBUS_SENSOR_TYPE_RPM:
|
||||||
|
return sendIbusMeasurement(address, (uint16_t) rcCommand[THROTTLE]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void autodetectFirstReceivedAddressAsBaseAddress(ibusAddress_t returnAddress)
|
||||||
|
{
|
||||||
|
if ((INVALID_IBUS_ADDRESS == ibusBaseAddress) &&
|
||||||
|
(INVALID_IBUS_ADDRESS != returnAddress)) {
|
||||||
|
ibusBaseAddress = returnAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool theAddressIsWithinOurRange(ibusAddress_t returnAddress)
|
||||||
|
{
|
||||||
|
return (returnAddress >= ibusBaseAddress) &&
|
||||||
|
(ibusAddress_t)(returnAddress - ibusBaseAddress) < ARRAYLEN(sensorAddressTypeLookup);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t respondToIbusRequest(uint8_t const * const ibusPacket)
|
||||||
|
{
|
||||||
|
ibusAddress_t returnAddress = getAddress(ibusPacket);
|
||||||
|
autodetectFirstReceivedAddressAsBaseAddress(returnAddress);
|
||||||
|
|
||||||
|
if (theAddressIsWithinOurRange(returnAddress)) {
|
||||||
|
if (isCommand(IBUS_COMMAND_DISCOVER_SENSOR, ibusPacket)) {
|
||||||
|
return sendIbusDiscoverSensorReply(returnAddress);
|
||||||
|
} else if (isCommand(IBUS_COMMAND_SENSOR_TYPE, ibusPacket)) {
|
||||||
|
return sendIbusSensorType(returnAddress);
|
||||||
|
} else if (isCommand(IBUS_COMMAND_MEASUREMENT, ibusPacket)) {
|
||||||
|
return dispatchMeasurementReply(returnAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void initSharedIbusTelemetry(serialPort_t *port)
|
||||||
|
{
|
||||||
|
ibusSerialPort = port;
|
||||||
|
ibusBaseAddress = INVALID_IBUS_ADDRESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif //defined(TELEMETRY) && defined(TELEMETRY_IBUS)
|
||||||
|
|
||||||
|
static uint16_t calculateChecksum(const uint8_t *ibusPacket, size_t packetLength)
|
||||||
|
{
|
||||||
|
uint16_t checksum = 0xFFFF;
|
||||||
|
for (size_t i = 0; i < packetLength - IBUS_CHECKSUM_SIZE; i++) {
|
||||||
|
checksum -= ibusPacket[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isChecksumOkIa6b(const uint8_t *ibusPacket, const uint8_t length)
|
||||||
|
{
|
||||||
|
uint16_t calculatedChecksum = calculateChecksum(ibusPacket, length);
|
||||||
|
|
||||||
|
// Note that there's a byte order swap to little endian here
|
||||||
|
return (calculatedChecksum >> 8) == ibusPacket[length - 1]
|
||||||
|
&& (calculatedChecksum & 0xFF) == ibusPacket[length - 2];
|
||||||
|
}
|
||||||
|
|
44
src/main/telemetry/ibus_shared.h
Normal file
44
src/main/telemetry/ibus_shared.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Cleanflight.
|
||||||
|
*
|
||||||
|
* Cleanflight is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Cleanflight 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.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The ibus_shared module implements the ibus telemetry packet handling
|
||||||
|
* which is shared between the ibus serial rx and the ibus telemetry.
|
||||||
|
*
|
||||||
|
* There is only one 'user' active at any time, serial rx will open the
|
||||||
|
* serial port if both functions are enabled at the same time
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "platform.h"
|
||||||
|
#include "drivers/serial.h"
|
||||||
|
|
||||||
|
#define IBUS_CHECKSUM_SIZE (2)
|
||||||
|
|
||||||
|
#if defined(TELEMETRY) && defined(TELEMETRY_IBUS)
|
||||||
|
|
||||||
|
uint8_t respondToIbusRequest(uint8_t const * const ibusPacket);
|
||||||
|
void initSharedIbusTelemetry(serialPort_t * port);
|
||||||
|
|
||||||
|
#endif //defined(TELEMETRY) && defined(TELEMETRY_IBUS)
|
||||||
|
|
||||||
|
|
||||||
|
bool isChecksumOkIa6b(const uint8_t *ibusPacket, const uint8_t length);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -126,7 +126,18 @@ bool telemetryDetermineEnabledState(portSharing_e portSharing)
|
||||||
|
|
||||||
bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
|
bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
|
||||||
{
|
{
|
||||||
return portConfig->functionMask & FUNCTION_RX_SERIAL && portConfig->functionMask & TELEMETRY_SHAREABLE_PORT_FUNCTIONS_MASK;
|
if (portConfig->functionMask & FUNCTION_RX_SERIAL && portConfig->functionMask & TELEMETRY_SHAREABLE_PORT_FUNCTIONS_MASK) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#ifdef TELEMETRY_IBUS
|
||||||
|
if ( portConfig->functionMask & FUNCTION_TELEMETRY_IBUS
|
||||||
|
&& portConfig->functionMask & FUNCTION_RX_SERIAL
|
||||||
|
&& rxConfig()->serialrx_provider == SERIALRX_IBUS) {
|
||||||
|
// IBUS serial RX & telemetry
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
serialPort_t *telemetrySharedPort = NULL;
|
serialPort_t *telemetrySharedPort = NULL;
|
||||||
|
|
|
@ -851,6 +851,14 @@ $(OBJECT_DIR)/telemetry/ibus.o : \
|
||||||
@mkdir -p $(dir $@)
|
@mkdir -p $(dir $@)
|
||||||
$(CC) $(C_FLAGS) $(TEST_CFLAGS) -c $(USER_DIR)/telemetry/ibus.c -o $@
|
$(CC) $(C_FLAGS) $(TEST_CFLAGS) -c $(USER_DIR)/telemetry/ibus.c -o $@
|
||||||
|
|
||||||
|
$(OBJECT_DIR)/telemetry/ibus_shared.o : \
|
||||||
|
$(USER_DIR)/telemetry/ibus_shared.c \
|
||||||
|
$(USER_DIR)/telemetry/ibus_shared.h \
|
||||||
|
$(GTEST_HEADERS)
|
||||||
|
|
||||||
|
@mkdir -p $(dir $@)
|
||||||
|
$(CC) $(C_FLAGS) $(TEST_CFLAGS) -c $(USER_DIR)/telemetry/ibus_shared.c -o $@
|
||||||
|
|
||||||
$(OBJECT_DIR)/telemetry_ibus_unittest.o : \
|
$(OBJECT_DIR)/telemetry_ibus_unittest.o : \
|
||||||
$(TEST_DIR)/telemetry_ibus_unittest.cc \
|
$(TEST_DIR)/telemetry_ibus_unittest.cc \
|
||||||
$(GTEST_HEADERS)
|
$(GTEST_HEADERS)
|
||||||
|
@ -860,6 +868,7 @@ $(OBJECT_DIR)/telemetry_ibus_unittest.o : \
|
||||||
|
|
||||||
$(OBJECT_DIR)/telemetry_ibus_unittest : \
|
$(OBJECT_DIR)/telemetry_ibus_unittest : \
|
||||||
$(OBJECT_DIR)/telemetry_ibus_unittest.o \
|
$(OBJECT_DIR)/telemetry_ibus_unittest.o \
|
||||||
|
$(OBJECT_DIR)/telemetry/ibus_shared.o \
|
||||||
$(OBJECT_DIR)/telemetry/ibus.o \
|
$(OBJECT_DIR)/telemetry/ibus.o \
|
||||||
$(OBJECT_DIR)/gtest_main.a
|
$(OBJECT_DIR)/gtest_main.a
|
||||||
|
|
||||||
|
|
|
@ -25,13 +25,25 @@ extern "C" {
|
||||||
#include "io/serial.h"
|
#include "io/serial.h"
|
||||||
#include "rx/rx.h"
|
#include "rx/rx.h"
|
||||||
#include "rx/ibus.h"
|
#include "rx/ibus.h"
|
||||||
|
#include "telemetry/ibus_shared.h"
|
||||||
#include "telemetry/telemetry.h"
|
#include "telemetry/telemetry.h"
|
||||||
|
#include "fc/rc_controls.h"
|
||||||
|
#include "sensors/barometer.h"
|
||||||
|
#include "sensors/battery.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "unittest_macros.h"
|
#include "unittest_macros.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
uint8_t batteryCellCount = 3;
|
||||||
|
int16_t rcCommand[4] = {0, 0, 0, 0};
|
||||||
|
int16_t telemTemperature1 = 0;
|
||||||
|
baro_t baro = { .baroTemperature = 50 };
|
||||||
|
telemetryConfig_t telemetryConfig_System;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
|
bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
|
||||||
{
|
{
|
||||||
|
@ -42,6 +54,11 @@ bool telemetryCheckRxPortShared(const serialPortConfig_t *portConfig)
|
||||||
|
|
||||||
serialPort_t * telemetrySharedPort = NULL;
|
serialPort_t * telemetrySharedPort = NULL;
|
||||||
|
|
||||||
|
static uint16_t vbat = 100;
|
||||||
|
uint16_t getVbat(void)
|
||||||
|
{
|
||||||
|
return vbat;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t microseconds_stub_value = 0;
|
uint32_t microseconds_stub_value = 0;
|
||||||
uint32_t micros(void)
|
uint32_t micros(void)
|
||||||
|
@ -49,8 +66,15 @@ uint32_t micros(void)
|
||||||
return microseconds_stub_value;
|
return microseconds_stub_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SERIAL_BUFFER_SIZE 256
|
||||||
#define SERIAL_PORT_DUMMY_IDENTIFIER (serialPortIdentifier_e)0x1234
|
#define SERIAL_PORT_DUMMY_IDENTIFIER (serialPortIdentifier_e)0x1234
|
||||||
|
|
||||||
|
typedef struct serialPortStub_s {
|
||||||
|
uint8_t buffer[SERIAL_BUFFER_SIZE];
|
||||||
|
int pos = 0;
|
||||||
|
int end = 0;
|
||||||
|
} serialPortStub_t;
|
||||||
|
|
||||||
static serialPort_t serialTestInstance;
|
static serialPort_t serialTestInstance;
|
||||||
static serialPortConfig_t serialTestInstanceConfig = {
|
static serialPortConfig_t serialTestInstanceConfig = {
|
||||||
.identifier = SERIAL_PORT_DUMMY_IDENTIFIER,
|
.identifier = SERIAL_PORT_DUMMY_IDENTIFIER,
|
||||||
|
@ -60,7 +84,18 @@ static serialPortConfig_t serialTestInstanceConfig = {
|
||||||
static serialReceiveCallbackPtr stub_serialRxCallback;
|
static serialReceiveCallbackPtr stub_serialRxCallback;
|
||||||
static serialPortConfig_t *findSerialPortConfig_stub_retval;
|
static serialPortConfig_t *findSerialPortConfig_stub_retval;
|
||||||
static bool openSerial_called = false;
|
static bool openSerial_called = false;
|
||||||
|
static serialPortStub_t serialWriteStub;
|
||||||
|
static bool portIsShared = false;
|
||||||
|
|
||||||
|
bool isSerialPortShared(const serialPortConfig_t *portConfig,
|
||||||
|
uint16_t functionMask,
|
||||||
|
serialPortFunction_e sharedWithFunction)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(portConfig, findSerialPortConfig_stub_retval);
|
||||||
|
EXPECT_EQ(functionMask, FUNCTION_RX_SERIAL);
|
||||||
|
EXPECT_EQ(sharedWithFunction, FUNCTION_TELEMETRY_IBUS);
|
||||||
|
return portIsShared;
|
||||||
|
}
|
||||||
|
|
||||||
serialPortConfig_t *findSerialPortConfig(serialPortFunction_e function)
|
serialPortConfig_t *findSerialPortConfig(serialPortFunction_e function)
|
||||||
{
|
{
|
||||||
|
@ -68,6 +103,8 @@ serialPortConfig_t *findSerialPortConfig(serialPortFunction_e function)
|
||||||
return findSerialPortConfig_stub_retval;
|
return findSerialPortConfig_stub_retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static portMode_t serialExpectedMode = MODE_RX;
|
||||||
|
static portOptions_t serialExpectedOptions = SERIAL_UNIDIR;
|
||||||
|
|
||||||
serialPort_t *openSerialPort(
|
serialPort_t *openSerialPort(
|
||||||
serialPortIdentifier_e identifier,
|
serialPortIdentifier_e identifier,
|
||||||
|
@ -81,22 +118,71 @@ serialPort_t *openSerialPort(
|
||||||
openSerial_called = true;
|
openSerial_called = true;
|
||||||
EXPECT_FALSE(NULL == callback);
|
EXPECT_FALSE(NULL == callback);
|
||||||
EXPECT_EQ(identifier, SERIAL_PORT_DUMMY_IDENTIFIER);
|
EXPECT_EQ(identifier, SERIAL_PORT_DUMMY_IDENTIFIER);
|
||||||
EXPECT_EQ(options, SERIAL_UNIDIR);
|
EXPECT_EQ(options, serialExpectedOptions);
|
||||||
EXPECT_EQ(function, FUNCTION_RX_SERIAL);
|
EXPECT_EQ(function, FUNCTION_RX_SERIAL);
|
||||||
EXPECT_EQ(baudrate, 115200);
|
EXPECT_EQ(baudrate, 115200);
|
||||||
EXPECT_EQ(mode, MODE_RX);
|
EXPECT_EQ(mode, serialExpectedMode);
|
||||||
stub_serialRxCallback = callback;
|
stub_serialRxCallback = callback;
|
||||||
return &serialTestInstance;
|
return &serialTestInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serialWrite(serialPort_t *instance, uint8_t ch)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(instance, &serialTestInstance);
|
||||||
|
EXPECT_LT(serialWriteStub.pos, sizeof(serialWriteStub.buffer));
|
||||||
|
serialWriteStub.buffer[serialWriteStub.pos++] = ch;
|
||||||
|
//TODO serialReadStub.buffer[serialReadStub.end++] = ch; //characters echoes back on the shared wire
|
||||||
|
//printf("w: %02d 0x%02x\n", serialWriteStub.pos, ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void serialTestResetPort()
|
void serialTestResetPort()
|
||||||
{
|
{
|
||||||
openSerial_called = false;
|
openSerial_called = false;
|
||||||
stub_serialRxCallback = NULL;
|
stub_serialRxCallback = NULL;
|
||||||
|
portIsShared = false;
|
||||||
|
serialExpectedMode = MODE_RX;
|
||||||
|
serialExpectedOptions = SERIAL_UNIDIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool isChecksumOkReturnValue = true;
|
||||||
|
bool isChecksumOkIa6b(const uint8_t *ibusPacket, const uint8_t length)
|
||||||
|
{
|
||||||
|
(void) ibusPacket;
|
||||||
|
(void) length;
|
||||||
|
return isChecksumOkReturnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool initSharedIbusTelemetryCalled = false;
|
||||||
|
void initSharedIbusTelemetry(serialPort_t * port)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(port, &serialTestInstance);
|
||||||
|
initSharedIbusTelemetryCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool stubTelemetryCalled = false;
|
||||||
|
static uint8_t stubTelemetryPacket[100];
|
||||||
|
static uint8_t stubTelemetryIgnoreRxChars = 0;
|
||||||
|
|
||||||
|
uint8_t respondToIbusRequest(uint8_t const * const ibusPacket) {
|
||||||
|
uint8_t len = ibusPacket[0];
|
||||||
|
EXPECT_LT(len, sizeof(stubTelemetryPacket));
|
||||||
|
memcpy(stubTelemetryPacket, ibusPacket, len);
|
||||||
|
stubTelemetryCalled = true;
|
||||||
|
return stubTelemetryIgnoreRxChars;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resetStubTelemetry(void)
|
||||||
|
{
|
||||||
|
memset(stubTelemetryPacket, 0, sizeof(stubTelemetryPacket));
|
||||||
|
stubTelemetryCalled = false;
|
||||||
|
stubTelemetryIgnoreRxChars = 0;
|
||||||
|
initSharedIbusTelemetryCalled = false;
|
||||||
|
isChecksumOkReturnValue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class IbusRxInitUnitTest : public ::testing::Test
|
class IbusRxInitUnitTest : public ::testing::Test
|
||||||
{
|
{
|
||||||
|
@ -154,23 +240,36 @@ protected:
|
||||||
virtual void SetUp()
|
virtual void SetUp()
|
||||||
{
|
{
|
||||||
serialTestResetPort();
|
serialTestResetPort();
|
||||||
|
resetStubTelemetry();
|
||||||
|
portIsShared = true;
|
||||||
|
serialExpectedOptions = SERIAL_BIDIR;
|
||||||
|
serialExpectedMode = MODE_RXTX;
|
||||||
|
|
||||||
const rxConfig_t initialRxConfig = {};
|
const rxConfig_t initialRxConfig = {};
|
||||||
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
|
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
|
||||||
|
|
||||||
EXPECT_TRUE(ibusInit(&initialRxConfig, &rxRuntimeConfig));
|
EXPECT_TRUE(ibusInit(&initialRxConfig, &rxRuntimeConfig));
|
||||||
|
|
||||||
|
EXPECT_TRUE(initSharedIbusTelemetryCalled);
|
||||||
|
|
||||||
//handle that internal ibus position is not set to zero at init
|
//handle that internal ibus position is not set to zero at init
|
||||||
microseconds_stub_value += 5000;
|
microseconds_stub_value += 5000;
|
||||||
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void receivePacket(uint8_t const * const packet, const size_t length)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
for (size_t i=0; i < length; i++) {
|
||||||
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
stub_serialRxCallback(packet[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
TEST_F(IbusRxProtocollUnitTest, Test_InitialFrameState)
|
TEST_F(IbusRxProtocollUnitTest, Test_InitialFrameState)
|
||||||
{
|
{
|
||||||
|
|
||||||
//TODO: ibusFrameStatus should return rxFrameState_t not uint8_t
|
|
||||||
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
|
||||||
//TODO: is it ok to have undefined channel values after init?
|
//TODO: is it ok to have undefined channel values after init?
|
||||||
|
@ -209,6 +308,7 @@ TEST_F(IbusRxProtocollUnitTest, Test_IA6B_OnePacketReceivedWithBadCrc)
|
||||||
0x0a, 0x33, 0x0b, 0x33, 0x0c, 0x33, 0x0d, 0x33, //channel 11..14
|
0x0a, 0x33, 0x0b, 0x33, 0x0c, 0x33, 0x0d, 0x33, //channel 11..14
|
||||||
0x00, 0x00}; //checksum
|
0x00, 0x00}; //checksum
|
||||||
|
|
||||||
|
isChecksumOkReturnValue = false;
|
||||||
for (size_t i=0; i < sizeof(packet); i++) {
|
for (size_t i=0; i < sizeof(packet); i++) {
|
||||||
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
stub_serialRxCallback(packet[i]);
|
stub_serialRxCallback(packet[i]);
|
||||||
|
@ -304,3 +404,110 @@ TEST_F(IbusRxProtocollUnitTest, Test_IA6_OnePacketReceivedBadCrc)
|
||||||
ASSERT_NE(i + (0x33 << 8), rxRuntimeConfig.rcReadRawFn(&rxRuntimeConfig, i));
|
ASSERT_NE(i + (0x33 << 8), rxRuntimeConfig.rcReadRawFn(&rxRuntimeConfig, i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(IbusRxProtocollUnitTest, Test_IA6B_OnePacketReceived_not_shared_port)
|
||||||
|
{
|
||||||
|
uint8_t packet[] = {0x20, 0x00, //length and reserved (unknown) bits
|
||||||
|
0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, //channel 1..5
|
||||||
|
0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x08, 0x00, 0x09, 0x00, //channel 6..10
|
||||||
|
0x0a, 0x00, 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, //channel 11..14
|
||||||
|
0x84, 0xff}; //checksum
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
serialTestResetPort();
|
||||||
|
resetStubTelemetry();
|
||||||
|
portIsShared = false;
|
||||||
|
serialExpectedOptions = SERIAL_NOT_INVERTED;
|
||||||
|
serialExpectedMode = MODE_RX;
|
||||||
|
|
||||||
|
const rxConfig_t initialRxConfig = {};
|
||||||
|
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
|
||||||
|
|
||||||
|
EXPECT_TRUE(ibusInit(&initialRxConfig, &rxRuntimeConfig));
|
||||||
|
EXPECT_FALSE(initSharedIbusTelemetryCalled);
|
||||||
|
|
||||||
|
//handle that internal ibus position is not set to zero at init
|
||||||
|
microseconds_stub_value += 5000;
|
||||||
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i=0; i < sizeof(packet); i++) {
|
||||||
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
stub_serialRxCallback(packet[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//report frame complete once
|
||||||
|
EXPECT_EQ(RX_FRAME_COMPLETE, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
|
||||||
|
//check that channel values have been updated
|
||||||
|
for (int i=0; i<14; i++) {
|
||||||
|
ASSERT_EQ(i, rxRuntimeConfig.rcReadRawFn(&rxRuntimeConfig, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(IbusRxProtocollUnitTest, Test_OneTelemetryPacketReceived)
|
||||||
|
{
|
||||||
|
uint8_t packet[] = {0x04, 0x81, 0x7a, 0xff}; //ibus sensor discovery
|
||||||
|
resetStubTelemetry();
|
||||||
|
|
||||||
|
receivePacket(packet, sizeof(packet));
|
||||||
|
|
||||||
|
//no frame complete signal to rx system, but telemetry system is called
|
||||||
|
EXPECT_EQ(RX_FRAME_PENDING, rxRuntimeConfig.rcFrameStatusFn());
|
||||||
|
EXPECT_TRUE(stubTelemetryCalled);
|
||||||
|
EXPECT_TRUE( 0 == memcmp( stubTelemetryPacket, packet, sizeof(packet)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(IbusRxProtocollUnitTest, Test_OneTelemetryIgnoreTxEchoToRx)
|
||||||
|
{
|
||||||
|
uint8_t packet[] = {0x04, 0x81, 0x7a, 0xff}; //ibus sensor discovery
|
||||||
|
resetStubTelemetry();
|
||||||
|
stubTelemetryIgnoreRxChars = 4;
|
||||||
|
|
||||||
|
//given one packet received, that will respond with four characters to be ignored
|
||||||
|
receivePacket(packet, sizeof(packet));
|
||||||
|
rxRuntimeConfig.rcFrameStatusFn();
|
||||||
|
EXPECT_TRUE(stubTelemetryCalled);
|
||||||
|
|
||||||
|
//when those four bytes are sent and looped back
|
||||||
|
resetStubTelemetry();
|
||||||
|
rxRuntimeConfig.rcFrameStatusFn();
|
||||||
|
receivePacket(packet, sizeof(packet));
|
||||||
|
|
||||||
|
//then they are ignored
|
||||||
|
EXPECT_FALSE(stubTelemetryCalled);
|
||||||
|
|
||||||
|
//and then next packet can be received
|
||||||
|
receivePacket(packet, sizeof(packet));
|
||||||
|
rxRuntimeConfig.rcFrameStatusFn();
|
||||||
|
EXPECT_TRUE(stubTelemetryCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(IbusRxProtocollUnitTest, Test_OneTelemetryShouldNotIgnoreTxEchoAfterInterFrameGap)
|
||||||
|
{
|
||||||
|
uint8_t packet[] = {0x04, 0x81, 0x7a, 0xff}; //ibus sensor discovery
|
||||||
|
resetStubTelemetry();
|
||||||
|
stubTelemetryIgnoreRxChars = 4;
|
||||||
|
|
||||||
|
//given one packet received, that will respond with four characters to be ignored
|
||||||
|
receivePacket(packet, sizeof(packet));
|
||||||
|
rxRuntimeConfig.rcFrameStatusFn();
|
||||||
|
EXPECT_TRUE(stubTelemetryCalled);
|
||||||
|
|
||||||
|
//when there is an interPacketGap
|
||||||
|
microseconds_stub_value += 5000;
|
||||||
|
resetStubTelemetry();
|
||||||
|
rxRuntimeConfig.rcFrameStatusFn();
|
||||||
|
|
||||||
|
//then next packet can be received
|
||||||
|
receivePacket(packet, sizeof(packet));
|
||||||
|
rxRuntimeConfig.rcFrameStatusFn();
|
||||||
|
EXPECT_TRUE(stubTelemetryCalled);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ extern "C" {
|
||||||
#include "fc/rc_controls.h"
|
#include "fc/rc_controls.h"
|
||||||
#include "telemetry/telemetry.h"
|
#include "telemetry/telemetry.h"
|
||||||
#include "telemetry/ibus.h"
|
#include "telemetry/ibus.h"
|
||||||
#include "sensors/barometer.h"
|
#include "sensors/gyro.h"
|
||||||
#include "sensors/battery.h"
|
#include "sensors/battery.h"
|
||||||
#include "scheduler/scheduler.h"
|
#include "scheduler/scheduler.h"
|
||||||
#include "fc/fc_tasks.h"
|
#include "fc/fc_tasks.h"
|
||||||
|
@ -39,11 +39,19 @@ extern "C" {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
uint8_t batteryCellCount = 3;
|
uint8_t batteryCellCount = 3;
|
||||||
int16_t rcCommand[4] = {0, 0, 0, 0};
|
int16_t rcCommand[4] = {0, 0, 0, 0};
|
||||||
int16_t telemTemperature1 = 0;
|
|
||||||
baro_t baro = { .baroTemperature = 50 };
|
|
||||||
telemetryConfig_t telemetryConfig_System;
|
telemetryConfig_t telemetryConfig_System;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int16_t gyroTemperature;
|
||||||
|
int16_t gyroGetTemperature(void) {
|
||||||
|
return gyroTemperature;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t vbat = 100;
|
||||||
|
uint16_t getVbat(void)
|
||||||
|
{
|
||||||
|
return vbat;
|
||||||
|
}
|
||||||
|
|
||||||
#define SERIAL_BUFFER_SIZE 256
|
#define SERIAL_BUFFER_SIZE 256
|
||||||
|
|
||||||
|
@ -54,12 +62,6 @@ typedef struct serialPortStub_s {
|
||||||
} serialPortStub_t;
|
} serialPortStub_t;
|
||||||
|
|
||||||
|
|
||||||
static uint16_t vbat = 100;
|
|
||||||
uint16_t getVbat(void)
|
|
||||||
{
|
|
||||||
return vbat;
|
|
||||||
}
|
|
||||||
|
|
||||||
static serialPortStub_t serialWriteStub;
|
static serialPortStub_t serialWriteStub;
|
||||||
static serialPortStub_t serialReadStub;
|
static serialPortStub_t serialReadStub;
|
||||||
|
|
||||||
|
@ -72,6 +74,7 @@ serialPortConfig_t serialTestInstanceConfig = {
|
||||||
|
|
||||||
static serialPortConfig_t *findSerialPortConfig_stub_retval;
|
static serialPortConfig_t *findSerialPortConfig_stub_retval;
|
||||||
static portSharing_e determinePortSharing_stub_retval;
|
static portSharing_e determinePortSharing_stub_retval;
|
||||||
|
static bool portIsShared = false;
|
||||||
static bool openSerial_called = false;
|
static bool openSerial_called = false;
|
||||||
static bool telemetryDetermineEnabledState_stub_retval;
|
static bool telemetryDetermineEnabledState_stub_retval;
|
||||||
|
|
||||||
|
@ -105,6 +108,17 @@ bool telemetryDetermineEnabledState(portSharing_e portSharing)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool isSerialPortShared(const serialPortConfig_t *portConfig,
|
||||||
|
uint16_t functionMask,
|
||||||
|
serialPortFunction_e sharedWithFunction)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(portConfig, findSerialPortConfig_stub_retval);
|
||||||
|
EXPECT_EQ(functionMask, FUNCTION_RX_SERIAL);
|
||||||
|
EXPECT_EQ(sharedWithFunction, FUNCTION_TELEMETRY_IBUS);
|
||||||
|
return portIsShared;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
serialPortConfig_t *findSerialPortConfig(uint16_t mask)
|
serialPortConfig_t *findSerialPortConfig(uint16_t mask)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(mask, FUNCTION_TELEMETRY_IBUS);
|
EXPECT_EQ(mask, FUNCTION_TELEMETRY_IBUS);
|
||||||
|
@ -179,6 +193,7 @@ void serialTestResetBuffers()
|
||||||
|
|
||||||
void serialTestResetPort()
|
void serialTestResetPort()
|
||||||
{
|
{
|
||||||
|
portIsShared = false;
|
||||||
openSerial_called = false;
|
openSerial_called = false;
|
||||||
determinePortSharing_stub_retval = PORTSHARING_UNUSED;
|
determinePortSharing_stub_retval = PORTSHARING_UNUSED;
|
||||||
telemetryDetermineEnabledState_stub_retval = true;
|
telemetryDetermineEnabledState_stub_retval = true;
|
||||||
|
@ -235,11 +250,31 @@ TEST_F(IbusTelemteryInitUnitTest, Test_IbusInitEnabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(IbusTelemteryInitUnitTest, Test_IbusInitSerialRxAndTelemetryEnabled)
|
||||||
|
{
|
||||||
|
findSerialPortConfig_stub_retval = &serialTestInstanceConfig;
|
||||||
|
|
||||||
|
//given stuff in serial read
|
||||||
|
serialReadStub.end++;
|
||||||
|
//and serial rx enabled too
|
||||||
|
portIsShared = true;
|
||||||
|
|
||||||
|
//when initializing and polling ibus
|
||||||
|
initIbusTelemetry();
|
||||||
|
checkIbusTelemetryState();
|
||||||
|
handleIbusTelemetry();
|
||||||
|
|
||||||
|
//then all is read from serial port
|
||||||
|
EXPECT_NE(serialReadStub.pos, serialReadStub.end);
|
||||||
|
EXPECT_FALSE(openSerial_called);
|
||||||
|
}
|
||||||
|
|
||||||
class IbusTelemetryProtocolUnitTestBase : public ::testing::Test
|
class IbusTelemetryProtocolUnitTestBase : public ::testing::Test
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
virtual void SetUp()
|
virtual void SetUp()
|
||||||
{
|
{
|
||||||
|
serialTestResetPort();
|
||||||
telemetryConfigMutable()->report_cell_voltage = false;
|
telemetryConfigMutable()->report_cell_voltage = false;
|
||||||
serialTestResetBuffers();
|
serialTestResetBuffers();
|
||||||
initIbusTelemetry();
|
initIbusTelemetry();
|
||||||
|
@ -373,33 +408,20 @@ TEST_F(IbusTelemteryProtocolUnitTest, Test_IbusRespondToGetMeasurementVbattPackV
|
||||||
|
|
||||||
TEST_F(IbusTelemteryProtocolUnitTest, Test_IbusRespondToGetMeasurementTemperature)
|
TEST_F(IbusTelemteryProtocolUnitTest, Test_IbusRespondToGetMeasurementTemperature)
|
||||||
{
|
{
|
||||||
#ifdef BARO
|
|
||||||
//Given ibus command: Sensor at address 2, please send your measurement
|
//Given ibus command: Sensor at address 2, please send your measurement
|
||||||
//then we respond
|
//then we respond
|
||||||
baro.baroTemperature = 50;
|
gyroTemperature = 50;
|
||||||
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x95\x01\xc1\xFE", 6);
|
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x84\x03\xd0\xfe", 6);
|
||||||
|
|
||||||
//Given ibus command: Sensor at address 2, please send your measurement
|
//Given ibus command: Sensor at address 2, please send your measurement
|
||||||
//then we respond
|
//then we respond
|
||||||
baro.baroTemperature = 59; //test integer rounding
|
gyroTemperature = 59; //test integer rounding
|
||||||
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x96\x01\xc0\xFE", 6);
|
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\xde\x03\x76\xfe", 6);
|
||||||
|
|
||||||
//Given ibus command: Sensor at address 2, please send your measurement
|
//Given ibus command: Sensor at address 2, please send your measurement
|
||||||
//then we respond
|
//then we respond
|
||||||
baro.baroTemperature = 150;
|
gyroTemperature = 150;
|
||||||
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x9f\x01\xb7\xFE", 6);
|
checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x6c\x07\xe4\xfe", 6);
|
||||||
#else
|
|
||||||
#error not tested, may be obsolete
|
|
||||||
// //Given ibus command: Sensor at address 2, please send your measurement
|
|
||||||
// //then we respond with: I'm reading 0 degrees + constant offset 0x190
|
|
||||||
// telemTemperature1 = 0;
|
|
||||||
// checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\x90\x01\xC6\xFE", 6);
|
|
||||||
|
|
||||||
// //Given ibus command: Sensor at address 2, please send your measurement
|
|
||||||
// //then we respond with: I'm reading 100 degrees + constant offset 0x190
|
|
||||||
// telemTemperature1 = 100;
|
|
||||||
// checkResponseToCommand("\x04\xA2\x59\xff", 4, "\x06\xA2\xF4\x01\x62\xFE", 6);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -468,19 +490,12 @@ TEST_F(IbusTelemteryProtocolUnitTestDaisyChained, Test_IbusRespondToGetMeasureme
|
||||||
//then we respond with: I'm reading 0.1 volts
|
//then we respond with: I'm reading 0.1 volts
|
||||||
batteryCellCount = 1;
|
batteryCellCount = 1;
|
||||||
vbat = 10;
|
vbat = 10;
|
||||||
checkResponseToCommand("\x04\xA3\x58\xff", 4, "\x06\xA3\x64\x00\xf2\xFe", 6);
|
checkResponseToCommand("\x04\xA3\x58\xff", 4, "\x06\xA3\x64\x00\xf2\xfe", 6);
|
||||||
|
|
||||||
#ifdef BARO
|
|
||||||
//Given ibus command: Sensor at address 4, please send your measurement
|
//Given ibus command: Sensor at address 4, please send your measurement
|
||||||
//then we respond
|
//then we respond
|
||||||
baro.baroTemperature = 150;
|
gyroTemperature = 150;
|
||||||
checkResponseToCommand("\x04\xA4\x57\xff", 4, "\x06\xA4\x9f\x01\xb5\xFE", 6);
|
checkResponseToCommand("\x04\xA4\x57\xff", 4, "\x06\xA4\x6c\x07\xe2\xfe", 6);
|
||||||
#else
|
|
||||||
//Given ibus command: Sensor at address 4, please send your measurement
|
|
||||||
//then we respond with: I'm reading 100 degrees + constant offset 0x190
|
|
||||||
telemTemperature1 = 100;
|
|
||||||
checkResponseToCommand("\x04\xA4\x57\xff", 4, "\x06\xA4\xF4\x01\x60\xFE", 6);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//Given ibus command: Sensor at address 5, please send your measurement
|
//Given ibus command: Sensor at address 5, please send your measurement
|
||||||
//then we respond with: I'm reading 100 rpm
|
//then we respond with: I'm reading 100 rpm
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue