The present document describes the Skycharge UART protocol between the BeagleBone single-board computer and another board, which are physically connected via UART.

Skycharge BeagleBone dedicates UART1 for possible communication with the external board, BeagleBone pins are RX P9.26 and TX P9.24 (please see BeagleBone manual https://github.com/beagleboard/beaglebone-black/wiki/System-Reference-Manual)

The UART1 port is configured as the following:
Baud rate: 9600
Data bits: 8
Stop bits: 1
Parity: None.
Flow control: None.

Each UART packet (either request or response) can be described with the following structure:

struct uart_packet {
	le16     magic;
	uint8_t  crc8;
	uint8_t  data_len;
	uint8_t  data[];
};

Where fields can be described as following:
"magic" is equal to 0xe5b5
"crc8" is calculated for the whole packet starting from "data_len" field with polynomial 0x31, initial
value 0x0 and final XOR 0x0. See crc8() function below.
"data_len" is the length of the following data

All fields are little-endian.

crc8 calculation can be done with the following function:

/**
 * CRC8, polynomial 0x31, initial value 0x0, final xor 0x0
 */
static inline uint8_t crc8(const uint8_t *data, uint16_t len)
{
	uint8_t crc = 0x0;
	uint8_t i;

	while (len--) {
		crc ^= *data++;

		for (i = 0; i < 8; i++)
			crc = crc & 0x80 ? (crc << 1) ^ 0x31 : crc << 1;
	}

    return crc;
}

All responses contain status or error code of the operation. Error code is 0 in case of success or is equal to POSIX errno number. Some errors are specific to UART and can be described by the following enum:

enum {
	UART_BAD_CRC       = 0xf0,
	UART_CMD_UNKNOWN   = 0xf1,
	UART_CMD_MALFORMED = 0xf2,
};

UART commands

Resume scan:

struct uart_resume_scan_req {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 2 */
    le16     type;     /* SKY_RESUME_SCAN_REQ = 5 */
};

/* b5 e5 fb 02 05 00 */
struct uart_start_charge_rsp {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 4 */
    le16     type;     /* SKY_START_CHARGE_RSP = 6 */
    le16     error;    /* 0 in case of success */ 
};

/* b5 e5 16 04 06 00 00 00 */

Stop scan:

struct uart_stop_scan_req {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 2 */
    le16     type;     /* SKY_STOP_SCAN_REQ = 7 */
};

/* b5 e5 22 02 07 00 */
struct uart_stop_charge_rsp {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 4 */
    le16     type;     /* SKY_STOP_CHARGE_RSP = 8 */
    le16     error;    /* 0 in case of success */ 
};

/* b5 e5 03 04 08 00 00 00 */

Charging state:

struct uart_charge_state_req {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 2 */
    le16     type;     /* SKY_CHARGING_STATE_REQ = 13 */
};

/* b5 e5 cc 02 0d 00 */
enum sky_dev_hw_state {
    SKY_UNKNOWN                     = 0,
    SKY_SCANNING_INIT               = 1,
    SKY_SCANNING_RUN_STATE          = 2,
    SKY_SCANNING_CHECK_MATRIX       = 3,
    SKY_SCANNING_CHECK_WATER        = 5,
    SKY_SCANNING_WET                = 6,
    SKY_SCANNING_DETECTING          = 7,
    SKY_PRE_CHARGING_INIT           = 8,
    SKY_PRE_CHARGING_RUN            = 9,
    SKY_PRE_CHARGING_CHECK_MATRIX   = 10,
    SKY_PRE_CHARGING_CHECK_WATER    = 12,
    SKY_PRE_CHARGING_WET            = 13,
    SKY_PRE_CHARGING_FIND_CHARGERS  = 14,
    SKY_CHARGING_INIT               = 15,
    SKY_CHARGING_RUN                = 16,
    SKY_CHARGING_MONITOR_CURRENT    = 17,
    SKY_POST_CHARGING_INIT          = 18,
    SKY_POST_CHARGING_RUN           = 19,
    SKY_POST_CHARGING_CHECK_MATRIX  = 20,
    SKY_POST_CHARGING_CHECK_WATER   = 22,
    SKY_POST_CHARGING_WET           = 23,
    SKY_POST_CHARGING_FIND_CHARGERS = 24,
    SKY_OVERLOAD                    = 25,
    SKY_AUTOSCAN_DISABLED           = 250,
};

struct uart_charge_state_rsp {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 24 */
    le16     type;     /* SKY_CHARGING_STATE_RSP = 14 */
    le16     error     /* 0 in case of success */
    le16 voltage;      /* mV */
    le16 current;      /* mA */
    le16 dev_hw_state; /* enum sky_dev_hw_state */
    struct {
       le16 charge_perc; /* 0-100% */
       le16 charge_time; /* seconds */
    } bms;
    le16 unused1;
    le32 unused2;
    le32 unused3;
};

/*
 * b5 e5 5e 18 0e 00 00 00 c3 00 00 00 02 00 00 00
 * 00 00 f0 b6 f0 e5 80 b6 14 69 69 0d
 */

Skyport open:

struct uart_open_droneport_req {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 2 */
    le16     type;     /* SKY_OPEN_DRONEPORT_REQ = 9 */
};

/* b5 e5 4f 02 09 00 */
struct uart_open_droneport_rsp {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 4 */
    le16     type;     /* SKY_OPEN_DRONEPORT_RSP = 10 */
    le16     error;    /* 0 in case of success */ 
};

/* b5 e5 04 04 0a 00 00 00 */

Skyport close:

struct uart_close_droneport_req {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 2 */
    le16     type;     /* SKY_CLOSE_DRONEPORT_REQ = 11 */
};

/* b5 e5 96 02 0b 00 */
struct uart_close_droneport_rsp {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 4 */
    le16     type;     /* SKY_CLOSE_DRONEPORT_RSP = 12 */
    le16     error;    /* 0 in case of success */ 
};

/* b5 e5 0d 04 0c 00 00 00 */

Skyport state:

struct uart_droneport_state_req {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 2 */
    le16     type;     /* SKY_DRONEPORT_STATE_REQ = 25 */
};

/* b5 e5 21 02 19 00 */
enum sky_droneport_status {
	SKY_DP_IS_READY      = 1<<0,
	SKY_DP_IS_OPENED     = 1<<1,
	SKY_DP_IS_CLOSED     = 1<<2,
	SKY_DP_IN_PROGRESS   = 1<<3,
	SKY_DP_LANDING_ERROR = 1<<4,
};

struct uart_droneport_state_rsp {
    le16     magic;    /* 0xe5b5 */
    uint8_t  crc8;
    uint8_t  data_len; /* 8 */
    le16     type;     /* SKY_DRONEPORT_STATE_RSP = 26 */
    le16     error;    /* 0 in case of success */
    le32     status    /* bitfield, enum sky_droneport_status */
};

/* b5 e5 24 08 1a 00 00 00 05 00 00 00 */