OreoLED_I2C.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /*
  2. This program is free software: you can redistribute it and/or modify
  3. it under the terms of the GNU General Public License as published by
  4. the Free Software Foundation, either version 3 of the License, or
  5. (at your option) any later version.
  6. This program is distributed in the hope that it will be useful,
  7. but WITHOUT ANY WARRANTY; without even the implied warranty of
  8. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  9. GNU General Public License for more details.
  10. You should have received a copy of the GNU General Public License
  11. along with this program. If not, see <http://www.gnu.org/licenses/>.
  12. */
  13. /*
  14. OreoLED I2C driver. Based primarily on ArduPilot OreoLED_PX4.cpp,
  15. but with some components from orleod.cpp from px4 firmware
  16. */
  17. #include <AP_HAL/AP_HAL.h>
  18. #include <AP_HAL/I2CDevice.h>
  19. #include <AP_BoardConfig/AP_BoardConfig.h>
  20. #include "OreoLED_I2C.h"
  21. #include "AP_Notify.h"
  22. #include <utility>
  23. // OreoLEDs start at address 0x68 and add device number. So LED2 is at 0x6A
  24. #define OREOLED_BASE_I2C_ADDR 0x68
  25. #define OREOLED_BACKLEFT 0 // back left led instance number
  26. #define OREOLED_BACKRIGHT 1 // back right led instance number
  27. #define OREOLED_FRONTRIGHT 2 // front right led instance number
  28. #define OREOLED_FRONTLEFT 3 // front left led instance number
  29. #define PERIOD_SLOW 800 // slow flash rate
  30. #define PERIOD_FAST 500 // fast flash rate
  31. #define PERIOD_SUPER 150 // super fast rate
  32. #define PO_ALTERNATE 180 // 180 degree phase offset
  33. #define OREOLED_BOOT_CMD_BOOT_APP 0x60
  34. #define OREOLED_BOOT_CMD_BOOT_NONCE 0xA2
  35. extern const AP_HAL::HAL& hal;
  36. // constructor
  37. OreoLED_I2C::OreoLED_I2C(uint8_t bus, uint8_t theme):
  38. NotifyDevice(),
  39. _bus(bus),
  40. _oreo_theme(theme)
  41. {
  42. }
  43. //
  44. // Initialize the LEDs
  45. //
  46. bool OreoLED_I2C::init()
  47. {
  48. // first look for led on external bus
  49. _dev = std::move(hal.i2c_mgr->get_device(_bus, OREOLED_BASE_I2C_ADDR));
  50. if (!_dev) {
  51. return false;
  52. }
  53. // register timer
  54. _dev->register_periodic_callback(1000, FUNCTOR_BIND_MEMBER(&OreoLED_I2C::update_timer, void));
  55. // return health
  56. return true;
  57. }
  58. // UPDATE device according to timed_updated. Called at 50Hz
  59. void OreoLED_I2C::update()
  60. {
  61. if (slow_counter()) {
  62. return; // slow rate from 50hz to 10hz
  63. }
  64. if (mode_firmware_update()) {
  65. return; // don't go any further if the Pixhawk is in firmware update
  66. }
  67. if (mode_init()) {
  68. return; // don't go any further if the Pixhawk is initializing
  69. }
  70. if (mode_failsafe_radio()) {
  71. return; // don't go any further if the Pixhawk is is in radio failsafe
  72. }
  73. set_standard_colors();
  74. if (mode_failsafe_batt()) {
  75. return; // stop here if the battery is low.
  76. }
  77. if (_pattern_override) {
  78. return; // stop here if in mavlink LED control override.
  79. }
  80. if (mode_auto_flight()) {
  81. return; // stop here if in an autopilot mode.
  82. }
  83. mode_pilot_flight(); // stop here if in an pilot controlled mode.
  84. }
  85. // Slow the update rate from 50hz to 10hz
  86. // Returns true if counting up
  87. // Returns false and resets one counter hits 5
  88. bool OreoLED_I2C::slow_counter()
  89. {
  90. _slow_count++;
  91. if (_slow_count < 5) {
  92. return true;
  93. } else {
  94. _slow_count = 0;
  95. return false;
  96. }
  97. }
  98. // Procedure for when Pixhawk is in FW update / bootloader
  99. // Makes all LEDs go into color cycle mode
  100. // Returns true if firmware update in progress. False if not
  101. bool OreoLED_I2C::mode_firmware_update()
  102. {
  103. if (AP_Notify::flags.firmware_update) {
  104. set_macro(OREOLED_INSTANCE_ALL, OREOLED_PARAM_MACRO_COLOUR_CYCLE);
  105. return true;
  106. } else {
  107. return false;
  108. }
  109. }
  110. // Makes all LEDs rapidly strobe blue while gyros initialize.
  111. bool OreoLED_I2C::mode_init()
  112. {
  113. if (AP_Notify::flags.initialising) {
  114. set_rgb(OREOLED_INSTANCE_ALL, OREOLED_PATTERN_STROBE, 0, 0, 255,0,0,0,PERIOD_SUPER,0);
  115. return true;
  116. } else {
  117. return false;
  118. }
  119. }
  120. // Procedure for when Pixhawk is in radio failsafe
  121. // LEDs perform alternating Red X pattern
  122. bool OreoLED_I2C::mode_failsafe_radio()
  123. {
  124. if (AP_Notify::flags.failsafe_radio) {
  125. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,0);
  126. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,PO_ALTERNATE);
  127. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,PO_ALTERNATE);
  128. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SLOW,0);
  129. }
  130. return AP_Notify::flags.failsafe_radio;
  131. }
  132. // Procedure to set standard rear LED colors in aviation theme
  133. // Back LEDS White for normal, yellow for GPS not usable, purple for EKF bad]
  134. // Returns true GPS or EKF problem, returns false if all ok
  135. bool OreoLED_I2C::set_standard_colors()
  136. {
  137. if (!(AP_Notify::flags.gps_fusion)) {
  138. _rear_color_r = 255;
  139. _rear_color_g = 50;
  140. _rear_color_b = 0;
  141. return true;
  142. } else if (AP_Notify::flags.ekf_bad) {
  143. _rear_color_r = 255;
  144. _rear_color_g = 0;
  145. _rear_color_b = 255;
  146. return true;
  147. } else {
  148. _rear_color_r = 255;
  149. _rear_color_g = 255;
  150. _rear_color_b = 255;
  151. return false;
  152. }
  153. }
  154. // Procedure to set low battery LED output
  155. // Colors standard
  156. // Fast strobe alternating front/back
  157. bool OreoLED_I2C::mode_failsafe_batt()
  158. {
  159. if (AP_Notify::flags.failsafe_battery) {
  160. switch (_oreo_theme) {
  161. case OreoLED_Aircraft:
  162. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,0);
  163. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 0, 255, 0,0,0,0,PERIOD_FAST,0);
  164. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  165. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  166. break;
  167. case OreoLED_Automobile:
  168. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
  169. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
  170. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  171. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  172. break;
  173. default:
  174. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
  175. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_FAST,0);
  176. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  177. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  178. break;
  179. }
  180. }
  181. return AP_Notify::flags.failsafe_battery;
  182. }
  183. // Procedure for when Pixhawk is in an autopilot mode
  184. // Makes all LEDs strobe super fast using standard colors
  185. bool OreoLED_I2C::mode_auto_flight()
  186. {
  187. switch (_oreo_theme) {
  188. case OreoLED_Aircraft:
  189. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,0);
  190. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 0, 255, 0,0,0,0,PERIOD_SUPER,0);
  191. if ((AP_Notify::flags.pre_arm_check && AP_Notify::flags.pre_arm_gps_check) || AP_Notify::flags.armed) {
  192. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
  193. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
  194. } else {
  195. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
  196. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
  197. }
  198. break;
  199. case OreoLED_Automobile:
  200. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
  201. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
  202. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
  203. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
  204. break;
  205. default:
  206. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
  207. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_STROBE, 255, 255, 255,0,0,0,PERIOD_SUPER,0);
  208. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
  209. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, 255, 0, 0,0,0,0,PERIOD_SUPER,PO_ALTERNATE);
  210. break;
  211. }
  212. return AP_Notify::flags.autopilot_mode;
  213. }
  214. // Procedure for when Pixhawk is in a pilot controlled mode
  215. // All LEDs use standard pattern and colors
  216. bool OreoLED_I2C::mode_pilot_flight()
  217. {
  218. switch (_oreo_theme) {
  219. case OreoLED_Aircraft:
  220. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_SOLID, 255, 0, 0);
  221. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_SOLID, 0, 255, 0);
  222. if ((AP_Notify::flags.pre_arm_check && AP_Notify::flags.pre_arm_gps_check) || AP_Notify::flags.armed) {
  223. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,0);
  224. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_STROBE, _rear_color_r, _rear_color_g, _rear_color_b,0,0,0,PERIOD_FAST,PO_ALTERNATE);
  225. } else {
  226. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
  227. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, _rear_color_r, _rear_color_g, _rear_color_b);
  228. }
  229. break;
  230. case OreoLED_Automobile:
  231. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_SOLID, 255, 255, 255);
  232. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_SOLID, 255, 255, 255);
  233. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, 255, 0, 0);
  234. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, 255, 0, 0);
  235. break;
  236. default:
  237. set_rgb(OREOLED_FRONTLEFT, OREOLED_PATTERN_SOLID, 255, 255, 255);
  238. set_rgb(OREOLED_FRONTRIGHT, OREOLED_PATTERN_SOLID, 255, 255, 255);
  239. set_rgb(OREOLED_BACKLEFT, OREOLED_PATTERN_SOLID, 255, 0, 0);
  240. set_rgb(OREOLED_BACKRIGHT, OREOLED_PATTERN_SOLID, 255, 0, 0);
  241. break;
  242. }
  243. return true;
  244. }
  245. // set_rgb - Solid color settings only
  246. void OreoLED_I2C::set_rgb(uint8_t instance, uint8_t red, uint8_t green, uint8_t blue)
  247. {
  248. set_rgb(instance, OREOLED_PATTERN_SOLID, red, green, blue);
  249. }
  250. // set_rgb - Set a color and selected pattern.
  251. void OreoLED_I2C::set_rgb(uint8_t instance, oreoled_pattern pattern, uint8_t red, uint8_t green, uint8_t blue)
  252. {
  253. // get semaphore
  254. WITH_SEMAPHORE(_sem);
  255. // check for all instances
  256. if (instance == OREOLED_INSTANCE_ALL) {
  257. // store desired rgb for all LEDs
  258. for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
  259. _state_desired[i].set_rgb(pattern, red, green, blue);
  260. if (!(_state_desired[i] == _state_sent[i])) {
  261. _send_required = true;
  262. }
  263. }
  264. } else if (instance < OREOLED_NUM_LEDS) {
  265. // store desired rgb for one LED
  266. _state_desired[instance].set_rgb(pattern, red, green, blue);
  267. if (!(_state_desired[instance] == _state_sent[instance])) {
  268. _send_required = true;
  269. }
  270. }
  271. }
  272. // set_rgb - Sets a color, pattern, and uses extended options for amplitude, period, and phase offset
  273. void OreoLED_I2C::set_rgb(uint8_t instance, oreoled_pattern pattern, uint8_t red, uint8_t green, uint8_t blue,
  274. uint8_t amplitude_red, uint8_t amplitude_green, uint8_t amplitude_blue,
  275. uint16_t period, uint16_t phase_offset)
  276. {
  277. WITH_SEMAPHORE(_sem);
  278. // check for all instances
  279. if (instance == OREOLED_INSTANCE_ALL) {
  280. // store desired rgb for all LEDs
  281. for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
  282. _state_desired[i].set_rgb(pattern, red, green, blue, amplitude_red, amplitude_green, amplitude_blue, period, phase_offset);
  283. if (!(_state_desired[i] == _state_sent[i])) {
  284. _send_required = true;
  285. }
  286. }
  287. } else if (instance < OREOLED_NUM_LEDS) {
  288. // store desired rgb for one LED
  289. _state_desired[instance].set_rgb(pattern, red, green, blue, amplitude_red, amplitude_green, amplitude_blue, period, phase_offset);
  290. if (!(_state_desired[instance] == _state_sent[instance])) {
  291. _send_required = true;
  292. }
  293. }
  294. }
  295. // set_macro - set macro for one or all LEDs
  296. void OreoLED_I2C::set_macro(uint8_t instance, oreoled_macro macro)
  297. {
  298. WITH_SEMAPHORE(_sem);
  299. // check for all instances
  300. if (instance == OREOLED_INSTANCE_ALL) {
  301. // store desired macro for all LEDs
  302. for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
  303. _state_desired[i].set_macro(macro);
  304. if (!(_state_desired[i] == _state_sent[i])) {
  305. _send_required = true;
  306. }
  307. }
  308. } else if (instance < OREOLED_NUM_LEDS) {
  309. // store desired macro for one LED
  310. _state_desired[instance].set_macro(macro);
  311. if (!(_state_desired[instance] == _state_sent[instance])) {
  312. _send_required = true;
  313. }
  314. }
  315. }
  316. // Clear the desired state
  317. void OreoLED_I2C::clear_state(void)
  318. {
  319. WITH_SEMAPHORE(_sem);
  320. for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
  321. _state_desired[i].clear_state();
  322. }
  323. _send_required = false;
  324. }
  325. /*
  326. send a command onto the I2C bus
  327. */
  328. bool OreoLED_I2C::command_send(oreoled_cmd_t &cmd)
  329. {
  330. //printf("sending %u\n", cmd.num_bytes);
  331. _dev->set_address(OREOLED_BASE_I2C_ADDR + cmd.led_num);
  332. /* Calculate XOR CRC and append to the i2c write data */
  333. uint8_t cmd_xor = OREOLED_BASE_I2C_ADDR + cmd.led_num;
  334. for (uint8_t i = 0; i < cmd.num_bytes; i++) {
  335. cmd_xor ^= cmd.buff[i];
  336. }
  337. cmd.buff[cmd.num_bytes++] = cmd_xor;
  338. uint8_t reply[3] {};
  339. bool ret = _dev->transfer(cmd.buff, cmd.num_bytes, reply, sizeof(reply));
  340. //printf("command[%u] %02x %02x %02x %s -> %02x %02x %02x\n", cmd.led_num, ret?"OK":"fail", reply[0], reply[1], reply[2]);
  341. return ret;
  342. }
  343. /*
  344. send boot command to all LEDs
  345. */
  346. void OreoLED_I2C::boot_leds(void)
  347. {
  348. oreoled_cmd_t cmd;
  349. for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
  350. cmd.led_num = i;
  351. cmd.buff[0] = OREOLED_BOOT_CMD_BOOT_APP;
  352. cmd.buff[1] = OREOLED_BOOT_CMD_BOOT_NONCE;
  353. cmd.buff[2] = OREOLED_BASE_I2C_ADDR + i;
  354. cmd.num_bytes = 3;
  355. command_send(cmd);
  356. }
  357. }
  358. // update_timer - called by scheduler and updates driver with commands
  359. void OreoLED_I2C::update_timer(void)
  360. {
  361. WITH_SEMAPHORE(_sem);
  362. uint32_t now = AP_HAL::millis();
  363. if (_boot_count < 20 &&
  364. now - _last_boot_ms > 100) {
  365. // send boot command 20 times
  366. _boot_count++;
  367. _last_boot_ms = now;
  368. boot_leds();
  369. }
  370. // send a sync every 4.1s. The driver uses 4ms, but using
  371. // exactly 4ms does not work. It seems that the oreoled firmware
  372. // relies on the inaccuracy of the NuttX scheduling for this to
  373. // work, and exactly 4ms from ChibiOS causes syncronisation to
  374. // fail
  375. if (now - _last_sync_ms > 4100) {
  376. _last_sync_ms = now;
  377. send_sync();
  378. }
  379. // exit immediately if send not required, or state is being updated
  380. if (!_send_required) {
  381. return;
  382. }
  383. // for each LED
  384. for (uint8_t i=0; i<OREOLED_NUM_LEDS; i++) {
  385. // check for state change
  386. if (true) {
  387. switch (_state_desired[i].mode) {
  388. case OREOLED_MODE_MACRO: {
  389. oreoled_cmd_t cmd {};
  390. cmd.led_num = i;
  391. cmd.buff[0] = OREOLED_PATTERN_PARAMUPDATE;
  392. cmd.buff[1] = OREOLED_PARAM_MACRO;
  393. cmd.buff[2] = _state_desired[i].macro;
  394. cmd.num_bytes = 3;
  395. command_send(cmd);
  396. break;
  397. }
  398. case OREOLED_MODE_RGB: {
  399. oreoled_cmd_t cmd {};
  400. cmd.led_num = i;
  401. cmd.buff[0] = _state_desired[i].pattern;
  402. cmd.buff[1] = OREOLED_PARAM_BIAS_RED;
  403. cmd.buff[2] = _state_desired[i].red;
  404. cmd.buff[3] = OREOLED_PARAM_BIAS_GREEN;
  405. cmd.buff[4] = _state_desired[i].green;
  406. cmd.buff[5] = OREOLED_PARAM_BIAS_BLUE;
  407. cmd.buff[6] = _state_desired[i].blue;
  408. cmd.num_bytes = 7;
  409. command_send(cmd);
  410. break;
  411. }
  412. case OREOLED_MODE_RGB_EXTENDED: {
  413. oreoled_cmd_t cmd {};
  414. cmd.led_num = i;
  415. cmd.buff[0] = _state_desired[i].pattern;
  416. cmd.buff[1] = OREOLED_PARAM_BIAS_RED;
  417. cmd.buff[2] = _state_desired[i].red;
  418. cmd.buff[3] = OREOLED_PARAM_BIAS_GREEN;
  419. cmd.buff[4] = _state_desired[i].green;
  420. cmd.buff[5] = OREOLED_PARAM_BIAS_BLUE;
  421. cmd.buff[6] = _state_desired[i].blue;
  422. cmd.buff[7] = OREOLED_PARAM_AMPLITUDE_RED;
  423. cmd.buff[8] = _state_desired[i].amplitude_red;
  424. cmd.buff[9] = OREOLED_PARAM_AMPLITUDE_GREEN;
  425. cmd.buff[10] = _state_desired[i].amplitude_green;
  426. cmd.buff[11] = OREOLED_PARAM_AMPLITUDE_BLUE;
  427. cmd.buff[12] = _state_desired[i].amplitude_blue;
  428. // Note: The Oreo LED controller expects to receive uint16 values
  429. // in little endian order
  430. cmd.buff[13] = OREOLED_PARAM_PERIOD;
  431. cmd.buff[14] = (_state_desired[i].period & 0xFF00) >> 8;
  432. cmd.buff[15] = (_state_desired[i].period & 0x00FF);
  433. cmd.buff[16] = OREOLED_PARAM_PHASEOFFSET;
  434. cmd.buff[17] = (_state_desired[i].phase_offset & 0xFF00) >> 8;
  435. cmd.buff[18] = (_state_desired[i].phase_offset & 0x00FF);
  436. cmd.num_bytes = 19;
  437. command_send(cmd);
  438. break;
  439. }
  440. default:
  441. break;
  442. };
  443. // save state change
  444. _state_sent[i] = _state_desired[i];
  445. }
  446. }
  447. // flag updates sent
  448. _send_required = false;
  449. }
  450. void OreoLED_I2C::send_sync(void)
  451. {
  452. /* set I2C address to zero */
  453. _dev->set_address(0);
  454. /* prepare command : 0x01 = general hardware call, 0x00 = I2C address of master (but we don't act as a slave so set to zero)*/
  455. uint8_t msg[] = {0x01, 0x00};
  456. /* send I2C command */
  457. _dev->set_retries(0);
  458. _dev->transfer(msg, sizeof(msg), nullptr, 0);
  459. _dev->set_retries(2);
  460. }
  461. // Handle an LED_CONTROL mavlink message
  462. void OreoLED_I2C::handle_led_control(const mavlink_message_t &msg)
  463. {
  464. // decode mavlink message
  465. mavlink_led_control_t packet;
  466. mavlink_msg_led_control_decode(&msg, &packet);
  467. // exit immediately if instance is invalid
  468. if (packet.instance >= OREOLED_NUM_LEDS && packet.instance != OREOLED_INSTANCE_ALL) {
  469. return;
  470. }
  471. // if pattern is OFF, we clear pattern override so normal lighting should resume
  472. if (packet.pattern == LED_CONTROL_PATTERN_OFF) {
  473. _pattern_override = 0;
  474. clear_state();
  475. return;
  476. }
  477. if (packet.pattern == LED_CONTROL_PATTERN_CUSTOM) {
  478. // Here we handle two different "sub commands",
  479. // depending on the bytes in the first CUSTOM_HEADER_LENGTH
  480. // of the custom pattern byte buffer
  481. // Return if we don't have at least CUSTOM_HEADER_LENGTH bytes
  482. if (packet.custom_len < CUSTOM_HEADER_LENGTH) {
  483. return;
  484. }
  485. // check for the RGB0 sub-command
  486. if (memcmp(packet.custom_bytes, "RGB0", CUSTOM_HEADER_LENGTH) == 0) {
  487. // check to make sure the total length matches the length of the RGB0 command + data values
  488. if (packet.custom_len != CUSTOM_HEADER_LENGTH + 4) {
  489. return;
  490. }
  491. // check for valid pattern id
  492. if (packet.custom_bytes[CUSTOM_HEADER_LENGTH] >= OREOLED_PATTERN_ENUM_COUNT) {
  493. return;
  494. }
  495. // convert the first byte after the command to a oreoled_pattern
  496. oreoled_pattern pattern = (oreoled_pattern)packet.custom_bytes[CUSTOM_HEADER_LENGTH];
  497. // call the set_rgb function, using the rest of the bytes as the RGB values
  498. 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]);
  499. } else if (memcmp(packet.custom_bytes, "RGB1", CUSTOM_HEADER_LENGTH) == 0) { // check for the RGB1 sub-command
  500. // check to make sure the total length matches the length of the RGB1 command + data values
  501. if (packet.custom_len != CUSTOM_HEADER_LENGTH + 11) {
  502. return;
  503. }
  504. // check for valid pattern id
  505. if (packet.custom_bytes[CUSTOM_HEADER_LENGTH] >= OREOLED_PATTERN_ENUM_COUNT) {
  506. return;
  507. }
  508. // convert the first byte after the command to a oreoled_pattern
  509. oreoled_pattern pattern = (oreoled_pattern)packet.custom_bytes[CUSTOM_HEADER_LENGTH];
  510. // uint16_t values are stored in custom_bytes in little endian order
  511. // assume the flight controller is little endian when decoding values
  512. uint16_t period =
  513. ((0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 7]) << 8) |
  514. (0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 8]);
  515. uint16_t phase_offset =
  516. ((0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 9]) << 8) |
  517. (0x00FF & (uint16_t)packet.custom_bytes[CUSTOM_HEADER_LENGTH + 10]);
  518. // call the set_rgb function, using the rest of the bytes as the RGB values
  519. set_rgb(packet.instance, pattern, packet.custom_bytes[CUSTOM_HEADER_LENGTH + 1], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 2],
  520. packet.custom_bytes[CUSTOM_HEADER_LENGTH + 3], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 4], packet.custom_bytes[CUSTOM_HEADER_LENGTH + 5],
  521. packet.custom_bytes[CUSTOM_HEADER_LENGTH + 6], period, phase_offset);
  522. } else { // unrecognized command
  523. return;
  524. }
  525. } else {
  526. // other patterns sent as macro
  527. set_macro(packet.instance, (oreoled_macro)packet.pattern);
  528. }
  529. _pattern_override = packet.pattern;
  530. }
  531. OreoLED_I2C::oreo_state::oreo_state()
  532. {
  533. clear_state();
  534. }
  535. void OreoLED_I2C::oreo_state::clear_state()
  536. {
  537. mode = OREOLED_MODE_NONE;
  538. pattern = OREOLED_PATTERN_OFF;
  539. macro = OREOLED_PARAM_MACRO_RESET;
  540. red = 0;
  541. green = 0;
  542. blue = 0;
  543. amplitude_red = 0;
  544. amplitude_green = 0;
  545. amplitude_blue = 0;
  546. period = 0;
  547. repeat = 0;
  548. phase_offset = 0;
  549. }
  550. void OreoLED_I2C::oreo_state::set_macro(oreoled_macro new_macro)
  551. {
  552. clear_state();
  553. mode = OREOLED_MODE_MACRO;
  554. macro = new_macro;
  555. }
  556. void OreoLED_I2C::oreo_state::set_rgb(enum oreoled_pattern new_pattern, uint8_t new_red, uint8_t new_green, uint8_t new_blue)
  557. {
  558. clear_state();
  559. mode = OREOLED_MODE_RGB;
  560. pattern = new_pattern;
  561. red = new_red;
  562. green = new_green;
  563. blue = new_blue;
  564. }
  565. void OreoLED_I2C::oreo_state::set_rgb(enum oreoled_pattern new_pattern, uint8_t new_red, uint8_t new_green,
  566. uint8_t new_blue, uint8_t new_amplitude_red, uint8_t new_amplitude_green, uint8_t new_amplitude_blue,
  567. uint16_t new_period, uint16_t new_phase_offset)
  568. {
  569. clear_state();
  570. mode = OREOLED_MODE_RGB_EXTENDED;
  571. pattern = new_pattern;
  572. red = new_red;
  573. green = new_green;
  574. blue = new_blue;
  575. amplitude_red = new_amplitude_red;
  576. amplitude_green = new_amplitude_green;
  577. amplitude_blue = new_amplitude_blue;
  578. period = new_period;
  579. phase_offset = new_phase_offset;
  580. }
  581. bool OreoLED_I2C::oreo_state::operator==(const OreoLED_I2C::oreo_state &os)
  582. {
  583. return ((os.mode==mode) && (os.pattern==pattern) && (os.macro==macro) && (os.red==red) && (os.green==green) && (os.blue==blue)
  584. && (os.amplitude_red==amplitude_red) && (os.amplitude_green==amplitude_green) && (os.amplitude_blue==amplitude_blue)
  585. && (os.period==period) && (os.repeat==repeat) && (os.phase_offset==phase_offset));
  586. }