AP_Notify: OreoLED rework

Reworked the process flow.  Created the aviation vs rover themes.
Created visual operator feedback for prearm checks, GPS, EKF, gyro init,
radio failsafe, and low battery. This also includes work by Hugh Eaves
to open up the full extended properties of the Oreo LEDs.  Not only are
far more functions available, but you can override and do custom things
via mavlink.
This commit is contained in:
Matt 2017-06-01 18:10:15 -04:00 committed by Randy Mackay
parent ddd327e365
commit fc3406d07f
2 changed files with 549 additions and 186 deletions

View File

@ -21,33 +21,35 @@
#if CONFIG_HAL_BOARD == HAL_BOARD_PX4 #if CONFIG_HAL_BOARD == HAL_BOARD_PX4
#include <AP_BoardConfig/AP_BoardConfig.h> #include <AP_BoardConfig/AP_BoardConfig.h>
#include "OreoLED_PX4.h" #include "OreoLED_PX4.h"
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <drivers/drv_oreoled.h> #include <drivers/drv_oreoled.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include "AP_Notify.h" #include "AP_Notify.h"
extern const AP_HAL::HAL& hal;
#define OREOLED_BACKLEFT 0 // back left led instance number #define OREOLED_BACKLEFT 0 // back left led instance number
#define OREOLED_BACKRIGHT 1 // back right led instance number #define OREOLED_BACKRIGHT 1 // back right led instance number
#define OREOLED_FRONTRIGHT 2 // front right led instance number #define OREOLED_FRONTRIGHT 2 // front right led instance number
#define OREOLED_FRONTLEFT 3 // front left led instance number #define OREOLED_FRONTLEFT 3 // front left led instance number
#define PERIOD_SLOW 800 // slow flash rate
#define PERIOD_FAST 500 // fast flash rate
#define PERIOD_SUPER 150 // super fast rate
#define PO_ALTERNATE 180 // 180 degree phase offset
extern const AP_HAL::HAL& hal;
// constructor // constructor
OreoLED_PX4::OreoLED_PX4() : NotifyDevice(), OreoLED_PX4::OreoLED_PX4(uint8_t theme): NotifyDevice(),
_overall_health(false), _overall_health(false),
_oreoled_fd(-1), _oreoled_fd(-1),
_send_required(false), _send_required(false),
_state_desired_semaphore(false), _state_desired_semaphore(false),
_pattern_override(0) _pattern_override(0),
_oreo_theme(theme)
{ {
// initialise desired and sent state // initialise desired and sent state
memset(_state_desired,0,sizeof(_state_desired)); memset(_state_desired,0,sizeof(_state_desired));
@ -56,9 +58,13 @@ OreoLED_PX4::OreoLED_PX4() : NotifyDevice(),
extern "C" int oreoled_main(int, char **); extern "C" int oreoled_main(int, char **);
// init - initialised the device
//
// Initialize the LEDs
//
bool OreoLED_PX4::init() bool OreoLED_PX4::init()
{ {
#if defined(CONFIG_ARCH_BOARD_PX4FMU_V2) #if defined(CONFIG_ARCH_BOARD_PX4FMU_V2)
if (!AP_BoardConfig::px4_start_driver(oreoled_main, "oreoled", "start autoupdate")) { if (!AP_BoardConfig::px4_start_driver(oreoled_main, "oreoled", "start autoupdate")) {
hal.console->printf("Unable to start oreoled driver\n"); hal.console->printf("Unable to start oreoled driver\n");
@ -67,7 +73,7 @@ bool OreoLED_PX4::init()
hal.scheduler->delay(500); hal.scheduler->delay(500);
} }
#endif #endif
// open the device // open the device
_oreoled_fd = open(OREOLED0_DEVICE_PATH, O_RDWR); _oreoled_fd = open(OREOLED0_DEVICE_PATH, O_RDWR);
if (_oreoled_fd == -1) { if (_oreoled_fd == -1) {
@ -84,163 +90,270 @@ bool OreoLED_PX4::init()
return _overall_health; return _overall_health;
} }
// update - updates device according to timed_updated. Should be
// called at 50Hz // UPDATE device according to timed_updated. Called at 50Hz
void OreoLED_PX4::update() void OreoLED_PX4::update()
{ {
static uint8_t counter = 0; // counter to reduce rate from 50hz to 10hz
static uint8_t step = 0; // step to control pattern
static uint8_t last_stage = 0; // unique id of the last messages sent to the LED, used to reduce resends which disrupt some patterns
static uint8_t initialization_done = 0; // Keep track if initialization has begun. There is a period when the driver
// is running but initialization has not yet begun -- this prevents post-initialization
// LED patterns from displaying before initialization has completed.
// return immediately if not healthy if (!_overall_health) { return; } // don't go any further if LED driver reports unhealthy
if (!_overall_health) {
return;
}
// handle firmware update event if (slow_counter()) { return; } // slow rate from 50hz to 10hz
if (AP_Notify::flags.firmware_update) {
// Force a syncronisation before setting the free-running colour cycle macro
send_sync();
set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_COLOUR_CYCLE);
return;
}
// return immediately if custom pattern has been sent sync_counter(); // syncronizes LEDs every 10 seconds
if (OreoLED_PX4::_pattern_override != 0) {
// reset stage so patterns will be resent once override clears
last_stage = 0;
return;
}
// slow rate from 50Hz to 10hz if (mode_firmware_update()) { return; } // don't go any further if the Pixhawk is in firmware update
counter++;
if (counter < 5) {
return;
}
counter = 0;
// move forward one step if (mode_init()) { return; } // don't go any further if the Pixhawk is initializing
step++;
if (step >= 10) {
step = 0;
}
// Pre-initialization pattern is all solid green if (mode_failsafe_radio()) { return; } // don't go any further if the Pixhawk is is in radio failsafe
if (!initialization_done) {
set_rgb(OREOLED_ALL_INSTANCES, 0, OREOLED_BRIGHT, 0);
}
// initialising pattern set_standard_colors(); // set the rear LED standard colors as described above
if (AP_Notify::flags.initialising) {
initialization_done = 1; // Record initialization has begun
last_stage = 1; // record stage
// exit so no other status modify this pattern if (mode_failsafe_batt()) { return; } // stop here if the battery is low.
return;
}
// save trim and esc calibration pattern if (_pattern_override) { return; } // stop here if in mavlink LED control override.
if (AP_Notify::flags.save_trim || AP_Notify::flags.esc_calibration) {
switch(step) {
case 0:
case 3:
case 6:
// red
set_rgb(OREOLED_INSTANCE_ALL, OREOLED_BRIGHT, 0, 0);
break;
case 1: if (mode_auto_flight()) { return; } // stop here if in an autopilot mode.
case 4:
case 7:
// blue
set_rgb(OREOLED_INSTANCE_ALL, 0, 0, OREOLED_BRIGHT);
break;
case 2: mode_pilot_flight(); // stop here if in an pilot controlled mode.
case 5: }
case 8:
// green on
set_rgb(OREOLED_INSTANCE_ALL, 0, OREOLED_BRIGHT, 0);
break;
case 9: // Slow the update rate from 50hz to 10hz
// all off // Returns true if counting up
set_rgb(OREOLED_INSTANCE_ALL, 0, 0, 0); // Returns false and resets one counter hits 5
break; bool OreoLED_PX4::slow_counter()
} {
// record stage static uint8_t update_counter;
last_stage = 2;
// exit so no other status modify this pattern
return;
}
// radio failsafe pattern: Alternate between front red/rear black and front black/rear red update_counter++;
if (AP_Notify::flags.failsafe_radio) { if (update_counter < 5) {
switch(step) { return 1;
case 0: } else {
case 1: update_counter = 0;
case 2: return 0;
case 3:
case 4:
// Front red/rear black
set_rgb(OREOLED_FRONTLEFT, OREOLED_BRIGHT, 0, 0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_BRIGHT, 0, 0);
set_rgb(OREOLED_BACKLEFT, 0, 0, 0);
set_rgb(OREOLED_BACKRIGHT, 0, 0, 0);
break;
case 5:
case 6:
case 7:
case 8:
case 9:
// Front black/rear red
set_rgb(OREOLED_FRONTLEFT, 0, 0, 0);
set_rgb(OREOLED_FRONTRIGHT, 0, 0, 0);
set_rgb(OREOLED_BACKLEFT, OREOLED_BRIGHT, 0, 0);
set_rgb(OREOLED_BACKRIGHT, OREOLED_BRIGHT, 0, 0);
break;
}
// record stage
last_stage = 3;
// exit so no other status modify this pattern
return;
}
// send colours (later we will set macro if required)
if (last_stage < 10 && initialization_done) {
set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_AUTOMOBILE);
last_stage = 10;
} else if (last_stage >= 10) {
static uint8_t previous_autopilot_mode = -1;
if (previous_autopilot_mode != AP_Notify::flags.autopilot_mode) {
if (AP_Notify::flags.autopilot_mode) {
// autopilot flight modes start breathing macro
set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_AUTOMOBILE);
set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_BREATH);
} else {
// manual flight modes stop breathing -- solid color
set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_AUTOMOBILE);
}
// record we have processed this change
previous_autopilot_mode = AP_Notify::flags.autopilot_mode;
}
last_stage = 11;
} }
} }
// set_rgb - set color as a combination of red, green and blue values for one or all LEDs
void OreoLED_PX4::set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t blue) // Calls resyncing the LEDs every 10 seconds
// Always returns false, no action needed.
void OreoLED_PX4::sync_counter()
{ {
// return immediately if no healty leds static uint8_t counter = 80;
if (!_overall_health) {
return; counter++;
if (counter > 100) {
counter = 0;
send_sync();
}
}
// Procedure for when Pixhawk is in FW update / bootloader
// Makes all LEDs go into color cycle mode
// Returns true if firmware update in progress. False if not
bool OreoLED_PX4::mode_firmware_update()
{
if (AP_Notify::flags.firmware_update) {
set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_COLOUR_CYCLE);
return true;
} else {
return false;
}
}
// Procedure for when Pixhawk is booting up and initializing
// Makes all LEDs rapidly strobe blue. Returns LED_INIT_STAGE
// 1 = Default, initialization has not yet begun
// 2 = Initialization flag found, initialization in progress
// 3 = Initialization flag no longer found, initialization complete
bool OreoLED_PX4::mode_init()
{
static uint16_t stage = 1;
// Pixhawk has not begun initializing yet. Strobe all blue
if ((!AP_Notify::flags.initialising) && ((stage == 1))) {
set_rgb(OREOLED_INSTANCE_ALL, OREOLED_PATTERN_STROBE, 0, 0, 255,0,0,0,PERIOD_SUPER,0);
// Pixhawk has begun initializing
} else if ((AP_Notify::flags.initialising) && ((stage == 1))) {
stage = 2;
set_rgb(OREOLED_INSTANCE_ALL, OREOLED_PATTERN_STROBE, 0, 0, 255,0,0,0,PERIOD_SUPER,0);
// Pixhawk still initializing
} else if ((AP_Notify::flags.initialising) && ((stage == 2))) {
set_rgb(OREOLED_INSTANCE_ALL, OREOLED_PATTERN_STROBE, 0, 0, 255,0,0,0,PERIOD_SUPER,0);
// Pixhawk has completed initialization
} else if((!AP_Notify::flags.initialising) && ((stage == 2))) {
stage = 0;
set_rgb(OREOLED_INSTANCE_ALL, OREOLED_PATTERN_SOLID, 0, 0, 255);
} else { stage = 0; }
return stage;
}
// Procedure for when Pixhawk is in radio failsafe
// LEDs perform alternating Red X pattern
bool OreoLED_PX4::mode_failsafe_radio()
{
if (AP_Notify::flags.failsafe_radio) {
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,PO_ALTERNATE);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,0);
}
return AP_Notify::flags.failsafe_radio;
}
// Procedure to set standard rear LED colors in aviation theme
// Back LEDS White for normal, yellow for GPS not usable, purple for EKF bad]
// Returns true GPS or EKF problem, returns false if all ok
bool OreoLED_PX4::set_standard_colors()
{
if (!(AP_Notify::flags.gps_fusion)) {
_rear_color_r = 255;
_rear_color_g = 50;
_rear_color_b = 0;
return true;
} else if (AP_Notify::flags.ekf_bad){
_rear_color_r = 255;
_rear_color_g = 0;
_rear_color_b = 255;
return true;
} else {
_rear_color_r = 255;
_rear_color_g = 255;
_rear_color_b = 255;
return false;
}
}
// Procedure to set low battery LED output
// Colors standard
// Fast strobe alternating front/back
bool OreoLED_PX4::mode_failsafe_batt()
{
if (AP_Notify::flags.failsafe_battery){
switch (_oreo_theme) {
case OreoLED_Aircraft:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 0, 255, 0,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,PO_ALTERNATE);
break;
case OreoLED_Automobile:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
break;
default:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
break;
}
}
return AP_Notify::flags.failsafe_battery;
}
// Procedure for when Pixhawk is in an autopilot mode
// Makes all LEDs strobe super fast using standard colors
bool OreoLED_PX4::mode_auto_flight()
{
switch (_oreo_theme) {
case OreoLED_Aircraft:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 0, 255, 0,0,0,0,PERIOD_SUPER,0);
if ((AP_Notify::flags.pre_arm_check && AP_Notify::flags.pre_arm_gps_check) || AP_Notify::flags.armed) {
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
} else {
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
}
break;
case OreoLED_Automobile:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
break;
default:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
break;
}
return AP_Notify::flags.autopilot_mode;
}
// Procedure for when Pixhawk is in a pilot controlled mode
// All LEDs use standard pattern and colors
bool OreoLED_PX4::mode_pilot_flight()
{
switch (_oreo_theme) {
case OreoLED_Aircraft:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_SOLID, 255, 0, 0);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_SOLID, 0, 255, 0);
if ((AP_Notify::flags.pre_arm_check && AP_Notify::flags.pre_arm_gps_check) || AP_Notify::flags.armed) {
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,0);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,PO_ALTERNATE);
} else {
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
}
break;
case OreoLED_Automobile:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_SOLID, 255, 255, 255);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_SOLID, 255, 255, 255);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, 255, 0, 0);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, 255, 0, 0);
break;
default:
set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_SOLID, 255, 255, 255);
set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_SOLID, 255, 255, 255);
set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, 255, 0, 0);
set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, 255, 0, 0);
break;
} }
return true;
}
// set_rgb - Solid color settings only
void OreoLED_PX4::set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t blue)
{
set_rgb(instance, OREOLED_PATTERN_SOLID, red, green, blue);
}
// set_rgb - Set a color and selected pattern.
void OreoLED_PX4::set_rgb(uint8_t instance, oreoled_pattern pattern, uint8_t red, uint8_t green, uint8_t blue)
{
// get semaphore // get semaphore
_state_desired_semaphore = true; _state_desired_semaphore = true;
@ -248,21 +361,15 @@ void OreoLED_PX4::set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t
if (instance == OREOLED_INSTANCE_ALL) { if (instance == OREOLED_INSTANCE_ALL) {
// store desired rgb for all LEDs // store desired rgb for all LEDs
for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) { for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
if (_state_desired[i].mode != OREOLED_MODE_RGB || _state_desired[i].red != red || _state_desired[i].green != green || _state_desired[i].blue != blue) { _state_desired[i].set_rgb(pattern, red, green, blue);
_state_desired[i].mode = OREOLED_MODE_RGB; if (!(_state_desired[i] == _state_sent[i])) {
_state_desired[i].red = red;
_state_desired[i].green = green;
_state_desired[i].blue = blue;
_send_required = true; _send_required = true;
} }
} }
} else if (instance < OREOLED_NUM_LEDS) { } else if (instance < OREOLED_NUM_LEDS) {
// store desired rgb for one LED // store desired rgb for one LED
if (_state_desired[instance].mode != OREOLED_MODE_RGB || _state_desired[instance].red != red || _state_desired[instance].green != green || _state_desired[instance].blue != blue) { _state_desired[instance].set_rgb(pattern, red, green, blue);
_state_desired[instance].mode = OREOLED_MODE_RGB; if (!(_state_desired[instance] == _state_sent[instance])) {
_state_desired[instance].red = red;
_state_desired[instance].green = green;
_state_desired[instance].blue = blue;
_send_required = true; _send_required = true;
} }
} }
@ -271,6 +378,37 @@ void OreoLED_PX4::set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t
_state_desired_semaphore = false; _state_desired_semaphore = false;
} }
// set_rgb - Sets a color, pattern, and uses extended options for amplitude, period, and phase offset
void OreoLED_PX4::set_rgb(uint8_t instance, oreoled_pattern pattern, uint8_t red, uint8_t green, uint8_t blue,
uint8_t amplitude_red, uint8_t amplitude_green, uint8_t amplitude_blue,
uint16_t period, uint16_t phase_offset)
{
// get semaphore
_state_desired_semaphore = true;
// check for all instances
if (instance == OREOLED_INSTANCE_ALL) {
// store desired rgb for all LEDs
for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
_state_desired[i].set_rgb(pattern, red, green, blue, amplitude_red, amplitude_green, amplitude_blue, period, phase_offset);
if (!(_state_desired[i] == _state_sent[i])) {
_send_required = true;
}
}
} else if (instance < OREOLED_NUM_LEDS) {
// store desired rgb for one LED
_state_desired[instance].set_rgb(pattern, red, green, blue, amplitude_red, amplitude_green, amplitude_blue, period, phase_offset);
if (!(_state_desired[instance] == _state_sent[instance])) {
_send_required = true;
}
}
// release semaphore
_state_desired_semaphore = false;
}
// set_macro - set macro for one or all LEDs // set_macro - set macro for one or all LEDs
void OreoLED_PX4::set_macro(uint8_t instance, oreoled_macro macro) void OreoLED_PX4::set_macro(uint8_t instance, oreoled_macro macro)
{ {
@ -286,17 +424,15 @@ void OreoLED_PX4::set_macro(uint8_t instance, oreoled_macro macro)
if (instance == OREOLED_INSTANCE_ALL) { if (instance == OREOLED_INSTANCE_ALL) {
// store desired macro for all LEDs // store desired macro for all LEDs
for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) { for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
if (_state_desired[i].mode != OREOLED_MODE_MACRO || _state_desired[i].macro != macro) { _state_desired[i].set_macro(macro);
_state_desired[i].mode = OREOLED_MODE_MACRO; if (!(_state_desired[i] == _state_sent[i])) {
_state_desired[i].macro = macro;
_send_required = true; _send_required = true;
} }
} }
} else if (instance < OREOLED_NUM_LEDS) { } else if (instance < OREOLED_NUM_LEDS) {
// store desired macro for one LED // store desired macro for one LED
if (_state_desired[instance].mode != OREOLED_MODE_MACRO || _state_desired[instance].macro != macro) { _state_desired[instance].set_macro(macro);
_state_desired[instance].mode = OREOLED_MODE_MACRO; if (!(_state_desired[instance] == _state_sent[instance])) {
_state_desired[instance].macro = macro;
_send_required = true; _send_required = true;
} }
} }
@ -305,8 +441,8 @@ void OreoLED_PX4::set_macro(uint8_t instance, oreoled_macro macro)
_state_desired_semaphore = false; _state_desired_semaphore = false;
} }
// set_macro - set macro for one or all LEDs // send_sync - force a syncronisation of the all LED's
void OreoLED_PX4::send_sync(void) void OreoLED_PX4::send_sync()
{ {
// return immediately if no healthy leds // return immediately if no healthy leds
if (!_overall_health) { if (!_overall_health) {
@ -316,17 +452,36 @@ void OreoLED_PX4::send_sync(void)
// set semaphore // set semaphore
_state_desired_semaphore = true; _state_desired_semaphore = true;
// store desired macro for all LEDs
for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) { for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
if (_state_desired[i].mode != OREOLED_MODE_SYNC) { _state_desired[i].send_sync();
_state_desired[i].mode = OREOLED_MODE_SYNC; if (!(_state_desired[i] == _state_sent[i])) {
_send_required = true; _send_required = true;
} }
} }
// release semaphore // release semaphore
_state_desired_semaphore = false; _state_desired_semaphore = false;
} }
// Clear the desired state
void OreoLED_PX4::clear_state(void)
{
// set semaphore
_state_desired_semaphore = true;
for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
_state_desired[i].clear_state();
}
_send_required = false;
// release semaphore
_state_desired_semaphore = false;
}
// update_timer - called by scheduler and updates PX4 driver with commands // update_timer - called by scheduler and updates PX4 driver with commands
void OreoLED_PX4::update_timer(void) void OreoLED_PX4::update_timer(void)
{ {
@ -346,9 +501,6 @@ void OreoLED_PX4::update_timer(void)
// check for state change // check for state change
if (!(_state_desired[i] == _state_sent[i])) { if (!(_state_desired[i] == _state_sent[i])) {
switch (_state_desired[i].mode) { switch (_state_desired[i].mode) {
case OREOLED_MODE_PATTERN:
// not yet supported
break;
case OREOLED_MODE_MACRO: case OREOLED_MODE_MACRO:
{ {
oreoled_macrorun_t macro_run = {i, _state_desired[i].macro}; oreoled_macrorun_t macro_run = {i, _state_desired[i].macro};
@ -357,16 +509,48 @@ void OreoLED_PX4::update_timer(void)
break; break;
case OREOLED_MODE_RGB: case OREOLED_MODE_RGB:
{ {
oreoled_rgbset_t rgb_set = {i, OREOLED_PATTERN_SOLID, _state_desired[i].red, _state_desired[i].green, _state_desired[i].blue}; oreoled_rgbset_t rgb_set = {i, _state_desired[i].pattern, _state_desired[i].red, _state_desired[i].green, _state_desired[i].blue};
ioctl(_oreoled_fd, OREOLED_SET_RGB, (unsigned long)&rgb_set); ioctl(_oreoled_fd, OREOLED_SET_RGB, (unsigned long)&rgb_set);
} }
break; break;
case OREOLED_MODE_RGB_EXTENDED:
{
oreoled_cmd_t cmd;
memset(&cmd, 0, sizeof(oreoled_cmd_t));
cmd.led_num = i;
cmd.buff[0] = _state_desired[i].pattern;
cmd.buff[1] = OREOLED_PARAM_BIAS_RED;
cmd.buff[2] = _state_desired[i].red;
cmd.buff[3] = OREOLED_PARAM_BIAS_GREEN;
cmd.buff[4] = _state_desired[i].green;
cmd.buff[5] = OREOLED_PARAM_BIAS_BLUE;
cmd.buff[6] = _state_desired[i].blue;
cmd.buff[7] = OREOLED_PARAM_AMPLITUDE_RED;
cmd.buff[8] = _state_desired[i].amplitude_red;
cmd.buff[9] = OREOLED_PARAM_AMPLITUDE_GREEN;
cmd.buff[10] = _state_desired[i].amplitude_green;
cmd.buff[11] = OREOLED_PARAM_AMPLITUDE_BLUE;
cmd.buff[12] = _state_desired[i].amplitude_blue;
// Note: The Oreo LED controller expects to receive uint16 values
// in little endian order
cmd.buff[13] = OREOLED_PARAM_PERIOD;
cmd.buff[14] = (_state_desired[i].period & 0xFF00) >> 8;
cmd.buff[15] = (_state_desired[i].period & 0x00FF);
cmd.buff[16] = OREOLED_PARAM_PHASEOFFSET;
cmd.buff[17] = (_state_desired[i].phase_offset & 0xFF00) >> 8;
cmd.buff[18] = (_state_desired[i].phase_offset & 0x00FF);
cmd.num_bytes = 19;
ioctl(_oreoled_fd, OREOLED_SEND_BYTES, (unsigned long)&cmd);
}
break;
case OREOLED_MODE_SYNC: case OREOLED_MODE_SYNC:
{ {
ioctl(_oreoled_fd, OREOLED_FORCE_SYNC, 0); ioctl(_oreoled_fd, OREOLED_FORCE_SYNC, 0);
} }
break; break;
} default:
break;
};
// save state change // save state change
_state_sent[i] = _state_desired[i]; _state_sent[i] = _state_desired[i];
} }
@ -376,7 +560,8 @@ void OreoLED_PX4::update_timer(void)
_send_required = false; _send_required = false;
} }
// handle a LED_CONTROL message
// Handle an LED_CONTROL mavlink message
void OreoLED_PX4::handle_led_control(mavlink_message_t *msg) void OreoLED_PX4::handle_led_control(mavlink_message_t *msg)
{ {
// exit immediately if unhealthy // exit immediately if unhealthy
@ -396,17 +581,142 @@ void OreoLED_PX4::handle_led_control(mavlink_message_t *msg)
// if pattern is OFF, we clear pattern override so normal lighting should resume // if pattern is OFF, we clear pattern override so normal lighting should resume
if (packet.pattern == LED_CONTROL_PATTERN_OFF) { if (packet.pattern == LED_CONTROL_PATTERN_OFF) {
_pattern_override = 0; _pattern_override = 0;
clear_state();
return; return;
} }
// custom patterns not implemented
if (packet.pattern == LED_CONTROL_PATTERN_CUSTOM) { if (packet.pattern == LED_CONTROL_PATTERN_CUSTOM) {
return; // Here we handle two different "sub commands",
} // depending on the bytes in the first CUSTOM_HEADER_LENGTH
// of the custom pattern byte buffer
// other patterns sent as macro // Return if we don't have at least CUSTOM_HEADER_LENGTH bytes
set_macro(packet.instance, (oreoled_macro)packet.pattern); if (packet.custom_len < CUSTOM_HEADER_LENGTH) {
return;
}
// check for the RGB0 sub-command
if (memcmp(packet.custom_bytes, "RGB0", CUSTOM_HEADER_LENGTH) == 0) {
// check to make sure the total length matches the length of the RGB0 command + data values
if (packet.custom_len != CUSTOM_HEADER_LENGTH + 4) {
return;
}
// check for valid pattern id
if (packet.custom_bytes[CUSTOM_HEADER_LENGTH] >= OREOLED_PATTERN_ENUM_COUNT) {
return;
}
// convert the first byte after the command to a oreoled_pattern
oreoled_pattern pattern = (oreoled_pattern)packet.custom_bytes[CUSTOM_HEADER_LENGTH];
// call the set_rgb function, using the rest of the bytes as the RGB values
set_rgb(packet.instance, pattern, packet.custom_bytes[CUSTOM_HEADER_LENGTH + 1], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 2], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 3]);
} else if (memcmp(packet.custom_bytes, "RGB1", CUSTOM_HEADER_LENGTH) == 0) { // check for the RGB1 sub-command
// check to make sure the total length matches the length of the RGB1 command + data values
if (packet.custom_len != CUSTOM_HEADER_LENGTH + 11) {
return;
}
// check for valid pattern id
if (packet.custom_bytes[CUSTOM_HEADER_LENGTH] >= OREOLED_PATTERN_ENUM_COUNT) {
return;
}
// convert the first byte after the command to a oreoled_pattern
oreoled_pattern pattern = (oreoled_pattern)packet.custom_bytes[CUSTOM_HEADER_LENGTH];
// uint16_t values are stored in custom_bytes in little endian order
// assume the flight controller is little endian when decoding values
uint16_t period =
((0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 7]) << 8) |
(0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 8]);
uint16_t phase_offset =
((0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 9]) << 8) |
(0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 10]);
// call the set_rgb function, using the rest of the bytes as the RGB values
set_rgb(packet.instance, pattern, packet.custom_bytes[CUSTOM_HEADER_LENGTH + 1], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 2],
packet.custom_bytes[CUSTOM_HEADER_LENGTH + 3], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 4], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 5],
packet.custom_bytes[CUSTOM_HEADER_LENGTH + 6], period, phase_offset);
} else if (memcmp(packet.custom_bytes, "SYNC", CUSTOM_HEADER_LENGTH) == 0) { // check for the SYNC sub-command
// check to make sure the total length matches the length of the SYN0 command + data values
if (packet.custom_len != CUSTOM_HEADER_LENGTH + 0) {
return;
}
send_sync();
} else { // unrecognized command
return;
}
} else {
// other patterns sent as macro
set_macro(packet.instance, (oreoled_macro)packet.pattern);
}
_pattern_override = packet.pattern; _pattern_override = packet.pattern;
} }
OreoLED_PX4::oreo_state::oreo_state() {
clear_state();
}
void OreoLED_PX4::oreo_state::clear_state() {
mode = OREOLED_MODE_NONE;
pattern = OREOLED_PATTERN_OFF;
macro = OREOLED_PARAM_MACRO_RESET;
red = 0;
green = 0;
blue = 0;
amplitude_red = 0;
amplitude_green = 0;
amplitude_blue = 0;
period = 0;
repeat = 0;
phase_offset = 0;
}
void OreoLED_PX4::oreo_state::send_sync() {
clear_state();
mode = OREOLED_MODE_SYNC;
}
void OreoLED_PX4::oreo_state::set_macro(oreoled_macro new_macro) {
clear_state();
mode = OREOLED_MODE_MACRO;
macro = new_macro;
}
void OreoLED_PX4::oreo_state::set_rgb(enum oreoled_pattern new_pattern, uint8_t new_red, uint8_t new_green, uint8_t new_blue) {
clear_state();
mode = OREOLED_MODE_RGB;
pattern = new_pattern;
red = new_red;
green = new_green;
blue = new_blue;
}
void OreoLED_PX4::oreo_state::set_rgb(enum oreoled_pattern new_pattern, uint8_t new_red, uint8_t new_green,
uint8_t new_blue, uint8_t new_amplitude_red, uint8_t new_amplitude_green, uint8_t new_amplitude_blue,
uint16_t new_period, uint16_t new_phase_offset) {
clear_state();
mode = OREOLED_MODE_RGB_EXTENDED;
pattern = new_pattern;
red = new_red;
green = new_green;
blue = new_blue;
amplitude_red = new_amplitude_red;
amplitude_green = new_amplitude_green;
amplitude_blue = new_amplitude_blue;
period = new_period;
phase_offset = new_phase_offset;
}
bool OreoLED_PX4::oreo_state::operator==(const OreoLED_PX4::oreo_state &os) {
return ((os.mode==mode) && (os.pattern==pattern) && (os.macro==macro) && (os.red==red) && (os.green==green) && (os.blue==blue)
&& (os.amplitude_red==amplitude_red) && (os.amplitude_green==amplitude_green) && (os.amplitude_blue==amplitude_blue)
&& (os.period==period) && (os.repeat==repeat) && (os.phase_offset==phase_offset));
}
#endif // CONFIG_HAL_BOARD == HAL_BOARD_PX4 #endif // CONFIG_HAL_BOARD == HAL_BOARD_PX4

