| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552 |
- #include "furi_hal_light.h"
- #include <furi.h>
- #include <furi_hal.h>
- #include <storage/storage.h>
- #include <input/input.h>
- #include "notification.h"
- #include "notification_messages.h"
- #include "notification_app.h"
- #define TAG "NotificationSrv"
- static const uint8_t minimal_delay = 100;
- static const uint8_t led_off_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
- static const uint8_t reset_red_mask = 1 << 0;
- static const uint8_t reset_green_mask = 1 << 1;
- static const uint8_t reset_blue_mask = 1 << 2;
- static const uint8_t reset_vibro_mask = 1 << 3;
- static const uint8_t reset_sound_mask = 1 << 4;
- static const uint8_t reset_display_mask = 1 << 5;
- static const uint8_t reset_blink_mask = 1 << 6;
- void notification_vibro_on();
- void notification_vibro_off();
- void notification_sound_on(float pwm, float freq);
- void notification_sound_off();
- uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value);
- uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value);
- uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app);
- void notification_message_save_settings(NotificationApp* app) {
- NotificationAppMessage m = {.type = SaveSettingsMessage, .back_event = osEventFlagsNew(NULL)};
- furi_check(osMessageQueuePut(app->queue, &m, 0, osWaitForever) == osOK);
- osEventFlagsWait(m.back_event, NOTIFICATION_EVENT_COMPLETE, osFlagsWaitAny, osWaitForever);
- osEventFlagsDelete(m.back_event);
- };
- // internal layer
- void notification_apply_internal_led_layer(NotificationLedLayer* layer, uint8_t layer_value) {
- furi_assert(layer);
- furi_assert(layer->index < LayerMAX);
- // set value
- layer->value[LayerInternal] = layer_value;
- // apply if current layer is internal
- if(layer->index == LayerInternal) {
- furi_hal_light_set(layer->light, layer->value[LayerInternal]);
- }
- }
- bool notification_is_any_led_layer_internal_and_not_empty(NotificationApp* app) {
- bool result = false;
- if((app->led[0].index == LayerInternal) || (app->led[1].index == LayerInternal) ||
- (app->led[2].index == LayerInternal)) {
- if((app->led[0].value[LayerInternal] != 0x00) ||
- (app->led[1].value[LayerInternal] != 0x00) ||
- (app->led[2].value[LayerInternal] != 0x00)) {
- result = true;
- }
- }
- return result;
- }
- // notification layer
- void notification_apply_notification_led_layer(
- NotificationLedLayer* layer,
- const uint8_t layer_value) {
- furi_assert(layer);
- furi_assert(layer->index < LayerMAX);
- // set value
- layer->index = LayerNotification;
- // set layer
- layer->value[LayerNotification] = layer_value;
- // apply
- furi_hal_light_set(layer->light, layer->value[LayerNotification]);
- }
- void notification_reset_notification_led_layer(NotificationLedLayer* layer) {
- furi_assert(layer);
- furi_assert(layer->index < LayerMAX);
- // set value
- layer->value[LayerNotification] = 0;
- // set layer
- layer->index = LayerInternal;
- // apply
- furi_hal_light_set(layer->light, layer->value[LayerInternal]);
- }
- void notification_reset_notification_layer(NotificationApp* app, uint8_t reset_mask) {
- if(reset_mask & reset_red_mask) {
- notification_reset_notification_led_layer(&app->led[0]);
- }
- if(reset_mask & reset_green_mask) {
- notification_reset_notification_led_layer(&app->led[1]);
- }
- if(reset_mask & reset_blue_mask) {
- notification_reset_notification_led_layer(&app->led[2]);
- }
- if(reset_mask & reset_blink_mask) {
- furi_hal_light_blink_stop();
- }
- if(reset_mask & reset_vibro_mask) {
- notification_vibro_off();
- }
- if(reset_mask & reset_sound_mask) {
- notification_sound_off();
- }
- if(reset_mask & reset_display_mask) {
- osTimerStart(app->display_timer, notification_settings_display_off_delay_ticks(app));
- }
- }
- static void notification_apply_notification_leds(NotificationApp* app, const uint8_t* values) {
- for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
- notification_apply_notification_led_layer(
- &app->led[i], notification_settings_get_rgb_led_brightness(app, values[i]));
- }
- }
- // settings
- uint8_t notification_settings_get_display_brightness(NotificationApp* app, uint8_t value) {
- return (value * app->settings.display_brightness);
- }
- uint8_t notification_settings_get_rgb_led_brightness(NotificationApp* app, uint8_t value) {
- return (value * app->settings.led_brightness);
- }
- uint32_t notification_settings_display_off_delay_ticks(NotificationApp* app) {
- return ((float)(app->settings.display_off_delay_ms) / (1000.0f / osKernelGetTickFreq()));
- }
- // generics
- void notification_vibro_on() {
- furi_hal_vibro_on(true);
- }
- void notification_vibro_off() {
- furi_hal_vibro_on(false);
- }
- void notification_sound_on(float freq, float volume) {
- furi_hal_speaker_start(freq, volume);
- }
- void notification_sound_off() {
- furi_hal_speaker_stop();
- }
- // display timer
- static void notification_display_timer(void* ctx) {
- furi_assert(ctx);
- NotificationApp* app = ctx;
- notification_message(app, &sequence_display_backlight_off);
- }
- // message processing
- void notification_process_notification_message(
- NotificationApp* app,
- NotificationAppMessage* message) {
- uint32_t notification_message_index = 0;
- const NotificationMessage* notification_message;
- notification_message = (*message->sequence)[notification_message_index];
- bool led_active = false;
- uint8_t led_values[NOTIFICATION_LED_COUNT] = {0x00, 0x00, 0x00};
- bool reset_notifications = true;
- float speaker_volume_setting = app->settings.speaker_volume;
- bool vibro_setting = app->settings.vibro_on;
- float display_brightness_setting = app->settings.display_brightness;
- uint8_t reset_mask = 0;
- while(notification_message != NULL) {
- switch(notification_message->type) {
- case NotificationMessageTypeLedDisplayBacklight:
- // if on - switch on and start timer
- // if off - switch off and stop timer
- // on timer - switch off
- if(notification_message->data.led.value > 0x00) {
- notification_apply_notification_led_layer(
- &app->display,
- notification_message->data.led.value * display_brightness_setting);
- } else {
- notification_reset_notification_led_layer(&app->display);
- if(osTimerIsRunning(app->display_timer)) {
- osTimerStop(app->display_timer);
- }
- }
- reset_mask |= reset_display_mask;
- break;
- case NotificationMessageTypeLedDisplayBacklightEnforceOn:
- furi_assert(app->display_led_lock < UINT8_MAX);
- app->display_led_lock++;
- if(app->display_led_lock == 1) {
- notification_apply_internal_led_layer(
- &app->display,
- notification_message->data.led.value * display_brightness_setting);
- }
- break;
- case NotificationMessageTypeLedDisplayBacklightEnforceAuto:
- furi_assert(app->display_led_lock > 0);
- app->display_led_lock--;
- if(app->display_led_lock == 0) {
- notification_apply_internal_led_layer(
- &app->display,
- notification_message->data.led.value * display_brightness_setting);
- }
- break;
- case NotificationMessageTypeLedRed:
- // store and send on delay or after seq
- led_active = true;
- led_values[0] = notification_message->data.led.value;
- app->led[0].value_last[LayerNotification] = led_values[0];
- reset_mask |= reset_red_mask;
- break;
- case NotificationMessageTypeLedGreen:
- // store and send on delay or after seq
- led_active = true;
- led_values[1] = notification_message->data.led.value;
- app->led[1].value_last[LayerNotification] = led_values[1];
- reset_mask |= reset_green_mask;
- break;
- case NotificationMessageTypeLedBlue:
- // store and send on delay or after seq
- led_active = true;
- led_values[2] = notification_message->data.led.value;
- app->led[2].value_last[LayerNotification] = led_values[2];
- reset_mask |= reset_blue_mask;
- break;
- case NotificationMessageTypeLedBlinkStart:
- // store and send on delay or after seq
- led_active = true;
- furi_hal_light_blink_start(
- notification_message->data.led_blink.color,
- app->settings.led_brightness * 255,
- notification_message->data.led_blink.on_time,
- notification_message->data.led_blink.period);
- reset_mask |= reset_blink_mask;
- break;
- case NotificationMessageTypeLedBlinkColor:
- led_active = true;
- furi_hal_light_blink_set_color(notification_message->data.led_blink.color);
- break;
- case NotificationMessageTypeLedBlinkStop:
- furi_hal_light_blink_stop();
- reset_mask &= ~reset_blink_mask;
- break;
- case NotificationMessageTypeVibro:
- if(notification_message->data.vibro.on) {
- if(vibro_setting) notification_vibro_on();
- } else {
- notification_vibro_off();
- }
- reset_mask |= reset_vibro_mask;
- break;
- case NotificationMessageTypeSoundOn:
- notification_sound_on(
- notification_message->data.sound.frequency,
- notification_message->data.sound.volume * speaker_volume_setting);
- reset_mask |= reset_sound_mask;
- break;
- case NotificationMessageTypeSoundOff:
- notification_sound_off();
- reset_mask |= reset_sound_mask;
- break;
- case NotificationMessageTypeDelay:
- if(led_active) {
- if(notification_is_any_led_layer_internal_and_not_empty(app)) {
- notification_apply_notification_leds(app, led_off_values);
- furi_hal_delay_ms(minimal_delay);
- }
- led_active = false;
- notification_apply_notification_leds(app, led_values);
- reset_mask |= reset_red_mask;
- reset_mask |= reset_green_mask;
- reset_mask |= reset_blue_mask;
- }
- furi_hal_delay_ms(notification_message->data.delay.length);
- break;
- case NotificationMessageTypeDoNotReset:
- reset_notifications = false;
- break;
- case NotificationMessageTypeForceSpeakerVolumeSetting:
- speaker_volume_setting = notification_message->data.forced_settings.speaker_volume;
- break;
- case NotificationMessageTypeForceVibroSetting:
- vibro_setting = notification_message->data.forced_settings.vibro;
- break;
- case NotificationMessageTypeForceDisplayBrightnessSetting:
- display_brightness_setting =
- notification_message->data.forced_settings.display_brightness;
- break;
- case NotificationMessageTypeLedBrightnessSettingApply:
- led_active = true;
- for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
- led_values[i] = app->led[i].value_last[LayerNotification];
- }
- reset_mask |= reset_red_mask;
- reset_mask |= reset_green_mask;
- reset_mask |= reset_blue_mask;
- break;
- }
- notification_message_index++;
- notification_message = (*message->sequence)[notification_message_index];
- };
- // send and do minimal delay
- if(led_active) {
- bool need_minimal_delay = false;
- if(notification_is_any_led_layer_internal_and_not_empty(app)) {
- need_minimal_delay = true;
- }
- notification_apply_notification_leds(app, led_values);
- reset_mask |= reset_red_mask;
- reset_mask |= reset_green_mask;
- reset_mask |= reset_blue_mask;
- if(need_minimal_delay) {
- notification_apply_notification_leds(app, led_off_values);
- furi_hal_delay_ms(minimal_delay);
- }
- }
- if(reset_notifications) {
- notification_reset_notification_layer(app, reset_mask);
- }
- }
- void notification_process_internal_message(NotificationApp* app, NotificationAppMessage* message) {
- uint32_t notification_message_index = 0;
- const NotificationMessage* notification_message;
- notification_message = (*message->sequence)[notification_message_index];
- while(notification_message != NULL) {
- switch(notification_message->type) {
- case NotificationMessageTypeLedDisplayBacklight:
- notification_apply_internal_led_layer(
- &app->display,
- notification_settings_get_display_brightness(
- app, notification_message->data.led.value));
- break;
- case NotificationMessageTypeLedRed:
- app->led[0].value_last[LayerInternal] = notification_message->data.led.value;
- notification_apply_internal_led_layer(
- &app->led[0],
- notification_settings_get_rgb_led_brightness(
- app, notification_message->data.led.value));
- break;
- case NotificationMessageTypeLedGreen:
- app->led[1].value_last[LayerInternal] = notification_message->data.led.value;
- notification_apply_internal_led_layer(
- &app->led[1],
- notification_settings_get_rgb_led_brightness(
- app, notification_message->data.led.value));
- break;
- case NotificationMessageTypeLedBlue:
- app->led[2].value_last[LayerInternal] = notification_message->data.led.value;
- notification_apply_internal_led_layer(
- &app->led[2],
- notification_settings_get_rgb_led_brightness(
- app, notification_message->data.led.value));
- break;
- case NotificationMessageTypeLedBrightnessSettingApply:
- for(uint8_t i = 0; i < NOTIFICATION_LED_COUNT; i++) {
- uint8_t new_val = notification_settings_get_rgb_led_brightness(
- app, app->led[i].value_last[LayerInternal]);
- notification_apply_internal_led_layer(&app->led[i], new_val);
- }
- break;
- default:
- break;
- }
- notification_message_index++;
- notification_message = (*message->sequence)[notification_message_index];
- }
- }
- static bool notification_load_settings(NotificationApp* app) {
- NotificationSettings settings;
- File* file = storage_file_alloc(furi_record_open("storage"));
- const size_t settings_size = sizeof(NotificationSettings);
- FURI_LOG_I(TAG, "loading settings from \"%s\"", NOTIFICATION_SETTINGS_PATH);
- bool fs_result =
- storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING);
- if(fs_result) {
- uint16_t bytes_count = storage_file_read(file, &settings, settings_size);
- if(bytes_count != settings_size) {
- fs_result = false;
- }
- }
- if(fs_result) {
- FURI_LOG_I(TAG, "load success");
- if(settings.version != NOTIFICATION_SETTINGS_VERSION) {
- FURI_LOG_E(
- TAG, "version(%d != %d) mismatch", settings.version, NOTIFICATION_SETTINGS_VERSION);
- } else {
- osKernelLock();
- memcpy(&app->settings, &settings, settings_size);
- osKernelUnlock();
- }
- } else {
- FURI_LOG_E(TAG, "load failed, %s", storage_file_get_error_desc(file));
- }
- storage_file_close(file);
- storage_file_free(file);
- furi_record_close("storage");
- return fs_result;
- };
- static bool notification_save_settings(NotificationApp* app) {
- NotificationSettings settings;
- File* file = storage_file_alloc(furi_record_open("storage"));
- const size_t settings_size = sizeof(NotificationSettings);
- FURI_LOG_I(TAG, "saving settings to \"%s\"", NOTIFICATION_SETTINGS_PATH);
- osKernelLock();
- memcpy(&settings, &app->settings, settings_size);
- osKernelUnlock();
- bool fs_result =
- storage_file_open(file, NOTIFICATION_SETTINGS_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS);
- if(fs_result) {
- uint16_t bytes_count = storage_file_write(file, &settings, settings_size);
- if(bytes_count != settings_size) {
- fs_result = false;
- }
- }
- if(fs_result) {
- FURI_LOG_I(TAG, "save success");
- } else {
- FURI_LOG_E(TAG, "save failed, %s", storage_file_get_error_desc(file));
- }
- storage_file_close(file);
- storage_file_free(file);
- furi_record_close("storage");
- return fs_result;
- };
- static void input_event_callback(const void* value, void* context) {
- furi_assert(value);
- furi_assert(context);
- NotificationApp* app = context;
- notification_message(app, &sequence_display_backlight_on);
- }
- // App alloc
- static NotificationApp* notification_app_alloc() {
- NotificationApp* app = malloc(sizeof(NotificationApp));
- app->queue = osMessageQueueNew(8, sizeof(NotificationAppMessage), NULL);
- app->display_timer = osTimerNew(notification_display_timer, osTimerOnce, app, NULL);
- app->settings.speaker_volume = 1.0f;
- app->settings.display_brightness = 1.0f;
- app->settings.led_brightness = 1.0f;
- app->settings.display_off_delay_ms = 30000;
- app->settings.vibro_on = true;
- app->display.value[LayerInternal] = 0x00;
- app->display.value[LayerNotification] = 0x00;
- app->display.index = LayerInternal;
- app->display.light = LightBacklight;
- app->led[0].value[LayerInternal] = 0x00;
- app->led[0].value[LayerNotification] = 0x00;
- app->led[0].index = LayerInternal;
- app->led[0].light = LightRed;
- app->led[1].value[LayerInternal] = 0x00;
- app->led[1].value[LayerNotification] = 0x00;
- app->led[1].index = LayerInternal;
- app->led[1].light = LightGreen;
- app->led[2].value[LayerInternal] = 0x00;
- app->led[2].value[LayerNotification] = 0x00;
- app->led[2].index = LayerInternal;
- app->led[2].light = LightBlue;
- app->settings.version = NOTIFICATION_SETTINGS_VERSION;
- // display backlight control
- app->event_record = furi_record_open("input_events");
- furi_pubsub_subscribe(app->event_record, input_event_callback, app);
- notification_message(app, &sequence_display_backlight_on);
- return app;
- };
- // App
- int32_t notification_srv(void* p) {
- UNUSED(p);
- NotificationApp* app = notification_app_alloc();
- if(!notification_load_settings(app)) {
- notification_save_settings(app);
- }
- notification_vibro_off();
- notification_sound_off();
- notification_apply_internal_led_layer(&app->display, 0x00);
- notification_apply_internal_led_layer(&app->led[0], 0x00);
- notification_apply_internal_led_layer(&app->led[1], 0x00);
- notification_apply_internal_led_layer(&app->led[2], 0x00);
- furi_record_create("notification", app);
- NotificationAppMessage message;
- while(1) {
- furi_check(osMessageQueueGet(app->queue, &message, NULL, osWaitForever) == osOK);
- switch(message.type) {
- case NotificationLayerMessage:
- notification_process_notification_message(app, &message);
- break;
- case InternalLayerMessage:
- notification_process_internal_message(app, &message);
- break;
- case SaveSettingsMessage:
- notification_save_settings(app);
- break;
- }
- if(message.back_event != NULL) {
- osEventFlagsSet(message.back_event, NOTIFICATION_EVENT_COMPLETE);
- }
- }
- return 0;
- };
|