Config interface
-
Upload
ryan-boland -
Category
Technology
-
view
211 -
download
0
Transcript of Config interface
Quadcopter Configuration
Interface
void text_debug() {
// ...
serial_printf("rc_throttle: %8.3f", rc_get(RC_THROTTLE)); serial_printf("\t m1: %d", motor_level(M1)); serial_printf("\t m2: %d", motor_level(M2)); serial_printf("\t m3: %d", motor_level(M3)); serial_printf("\t m4: %d", motor_level(M4)); serial_printlnf("");
}
Debugging with the console
Demo
Going wireless
bluetooth10 meters Radio telemetry
500m ++
• Chrome Packaged App
• Python with PySide (Qt)
• Electron (NodeJS)
Options
Python with PySide (Qt)
Electron
https://github.com/sindresorhus/awesome-electron
Electron
Electron
So, we can use Node SerialPort!
var SerialPort = require(“serialport").SerialPort
var serialPort = new SerialPort("/dev/tty-usbserial1", { baudrate: 57600 });
const onDataReceived = function(data) { processData(data); }
Electron
https://github.com/sindresorhus/awesome-electron
Start with a boilerplate!
Storing and passing configuration data
On the C side:typedef struct {
uint16_t version; uint16_t motors_min; uint16_t motors_max;
} CONFIG_struct;
JavaScript:let config = { version: 1, motors_min: 0, motors_max: 2000 }
Config struct requirementstypedef struct {
uint16_t version; uint16_t motors_min; uint16_t motors_max;
} CONFIG_struct;
1. ability to access the config data from anywhere in the program ( e.g. CONFIG.motors_min )
2. Ability to serialize the struct to binary to pass in the packet
[version][motors_min][motors_max]
On the C side:typedef struct {
uint16_t version; uint16_t motors_min; uint16_t motors_max;
} CONFIG_struct;
in memory (each 16 bits):
[version][motors_min][motors_max]
typedef struct __attribute__((packed)) {
uint16_t version; uint16_t motors_min; uint16_t motors_max;
} CONFIG_struct;
mark the struct as “packed”, ensure that there is no padding between values
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
typedef union { CONFIG_struct data; uint8_t raw[sizeof(CONFIG_struct)]; } CONFIG_union;
extern CONFIG_union CONFIG;
Config data struct
// anywhere in the code
make_something_happen(CONFIG.data.max);
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
typedef union { CONFIG_struct data; uint8_t raw[sizeof(CONFIG_struct)]; } CONFIG_union;
extern CONFIG_union CONFIG;
Config data struct
// anywhere in the code
make_something_happen(CONFIG.data.max);
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
typedef union { CONFIG_struct data; uint8_t raw[sizeof(CONFIG_struct)]; } CONFIG_union;
extern CONFIG_union CONFIG;
Config data struct
// anywhere in the code
make_something_happen(CONFIG.data.max);
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
typedef union { CONFIG_struct data; uint8_t raw[sizeof(CONFIG_struct)]; } CONFIG_union;
extern CONFIG_union CONFIG;
Config data struct
// anywhere in the code
make_something_happen(CONFIG.data.max);
typedef union { CONFIG_struct data; uint8_t raw[sizeof(CONFIG_struct)]; } CONFIG_union;
Config data struct
void config_update_eeprom() { for (uint16_t i = 0; i < sizeof(CONFIG); i++) { EEPROM.update(i, CONFIG.raw[i]); } }
var buffer = new ArrayBuffer(6) fillInArrayBufferFromSerialPort(buffer)
var dataView = new DataView(buffer)
var version = dataview.getUint16(0) var motorMin = dataview.getUint16(2) var motorMax = dataview.getUint16(4)
Config data - JavaScriptArrayBuffer & DataView
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
var buffer = new ArrayBuffer(6) fillInArrayBufferFromSerialPort(buffer)
var dataView = new DataView(buffer)
var version = dataview.getUint16(0) var motorMin = dataview.getUint16(2) var motorMax = dataview.getUint16(4)
Config data - JavaScriptArrayBuffer & DataView
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
var buffer = new ArrayBuffer(6) fillInArrayBufferFromSerialPort(buffer)
var dataView = new DataView(buffer)
var version = dataview.getUint16(0) var motorMin = dataview.getUint16(2) var motorMax = dataview.getUint16(4)
Config data - JavaScriptArrayBuffer & DataView
typedef struct __attribute__((packed)) { uint16_t version; uint16_t motor_min; uint16_t motor_max; } CONFIG_struct;
On the C side:typedef struct {
uint16_t version; uint16_t motors_min; uint16_t motors_max;
} CONFIG_struct;
JavaScript:let config = { version: 1, motors_min: 0, motors_max: 2000 }
set_config 1.00 0 2000
Sending data both ways? (configuration)
Simple approach - print text:
(but difficult to parse text and no error checking)
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Better approach: Binary packets
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Binary packet example
• JavaScript app sends a packet with a code of REQUEST_CONFIG and no data
• The micro controller responds with a packet with a code of REQUEST_CONFIG that contains all of the configuration data
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Binary packets
#define PACKET_HEADER1 0x02 #define PACKET_HEADER2 0xB5
Headers - indicate the start of a packet
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Binary packets
#define REQUEST_CONFIG 1 #define REQUEST_GYRO_ACC 2 #define REQUEST_RC 3 #define REQUEST_MOTORS 4 #define REQUEST_RATE_PIDS 5
#define SET_CONFIG 101
#define INFO_SUCCESS 201 #define INFO_FAILURE 202 #define INFO_BAD_CRC 203
Code - explains what the packet is about
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Binary packets
Length - how many bytes of data is being transmitted
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Binary packets
Data - binary data
We keep reading this until we get to the number of bytes stated in the LENGTH section of the packet
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Binary packets
CRC - cyclic redundancy check
This is a code that is calculated based on the data within the packet.
When the packet is received, the client calculates the CRC on their own, and ensure that it matches what is specified here
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
CRC Calculation
Calculated by XOR’ing the binary representation of all of the bytes of the packet body
Packet processing code: processCommand(data) { switch(this.state) { case 0: if (data === serialCodes.PACKET_HEADER1) { this.state++; } break; case 1: if (data === serialCodes.PACKET_HEADER2) { this.state++; } else { this.state = 0; } break; case 2: this.code = data; this.incomingCrc = data; this.state++; break;
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Packet processing code: processCommand(data) { switch(this.state) { case 0: if (data === serialCodes.PACKET_HEADER1) { this.state++; } break; case 1: if (data === serialCodes.PACKET_HEADER2) { this.state++; } else { this.state = 0; } break; case 2: this.code = data; this.incomingCrc = data; this.state++; break;
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Packet processing code: processCommand(data) { switch(this.state) { case 0: if (data === serialCodes.PACKET_HEADER1) { this.state++; } break; case 1: if (data === serialCodes.PACKET_HEADER2) { this.state++; } else { this.state = 0; } break; case 2: this.code = data; this.incomingCrc = data; this.state++; break;
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Packet processing code: processCommand(data) { switch(this.state) { case 0: if (data === serialCodes.PACKET_HEADER1) { this.state++; } break; case 1: if (data === serialCodes.PACKET_HEADER2) { this.state++; } else { this.state = 0; } break; case 2: this.code = data; this.incomingCrc = data; this.state++; break;
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Packet processing code: processCommand(data) { switch(this.state) { case 0: if (data === serialCodes.PACKET_HEADER1) { this.state++; } break; case 1: if (data === serialCodes.PACKET_HEADER2) { this.state++; } else { this.state = 0; } break; case 2: this.code = data; this.incomingCrc = data; this.state++; break;
[HEADER1][HEADER2][CODE][LENGTH_LSB][LENGTH_MSB][DATA][CRC]
Scenario - App requestingcurrent config data
• JavaScript app sends a packet with a code of REQUEST_CONFIG and no data
• The micro controller responds with a packet with a code of REQUEST_CONFIG that contains all of the configuration data
• JavaScript app sends a packet with a code of SET_CONFIG and config data
• The micro controller updates its config struct and responds with a packet with a code of INFO_SUCCESS
Scenario - App updatingconfig data
• JavaScript app sends a packet with a code of REQUEST_MOTORS and no data
• The micro controller responds with a packet with a code of REQUEST_MOTORS that contains the current motor speeds
Scenario - Displaying motor speed data
Questions?