notification-app.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. #include <furi.h>
  2. #include <api-hal.h>
  3. #include <storage/storage.h>
  4. #include "notification.h"
  5. #include "notification-messages.h"
  6. #include "notification-app.h"
  7. static const uint8_t minimal_delay = 100;
  8. static const uint8_t led_off_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
  9. static const uint8_t reset_red_mask = 1 << 0;
  10. static const uint8_t reset_green_mask = 1 << 1;
  11. static const uint8_t reset_blue_mask = 1 << 2;
  12. static const uint8_t reset_vibro_mask = 1 << 3;
  13. static const uint8_t reset_sound_mask = 1 << 4;
  14. static const uint8_t reset_display_mask = 1 << 5;
  15. void notification_vibro_on();
  16. void notification_vibro_off();
  17. void notification_sound_on(float pwm, float freq);
  18. void notification_sound_off();
  19. uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value);
  20. uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value);
  21. uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app);
  22. void notification_message_save_settings(NotificationApp* app) {
  23. NotificationAppMessage m = {.type = SaveSettingsMessage, .back_event = osEventFlagsNew(NULL)};
  24. furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK);
  25. osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever);
  26. osEventFlagsDelete(m.back_event);
  27. };
  28. // internal layer
  29. void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) {
  30. furi_assert(layer);
  31. furi_assert(layer->index < LayerMAX);
  32. // set value
  33. layer->value[LayerInternal] = layer_value;
  34. // apply if current layer is internal
  35. if(layer->index == LayerInternal) {
  36. api_hal_light_set(layer->light, layer->value[LayerInternal]);
  37. }
  38. }
  39. bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) {
  40. bool result = false;
  41. if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) ||
  42. (app->led[2].index == LayerInternal)) {
  43. if((app->led[0].value[LayerInternal] != 0x00) ||
  44. (app->led[1].value[LayerInternal] != 0x00) ||
  45. (app->led[2].value[LayerInternal] != 0x00)) {
  46. result = true;
  47. }
  48. }
  49. return result;
  50. }
  51. // notification layer
  52. void notification_apply_notification_led_layer(
  53. NotificationLedLayer* layer,
  54. const uint8_t layer_value) {
  55. furi_assert(layer);
  56. furi_assert(layer->index < LayerMAX);
  57. // set value
  58. layer->index = LayerNotification;
  59. // set layer
  60. layer->value[LayerNotification] = layer_value;
  61. // apply
  62. api_hal_light_set(layer->light, layer->value[LayerNotification]);
  63. }
  64. void notification_reset_notification_led_layer(NotificationLedLayer* layer) {
  65. furi_assert(layer);
  66. furi_assert(layer->index < LayerMAX);
  67. // set value
  68. layer->value[LayerNotification] = 0;
  69. // set layer
  70. layer->index = LayerInternal;
  71. // apply
  72. api_hal_light_set(layer->light, layer->value[LayerInternal]);
  73. }
  74. void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) {
  75. if(reset_mask & reset_red_mask) {
  76. notification_reset_notification_led_layer(&app->led[0]);
  77. }
  78. if(reset_mask & reset_green_mask) {
  79. notification_reset_notification_led_layer(&app->led[1]);
  80. }
  81. if(reset_mask & reset_blue_mask) {
  82. notification_reset_notification_led_layer(&app->led[2]);
  83. }
  84. if(reset_mask & reset_vibro_mask) {
  85. notification_vibro_off();
  86. }
  87. if(reset_mask & reset_sound_mask) {
  88. notification_sound_off();
  89. }
  90. if(reset_mask & reset_display_mask) {
  91. osTimerStart(app->display_timer, notification_settings_display_off_delay_ticks(app));
  92. }
  93. }
  94. static void notification_apply_notification_leds(NotificationApp* app, const uint8_t* values) {
  95. for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
  96. notification_apply_notification_led_layer(
  97. &app->led[i], notification_settings_get_rgb_led_brightness(app, values[i]));
  98. }
  99. }
  100. // settings
  101. uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value) {
  102. return (value * app->settings.display_brightness);
  103. }
  104. uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) {
  105. return (value * app->settings.led_brightness);
  106. }
  107. uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) {
  108. return ((float)(app->settings.display_off_delay_ms) / (1000.0f / osKernelGetTickFreq()));
  109. }
  110. // generics
  111. void notification_vibro_on() {
  112. api_hal_vibro_on(true);
  113. }
  114. void notification_vibro_off() {
  115. api_hal_vibro_on(false);
  116. }
  117. void notification_sound_on(float pwm, float freq) {
  118. hal_pwm_set(pwm, freq, &SPEAKER_TIM, SPEAKER_CH);
  119. }
  120. void notification_sound_off() {
  121. hal_pwm_stop(&SPEAKER_TIM, SPEAKER_CH);
  122. }
  123. // display timer
  124. static void notification_display_timer(void* ctx) {
  125. furi_assert(ctx);
  126. NotificationApp* app = ctx;
  127. notification_message(app, &sequence_display_off);
  128. }
  129. // message processing
  130. void notification_process_notification_message(
  131. NotificationApp* app,
  132. NotificationAppMessage* message) {
  133. uint32_t notification_message_index = 0;
  134. const NotificationMessage* notification_message;
  135. notification_message = (*message->sequence)[notification_message_index];
  136. bool led_active = false;
  137. uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
  138. bool reset_notifications = true;
  139. uint8_t reset_mask = 0;
  140. while(notification_message != NULL) {
  141. switch(notification_message->type) {
  142. case NotificationMessageTypeLedDisplay:
  143. // if on - switch on and start timer
  144. // if off - switch off and stop timer
  145. // on timer - switch off
  146. if(notification_message->data.led.value > 0x00) {
  147. notification_apply_notification_led_layer(
  148. &app->display,
  149. notification_settings_get_display_brightness(
  150. app, notification_message->data.led.value));
  151. } else {
  152. notification_reset_notification_led_layer(&app->display);
  153. if(osTimerIsRunning(app->display_timer)) {
  154. osTimerStop(app->display_timer);
  155. }
  156. }
  157. reset_mask |= reset_display_mask;
  158. break;
  159. case NotificationMessageTypeLedRed:
  160. // store and send on delay or after seq
  161. led_active = true;
  162. led_values[0] = notification_message->data.led.value;
  163. reset_mask |= reset_red_mask;
  164. break;
  165. case NotificationMessageTypeLedGreen:
  166. // store and send on delay or after seq
  167. led_active = true;
  168. led_values[1] = notification_message->data.led.value;
  169. reset_mask |= reset_green_mask;
  170. break;
  171. case NotificationMessageTypeLedBlue:
  172. // store and send on delay or after seq
  173. led_active = true;
  174. led_values[2] = notification_message->data.led.value;
  175. reset_mask |= reset_blue_mask;
  176. break;
  177. case NotificationMessageTypeVibro:
  178. if(notification_message->data.vibro.on) {
  179. if(app->settings.vibro_on) notification_vibro_on();
  180. } else {
  181. notification_vibro_off();
  182. }
  183. reset_mask |= reset_vibro_mask;
  184. break;
  185. case NotificationMessageTypeSoundOn:
  186. notification_sound_on(
  187. notification_message->data.sound.pwm * app->settings.speaker_volume,
  188. notification_message->data.sound.frequency);
  189. reset_mask |= reset_sound_mask;
  190. break;
  191. case NotificationMessageTypeSoundOff:
  192. notification_sound_off();
  193. reset_mask |= reset_sound_mask;
  194. break;
  195. case NotificationMessageTypeDelay:
  196. if(led_active) {
  197. if(notification_is_any_led_layer_internal_and_not_empty(app)) {
  198. notification_apply_notification_leds(app, led_off_values);
  199. delay(minimal_delay);
  200. }
  201. led_active = false;
  202. notification_apply_notification_leds(app, led_values);
  203. reset_mask |= reset_red_mask;
  204. reset_mask |= reset_green_mask;
  205. reset_mask |= reset_blue_mask;
  206. }
  207. delay(notification_message->data.delay.length);
  208. break;
  209. case NotificationMessageTypeDoNotReset:
  210. reset_notifications = false;
  211. break;
  212. }
  213. notification_message_index++;
  214. notification_message = (*message->sequence)[notification_message_index];
  215. };
  216. // send and do minimal delay
  217. if(led_active) {
  218. bool need_minimal_delay = false;
  219. if(notification_is_any_led_layer_internal_and_not_empty(app)) {
  220. need_minimal_delay = true;
  221. }
  222. led_active = false;
  223. notification_apply_notification_leds(app, led_values);
  224. reset_mask |= reset_red_mask;
  225. reset_mask |= reset_green_mask;
  226. reset_mask |= reset_blue_mask;
  227. if(need_minimal_delay) {
  228. notification_apply_notification_leds(app, led_off_values);
  229. delay(minimal_delay);
  230. }
  231. }
  232. if(reset_notifications) {
  233. notification_reset_notification_layer(app, reset_mask);
  234. }
  235. }
  236. void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) {
  237. uint32_t notification_message_index = 0;
  238. const NotificationMessage* notification_message;
  239. notification_message = (*message->sequence)[notification_message_index];
  240. while(notification_message != NULL) {
  241. switch(notification_message->type) {
  242. case NotificationMessageTypeLedDisplay:
  243. notification_apply_internal_led_layer(
  244. &app->display,
  245. notification_settings_get_display_brightness(
  246. app, notification_message->data.led.value));
  247. break;
  248. case NotificationMessageTypeLedRed:
  249. notification_apply_internal_led_layer(
  250. &app->led[0],
  251. notification_settings_get_rgb_led_brightness(
  252. app, notification_message->data.led.value));
  253. break;
  254. case NotificationMessageTypeLedGreen:
  255. notification_apply_internal_led_layer(
  256. &app->led[1],
  257. notification_settings_get_rgb_led_brightness(
  258. app, notification_message->data.led.value));
  259. break;
  260. case NotificationMessageTypeLedBlue:
  261. notification_apply_internal_led_layer(
  262. &app->led[2],
  263. notification_settings_get_rgb_led_brightness(
  264. app, notification_message->data.led.value));
  265. break;
  266. default:
  267. break;
  268. }
  269. notification_message_index++;
  270. notification_message = (*message->sequence)[notification_message_index];
  271. }
  272. }
  273. static bool notification_load_settings(NotificationApp* app) {
  274. NotificationSettings settings;
  275. File* file = storage_file_alloc(furi_record_open("storage"));
  276. const size_t settings_size = sizeof(NotificationSettings);
  277. FURI_LOG_I("notification", "loading settings from \"%s\"", NOTIFICATION_SETTINGS_PATH);
  278. bool fs_result =
  279. storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
  280. if(fs_result) {
  281. uint16_t bytes_count = storage_file_read(file, &settings, settings_size);
  282. if(bytes_count != settings_size) {
  283. fs_result = false;
  284. }
  285. }
  286. if(fs_result) {
  287. FURI_LOG_I("notification", "load success");
  288. if(settings.version != NOTIFICATION_SETTINGS_VERSION) {
  289. FURI_LOG_E(
  290. "notification",
  291. "version(%d != %d) mismatch",
  292. app->settings.version,
  293. NOTIFICATION_SETTINGS_VERSION);
  294. } else {
  295. osKernelLock();
  296. memcpy(&app->settings, &settings, settings_size);
  297. osKernelUnlock();
  298. }
  299. } else {
  300. FURI_LOG_E("notification", "load failed, %s", storage_file_get_error_desc(file));
  301. }
  302. storage_file_close(file);
  303. storage_file_free(file);
  304. furi_record_close("storage");
  305. return fs_result;
  306. };
  307. static bool notification_save_settings(NotificationApp* app) {
  308. NotificationSettings settings;
  309. File* file = storage_file_alloc(furi_record_open("storage"));
  310. const size_t settings_size = sizeof(NotificationSettings);
  311. FURI_LOG_I("notification", "saving settings to \"%s\"", NOTIFICATION_SETTINGS_PATH);
  312. osKernelLock();
  313. memcpy(&settings, &app->settings, settings_size);
  314. osKernelUnlock();
  315. bool fs_result =
  316. storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS);
  317. if(fs_result) {
  318. uint16_t bytes_count = storage_file_write(file, &settings, settings_size);
  319. if(bytes_count != settings_size) {
  320. fs_result = false;
  321. }
  322. }
  323. if(fs_result) {
  324. FURI_LOG_I("notification", "save success");
  325. } else {
  326. FURI_LOG_E("notification", "save failed, %s", storage_file_get_error_desc(file));
  327. }
  328. storage_file_close(file);
  329. storage_file_free(file);
  330. furi_record_close("storage");
  331. return fs_result;
  332. };
  333. static void input_event_callback(const void* value, void* context) {
  334. NotificationApp* app = context;
  335. notification_message(app, &sequence_display_on);
  336. }
  337. // App alloc
  338. static NotificationApp* notification_app_alloc() {
  339. NotificationApp* app = furi_alloc(sizeof(NotificationApp));
  340. app->queue = osMessageQueueNew(8, sizeof(NotificationAppMessage), NULL);
  341. app->display_timer = osTimerNew(notification_display_timer, osTimerOnce, app, NULL);
  342. app->settings.speaker_volume = 1.0f;
  343. app->settings.display_brightness = 1.0f;
  344. app->settings.led_brightness = 1.0f;
  345. app->settings.display_off_delay_ms = 30000;
  346. app->settings.vibro_on = true;
  347. app->display.value[LayerInternal] = 0x00;
  348. app->display.value[LayerNotification] = 0x00;
  349. app->display.index = LayerInternal;
  350. app->display.light = LightBacklight;
  351. app->led[0].value[LayerInternal] = 0x00;
  352. app->led[0].value[LayerNotification] = 0x00;
  353. app->led[0].index = LayerInternal;
  354. app->led[0].light = LightRed;
  355. app->led[1].value[LayerInternal] = 0x00;
  356. app->led[1].value[LayerNotification] = 0x00;
  357. app->led[1].index = LayerInternal;
  358. app->led[1].light = LightGreen;
  359. app->led[2].value[LayerInternal] = 0x00;
  360. app->led[2].value[LayerNotification] = 0x00;
  361. app->led[2].index = LayerInternal;
  362. app->led[2].light = LightBlue;
  363. app->settings.version = NOTIFICATION_SETTINGS_VERSION;
  364. // display backlight control
  365. app->event_record = furi_record_open("input_events");
  366. subscribe_pubsub(app->event_record, input_event_callback, app);
  367. notification_message(app, &sequence_display_on);
  368. return app;
  369. };
  370. // App
  371. int32_t notification_app(void* p) {
  372. NotificationApp* app = notification_app_alloc();
  373. if(!notification_load_settings(app)) {
  374. notification_save_settings(app);
  375. }
  376. notification_vibro_off();
  377. notification_sound_off();
  378. notification_apply_internal_led_layer(&app->display, 0x00);
  379. notification_apply_internal_led_layer(&app->led[0], 0x00);
  380. notification_apply_internal_led_layer(&app->led[1], 0x00);
  381. notification_apply_internal_led_layer(&app->led[2], 0x00);
  382. furi_record_create("notification", app);
  383. NotificationAppMessage message;
  384. while(1) {
  385. furi_check(osMessageQueueGet(app->queue, &message, NULL, osWaitForever) == osOK);
  386. switch(message.type) {
  387. case NotificationLayerMessage:
  388. notification_process_notification_message(app, &message);
  389. break;
  390. case InternalLayerMessage:
  391. notification_process_internal_message(app, &message);
  392. break;
  393. case SaveSettingsMessage:
  394. notification_save_settings(app);
  395. break;
  396. }
  397. if(message.back_event != NULL) {
  398. osEventFlagsSet(message.back_event, NOTIFICATION_EVENT_COMPLETE);
  399. }
  400. }
  401. return 0;
  402. };