2022-11-02 01:38:09 +03:00
# include "quenon_dht_mon.h"
//Порты ввода/вывода, которые не были обозначены в общем списке
const GpioPin SWC_10 = { . pin = LL_GPIO_PIN_14 , . port = GPIOA } ;
const GpioPin SIO_12 = { . pin = LL_GPIO_PIN_13 , . port = GPIOA } ;
const GpioPin TX_13 = { . pin = LL_GPIO_PIN_6 , . port = GPIOB } ;
const GpioPin RX_14 = { . pin = LL_GPIO_PIN_7 , . port = GPIOB } ;
//Количество доступных портов ввода/вывода
# define GPIO_ITEMS (sizeof(gpio_item) / sizeof(GpioItem))
//Перечень достуных портов ввода/вывода
static const GpioItem gpio_item [ ] = {
{ 2 , " 2 " , & gpio_ext_pa7 } ,
{ 3 , " 3 " , & gpio_ext_pa6 } ,
{ 4 , " 4 " , & gpio_ext_pa4 } ,
{ 5 , " 5 " , & gpio_ext_pb3 } ,
{ 6 , " 6 " , & gpio_ext_pb2 } ,
{ 7 , " 7 " , & gpio_ext_pc3 } ,
{ 10 , " 10 (SWC) " , & SWC_10 } ,
{ 12 , " 12 (SIO) " , & SIO_12 } ,
{ 13 , " 13 (TX) " , & TX_13 } ,
{ 14 , " 14 (RX) " , & RX_14 } ,
{ 15 , " 15 " , & gpio_ext_pc1 } ,
{ 16 , " 16 " , & gpio_ext_pc0 } ,
{ 17 , " 17 (1W) " , & ibutton_gpio } } ;
//Данные плагина
static PluginData * app ;
uint8_t DHTMon_GPIO_to_int ( 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 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 ;
2022-11-02 23:07:00 +03:00
default :
break ;
2022-11-02 01:38:09 +03:00
}
}
}
}
view_port_update ( app - > view_port ) ;
release_mutex ( & app - > state_mutex , app ) ;
}
//Освобождение памяти и деинициализация
DHTMon_sensors_deinit ( ) ;
DHTMon_free ( ) ;
return 0 ;
}
//TODO: Обработка ошибок
//TODO: Пропуск использованных портов в меню добавления датчиков