View File

@ -26,11 +26,13 @@
#define OREOLED_INSTANCE_ALL 0xff // instance number to indicate all LEDs (used for set_rgb and set_macro) #define OREOLED_INSTANCE_ALL 0xff // instance number to indicate all LEDs (used for set_rgb and set_macro)
#define OREOLED_BRIGHT 0xff // maximum brightness when flying (disconnected from usb) #define OREOLED_BRIGHT 0xff // maximum brightness when flying (disconnected from usb)
#define CUSTOM_HEADER_LENGTH 4 // number of bytes in the custom LED buffer that are used to identify the command
class OreoLED_PX4 : public NotifyDevice class OreoLED_PX4 : public NotifyDevice
{ {
public: public:
// constuctor // constuctor
OreoLED_PX4(); OreoLED_PX4(uint8_t theme);
// init - initialised the device // init - initialised the device
bool init(void); bool init(void);
@ -49,23 +51,53 @@ private:
// update_timer - called by scheduler and updates PX4 driver with commands // update_timer - called by scheduler and updates PX4 driver with commands
void update_timer(void); void update_timer(void);
// set_rgb - set color as a combination of red, green and blue values for one or all LEDs // set_rgb - set color as a combination of red, green and blue values for one or all LEDs, pattern defaults to solid color
void set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t blue); void set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t blue);
// set_rgb - set color as a combination of red, green and blue values for one or all LEDs, using the specified pattern
void set_rgb(uint8_t instance, enum oreoled_pattern pattern, uint8_t red, uint8_t green, uint8_t blue);
// set_rgb - set color as a combination of red, green and blue values for one or all LEDs, using the specified pattern and other parameters
void set_rgb(uint8_t instance, oreoled_pattern pattern, uint8_t red, uint8_t green, uint8_t blue,
uint8_t amplitude_red, uint8_t amplitude_green, uint8_t amplitude_blue,
uint16_t period, uint16_t phase_offset);
// set_macro - set macro for one or all LEDs // set_macro - set macro for one or all LEDs
void set_macro(uint8_t instance, enum oreoled_macro macro); void set_macro(uint8_t instance, enum oreoled_macro macro);
// send_sync - force a syncronisation of the LEDs // send_sync - force a syncronisation of the all LED's
void send_sync(void); void send_sync();
// functions to set LEDs to specific patterns. These functions return true if no further updates should be made to LEDs this iteration
bool slow_counter(void);
void sync_counter(void);
bool mode_firmware_update(void);
bool mode_init(void);
bool mode_failsafe_radio(void);
bool set_standard_colors(void);
bool mode_failsafe_batt(void);
bool mode_auto_flight(void);
bool mode_pilot_flight(void);
// Clear the desired state
void clear_state(void);
// oreo led modes (pattern, macro or rgb) // oreo led modes (pattern, macro or rgb)
enum oreoled_mode { enum oreoled_mode {
OREOLED_MODE_PATTERN, OREOLED_MODE_NONE,
OREOLED_MODE_MACRO, OREOLED_MODE_MACRO,
OREOLED_MODE_RGB, OREOLED_MODE_RGB,
OREOLED_MODE_RGB_EXTENDED,
OREOLED_MODE_SYNC OREOLED_MODE_SYNC
}; };
// Oreo LED modes
enum Oreo_LED_Theme {
OreoLED_Disabled = 0,
OreoLED_Aircraft = 1,
OreoLED_Automobile = 2,
};
// oreo_state structure holds possible state of an led // oreo_state structure holds possible state of an led
struct oreo_state { struct oreo_state {
enum oreoled_mode mode; enum oreoled_mode mode;
@ -74,11 +106,28 @@ private:
uint8_t red; uint8_t red;
uint8_t green; uint8_t green;
uint8_t blue; uint8_t blue;
uint8_t amplitude_red;
uint8_t amplitude_green;
uint8_t amplitude_blue;
uint16_t period;
int8_t repeat;
uint16_t phase_offset;
// operator== oreo_state();
inline bool operator==(const oreo_state &os) {
return ((os.mode==mode) && (os.pattern==pattern) && (os.macro==macro) && (os.red==red) && (os.green==green) && (os.blue==blue)); void clear_state();
}
void send_sync();
void set_macro(oreoled_macro new_macro);
void set_rgb(enum oreoled_pattern new_pattern, uint8_t new_red, uint8_t new_green, uint8_t new_blue);
void set_rgb(enum oreoled_pattern new_pattern, uint8_t new_red, uint8_t new_green,
uint8_t new_blue, uint8_t new_amplitude_red, uint8_t new_amplitude_green, uint8_t new_amplitude_blue,
uint16_t new_period, uint16_t new_phase_offset);
bool operator==(const oreo_state &os);
}; };
// private members // private members
@ -89,6 +138,10 @@ private:
oreo_state _state_desired[OREOLED_NUM_LEDS]; // desired state oreo_state _state_desired[OREOLED_NUM_LEDS]; // desired state
oreo_state _state_sent[OREOLED_NUM_LEDS]; // last state sent to led oreo_state _state_sent[OREOLED_NUM_LEDS]; // last state sent to led
uint8_t _pattern_override; // holds last processed pattern override, 0 if we are not overriding a pattern uint8_t _pattern_override; // holds last processed pattern override, 0 if we are not overriding a pattern
uint8_t _oreo_theme; // theme (1=AirCraft, 2=Ground Vehicle)
uint8_t _rear_color_r = 255; // the rear LED red value
uint8_t _rear_color_g = 255; // the rear LED green value
uint8_t _rear_color_b = 255; // the rear LED blue value
}; };
#endif // CONFIG_HAL_BOARD == HAL_BOARD_PX4 #endif // CONFIG_HAL_BOARD == HAL_BOARD_PX4