mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-12-19 09:13:11 +00:00
280 lines
11 KiB
C
280 lines
11 KiB
C
|
/*
|
|||
|
Unitemp - Universal temperature reader
|
|||
|
Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n)
|
|||
|
|
|||
|
This program is free software: you can redistribute it and/or modify
|
|||
|
it under the terms of the GNU General Public License as published by
|
|||
|
the Free Software Foundation, either version 3 of the License, or
|
|||
|
(at your option) any later version.
|
|||
|
|
|||
|
This program is distributed in the hope that it will be useful,
|
|||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
|
GNU General Public License for more details.
|
|||
|
|
|||
|
You should have received a copy of the GNU General Public License
|
|||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
*/
|
|||
|
#include "SingleWireSensor.h"
|
|||
|
|
|||
|
//Максимальное количество попугаев ожидания датчика
|
|||
|
#define POLLING_TIMEOUT_TICKS 500
|
|||
|
|
|||
|
/* Типы датчиков и их параметры */
|
|||
|
const SensorType DHT11 = {
|
|||
|
.typename = "DHT11",
|
|||
|
.interface = &SINGLE_WIRE,
|
|||
|
.datatype = UT_DATA_TYPE_TEMP_HUM,
|
|||
|
.pollingInterval = 2000,
|
|||
|
.allocator = unitemp_singlewire_alloc,
|
|||
|
.mem_releaser = unitemp_singlewire_free,
|
|||
|
.initializer = unitemp_singlewire_init,
|
|||
|
.deinitializer = unitemp_singlewire_deinit,
|
|||
|
.updater = unitemp_singlewire_update};
|
|||
|
const SensorType DHT12_SW = {
|
|||
|
.typename = "DHT12",
|
|||
|
.interface = &SINGLE_WIRE,
|
|||
|
.datatype = UT_DATA_TYPE_TEMP_HUM,
|
|||
|
.pollingInterval = 2000,
|
|||
|
.allocator = unitemp_singlewire_alloc,
|
|||
|
.mem_releaser = unitemp_singlewire_free,
|
|||
|
.initializer = unitemp_singlewire_init,
|
|||
|
.deinitializer = unitemp_singlewire_deinit,
|
|||
|
.updater = unitemp_singlewire_update};
|
|||
|
const SensorType DHT21 = {
|
|||
|
.typename = "DHT21",
|
|||
|
.altname = "DHT21 (AM2301)",
|
|||
|
.interface = &SINGLE_WIRE,
|
|||
|
.datatype = UT_DATA_TYPE_TEMP_HUM,
|
|||
|
.pollingInterval = 1000,
|
|||
|
.allocator = unitemp_singlewire_alloc,
|
|||
|
.mem_releaser = unitemp_singlewire_free,
|
|||
|
.initializer = unitemp_singlewire_init,
|
|||
|
.deinitializer = unitemp_singlewire_deinit,
|
|||
|
.updater = unitemp_singlewire_update};
|
|||
|
const SensorType DHT22 = {
|
|||
|
.typename = "DHT22",
|
|||
|
.altname = "DHT22 (AM2302)",
|
|||
|
.interface = &SINGLE_WIRE,
|
|||
|
.datatype = UT_DATA_TYPE_TEMP_HUM,
|
|||
|
.pollingInterval = 2000,
|
|||
|
.allocator = unitemp_singlewire_alloc,
|
|||
|
.mem_releaser = unitemp_singlewire_free,
|
|||
|
.initializer = unitemp_singlewire_init,
|
|||
|
.deinitializer = unitemp_singlewire_deinit,
|
|||
|
.updater = unitemp_singlewire_update};
|
|||
|
const SensorType AM2320_SW = {
|
|||
|
.typename = "AM2320",
|
|||
|
.altname = "AM2320 (single wire)",
|
|||
|
.interface = &SINGLE_WIRE,
|
|||
|
.datatype = UT_DATA_TYPE_TEMP_HUM,
|
|||
|
.pollingInterval = 2000,
|
|||
|
.allocator = unitemp_singlewire_alloc,
|
|||
|
.mem_releaser = unitemp_singlewire_free,
|
|||
|
.initializer = unitemp_singlewire_init,
|
|||
|
.deinitializer = unitemp_singlewire_deinit,
|
|||
|
.updater = unitemp_singlewire_update};
|
|||
|
|
|||
|
bool unitemp_singlewire_alloc(Sensor* sensor, char* args) {
|
|||
|
if(args == NULL) return false;
|
|||
|
SingleWireSensor* instance = malloc(sizeof(SingleWireSensor));
|
|||
|
if(instance == NULL) {
|
|||
|
FURI_LOG_E(APP_NAME, "Sensor %s instance allocation error", sensor->name);
|
|||
|
return false;
|
|||
|
}
|
|||
|
sensor->instance = instance;
|
|||
|
|
|||
|
int gpio = 255;
|
|||
|
sscanf(args, "%d", &gpio);
|
|||
|
|
|||
|
if(unitemp_singlewire_sensorSetGPIO(sensor, unitemp_gpio_getFromInt(gpio))) {
|
|||
|
return true;
|
|||
|
}
|
|||
|
FURI_LOG_E(APP_NAME, "Sensor %s GPIO setting error", sensor->name);
|
|||
|
free(instance);
|
|||
|
return false;
|
|||
|
}
|
|||
|
bool unitemp_singlewire_free(Sensor* sensor) {
|
|||
|
free(sensor->instance);
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool unitemp_singlewire_init(Sensor* sensor) {
|
|||
|
SingleWireSensor* instance = ((Sensor*)sensor)->instance;
|
|||
|
if(instance == NULL || instance->gpio == NULL) {
|
|||
|
FURI_LOG_E(APP_NAME, "Sensor pointer is null!");
|
|||
|
return false;
|
|||
|
}
|
|||
|
unitemp_gpio_lock(instance->gpio, &SINGLE_WIRE);
|
|||
|
//Высокий уровень по умолчанию
|
|||
|
furi_hal_gpio_write(instance->gpio->pin, true);
|
|||
|
//Режим работы - OpenDrain, подтяжка включается на всякий случай
|
|||
|
furi_hal_gpio_init(
|
|||
|
instance->gpio->pin, //Порт FZ
|
|||
|
GpioModeOutputOpenDrain, //Режим работы - открытый сток
|
|||
|
GpioPullUp, //Принудительная подтяжка линии данных к питанию
|
|||
|
GpioSpeedVeryHigh); //Скорость работы - максимальная
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool unitemp_singlewire_deinit(Sensor* sensor) {
|
|||
|
SingleWireSensor* instance = ((Sensor*)sensor)->instance;
|
|||
|
if(instance == NULL || instance->gpio == NULL) return false;
|
|||
|
unitemp_gpio_unlock(instance->gpio);
|
|||
|
//Низкий уровень по умолчанию
|
|||
|
furi_hal_gpio_write(instance->gpio->pin, false);
|
|||
|
//Режим работы - аналог, подтяжка выключена
|
|||
|
furi_hal_gpio_init(
|
|||
|
instance->gpio->pin, //Порт FZ
|
|||
|
GpioModeAnalog, //Режим работы - аналог
|
|||
|
GpioPullNo, //Подтяжка выключена
|
|||
|
GpioSpeedLow); //Скорость работы - минимальная
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
bool unitemp_singlewire_sensorSetGPIO(Sensor* sensor, const GPIO* gpio) {
|
|||
|
if(sensor == NULL || gpio == NULL) return false;
|
|||
|
SingleWireSensor* instance = sensor->instance;
|
|||
|
instance->gpio = gpio;
|
|||
|
return true;
|
|||
|
}
|
|||
|
const GPIO* unitemp_singlewire_sensorGetGPIO(Sensor* sensor) {
|
|||
|
if(sensor == NULL) return NULL;
|
|||
|
SingleWireSensor* instance = sensor->instance;
|
|||
|
return instance->gpio;
|
|||
|
}
|
|||
|
|
|||
|
UnitempStatus unitemp_singlewire_update(Sensor* sensor) {
|
|||
|
SingleWireSensor* instance = sensor->instance;
|
|||
|
|
|||
|
//Массив для приёма данных
|
|||
|
uint8_t data[5] = {0};
|
|||
|
|
|||
|
/* Запрос */
|
|||
|
//Опускание линии
|
|||
|
furi_hal_gpio_write(instance->gpio->pin, false);
|
|||
|
//Ожидание более 18 мс
|
|||
|
furi_delay_ms(19);
|
|||
|
//Выключение прерываний, чтобы ничто не мешало обработке данных
|
|||
|
__disable_irq();
|
|||
|
//Подъём линии
|
|||
|
furi_hal_gpio_write(instance->gpio->pin, true);
|
|||
|
|
|||
|
/* Ответ датчика */
|
|||
|
//Переменная-счётчик
|
|||
|
uint16_t timeout = 0;
|
|||
|
|
|||
|
//Ожидание подъёма линии
|
|||
|
while(!furi_hal_gpio_read(instance->gpio->pin)) {
|
|||
|
timeout++;
|
|||
|
if(timeout > POLLING_TIMEOUT_TICKS) {
|
|||
|
//Включение прерываний
|
|||
|
__enable_irq();
|
|||
|
//Возврат признака отсутствующего датчика
|
|||
|
return UT_SENSORSTATUS_TIMEOUT;
|
|||
|
}
|
|||
|
}
|
|||
|
timeout = 0;
|
|||
|
|
|||
|
//Ожидание спада линии
|
|||
|
while(furi_hal_gpio_read(instance->gpio->pin)) {
|
|||
|
timeout++;
|
|||
|
if(timeout > POLLING_TIMEOUT_TICKS) {
|
|||
|
//Включение прерываний
|
|||
|
__enable_irq();
|
|||
|
//Возврат признака отсутствующего датчика
|
|||
|
return UT_SENSORSTATUS_TIMEOUT;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//Ожидание подъёма линии
|
|||
|
while(!furi_hal_gpio_read(instance->gpio->pin)) {
|
|||
|
timeout++;
|
|||
|
if(timeout > POLLING_TIMEOUT_TICKS) {
|
|||
|
//Включение прерываний
|
|||
|
__enable_irq();
|
|||
|
//Возврат признака отсутствующего датчика
|
|||
|
return UT_SENSORSTATUS_TIMEOUT;
|
|||
|
}
|
|||
|
}
|
|||
|
timeout = 0;
|
|||
|
|
|||
|
//Ожидание спада линии
|
|||
|
while(furi_hal_gpio_read(instance->gpio->pin)) {
|
|||
|
timeout++;
|
|||
|
if(timeout > POLLING_TIMEOUT_TICKS) {
|
|||
|
//Включение прерываний
|
|||
|
__enable_irq();
|
|||
|
//Возврат признака отсутствующего датчика
|
|||
|
return UT_SENSORSTATUS_TIMEOUT;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/* Чтение данных с датчика*/
|
|||
|
//Приём 5 байт
|
|||
|
for(uint8_t a = 0; a < 5; a++) {
|
|||
|
for(uint8_t b = 7; b != 255; b--) {
|
|||
|
uint16_t hT = 0, lT = 0;
|
|||
|
//Пока линия в низком уровне, инкремент переменной lT
|
|||
|
while(!furi_hal_gpio_read(instance->gpio->pin) && lT != 65535) lT++;
|
|||
|
//Пока линия в высоком уровне, инкремент переменной hT
|
|||
|
while(furi_hal_gpio_read(instance->gpio->pin) && hT != 65535) hT++;
|
|||
|
//Если hT больше lT, то пришла единица
|
|||
|
if(hT > lT) data[a] |= (1 << b);
|
|||
|
}
|
|||
|
}
|
|||
|
//Включение прерываний
|
|||
|
__enable_irq();
|
|||
|
|
|||
|
//Проверка контрольной суммы
|
|||
|
if((uint8_t)(data[0] + data[1] + data[2] + data[3]) != data[4]) {
|
|||
|
//Если контрольная сумма не совпала, возврат ошибки
|
|||
|
return UT_SENSORSTATUS_BADCRC;
|
|||
|
}
|
|||
|
|
|||
|
/* Преобразование данных в явный вид */
|
|||
|
//DHT11 и DHT12
|
|||
|
if(sensor->type == &DHT11 || sensor->type == &DHT12_SW) {
|
|||
|
sensor->hum = (float)data[0];
|
|||
|
sensor->temp = (float)data[2];
|
|||
|
|
|||
|
//Проверка на отрицательность температуры
|
|||
|
if(data[3] != 0) {
|
|||
|
//Проверка знака
|
|||
|
if(!(data[3] & (1 << 7))) {
|
|||
|
//Добавление положительной дробной части
|
|||
|
sensor->temp += data[3] * 0.1f;
|
|||
|
} else {
|
|||
|
//А тут делаем отрицательное значение
|
|||
|
data[3] &= ~(1 << 7);
|
|||
|
sensor->temp += data[3] * 0.1f;
|
|||
|
sensor->temp *= -1;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//DHT21, DHT22, AM2320
|
|||
|
if(sensor->type == &DHT21 || sensor->type == &DHT22 || sensor->type == &AM2320_SW) {
|
|||
|
sensor->hum = (float)(((uint16_t)data[0] << 8) | data[1]) / 10;
|
|||
|
|
|||
|
uint16_t raw = (((uint16_t)data[2] << 8) | data[3]);
|
|||
|
//Проверка на отрицательность температуры
|
|||
|
if(READ_BIT(raw, 1 << 15)) {
|
|||
|
//Проверка на способ кодирования данных
|
|||
|
if(READ_BIT(raw, 0x60)) {
|
|||
|
//Не оригинал
|
|||
|
sensor->temp = (float)((int16_t)raw) / 10;
|
|||
|
} else {
|
|||
|
//Оригинальный датчик
|
|||
|
CLEAR_BIT(raw, 1 << 15);
|
|||
|
sensor->temp = (float)(raw) / -10;
|
|||
|
}
|
|||
|
} else {
|
|||
|
sensor->temp = (float)(raw) / 10;
|
|||
|
}
|
|||
|
}
|
|||
|
//Возврат признака успешного опроса
|
|||
|
return UT_SENSORSTATUS_OK;
|
|||
|
}
|