From 7d139517b85d853ca4cf9400404947450d0d2025 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 2 Nov 2022 01:38:09 +0300 Subject: [PATCH] add plugin as plain files --- applications/plugins/dht_monitor | 1 - applications/plugins/dht_temp_sensor/DHT.c | 169 +++++++ applications/plugins/dht_temp_sensor/DHT.h | 39 ++ .../plugins/dht_temp_sensor/application.fam | 13 + applications/plugins/dht_temp_sensor/icon.png | Bin 0 -> 1818 bytes .../plugins/dht_temp_sensor/quenon_dht_mon.c | 460 ++++++++++++++++++ .../plugins/dht_temp_sensor/quenon_dht_mon.h | 176 +++++++ .../scenes/DHTMon_mainMenu_scene.c | 157 ++++++ .../scenes/DHTMon_main_scene.c | 40 ++ .../scenes/DHTMon_sensorActions_scene.c | 194 ++++++++ .../scenes/DHTMon_sensorEdit_scene.c | 100 ++++ 11 files changed, 1348 insertions(+), 1 deletion(-) delete mode 160000 applications/plugins/dht_monitor create mode 100644 applications/plugins/dht_temp_sensor/DHT.c create mode 100644 applications/plugins/dht_temp_sensor/DHT.h create mode 100644 applications/plugins/dht_temp_sensor/application.fam create mode 100644 applications/plugins/dht_temp_sensor/icon.png create mode 100644 applications/plugins/dht_temp_sensor/quenon_dht_mon.c create mode 100644 applications/plugins/dht_temp_sensor/quenon_dht_mon.h create mode 100644 applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c create mode 100644 applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c create mode 100644 applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c create mode 100644 applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c diff --git a/applications/plugins/dht_monitor b/applications/plugins/dht_monitor deleted file mode 160000 index 922025d0e..000000000 --- a/applications/plugins/dht_monitor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 922025d0e5d44376e5710b719adbeb4e3c81736f diff --git a/applications/plugins/dht_temp_sensor/DHT.c b/applications/plugins/dht_temp_sensor/DHT.c new file mode 100644 index 000000000..63a189ce1 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/DHT.c @@ -0,0 +1,169 @@ +#include "DHT.h" + +#define lineDown() furi_hal_gpio_write(sensor->GPIO, false) +#define lineUp() furi_hal_gpio_write(sensor->GPIO, true) +#define getLine() furi_hal_gpio_read(sensor->GPIO) +#define Delay(d) furi_delay_ms(d) + +DHT_data DHT_getData(DHT_sensor* sensor) { + DHT_data data = {-128.0f, -128.0f}; + +#if DHT_POLLING_CONTROL == 1 + /* Ограничение по частоте опроса датчика */ + //Определение интервала опроса в зависимости от датчика + uint16_t pollingInterval; + if(sensor->type == DHT11) { + pollingInterval = DHT_POLLING_INTERVAL_DHT11; + } else { + pollingInterval = DHT_POLLING_INTERVAL_DHT22; + } + + //Если интервал маленький, то возврат последнего удачного значения + if((furi_get_tick() - sensor->lastPollingTime < pollingInterval) && + sensor->lastPollingTime != 0) { + data.hum = sensor->lastHum; + data.temp = sensor->lastTemp; + return data; + } + sensor->lastPollingTime = furi_get_tick() + 1; +#endif + + //Опускание линии данных на 18 мс + lineDown(); +#ifdef DHT_IRQ_CONTROL + //Выключение прерываний, чтобы ничто не мешало обработке данных + __disable_irq(); +#endif + Delay(18); + + //Подъём линии + lineUp(); + + /* Ожидание ответа от датчика */ + uint16_t timeout = 0; + while(!getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если датчик не отозвался, значит его точно нет + //Обнуление последнего удачного значения, чтобы + //не получать фантомные значения + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + + return data; + } + } + //Ожидание спада + while(getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если датчик не отозвался, значит его точно нет + //Обнуление последнего удачного значения, чтобы + //не получать фантомные значения + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + + return data; + } + } + timeout = 0; + //Ожидание подъёма + while(!getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если датчик не отозвался, значит его точно нет + //Обнуление последнего удачного значения, чтобы + //не получать фантомные значения + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + + return data; + } + } + } + timeout = 0; + //Ожидание спада + while(getLine()) { + timeout++; + if(timeout > DHT_TIMEOUT) { +#ifdef DHT_IRQ_CONTROL + __enable_irq(); +#endif + //Если датчик не отозвался, значит его точно нет + //Обнуление последнего удачного значения, чтобы + //не получать фантомные значения + sensor->lastHum = -128.0f; + sensor->lastTemp = -128.0f; + return data; + } + } + + /* Чтение ответа от датчика */ + uint8_t rawData[5] = {0, 0, 0, 0, 0}; + for(uint8_t a = 0; a < 5; a++) { + for(uint8_t b = 7; b != 255; b--) { + uint16_t hT = 0, lT = 0; + //Пока линия в низком уровне, инкремент переменной lT + while(!getLine() && lT != 65535) lT++; + //Пока линия в высоком уровне, инкремент переменной hT + timeout = 0; + while(getLine() && hT != 65535) hT++; + //Если hT больше lT, то пришла единица + if(hT > lT) rawData[a] |= (1 << b); + } + } +#ifdef DHT_IRQ_CONTROL + //Включение прерываний после приёма данных + __enable_irq(); +#endif + /* Проверка целостности данных */ + if((uint8_t)(rawData[0] + rawData[1] + rawData[2] + rawData[3]) == rawData[4]) { + //Если контрольная сумма совпадает, то конвертация и возврат полученных значений + if(sensor->type == DHT22) { + data.hum = (float)(((uint16_t)rawData[0] << 8) | rawData[1]) * 0.1f; + //Проверка на отрицательность температуры + if(!(rawData[2] & (1 << 7))) { + data.temp = (float)(((uint16_t)rawData[2] << 8) | rawData[3]) * 0.1f; + } else { + rawData[2] &= ~(1 << 7); + data.temp = (float)(((uint16_t)rawData[2] << 8) | rawData[3]) * -0.1f; + } + } + if(sensor->type == DHT11) { + data.hum = (float)rawData[0]; + data.temp = (float)rawData[2]; + //DHT11 производства ASAIR имеют дробную часть в температуре + //А ещё температуру измеряет от -20 до +60 *С + //Вот прикол, да? + if(rawData[3] != 0) { + //Проверка знака + if(!(rawData[3] & (1 << 7))) { + //Добавление положительной дробной части + data.temp += rawData[3] * 0.1f; + } else { + //А тут делаем отрицательное значение + rawData[3] &= ~(1 << 7); + data.temp += rawData[3] * 0.1f; + data.temp *= -1; + } + } + } + } + +#if DHT_POLLING_CONTROL == 1 + sensor->lastHum = data.hum; + sensor->lastTemp = data.temp; +#endif + + return data; +} \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/DHT.h b/applications/plugins/dht_temp_sensor/DHT.h new file mode 100644 index 000000000..5b460e5cf --- /dev/null +++ b/applications/plugins/dht_temp_sensor/DHT.h @@ -0,0 +1,39 @@ +#ifndef DHT_H_ +#define DHT_H_ + +#include + +/* Настройки */ +#define DHT_TIMEOUT 65534 //Количество итераций, после которых функция вернёт пустые значения +#define DHT_POLLING_CONTROL 1 //Включение проверки частоты опроса датчика +#define DHT_POLLING_INTERVAL_DHT11 \ + 2000 //Интервал опроса DHT11 (0.5 Гц по даташиту). Можно поставить 1500, будет работать +#define DHT_POLLING_INTERVAL_DHT22 1000 //Интервал опроса DHT22 (1 Гц по даташиту) +#define DHT_IRQ_CONTROL //Выключать прерывания во время обмена данных с датчиком +/* Структура возвращаемых датчиком данных */ +typedef struct { + float hum; + float temp; +} DHT_data; + +/* Тип используемого датчика */ +typedef enum { DHT11, DHT22 } DHT_type; + +/* Структура объекта датчика */ +typedef struct { + char name[11]; + const GpioPin* GPIO; //Пин датчика + DHT_type type; //Тип датчика (DHT11 или DHT22) + +//Контроль частоты опроса датчика. Значения не заполнять! +#if DHT_POLLING_CONTROL == 1 + uint32_t lastPollingTime; //Время последнего опроса датчика + float lastTemp; //Последнее значение температуры + float lastHum; //Последнее значение влажности +#endif +} DHT_sensor; + +/* Прототипы функций */ +DHT_data DHT_getData(DHT_sensor* sensor); //Получить данные с датчика + +#endif diff --git a/applications/plugins/dht_temp_sensor/application.fam b/applications/plugins/dht_temp_sensor/application.fam new file mode 100644 index 000000000..ad74919a4 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/application.fam @@ -0,0 +1,13 @@ +App( + appid="quenon_dht_mon", + name="[DHT] Temp. Monitor", + apptype=FlipperAppType.EXTERNAL, + entry_point="quenon_dht_mon_app", + cdefines=["QUENON_DHT_MON"], + requires=[ + "gui", + ], + fap_category="GPIO", + fap_icon="icon.png", + stack_size=2 * 1024, +) \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/icon.png b/applications/plugins/dht_temp_sensor/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1730432e7a39ec5136000c484e1b745584c456ad GIT binary patch literal 1818 zcmcIl&u`pB7Y*TtDlVL%{{Rw#Q#pX(zzM0$?AqIc+B6p|tv%y?^S(Fl z`~1zj8|xS5=boCABx%0A*4pGxF`k<}$?xyMA8zu;iDL7@vUK(P)3^C#rq>Lcl639; z$Nol-@cE5Z=EVslDs(bIYB~8B#)9oTKJ$>cCs?lxO$njo~p$?&@H#EZphT~fe z->~HBQh2zWME+*$#VQz|1xmjxGhfq&!=XAfRhIWO!}B~%hZ=-{BS3MGmS_ahVxeYe z(E{gjR>mxq1taRRy)sa^)X5%_Y)YFJl~3F;ZG$8#-ef9ut)4%_vkl<;+Y%d>-?IBpB1DbH|f@6)b zzRX|ADJogsVQf%mYNL)QLsPZov)ge>*swS!6jcjo3zalbMAm@r0mJSXrf=E4Q zzOGN8Auj}iN_0FhBXM+a9B3H&?X)OSifOwQC|r~p$HZ?$uH|(d1gr=)fNok8xCrxl z>e>#$kxotMj&ls3JlYbQ=w1n*c}H1zl(%;L<1p6hZ9TmgQi_Q{=mGSJ;5%AtT&f zQ;`YwBbFzK*H4_F9@Vn6C#spV^IfdP1y9{U^?hv7H>=Sgmd8csBP?n?P_RfcB}(-= zzMp%?(mA0W_9-VlBKZ?o!J=}Aa@y>1ryp8e+JnRwX#XM7kCpiUQF>)7?xVCvdFRv= zu^COYm`PH!|K{s%zU!K4@@f{@EGiq8QEzINOd?32 zV_+w0OyUmWTET=!J38W6>pktUUd(&07*4xApG?mSn)0pY{4}`y#^WFHljNm#tJyib zb^S_nPLgM5uHU)y^`-dS_80T-eDdL^OU_?E{dVe^pin == gpio->pin && gpio_item[i].pin->port == gpio->port) { + return gpio_item[i].num; + } + } + return 255; +} + +const GpioPin* DHTMon_GPIO_form_int(uint8_t name) { + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].num == name) { + return gpio_item[i].pin; + } + } + return NULL; +} + +const GpioPin* DHTMon_GPIO_from_index(uint8_t index) { + if(index > GPIO_ITEMS) return NULL; + return gpio_item[index].pin; +} + +uint8_t DHTMon_GPIO_to_index(const GpioPin* gpio) { + if(gpio == NULL) return 255; + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) { + return i; + } + } + return 255; +} + +const char* DHTMon_GPIO_getName(const GpioPin* gpio) { + if(gpio == NULL) return NULL; + for(uint8_t i = 0; i < GPIO_ITEMS; i++) { + if(gpio_item[i].pin->pin == gpio->pin && gpio_item[i].pin->port == gpio->port) { + return gpio_item[i].name; + } + } + return NULL; +} + +void DHTMon_sensors_init(void) { + //Включение 5V если на порту 1 FZ его нет + if(furi_hal_power_is_otg_enabled() != true) { + furi_hal_power_enable_otg(); + } + + //Настройка GPIO загруженных датчиков + for(uint8_t i = 0; i < app->sensors_count; i++) { + //Высокий уровень по умолчанию + furi_hal_gpio_write(app->sensors[i].GPIO, true); + //Режим работы - OpenDrain, подтяжка включается на всякий случай + furi_hal_gpio_init( + app->sensors[i].GPIO, //Порт FZ + GpioModeOutputOpenDrain, //Режим работы - открытый сток + GpioPullUp, //Принудительная подтяжка линии данных к питанию + GpioSpeedVeryHigh); //Скорость работы - максимальная + } +} + +void DHTMon_sensors_deinit(void) { + //Возврат исходного состояния 5V + if(app->last_OTG_State != true) { + furi_hal_power_disable_otg(); + } + + //Перевод портов GPIO в состояние по умолчанию + for(uint8_t i = 0; i < app->sensors_count; i++) { + furi_hal_gpio_init( + app->sensors[i].GPIO, //Порт FZ + GpioModeAnalog, //Режим работы - аналог + GpioPullNo, //Отключение подтяжки + GpioSpeedLow); //Скорость работы - низкая + //Установка низкого уровня + furi_hal_gpio_write(app->sensors[i].GPIO, false); + } +} + +bool DHTMon_sensor_check(DHT_sensor* sensor) { + /* Проверка имени */ + //1) Строка должна быть длиной от 1 до 10 символов + //2) Первый символ строки должен быть только 0-9, A-Z, a-z и _ + if(strlen(sensor->name) == 0 || strlen(sensor->name) > 10 || + (!(sensor->name[0] >= '0' && sensor->name[0] <= '9') && + !(sensor->name[0] >= 'A' && sensor->name[0] <= 'Z') && + !(sensor->name[0] >= 'a' && sensor->name[0] <= 'z') && !(sensor->name[0] == '_'))) { + FURI_LOG_D(APP_NAME, "Sensor [%s] name check failed\r\n", sensor->name); + return false; + } + //Проверка GPIO + if(DHTMon_GPIO_to_int(sensor->GPIO) == 255) { + FURI_LOG_D( + APP_NAME, + "Sensor [%s] GPIO check failed: %d\r\n", + sensor->name, + DHTMon_GPIO_to_int(sensor->GPIO)); + return false; + } + //Проверка типа датчика + if(sensor->type != DHT11 && sensor->type != DHT22) { + FURI_LOG_D(APP_NAME, "Sensor [%s] type check failed: %d\r\n", sensor->name, sensor->type); + return false; + } + + //Возврат истины если всё ок + FURI_LOG_D(APP_NAME, "Sensor [%s] all checks passed\r\n", sensor->name); + return true; +} + +void DHTMon_sensor_delete(DHT_sensor* sensor) { + if(sensor == NULL) return; + //Делаем параметры датчика неверными + sensor->name[0] = '\0'; + sensor->type = 255; + //Теперь сохраняем текущие датчики. Сохранятор не сохранит неисправный датчик + DHTMon_sensors_save(); + //Перезагружаемся с SD-карты + DHTMon_sensors_reload(); +} + +uint8_t DHTMon_sensors_save(void) { + //Выделение памяти для потока + app->file_stream = file_stream_alloc(app->storage); + uint8_t savedSensorsCount = 0; + //Переменная пути к файлу + char filepath[sizeof(APP_PATH_FOLDER) + sizeof(APP_FILENAME)] = {0}; + //Составление пути к файлу + strcpy(filepath, APP_PATH_FOLDER); + strcat(filepath, "/"); + strcat(filepath, APP_FILENAME); + + //Открытие потока. Если поток открылся, то выполнение сохранения датчиков + if(file_stream_open(app->file_stream, filepath, FSAM_READ_WRITE, FSOM_CREATE_ALWAYS)) { + const char template[] = + "#DHT monitor sensors file\n#Name - name of sensor. Up to 10 sumbols\n#Type - type of sensor. DHT11 - 0, DHT22 - 1\n#GPIO - connection port. May being 2-7, 10, 12-17\n#Name Type GPIO\n"; + stream_write(app->file_stream, (uint8_t*)template, strlen(template)); + //Сохранение датчиков + for(uint8_t i = 0; i < app->sensors_count; i++) { + //Если параметры датчика верны, то сохраняемся + if(DHTMon_sensor_check(&app->sensors[i])) { + stream_write_format( + app->file_stream, + "%s %d %d\n", + app->sensors[i].name, + app->sensors[i].type, + DHTMon_GPIO_to_int(app->sensors[i].GPIO)); + savedSensorsCount++; + } + } + } else { + //TODO: печать ошибки на экран + FURI_LOG_E(APP_NAME, "cannot create sensors file\r\n"); + } + stream_free(app->file_stream); + + return savedSensorsCount; +} + +bool DHTMon_sensors_load(void) { + //Обнуление количества датчиков + app->sensors_count = -1; + //Очистка предыдущих датчиков + memset(app->sensors, 0, sizeof(app->sensors)); + + //Открытие файла на SD-карте + //Выделение памяти для потока + app->file_stream = file_stream_alloc(app->storage); + //Переменная пути к файлу + char filepath[sizeof(APP_PATH_FOLDER) + sizeof(APP_FILENAME)] = {0}; + //Составление пути к файлу + strcpy(filepath, APP_PATH_FOLDER); + strcat(filepath, "/"); + strcat(filepath, APP_FILENAME); + //Открытие потока к файлу + if(!file_stream_open(app->file_stream, filepath, FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + //Если файл отсутствует, то создание болванки + FURI_LOG_W(APP_NAME, "Missing sensors file. Creating new file\r\n"); + app->sensors_count = 0; + stream_free(app->file_stream); + DHTMon_sensors_save(); + return false; + } + //Вычисление размера файла + size_t file_size = stream_size(app->file_stream); + if(file_size == (size_t)0) { + //Выход если файл пустой + FURI_LOG_W(APP_NAME, "Sensors file is empty\r\n"); + app->sensors_count = 0; + stream_free(app->file_stream); + return false; + } + + //Выделение памяти под загрузку файла + uint8_t* file_buf = malloc(file_size); + //Опустошение буфера файла + memset(file_buf, 0, file_size); + //Загрузка файла + if(stream_read(app->file_stream, file_buf, file_size) != file_size) { + //Выход при ошибке чтения + FURI_LOG_E(APP_NAME, "Error reading sensor file\r\n"); + app->sensors_count = 0; + stream_free(app->file_stream); + return false; + } + //Построчное чтение файла + char* line = strtok((char*)file_buf, "\n"); + while(line != NULL && app->sensors_count < MAX_SENSORS) { + if(line[0] != '#') { + DHT_sensor s = {0}; + int type, port; + char name[11]; + sscanf(line, "%s %d %d", name, &type, &port); + s.type = type; + s.GPIO = DHTMon_GPIO_form_int(port); + + name[10] = '\0'; + strcpy(s.name, name); + //Если данные корректны, то + if(DHTMon_sensor_check(&s) == true) { + //Установка нуля при первом датчике + if(app->sensors_count == -1) app->sensors_count = 0; + //Добавление датчика в общий список + app->sensors[app->sensors_count] = s; + //Увеличение количества загруженных датчиков + app->sensors_count++; + } + } + line = strtok((char*)NULL, "\n"); + } + stream_free(app->file_stream); + free(file_buf); + + //Обнуление количества датчиков если ни один из них не был загружен + if(app->sensors_count == -1) app->sensors_count = 0; + + //Инициализация портов датчиков если таковые есть + if(app->sensors_count > 0) { + DHTMon_sensors_init(); + return true; + } else { + return false; + } + return false; +} + +bool DHTMon_sensors_reload(void) { + DHTMon_sensors_deinit(); + return DHTMon_sensors_load(); +} + +/** + * @brief Обработчик отрисовки экрана + * + * @param canvas Указатель на холст + * @param ctx Данные плагина + */ +static void render_callback(Canvas* const canvas, void* ctx) { + PluginData* app = acquire_mutex((ValueMutex*)ctx, 25); + if(app == NULL) { + return; + } + //Вызов отрисовки главного экрана + scene_main(canvas, app); + + release_mutex((ValueMutex*)ctx, app); +} + +/** + * @brief Обработчик нажатия кнопок главного экрана + * + * @param input_event Указатель на событие + * @param event_queue Указатель на очередь событий + */ +static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +/** + * @brief Выделение места под переменные плагина + * + * @return true Если всё прошло успешно + * @return false Если в процессе загрузки произошла ошибка + */ +static bool DHTMon_alloc(void) { + //Выделение места под данные плагина + app = malloc(sizeof(PluginData)); + //Выделение места под очередь событий + app->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + + //Обнуление количества датчиков + app->sensors_count = -1; + + //Инициализация мутекса + if(!init_mutex(&app->state_mutex, app, sizeof(PluginData))) { + FURI_LOG_E(APP_NAME, "cannot create mutex\r\n"); + return false; + } + + // Set system callbacks + app->view_port = view_port_alloc(); + view_port_draw_callback_set(app->view_port, render_callback, &app->state_mutex); + view_port_input_callback_set(app->view_port, input_callback, app->event_queue); + + // Open GUI and register view_port + app->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen); + + app->view_dispatcher = view_dispatcher_alloc(); + + sensorActions_sceneCreate(app); + sensorEdit_sceneCreate(app); + + app->widget = widget_alloc(); + view_dispatcher_add_view(app->view_dispatcher, WIDGET_VIEW, widget_get_view(app->widget)); + + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, TEXTINPUT_VIEW, text_input_get_view(app->text_input)); + + view_dispatcher_enable_queue(app->view_dispatcher); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + //Уведомления + app->notifications = furi_record_open(RECORD_NOTIFICATION); + + //Подготовка хранилища + app->storage = furi_record_open(RECORD_STORAGE); + storage_common_mkdir(app->storage, APP_PATH_FOLDER); + app->file_stream = file_stream_alloc(app->storage); + + return true; +} + +/** + * @brief Освыбождение памяти после работы приложения + */ +static void DHTMon_free(void) { + //Автоматическое управление подсветкой + notification_message(app->notifications, &sequence_display_backlight_enforce_auto); + + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_NOTIFICATION); + + text_input_free(app->text_input); + widget_free(app->widget); + sensorEdit_sceneRemove(); + sensorActions_screneRemove(); + view_dispatcher_free(app->view_dispatcher); + + furi_record_close(RECORD_GUI); + + view_port_enabled_set(app->view_port, false); + gui_remove_view_port(app->gui, app->view_port); + + view_port_free(app->view_port); + furi_message_queue_free(app->event_queue); + delete_mutex(&app->state_mutex); + + free(app); +} + +/** + * @brief Точка входа в приложение + * + * @return Код ошибки + */ +int32_t quenon_dht_mon_app() { + if(!DHTMon_alloc()) { + DHTMon_free(); + return 255; + } + //Постоянное свечение подсветки + notification_message(app->notifications, &sequence_display_backlight_enforce_on); + //Сохранение состояния наличия 5V на порту 1 FZ + app->last_OTG_State = furi_hal_power_is_otg_enabled(); + + //Загрузка датчиков с SD-карты + DHTMon_sensors_load(); + + app->currentSensorEdit = &app->sensors[0]; + + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(app->event_queue, &event, 100); + + acquire_mutex_block(&app->state_mutex); + + if(event_status == FuriStatusOk) { + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypePress) { + switch(event.input.key) { + case InputKeyUp: + break; + case InputKeyDown: + break; + case InputKeyRight: + break; + case InputKeyLeft: + break; + case InputKeyOk: + view_port_update(app->view_port); + release_mutex(&app->state_mutex, app); + mainMenu_scene(app); + break; + case InputKeyBack: + processing = false; + break; + } + } + } + } + + view_port_update(app->view_port); + release_mutex(&app->state_mutex, app); + } + //Освобождение памяти и деинициализация + DHTMon_sensors_deinit(); + DHTMon_free(); + + return 0; +} +//TODO: Обработка ошибок +//TODO: Пропуск использованных портов в меню добавления датчиков \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/quenon_dht_mon.h b/applications/plugins/dht_temp_sensor/quenon_dht_mon.h new file mode 100644 index 000000000..4e888f6c1 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/quenon_dht_mon.h @@ -0,0 +1,176 @@ +#ifndef QUENON_DHT_MON +#define QUENON_DHT_MON + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "DHT.h" + +#define APP_NAME "DHT_monitor" +#define APP_PATH_FOLDER "/ext/dht_monitor" +#define APP_FILENAME "sensors.txt" +#define MAX_SENSORS 5 + +// //Виды менюшек +typedef enum { + MAIN_MENU_VIEW, + ADDSENSOR_MENU_VIEW, + TEXTINPUT_VIEW, + SENSOR_ACTIONS_VIEW, + WIDGET_VIEW, +} MENU_VIEWS; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + const uint8_t num; + const char* name; + const GpioPin* pin; +} GpioItem; + +//Структура с данными плагина +typedef struct { + //Очередь сообщений + FuriMessageQueue* event_queue; + //Мутекс + ValueMutex state_mutex; + //Вьюпорт + ViewPort* view_port; + //GUI + Gui* gui; + NotificationApp* notifications; + ViewDispatcher* view_dispatcher; + View* view; + TextInput* text_input; + VariableItem* item; + Widget* widget; + + char txtbuff[30]; //Буффер для печати строк на экране + bool last_OTG_State; //Состояние OTG до запуска приложения + Storage* storage; //Хранилище датчиков + Stream* file_stream; //Поток файла с датчиками + int8_t sensors_count; // Количество загруженных датчиков + DHT_sensor sensors[MAX_SENSORS]; //Сохранённые датчики + DHT_data data; //Инфа из датчика + DHT_sensor* currentSensorEdit; //Указатель на редактируемый датчик + +} PluginData; + +/* ================== Работа с GPIO ================== */ +/** + * @brief Конвертация GPIO в его номер на корпусе FZ + * + * @param gpio Указатель на преобразовываемый GPIO + * @return Номер порта на корпусе FZ + */ +uint8_t DHTMon_GPIO_to_int(const GpioPin* gpio); +/** + * @brief Конвертация номера порта на корпусе FZ в GPIO + * + * @param name Номер порта на корпусе FZ + * @return Указатель на GPIO при успехе, NULL при ошибке + */ +const GpioPin* DHTMon_GPIO_form_int(uint8_t name); +/** + * @brief Преобразование порядкового номера порта в GPIO + * + * @param index Индекс порта от 0 до GPIO_ITEMS-1 + * @return Указатель на GPIO при успехе, NULL при ошибке + */ +const GpioPin* DHTMon_GPIO_from_index(uint8_t index); +/** + * @brief Преобразование GPIO в порядковый номер порта + * + * @param gpio Указатель на GPIO + * @return index при успехе, 255 при ошибке + */ +uint8_t DHTMon_GPIO_to_index(const GpioPin* gpio); + +/** + * @brief Получить имя GPIO в виде строки + * + * @param gpio Искомый порт + * @return char* Указатель на строку с именем порта + */ +const char* DHTMon_GPIO_getName(const GpioPin* gpio); + +/* ================== Работа с датчиками ================== */ +/** + * @brief Инициализация портов ввода/вывода датчиков + */ +void DHTMon_sensors_init(void); +/** + * @brief Функция деинициализации портов ввода/вывода датчиков + */ +void DHTMon_sensors_deinit(void); +/** + * @brief Проверка корректности параметров датчика + * + * @param sensor Указатель на проверяемый датчик + * @return true Параметры датчика корректные + * @return false Параметры датчика некорректные + */ +bool DHTMon_sensor_check(DHT_sensor* sensor); +/** + * @brief Удаление датчика из списка и перезагрузка + * + * @param sensor Указатель на удаляемый датчик + */ +void DHTMon_sensor_delete(DHT_sensor* sensor); +/** + * @brief Сохранение датчиков на SD-карту + * + * @return Количество сохранённых датчиков + */ +uint8_t DHTMon_sensors_save(void); +/** + * @brief Загрузка датчиков с SD-карты + * + * @return true Был загружен хотя бы 1 датчик + * @return false Датчики отсутствуют + */ +bool DHTMon_sensors_load(void); +/** + * @brief Перезагрузка датчиков с SD-карты + * + * @return true Когда был загружен хотя бы 1 датчик + * @return false Ни один из датчиков не был загружен + */ +bool DHTMon_sensors_reload(void); + +void scene_main(Canvas* const canvas, PluginData* app); +void mainMenu_scene(PluginData* app); + +void sensorEdit_sceneCreate(PluginData* app); +void sensorEdit_scene(PluginData* app); +void sensorEdit_sceneRemove(void); + +void sensorActions_sceneCreate(PluginData* app); +void sensorActions_scene(PluginData* app); +void sensorActions_screneRemove(void); +#endif \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c new file mode 100644 index 000000000..26ac9ca89 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_mainMenu_scene.c @@ -0,0 +1,157 @@ +#include "../quenon_dht_mon.h" +//Текущий вид +static View* view; +//Список +static VariableItemList* variable_item_list; + +/** + * @brief Функция обработки нажатия кнопки "Назад" + * + * @param context Указатель на данные приложения + * @return ID вида в который нужно переключиться + */ +static uint32_t actions_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Возвращаем ID вида, в который нужно вернуться + return VIEW_NONE; +} +/** + * @brief Функция обработки нажатия средней кнопки + * + * @param context Указатель на данные приложения + * @param index На каком элементе списка была нажата кнопка + */ +static void enterCallback(void* context, uint32_t index) { + PluginData* app = context; + if((uint8_t)index < (uint8_t)app->sensors_count) { + app->currentSensorEdit = &app->sensors[index]; + sensorActions_scene(app); + } + if((uint8_t)index == (uint8_t)app->sensors_count) { + app->currentSensorEdit = &app->sensors[app->sensors_count++]; + strcpy(app->currentSensorEdit->name, "NewSensor"); + app->currentSensorEdit->GPIO = DHTMon_GPIO_from_index(0); + app->currentSensorEdit->type = DHT11; + sensorEdit_scene(app); + } +} + +/** + * @brief Создание списка действий с указанным датчиком + * + * @param app Указатель на данные плагина + */ +void mainMenu_scene(PluginData* app) { + variable_item_list = variable_item_list_alloc(); + //Сброс всех элементов меню + variable_item_list_reset(variable_item_list); + //Добавление названий датчиков в качестве элементов списка + for(uint8_t i = 0; i < app->sensors_count; i++) { + variable_item_list_add(variable_item_list, app->sensors[i].name, 1, NULL, NULL); + } + if(app->sensors_count < (uint8_t)MAX_SENSORS) { + variable_item_list_add(variable_item_list, " + Add new sensor +", 1, NULL, NULL); + } + + //Добавление колбека на нажатие средней кнопки + variable_item_list_set_enter_callback(variable_item_list, enterCallback, app); + + //Создание вида из списка + view = variable_item_list_get_view(variable_item_list); + //Добавление колбека на нажатие кнопки "Назад" + view_set_previous_callback(view, actions_exitCallback); + //Добавление вида в диспетчер + view_dispatcher_add_view(app->view_dispatcher, MAIN_MENU_VIEW, view); + + view_dispatcher_enable_queue(app->view_dispatcher); + + //Переключение на наш вид + view_dispatcher_switch_to_view(app->view_dispatcher, MAIN_MENU_VIEW); + + //Запуск диспетчера + view_dispatcher_run(app->view_dispatcher); + + //Очистка списка элементов + variable_item_list_free(variable_item_list); + //Удаление вида после обработки + view_dispatcher_remove_view(app->view_dispatcher, MAIN_MENU_VIEW); +} + +/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +*/ + +// static VariableItemList* variable_item_list; +// /* ============== Главное меню ============== */ +// static uint32_t mainMenu_exitCallback(void* context) { +// UNUSED(context); +// variable_item_list_free(variable_item_list); +// DHT_sensors_reload(); +// return VIEW_NONE; +// } +// static void mainMenu_enterCallback(void* context, uint32_t index) { +// PluginData* app = context; +// if((uint8_t)index == (uint8_t)app->sensors_count) { +// addSensor_scene(app); +// view_dispatcher_run(app->view_dispatcher); +// } +// } +// void mainMenu_scene(PluginData* app) { +// variable_item_list = variable_item_list_alloc(); +// variable_item_list_reset(variable_item_list); +// for(uint8_t i = 0; i < app->sensors_count; i++) { +// variable_item_list_add(variable_item_list, app->sensors[i].name, 1, NULL, NULL); +// } +// variable_item_list_add(variable_item_list, "+ Add new sensor +", 1, NULL, NULL); + +// app->view = variable_item_list_get_view(variable_item_list); +// app->view_dispatcher = view_dispatcher_alloc(); + +// view_dispatcher_enable_queue(app->view_dispatcher); +// view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); +// view_dispatcher_add_view(app->view_dispatcher, MAIN_MENU_VIEW, app->view); +// view_dispatcher_switch_to_view(app->view_dispatcher, MAIN_MENU_VIEW); + +// variable_item_list_set_enter_callback(variable_item_list, mainMenu_enterCallback, app); +// view_set_previous_callback(app->view, mainMenu_exitCallback); +// } \ No newline at end of file diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c new file mode 100644 index 000000000..aab343752 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_main_scene.c @@ -0,0 +1,40 @@ +#include "../quenon_dht_mon.h" + +/* ============== Главный экран ============== */ +void scene_main(Canvas* const canvas, PluginData* app) { + //Рисование бара + canvas_draw_box(canvas, 0, 0, 128, 14); + canvas_set_color(canvas, ColorWhite); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 32, 11, "DHT Monitor"); + + canvas_set_color(canvas, ColorBlack); + if(app->sensors_count > 0) { + if(!furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + } + for(uint8_t i = 0; i < app->sensors_count; i++) { + app->data = DHT_getData(&app->sensors[i]); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 0, 24 + 10 * i, app->sensors[i].name); + + canvas_set_font(canvas, FontSecondary); + if(app->data.hum == -128.0f && app->data.temp == -128.0f) { + canvas_draw_str(canvas, 96, 24 + 10 * i, "timeout"); + } else { + snprintf( + app->txtbuff, + sizeof(app->txtbuff), + "%2.1f*C/%d%%", + (double)app->data.temp, + (int8_t)app->data.hum); + canvas_draw_str(canvas, 64, 24 + 10 * i, app->txtbuff); + } + } + } else { + canvas_set_font(canvas, FontSecondary); + if(app->sensors_count == 0) canvas_draw_str(canvas, 0, 24, "Sensors not found"); + if(app->sensors_count == -1) canvas_draw_str(canvas, 0, 24, "Loading..."); + } +} diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c new file mode 100644 index 000000000..ae7674f70 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorActions_scene.c @@ -0,0 +1,194 @@ +#include "../quenon_dht_mon.h" + +//Текущий вид +static View* view; +//Список +static VariableItemList* variable_item_list; + +/* ================== Информация о датчике ================== */ +/** + * @brief Функция обработки нажатия кнопки "Назад" + * + * @param context Указатель на данные приложения + * @return ID вида в который нужно переключиться + */ +static uint32_t infoWidget_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Возвращаем ID вида, в который нужно вернуться + return SENSOR_ACTIONS_VIEW; +} +/** + * @brief Обработчик нажатий на кнопку в виджете + * + * @param result Какая из кнопок была нажата + * @param type Тип нажатия + * @param context Указатель на данные плагина + */ +static void infoWidget_callback(GuiButtonType result, InputType type, void* context) { + PluginData* app = context; + //Коротко нажата левая кнопка (Back) + if(result == GuiButtonTypeLeft && type == InputTypeShort) { + view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW); + } +} +/** + * @brief Создание виджета информации о датчике + * + * @param app Указатель на данные плагина + */ +static void sensorInfo_widget(PluginData* app) { + //Очистка виджета + widget_reset(app->widget); + //Добавление кнопок + widget_add_button_element(app->widget, GuiButtonTypeLeft, "Back", infoWidget_callback, app); + + char str[32]; + snprintf(str, sizeof(str), "\e#%s\e#", app->currentSensorEdit->name); + widget_add_text_box_element(app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, str, false); + snprintf(str, sizeof(str), "\e#Type:\e# %s", app->currentSensorEdit->type ? "DHT22" : "DHT11"); + widget_add_text_box_element(app->widget, 0, 0, 128, 47, AlignLeft, AlignCenter, str, false); + snprintf( + str, sizeof(str), "\e#GPIO:\e# %s", DHTMon_GPIO_getName(app->currentSensorEdit->GPIO)); + widget_add_text_box_element(app->widget, 0, 0, 128, 72, AlignLeft, AlignCenter, str, false); + view_set_previous_callback(widget_get_view(app->widget), infoWidget_exitCallback); + view_dispatcher_switch_to_view(app->view_dispatcher, WIDGET_VIEW); +} + +/* ================== Подтверждение удаления ================== */ +/** + * @brief Функция обработки нажатия кнопки "Назад" + * + * @param context Указатель на данные приложения + * @return ID вида в который нужно переключиться + */ +static uint32_t deleteWidget_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Возвращаем ID вида, в который нужно вернуться + return SENSOR_ACTIONS_VIEW; +} +/** + * @brief Обработчик нажатий на кнопку в виджете + * + * @param result Какая из кнопок была нажата + * @param type Тип нажатия + * @param context Указатель на данные плагина + */ +static void deleteWidget_callback(GuiButtonType result, InputType type, void* context) { + PluginData* app = context; + //Коротко нажата левая кнопка (Cancel) + if(result == GuiButtonTypeLeft && type == InputTypeShort) { + view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW); + } + //Коротко нажата правая кнопка (Delete) + if(result == GuiButtonTypeRight && type == InputTypeShort) { + //Удаление датчика + DHTMon_sensor_delete(app->currentSensorEdit); + //Выход из меню + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); + } +} +/** + * @brief Создание виджета удаления датчика + * + * @param app Указатель на данные плагина + */ +static void sensorDelete_widget(PluginData* app) { + //Очистка виджета + widget_reset(app->widget); + //Добавление кнопок + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Cancel", deleteWidget_callback, app); + widget_add_button_element( + app->widget, GuiButtonTypeRight, "Delete", deleteWidget_callback, app); + + char delete_str[32]; + snprintf(delete_str, sizeof(delete_str), "\e#Delete %s?\e#", app->currentSensorEdit->name); + widget_add_text_box_element( + app->widget, 0, 0, 128, 23, AlignCenter, AlignCenter, delete_str, false); + snprintf( + delete_str, + sizeof(delete_str), + "\e#Type:\e# %s", + app->currentSensorEdit->type ? "DHT22" : "DHT11"); + widget_add_text_box_element( + app->widget, 0, 0, 128, 47, AlignLeft, AlignCenter, delete_str, false); + snprintf( + delete_str, + sizeof(delete_str), + "\e#GPIO:\e# %s", + DHTMon_GPIO_getName(app->currentSensorEdit->GPIO)); + widget_add_text_box_element( + app->widget, 0, 0, 128, 72, AlignLeft, AlignCenter, delete_str, false); + view_set_previous_callback(widget_get_view(app->widget), deleteWidget_exitCallback); + view_dispatcher_switch_to_view(app->view_dispatcher, WIDGET_VIEW); +} + +/* ================== Меню действий ================== */ +/** + * @brief Функция обработки нажатия средней кнопки + * + * @param context Указатель на данные приложения + * @param index На каком элементе списка была нажата кнопка + */ +static void enterCallback(void* context, uint32_t index) { + PluginData* app = context; + if(index == 0) { + sensorInfo_widget(app); + } + if(index == 1) { + sensorEdit_scene(app); + } + if(index == 2) { + sensorDelete_widget(app); + } +} + +/** + * @brief Функция обработки нажатия кнопки "Назад" + * + * @param context Указатель на данные приложения + * @return ID вида в который нужно переключиться + */ +static uint32_t actions_exitCallback(void* context) { + PluginData* app = context; + UNUSED(app); + //Возвращаем ID вида, в который нужно вернуться + return MAIN_MENU_VIEW; +} + +/** + * @brief Создание списка действий с указанным датчиком + * + * @param app Указатель на данные плагина + */ +void sensorActions_sceneCreate(PluginData* app) { + variable_item_list = variable_item_list_alloc(); + //Сброс всех элементов меню + variable_item_list_reset(variable_item_list); + //Добавление элементов в список + variable_item_list_add(variable_item_list, "Info", 0, NULL, NULL); + variable_item_list_add(variable_item_list, "Edit", 0, NULL, NULL); + variable_item_list_add(variable_item_list, "Delete", 0, NULL, NULL); + + //Добавление колбека на нажатие средней кнопки + variable_item_list_set_enter_callback(variable_item_list, enterCallback, app); + + //Создание вида из списка + view = variable_item_list_get_view(variable_item_list); + //Добавление колбека на нажатие кнопки "Назад" + view_set_previous_callback(view, actions_exitCallback); + //Добавление вида в диспетчер + view_dispatcher_add_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW, view); +} +void sensorActions_scene(PluginData* app) { + //Сброс выбранного пункта в ноль + variable_item_list_set_selected_item(variable_item_list, 0); + //Переключение на наш вид + view_dispatcher_switch_to_view(app->view_dispatcher, SENSOR_ACTIONS_VIEW); +} + +void sensorActions_screneRemove(void) { + variable_item_list_free(variable_item_list); +} diff --git a/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c new file mode 100644 index 000000000..b9c5a1134 --- /dev/null +++ b/applications/plugins/dht_temp_sensor/scenes/DHTMon_sensorEdit_scene.c @@ -0,0 +1,100 @@ +#include "../quenon_dht_mon.h" + +static VariableItem* nameItem; +static VariableItemList* variable_item_list; + +static const char* const sensorsTypes[2] = { + "DHT11", + "DHT22", +}; + +// /* ============== Добавление датчика ============== */ +static uint32_t addSensor_exitCallback(void* context) { + UNUSED(context); + DHTMon_sensors_reload(); + return VIEW_NONE; +} + +static void addSensor_sensorTypeChanged(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + PluginData* app = variable_item_get_context(item); + variable_item_set_current_value_text(item, sensorsTypes[index]); + app->currentSensorEdit->type = index; +} + +static void addSensor_GPIOChanged(VariableItem* item) { + uint8_t index = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, DHTMon_GPIO_getName(DHTMon_GPIO_from_index(index))); + PluginData* app = variable_item_get_context(item); + app->currentSensorEdit->GPIO = DHTMon_GPIO_from_index(index); +} + +static void addSensor_sensorNameChanged(void* context) { + PluginData* app = context; + variable_item_set_current_value_text(nameItem, app->currentSensorEdit->name); + view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW); +} +static void addSensor_sensorNameChange(PluginData* app) { + text_input_set_header_text(app->text_input, "Sensor name"); + //По неясной мне причине в длину строки входит терминатор. Поэтому при длине 10 приходится указывать 11 + text_input_set_result_callback( + app->text_input, addSensor_sensorNameChanged, app, app->currentSensorEdit->name, 11, true); + view_dispatcher_switch_to_view(app->view_dispatcher, TEXTINPUT_VIEW); +} + +static void addSensor_enterCallback(void* context, uint32_t index) { + PluginData* app = context; + if(index == 0) { + addSensor_sensorNameChange(app); + } + if(index == 3) { + //Сохранение датчика + DHTMon_sensors_save(); + DHTMon_sensors_reload(); + view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_NONE); + } +} + +void sensorEdit_sceneCreate(PluginData* app) { + variable_item_list = variable_item_list_alloc(); + + variable_item_list_reset(variable_item_list); + + variable_item_list_set_enter_callback(variable_item_list, addSensor_enterCallback, app); + + app->view = variable_item_list_get_view(variable_item_list); + + view_set_previous_callback(app->view, addSensor_exitCallback); + + view_dispatcher_add_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW, app->view); +} +void sensorEdit_scene(PluginData* app) { + //Очистка списка + variable_item_list_reset(variable_item_list); + + //Имя редактируемого датчика + nameItem = variable_item_list_add(variable_item_list, "Name: ", 1, NULL, NULL); + variable_item_set_current_value_index(nameItem, 0); + variable_item_set_current_value_text(nameItem, app->currentSensorEdit->name); + + //Тип датчика + app->item = + variable_item_list_add(variable_item_list, "Type:", 2, addSensor_sensorTypeChanged, app); + + variable_item_set_current_value_index(nameItem, app->currentSensorEdit->type); + variable_item_set_current_value_text(app->item, sensorsTypes[app->currentSensorEdit->type]); + + //GPIO + app->item = + variable_item_list_add(variable_item_list, "GPIO:", 13, addSensor_GPIOChanged, app); + variable_item_set_current_value_index( + app->item, DHTMon_GPIO_to_index(app->currentSensorEdit->GPIO)); + variable_item_set_current_value_text( + app->item, DHTMon_GPIO_getName(app->currentSensorEdit->GPIO)); + variable_item_list_add(variable_item_list, "Save", 1, NULL, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, ADDSENSOR_MENU_VIEW); +} +void sensorEdit_sceneRemove(void) { + variable_item_list_free(variable_item_list); +} \ No newline at end of file