mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-26 03:39:58 -07:00
New plugin: Unitemp
This commit is contained in:
131
applications/plugins/unitemp/interfaces/I2CSensor.c
Normal file
131
applications/plugins/unitemp/interfaces/I2CSensor.c
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
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 "I2CSensor.h"
|
||||
|
||||
static uint8_t sensors_count = 0;
|
||||
|
||||
void unitemp_i2c_acquire(FuriHalI2cBusHandle* handle) {
|
||||
furi_hal_i2c_acquire(handle);
|
||||
LL_GPIO_SetPinPull(gpio_ext_pc1.port, gpio_ext_pc1.pin, LL_GPIO_PULL_UP);
|
||||
LL_GPIO_SetPinPull(gpio_ext_pc0.port, gpio_ext_pc0.pin, LL_GPIO_PULL_UP);
|
||||
}
|
||||
|
||||
bool unitemp_i2c_isDeviceReady(I2CSensor* i2c_sensor) {
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
bool status = furi_hal_i2c_is_device_ready(i2c_sensor->i2c, i2c_sensor->currentI2CAdr, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t unitemp_i2c_readReg(I2CSensor* i2c_sensor, uint8_t reg) {
|
||||
//Блокировка шины
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
uint8_t buff[1] = {0};
|
||||
furi_hal_i2c_read_mem(i2c_sensor->i2c, i2c_sensor->currentI2CAdr, reg, buff, 1, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return buff[0];
|
||||
}
|
||||
|
||||
bool unitemp_i2c_readArray(I2CSensor* i2c_sensor, uint8_t len, uint8_t* data) {
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
bool status = furi_hal_i2c_rx(i2c_sensor->i2c, i2c_sensor->currentI2CAdr, data, len, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool unitemp_i2c_readRegArray(I2CSensor* i2c_sensor, uint8_t startReg, uint8_t len, uint8_t* data) {
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
bool status =
|
||||
furi_hal_i2c_read_mem(i2c_sensor->i2c, i2c_sensor->currentI2CAdr, startReg, data, len, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool unitemp_i2c_writeReg(I2CSensor* i2c_sensor, uint8_t reg, uint8_t value) {
|
||||
//Блокировка шины
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
uint8_t buff[1] = {value};
|
||||
bool status =
|
||||
furi_hal_i2c_write_mem(i2c_sensor->i2c, i2c_sensor->currentI2CAdr, reg, buff, 1, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool unitemp_i2c_writeArray(I2CSensor* i2c_sensor, uint8_t len, uint8_t* data) {
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
bool status = furi_hal_i2c_tx(i2c_sensor->i2c, i2c_sensor->currentI2CAdr, data, len, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool unitemp_i2c_writeRegArray(I2CSensor* i2c_sensor, uint8_t startReg, uint8_t len, uint8_t* data) {
|
||||
//Блокировка шины
|
||||
unitemp_i2c_acquire(i2c_sensor->i2c);
|
||||
bool status = furi_hal_i2c_write_mem(
|
||||
i2c_sensor->i2c, i2c_sensor->currentI2CAdr, startReg, data, len, 10);
|
||||
furi_hal_i2c_release(i2c_sensor->i2c);
|
||||
return status;
|
||||
}
|
||||
|
||||
bool unitemp_I2C_sensor_alloc(Sensor* sensor, char* args) {
|
||||
bool status = false;
|
||||
I2CSensor* instance = malloc(sizeof(I2CSensor));
|
||||
if(instance == NULL) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor %s instance allocation error", sensor->name);
|
||||
return false;
|
||||
}
|
||||
instance->i2c = &furi_hal_i2c_handle_external;
|
||||
sensor->instance = instance;
|
||||
|
||||
//Указание функций инициализации, деинициализации и обновления данных, а так же адреса на шине I2C
|
||||
status = sensor->type->allocator(sensor, args);
|
||||
int i2c_addr;
|
||||
sscanf(args, "%X", &i2c_addr);
|
||||
|
||||
//Установка адреса шины I2C
|
||||
if(i2c_addr >= instance->minI2CAdr && i2c_addr <= instance->maxI2CAdr) {
|
||||
instance->currentI2CAdr = i2c_addr;
|
||||
} else {
|
||||
instance->currentI2CAdr = instance->minI2CAdr;
|
||||
}
|
||||
|
||||
//Блокировка портов GPIO
|
||||
sensors_count++;
|
||||
unitemp_gpio_lock(unitemp_gpio_getFromInt(15), &I2C);
|
||||
unitemp_gpio_lock(unitemp_gpio_getFromInt(16), &I2C);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool unitemp_I2C_sensor_free(Sensor* sensor) {
|
||||
bool status = sensor->type->mem_releaser(sensor);
|
||||
free(sensor->instance);
|
||||
if(--sensors_count == 0) {
|
||||
unitemp_gpio_unlock(unitemp_gpio_getFromInt(15));
|
||||
unitemp_gpio_unlock(unitemp_gpio_getFromInt(16));
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
UnitempStatus unitemp_I2C_sensor_update(Sensor* sensor) {
|
||||
if(sensor->status != UT_SENSORSTATUS_OK) {
|
||||
sensor->type->initializer(sensor);
|
||||
}
|
||||
return sensor->type->updater(sensor);
|
||||
}
|
||||
128
applications/plugins/unitemp/interfaces/I2CSensor.h
Normal file
128
applications/plugins/unitemp/interfaces/I2CSensor.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#ifndef UNITEMP_I2C
|
||||
#define UNITEMP_I2C
|
||||
|
||||
#include "../unitemp.h"
|
||||
|
||||
#include <furi_hal_i2c.h>
|
||||
|
||||
//Структура I2C датчика
|
||||
typedef struct I2CSensor {
|
||||
//Указатель на интерфейс I2C
|
||||
FuriHalI2cBusHandle* i2c;
|
||||
//Минимальный адрес устройства на шине I2C
|
||||
uint8_t minI2CAdr;
|
||||
//Максимальный адрес устройства на шине I2C
|
||||
uint8_t maxI2CAdr;
|
||||
//Текущий адрес устройства на шине I2C
|
||||
uint8_t currentI2CAdr;
|
||||
//Указатель на собственный экземпляр датчика
|
||||
void* sensorInstance;
|
||||
} I2CSensor;
|
||||
|
||||
/**
|
||||
* @brief Заблокировать шину I2C
|
||||
*
|
||||
* @param handle Указатель на шину
|
||||
*/
|
||||
void unitemp_i2c_acquire(FuriHalI2cBusHandle* handle);
|
||||
|
||||
/**
|
||||
* @brief Проверить наличие датчика на шине
|
||||
*
|
||||
* @param i2c_sensor Указатель на датчик
|
||||
* @return Истина если устройство отозвалось
|
||||
*/
|
||||
bool unitemp_i2c_isDeviceReady(I2CSensor* i2c_sensor);
|
||||
|
||||
/**
|
||||
* @brief Выделение памяти для датчика на шине I2C
|
||||
* @param sensor Указатель на датчик
|
||||
* @param st Тип датчика
|
||||
* @return Истина если всё ок
|
||||
*/
|
||||
bool unitemp_I2C_sensor_alloc(Sensor* sensor, char* args);
|
||||
|
||||
/**
|
||||
* @brief Высвобождение памяти инстанса датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_I2C_sensor_free(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Обновить значение с датчка
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Статус обновления
|
||||
*/
|
||||
UnitempStatus unitemp_I2C_sensor_update(Sensor* sensor);
|
||||
/**
|
||||
* @brief Прочитать значение регистра reg
|
||||
* @param i2c_sensor Указатель на инстанс датчика
|
||||
* @param reg Номер регистра
|
||||
* @return Значение регистра
|
||||
*/
|
||||
uint8_t unitemp_i2c_readReg(I2CSensor* i2c_sensor, uint8_t reg);
|
||||
|
||||
/**
|
||||
* @brief Прочитать масссив значений из памяти
|
||||
* @param i2c_sensor Указатель на инстанс датчика
|
||||
* @param startReg Адрес регистра с которого начнётся чтение
|
||||
* @param len Количество байт для считывания из регистра
|
||||
* @param data Указатель на массив куда будут считаны данные
|
||||
* @return Истина если устройство вернуло данные
|
||||
*/
|
||||
bool unitemp_i2c_readRegArray(I2CSensor* i2c_sensor, uint8_t startReg, uint8_t len, uint8_t* data);
|
||||
|
||||
/**
|
||||
* @brief Записать значение в регистр
|
||||
* @param i2c_sensor Указатель на инстанс датчика
|
||||
* @param reg Номер регистра
|
||||
* @param value Значение для записи
|
||||
* @return Истина если значение записано
|
||||
*/
|
||||
bool unitemp_i2c_writeReg(I2CSensor* i2c_sensor, uint8_t reg, uint8_t value);
|
||||
|
||||
/**
|
||||
* @brief Записать масссив значений в память
|
||||
* @param i2c_sensor Указатель на инстанс датчика
|
||||
* @param startReg Адрес регистра с которого начнётся запись
|
||||
* @param len Количество байт для считывания из регистра
|
||||
* @param data Указатель на массив откуда будут записаны данные
|
||||
* @return Истина если устройство вернуло данные
|
||||
*/
|
||||
bool unitemp_i2c_writeRegArray(I2CSensor* i2c_sensor, uint8_t startReg, uint8_t len, uint8_t* data);
|
||||
|
||||
/**
|
||||
* @brief Прочитать массив данных по шине I2C
|
||||
* @param i2c_sensor Указатель на инстанс датчика
|
||||
* @param startReg Адрес регистра с которого начнётся чтение
|
||||
* @param data Указатель на массив куда будут считаны данные
|
||||
* @return Истина если устройство вернуло данные
|
||||
*/
|
||||
bool unitemp_i2c_readArray(I2CSensor* i2c_sensor, uint8_t len, uint8_t* data);
|
||||
|
||||
/**
|
||||
* @brief Записать масссив данных по шине I2C
|
||||
* @param i2c_sensor Указатель на инстанс датчика
|
||||
* @param len Количество байт для считывания из регистра
|
||||
* @param data Указатель на массив откуда будут записаны данные
|
||||
* @return Истина если устройство вернуло данные
|
||||
*/
|
||||
bool unitemp_i2c_writeArray(I2CSensor* i2c_sensor, uint8_t len, uint8_t* data);
|
||||
#endif
|
||||
490
applications/plugins/unitemp/interfaces/OneWireSensor.c
Normal file
490
applications/plugins/unitemp/interfaces/OneWireSensor.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
//Использован код Дмитрия Погребняка: https://aterlux.ru/article/1wire
|
||||
|
||||
#include "OneWireSensor.h"
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include <one_wire/one_wire_host.h>
|
||||
|
||||
const SensorType Dallas = {
|
||||
.typename = "Dallas",
|
||||
.altname = "Dallas (DS18x2x)",
|
||||
.interface = &ONE_WIRE,
|
||||
.datatype = UT_DATA_TYPE_TEMP,
|
||||
.pollingInterval = 1000,
|
||||
.allocator = unitemp_onewire_sensor_alloc,
|
||||
.mem_releaser = unitemp_onewire_sensor_free,
|
||||
.initializer = unitemp_onewire_sensor_init,
|
||||
.deinitializer = unitemp_onewire_sensor_deinit,
|
||||
.updater = unitemp_onewire_sensor_update};
|
||||
|
||||
// Переменные для хранения промежуточного результата сканирования шины
|
||||
// найденный восьмибайтовый адрес
|
||||
static uint8_t onewire_enum[8] = {0};
|
||||
// последний нулевой бит, где была неоднозначность (нумеруя с единицы)
|
||||
static uint8_t onewire_enum_fork_bit = 65;
|
||||
|
||||
OneWireBus* uintemp_onewire_bus_alloc(const GPIO* gpio) {
|
||||
if(gpio == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//Проверка на наличие шины на этом порте
|
||||
for(uint8_t i = 0; i < unitemp_sensors_getActiveCount(); i++) {
|
||||
if(unitemp_sensor_getActive(i)->type->interface == &ONE_WIRE &&
|
||||
((OneWireSensor*)unitemp_sensor_getActive(i)->instance)->bus->gpio->num == gpio->num) {
|
||||
//Если шина на этом порту уже есть, то возврат указателя на шину
|
||||
return ((OneWireSensor*)unitemp_sensor_getActive(i)->instance)->bus;
|
||||
}
|
||||
}
|
||||
|
||||
OneWireBus* bus = malloc(sizeof(OneWireBus));
|
||||
bus->device_count = 0;
|
||||
bus->gpio = gpio;
|
||||
bus->powerMode = PWR_PASSIVE;
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "one wire bus (port %d) allocated", gpio->num);
|
||||
#endif
|
||||
|
||||
return bus;
|
||||
}
|
||||
|
||||
bool unitemp_onewire_bus_init(OneWireBus* bus) {
|
||||
if(bus == NULL) return false;
|
||||
bus->device_count++;
|
||||
//Выход если шина уже была инициализирована
|
||||
if(bus->device_count > 1) return true;
|
||||
|
||||
unitemp_gpio_lock(bus->gpio, &ONE_WIRE);
|
||||
//Высокий уровень по умолчанию
|
||||
furi_hal_gpio_write(bus->gpio->pin, true);
|
||||
//Режим работы - OpenDrain, подтяжка включается на всякий случай
|
||||
furi_hal_gpio_init(
|
||||
bus->gpio->pin, //Порт FZ
|
||||
GpioModeOutputOpenDrain, //Режим работы - открытый сток
|
||||
GpioPullUp, //Принудительная подтяжка линии данных к питанию
|
||||
GpioSpeedVeryHigh); //Скорость работы - максимальная
|
||||
|
||||
return true;
|
||||
}
|
||||
bool unitemp_onewire_bus_deinit(OneWireBus* bus) {
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "devices on wire %d: %d", bus->gpio->num, bus->device_count);
|
||||
#endif
|
||||
bus->device_count--;
|
||||
if(bus->device_count <= 0) {
|
||||
bus->device_count = 0;
|
||||
unitemp_gpio_unlock(bus->gpio);
|
||||
//Режим работы - аналог, подтяжка выключена
|
||||
furi_hal_gpio_init(
|
||||
bus->gpio->pin, //Порт FZ
|
||||
GpioModeAnalog, //Режим работы - аналог
|
||||
GpioPullNo, //Подтяжка выключена
|
||||
GpioSpeedLow); //Скорость работы - минимальная
|
||||
//Низкий уровень по умолчанию
|
||||
furi_hal_gpio_write(bus->gpio->pin, false);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool unitemp_onewire_bus_start(OneWireBus* bus) {
|
||||
furi_hal_gpio_write(bus->gpio->pin, false);
|
||||
furi_delay_us(500);
|
||||
|
||||
furi_hal_gpio_write(bus->gpio->pin, true);
|
||||
|
||||
//Ожидание подъёма шины
|
||||
uint32_t t = furi_get_tick();
|
||||
while(!furi_hal_gpio_read(bus->gpio->pin)) {
|
||||
//Выход если шина не поднялась
|
||||
if(furi_get_tick() - t > 10) return false;
|
||||
}
|
||||
|
||||
furi_delay_us(100);
|
||||
bool status = !furi_hal_gpio_read(bus->gpio->pin);
|
||||
furi_delay_us(400);
|
||||
return status;
|
||||
}
|
||||
|
||||
void unitemp_onewire_bus_send_bit(OneWireBus* bus, bool state) {
|
||||
//Необходимо для стабильной работы при пассивном питании
|
||||
if(bus->powerMode == PWR_PASSIVE) furi_delay_us(100);
|
||||
|
||||
if(state) {
|
||||
// write 1
|
||||
furi_hal_gpio_write(bus->gpio->pin, false);
|
||||
furi_delay_us(1);
|
||||
furi_hal_gpio_write(bus->gpio->pin, true);
|
||||
furi_delay_us(90);
|
||||
} else {
|
||||
furi_hal_gpio_write(bus->gpio->pin, false);
|
||||
furi_delay_us(90);
|
||||
furi_hal_gpio_write(bus->gpio->pin, true);
|
||||
//Ожидание подъёма шины
|
||||
uint32_t t = furi_get_tick();
|
||||
while(!furi_hal_gpio_read(bus->gpio->pin)) {
|
||||
//Выход если шина не поднялась
|
||||
if(furi_get_tick() - t > 10) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unitemp_onewire_bus_send_byte(OneWireBus* bus, uint8_t data) {
|
||||
for(int i = 0; i < 8; i++) {
|
||||
unitemp_onewire_bus_send_bit(bus, (data & (1 << i)) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
void unitemp_onewire_bus_send_byteArray(OneWireBus* bus, uint8_t* data, uint8_t len) {
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
unitemp_onewire_bus_send_byte(bus, data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool unitemp_onewire_bus_read_bit(OneWireBus* bus) {
|
||||
furi_delay_ms(1);
|
||||
furi_hal_gpio_write(bus->gpio->pin, false);
|
||||
furi_delay_us(2); // Длительность низкого уровня, минимум 1 мкс
|
||||
furi_hal_gpio_write(bus->gpio->pin, true);
|
||||
furi_delay_us(8); // Пауза до момента сэмплирования, всего не более 15 мкс
|
||||
bool r = furi_hal_gpio_read(bus->gpio->pin);
|
||||
furi_delay_us(80); // Ожидание до следующего тайм-слота, минимум 60 мкс с начала низкого уровня
|
||||
return r;
|
||||
}
|
||||
|
||||
uint8_t unitemp_onewire_bus_read_byte(OneWireBus* bus) {
|
||||
uint8_t r = 0;
|
||||
for(uint8_t p = 8; p; p--) {
|
||||
r >>= 1;
|
||||
if(unitemp_onewire_bus_read_bit(bus)) r |= 0x80;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void unitemp_onewire_bus_read_byteArray(OneWireBus* bus, uint8_t* data, uint8_t len) {
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
data[i] = unitemp_onewire_bus_read_byte(bus);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t onewire_CRC_update(uint8_t crc, uint8_t b) {
|
||||
for(uint8_t p = 8; p; p--) {
|
||||
crc = ((crc ^ b) & 1) ? (crc >> 1) ^ 0b10001100 : (crc >> 1);
|
||||
b >>= 1;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool unitemp_onewire_CRC_check(uint8_t* data, uint8_t len) {
|
||||
uint8_t crc = 0;
|
||||
for(uint8_t i = 0; i < len; i++) {
|
||||
crc = onewire_CRC_update(crc, data[i]);
|
||||
}
|
||||
return !crc;
|
||||
}
|
||||
|
||||
char* unitemp_onewire_sensor_getModel(Sensor* sensor) {
|
||||
OneWireSensor* ow_sensor = sensor->instance;
|
||||
switch(ow_sensor->deviceID[0]) {
|
||||
case FC_DS18B20:
|
||||
return "DS18B20";
|
||||
case FC_DS18S20:
|
||||
return "DS18S20";
|
||||
case FC_DS1822:
|
||||
return "DS1822";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
bool unitemp_onewire_sensor_readID(OneWireSensor* instance) {
|
||||
if(!unitemp_onewire_bus_start(instance->bus)) return false;
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0x33); // Чтение ПЗУ
|
||||
unitemp_onewire_bus_read_byteArray(instance->bus, instance->deviceID, 8);
|
||||
if(!unitemp_onewire_CRC_check(instance->deviceID, 8)) {
|
||||
memset(instance->deviceID, 0, 8);
|
||||
return false;
|
||||
}
|
||||
instance->familyCode = instance->deviceID[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
void unitemp_onewire_bus_enum_init(void) {
|
||||
for(uint8_t p = 0; p < 8; p++) {
|
||||
onewire_enum[p] = 0;
|
||||
}
|
||||
onewire_enum_fork_bit = 65; // правее правого
|
||||
}
|
||||
|
||||
uint8_t* unitemp_onewire_bus_enum_next(OneWireBus* bus) {
|
||||
furi_delay_ms(10);
|
||||
if(!onewire_enum_fork_bit) { // Если на предыдущем шаге уже не было разногласий
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "All devices on wire %d is found", unitemp_gpio_toInt(bus->gpio));
|
||||
#endif
|
||||
return 0; // то просто выходим ничего не возвращая
|
||||
}
|
||||
if(!unitemp_onewire_bus_start(bus)) {
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "Wire %d is empty", unitemp_gpio_toInt(bus->gpio));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
uint8_t bp = 8;
|
||||
uint8_t* pprev = &onewire_enum[0];
|
||||
uint8_t prev = *pprev;
|
||||
uint8_t next = 0;
|
||||
|
||||
uint8_t p = 1;
|
||||
unitemp_onewire_bus_send_byte(bus, 0xF0);
|
||||
uint8_t newfork = 0;
|
||||
for(;;) {
|
||||
uint8_t not0 = unitemp_onewire_bus_read_bit(bus);
|
||||
uint8_t not1 = unitemp_onewire_bus_read_bit(bus);
|
||||
if(!not0) { // Если присутствует в адресах бит ноль
|
||||
if(!not1) { // Но также присустствует бит 1 (вилка)
|
||||
if(p <
|
||||
onewire_enum_fork_bit) { // Если мы левее прошлого правого конфликтного бита,
|
||||
if(prev & 1) {
|
||||
next |= 0x80; // то копируем значение бита из прошлого прохода
|
||||
} else {
|
||||
newfork = p; // если ноль, то запомним конфликтное место
|
||||
}
|
||||
} else if(p == onewire_enum_fork_bit) {
|
||||
next |=
|
||||
0x80; // если на этом месте в прошлый раз был правый конфликт с нулём, выведем 1
|
||||
} else {
|
||||
newfork = p; // правее - передаём ноль и запоминаем конфликтное место
|
||||
}
|
||||
} // в противном случае идём, выбирая ноль в адресе
|
||||
} else {
|
||||
if(!not1) { // Присутствует единица
|
||||
next |= 0x80;
|
||||
} else { // Нет ни нулей ни единиц - ошибочная ситуация
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "Wrong wire %d situation", unitemp_gpio_toInt(bus->gpio));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
unitemp_onewire_bus_send_bit(bus, next & 0x80);
|
||||
bp--;
|
||||
if(!bp) {
|
||||
*pprev = next;
|
||||
if(p >= 64) break;
|
||||
next = 0;
|
||||
pprev++;
|
||||
prev = *pprev;
|
||||
bp = 8;
|
||||
} else {
|
||||
if(p >= 64) break;
|
||||
prev >>= 1;
|
||||
next >>= 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
onewire_enum_fork_bit = newfork;
|
||||
return &onewire_enum[0];
|
||||
}
|
||||
|
||||
void unitemp_onewire_bus_select_sensor(OneWireSensor* instance) {
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0x55);
|
||||
unitemp_onewire_bus_send_byteArray(instance->bus, instance->deviceID, 8);
|
||||
}
|
||||
|
||||
bool unitemp_onewire_sensor_alloc(Sensor* sensor, char* args) {
|
||||
OneWireSensor* instance = malloc(sizeof(OneWireSensor));
|
||||
if(instance == NULL) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor %s instance allocation error", sensor->name);
|
||||
return false;
|
||||
}
|
||||
sensor->instance = instance;
|
||||
//Очистка адреса
|
||||
memset(instance->deviceID, 0, 8);
|
||||
|
||||
int gpio, addr_0, addr_1, addr_2, addr_3, addr_4, addr_5, addr_6, addr_7;
|
||||
sscanf(
|
||||
args,
|
||||
"%d %2X%2X%2X%2X%2X%2X%2X%2X",
|
||||
&gpio,
|
||||
&addr_0,
|
||||
&addr_1,
|
||||
&addr_2,
|
||||
&addr_3,
|
||||
&addr_4,
|
||||
&addr_5,
|
||||
&addr_6,
|
||||
&addr_7);
|
||||
instance->deviceID[0] = addr_0;
|
||||
instance->deviceID[1] = addr_1;
|
||||
instance->deviceID[2] = addr_2;
|
||||
instance->deviceID[3] = addr_3;
|
||||
instance->deviceID[4] = addr_4;
|
||||
instance->deviceID[5] = addr_5;
|
||||
instance->deviceID[6] = addr_6;
|
||||
instance->deviceID[7] = addr_7;
|
||||
|
||||
instance->familyCode = instance->deviceID[0];
|
||||
|
||||
instance->bus = uintemp_onewire_bus_alloc(unitemp_gpio_getFromInt(gpio));
|
||||
|
||||
if(instance != NULL) {
|
||||
return true;
|
||||
}
|
||||
FURI_LOG_E(APP_NAME, "Sensor %s bus allocation error", sensor->name);
|
||||
free(instance);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool unitemp_onewire_sensor_free(Sensor* sensor) {
|
||||
if(((OneWireSensor*)sensor->instance)->bus != NULL) {
|
||||
if(((OneWireSensor*)sensor->instance)->bus->device_count == 0) {
|
||||
free(((OneWireSensor*)sensor->instance)->bus);
|
||||
}
|
||||
}
|
||||
|
||||
free(sensor->instance);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_onewire_sensor_init(Sensor* sensor) {
|
||||
OneWireSensor* instance = sensor->instance;
|
||||
if(instance == NULL || instance->bus == NULL) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor pointer is null!");
|
||||
return false;
|
||||
}
|
||||
|
||||
unitemp_onewire_bus_init(instance->bus);
|
||||
furi_delay_ms(1);
|
||||
|
||||
if(instance->familyCode == FC_DS18B20 || instance->familyCode == FC_DS1822) {
|
||||
//Установка разрядности в 10 бит
|
||||
if(!unitemp_onewire_bus_start(instance->bus)) return false;
|
||||
unitemp_onewire_bus_select_sensor(instance);
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0x4E); // Запись в память
|
||||
uint8_t buff[3];
|
||||
//Значения тревоги
|
||||
buff[0] = 0x4B; //Значение нижнего предела температуры
|
||||
buff[1] = 0x46; //Значение верхнего предела температуры
|
||||
//Конфигурация
|
||||
buff[2] = 0b01111111; //12 бит разрядность преобразования
|
||||
unitemp_onewire_bus_send_byteArray(instance->bus, buff, 3);
|
||||
|
||||
//Сохранение значений в EEPROM для автоматического восстановления после сбоев питания
|
||||
if(!unitemp_onewire_bus_start(instance->bus)) return false;
|
||||
unitemp_onewire_bus_select_sensor(instance);
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0x48); // Запись в EEPROM
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_onewire_sensor_deinit(Sensor* sensor) {
|
||||
OneWireSensor* instance = sensor->instance;
|
||||
if(instance == NULL || instance->bus == NULL) return false;
|
||||
unitemp_onewire_bus_deinit(instance->bus);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UnitempStatus unitemp_onewire_sensor_update(Sensor* sensor) {
|
||||
//Снятие особого статуса с датчика при пассивном режиме питания
|
||||
if(sensor->status == UT_SENSORSTATUS_EARLYPOOL) {
|
||||
return UT_SENSORSTATUS_POLLING;
|
||||
}
|
||||
|
||||
OneWireSensor* instance = sensor->instance;
|
||||
uint8_t buff[9] = {0};
|
||||
if(sensor->status != UT_SENSORSTATUS_POLLING) {
|
||||
//Если датчик в прошлый раз не отозвался, проверка его наличия на шине
|
||||
if(sensor->status == UT_SENSORSTATUS_TIMEOUT || sensor->status == UT_SENSORSTATUS_BADCRC) {
|
||||
if(!unitemp_onewire_bus_start(instance->bus)) return UT_SENSORSTATUS_TIMEOUT;
|
||||
unitemp_onewire_bus_select_sensor(instance);
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0xBE); // Read Scratch-pad
|
||||
unitemp_onewire_bus_read_byteArray(instance->bus, buff, 9);
|
||||
if(!unitemp_onewire_CRC_check(buff, 9)) {
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "Sensor %s is not found", sensor->name);
|
||||
#endif
|
||||
return UT_SENSORSTATUS_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
if(!unitemp_onewire_bus_start(instance->bus)) return UT_SENSORSTATUS_TIMEOUT;
|
||||
//Запуск преобразования на всех датчиках в режиме пассивного питания
|
||||
if(instance->bus->powerMode == PWR_PASSIVE) {
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0xCC); // skip addr
|
||||
//Установка на всех датчиках этой шины особого статуса, чтобы не запускать преобразование ещё раз
|
||||
for(uint8_t i = 0; i < unitemp_sensors_getActiveCount(); i++) {
|
||||
if(unitemp_sensor_getActive(i)->type->interface == &ONE_WIRE &&
|
||||
((OneWireSensor*)unitemp_sensor_getActive(i)->instance)->bus == instance->bus) {
|
||||
unitemp_sensor_getActive(i)->status = UT_SENSORSTATUS_EARLYPOOL;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
unitemp_onewire_bus_select_sensor(instance);
|
||||
}
|
||||
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0x44); // convert t
|
||||
if(instance->bus->powerMode == PWR_PASSIVE) {
|
||||
furi_hal_gpio_write(instance->bus->gpio->pin, true);
|
||||
furi_hal_gpio_init(
|
||||
instance->bus->gpio->pin, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh);
|
||||
}
|
||||
return UT_SENSORSTATUS_POLLING;
|
||||
} else {
|
||||
if(instance->bus->powerMode == PWR_PASSIVE) {
|
||||
furi_hal_gpio_write(instance->bus->gpio->pin, true);
|
||||
furi_hal_gpio_init(
|
||||
instance->bus->gpio->pin, GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
|
||||
}
|
||||
if(!unitemp_onewire_bus_start(instance->bus)) return UT_SENSORSTATUS_TIMEOUT;
|
||||
unitemp_onewire_bus_select_sensor(instance);
|
||||
unitemp_onewire_bus_send_byte(instance->bus, 0xBE); // Read Scratch-pad
|
||||
unitemp_onewire_bus_read_byteArray(instance->bus, buff, 9);
|
||||
if(!unitemp_onewire_CRC_check(buff, 9)) {
|
||||
#ifdef UNITEMP_DEBUG
|
||||
FURI_LOG_D(APP_NAME, "Failed CRC check: %s", sensor->name);
|
||||
#endif
|
||||
return UT_SENSORSTATUS_BADCRC;
|
||||
}
|
||||
int16_t raw = buff[0] | ((int16_t)buff[1] << 8);
|
||||
if(instance->familyCode == FC_DS18S20) {
|
||||
//Песевдо-12-бит. Отключено из-за неестественности и нестабильности показаний по сравнению с DS18B20
|
||||
//sensor->temp = ((float)raw / 2.0f) - 0.25f + (16.0f - buff[6]) / 16.0f;
|
||||
//Честные 9 бит
|
||||
sensor->temp = ((float)raw / 2.0f);
|
||||
} else {
|
||||
sensor->temp = (float)raw / 16.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return UT_SENSORSTATUS_OK;
|
||||
}
|
||||
|
||||
bool unitemp_onewire_id_compare(uint8_t* id1, uint8_t* id2) {
|
||||
if(id1 == NULL || id2 == NULL) return false;
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
if(id1[i] != id2[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
223
applications/plugins/unitemp/interfaces/OneWireSensor.h
Normal file
223
applications/plugins/unitemp/interfaces/OneWireSensor.h
Normal file
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#ifndef UNITEMP_OneWire
|
||||
#define UNITEMP_OneWire
|
||||
|
||||
#include "../unitemp.h"
|
||||
|
||||
//Коды семейства устройств
|
||||
typedef enum DallasFamilyCode {
|
||||
FC_DS18S20 = 0x10,
|
||||
FC_DS1822 = 0x22,
|
||||
FC_DS18B20 = 0x28,
|
||||
} DallasFamilyCode;
|
||||
|
||||
//Режим питания датчка
|
||||
typedef enum PowerMode {
|
||||
PWR_PASSIVE, //Питание от линии данных
|
||||
PWR_ACTIVE //Питание от источника питания
|
||||
} PowerMode;
|
||||
|
||||
//Инстанс шины one wire
|
||||
typedef struct {
|
||||
//Порт подключения датчика
|
||||
const GPIO* gpio;
|
||||
//Количество устройств на шине
|
||||
//Обновляется при ручном добавлении датчика на эту шину
|
||||
int8_t device_count;
|
||||
//Режим питания датчиков на шине
|
||||
PowerMode powerMode;
|
||||
} OneWireBus;
|
||||
|
||||
//Инстанс датчика one wire
|
||||
typedef struct OneWireSensor {
|
||||
//Указатель на шину OneWire
|
||||
OneWireBus* bus;
|
||||
//Текущий адрес устройства на шине OneWire
|
||||
uint8_t deviceID[8];
|
||||
//Код семейства устройств
|
||||
DallasFamilyCode familyCode;
|
||||
} OneWireSensor;
|
||||
|
||||
/**
|
||||
* @brief Выделение памяти для датчика на шине OneWire
|
||||
* @param sensor Указатель на датчик
|
||||
* @param args Указатель на массив аргументов с параметрами датчика
|
||||
* @return Истина если всё ок
|
||||
*/
|
||||
bool unitemp_onewire_sensor_alloc(Sensor* sensor, char* args);
|
||||
|
||||
/**
|
||||
* @brief Высвобождение памяти инстанса датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_onewire_sensor_free(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Инициализации датчика на шине one wire
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Истина если инициализация упспешная
|
||||
*/
|
||||
bool unitemp_onewire_sensor_init(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_onewire_sensor_deinit(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Обновить значение с датчка
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Статус обновления
|
||||
*/
|
||||
UnitempStatus unitemp_onewire_sensor_update(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Выделение памяти для шины one wire и её инициализация
|
||||
* @param gpio Порт на котором необходимо создать шину
|
||||
* @return При успехе возвращает указатель на шину one wire
|
||||
*/
|
||||
OneWireBus* uintemp_onewire_bus_alloc(const GPIO* gpio);
|
||||
|
||||
/**
|
||||
* @brief Инициализация шины one wire
|
||||
*
|
||||
* @param bus Указатель на шину
|
||||
* @return Истина если инициализация успешна
|
||||
*/
|
||||
bool unitemp_onewire_bus_init(OneWireBus* bus);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация шины one wire
|
||||
*
|
||||
* @param bus Указатель на шину
|
||||
* @return Истина если шина была деинициализирована, ложь если на шине остались устройства
|
||||
*/
|
||||
bool unitemp_onewire_bus_deinit(OneWireBus* bus);
|
||||
|
||||
/**
|
||||
* @brief Запуск общения с датчиками на шине one wire
|
||||
* @param bus Указатель на шину
|
||||
* @return Истина если хотя бы одно устройство отозвалось
|
||||
*/
|
||||
bool unitemp_onewire_bus_start(OneWireBus* bus);
|
||||
|
||||
/**
|
||||
* @brief Отправить 1 бит данных на шину one wire
|
||||
* @param bus Указатель на шину
|
||||
* @param state Логический уровень
|
||||
*/
|
||||
void unitemp_onewire_bus_send_bit(OneWireBus* bus, bool state);
|
||||
|
||||
/**
|
||||
* @brief Запись байта на шину one wire
|
||||
*
|
||||
* @param bus Указатель на шину one wire
|
||||
* @param data Записываемый байт
|
||||
*/
|
||||
void unitemp_onewire_bus_send_byte(OneWireBus* bus, uint8_t data);
|
||||
|
||||
/**
|
||||
* @brief Запись массива байт на шину one wire
|
||||
*
|
||||
* @param bus Указатель на шину one wire
|
||||
* @param data Указатель на массив, откуда будут записаны данные
|
||||
* @param len Количество байт
|
||||
*/
|
||||
void unitemp_onewire_bus_send_byteArray(OneWireBus* bus, uint8_t* data, uint8_t len);
|
||||
|
||||
/**
|
||||
* @brief Чтение бита на шине one wire
|
||||
*
|
||||
* @param bus Указатель на шину one wire
|
||||
* @return Логический уровень бита
|
||||
*/
|
||||
bool unitemp_onewire_bus_read_bit(OneWireBus* bus);
|
||||
|
||||
/**
|
||||
* @brief Чтение байта с шины One Wire
|
||||
*
|
||||
* @param bus Указатель на шину one wire
|
||||
* @return Байт информации
|
||||
*/
|
||||
uint8_t unitemp_onewire_bus_read_byte(OneWireBus* bus);
|
||||
|
||||
/**
|
||||
* @brief Чтение массива байт с шины One Wire
|
||||
*
|
||||
* @param bus Указатель на шину one wire
|
||||
* @param data Указатель на массив, куда будут записаны данные
|
||||
* @param len Количество байт
|
||||
*/
|
||||
void unitemp_onewire_bus_read_byteArray(OneWireBus* bus, uint8_t* data, uint8_t len);
|
||||
|
||||
/**
|
||||
* @brief Проверить контрольную сумму массива данных
|
||||
*
|
||||
* @param data Указатель на массив данных
|
||||
* @param len Длина массива (включая байт CRC)
|
||||
* @return Истина если контрольная сумма корректная
|
||||
*/
|
||||
bool unitemp_onewire_CRC_check(uint8_t* data, uint8_t len);
|
||||
|
||||
/**
|
||||
* @brief Получить имя модели датчика на шине One Wire
|
||||
*
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Указатель на строку с названием
|
||||
*/
|
||||
char* unitemp_onewire_sensor_getModel(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Чтение индификатора единственного датчика. ID запишется в инстанс датчика
|
||||
*
|
||||
* @param instance Указатель на инстанс датчика
|
||||
* @return Истина, если код успешно прочитан, ложь если устройство отсутствует или устройств на шине больше одного
|
||||
*/
|
||||
bool unitemp_oneWire_sensor_readID(OneWireSensor* instance);
|
||||
|
||||
/**
|
||||
* @brief Команда выбора определённого датчка по его ID
|
||||
* @param instance Указатель на датчик one wire
|
||||
*/
|
||||
void unitemp_onewire_bus_select_sensor(OneWireSensor* instance);
|
||||
|
||||
/**
|
||||
* @brief Инициализация процесса поиска адресов на шине one wire
|
||||
*/
|
||||
void unitemp_onewire_bus_enum_init(void);
|
||||
|
||||
/**
|
||||
* @brief Перечисляет устройства на шине one wire и получает очередной адрес
|
||||
* @param bus Указатель на шину one wire
|
||||
* @return Возвращает указатель на буфер, содержащий восьмибайтовое значение адреса, либо NULL, если поиск завешён
|
||||
*/
|
||||
uint8_t* unitemp_onewire_bus_enum_next(OneWireBus* bus);
|
||||
|
||||
/**
|
||||
* @brief Сравнить ID датчиков
|
||||
*
|
||||
* @param id1 Указатель на адрес первого датчика
|
||||
* @param id2 Указатель на адрес второго датчика
|
||||
* @return Истина если ID индентичны
|
||||
*/
|
||||
bool unitemp_onewire_id_compare(uint8_t* id1, uint8_t* id2);
|
||||
|
||||
extern const SensorType Dallas;
|
||||
#endif
|
||||
279
applications/plugins/unitemp/interfaces/SingleWireSensor.c
Normal file
279
applications/plugins/unitemp/interfaces/SingleWireSensor.c
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
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;
|
||||
}
|
||||
92
applications/plugins/unitemp/interfaces/SingleWireSensor.h
Normal file
92
applications/plugins/unitemp/interfaces/SingleWireSensor.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
#ifndef UNITEMP_SINGLE_WIRE
|
||||
#define UNITEMP_SINGLE_WIRE
|
||||
|
||||
#include "../unitemp.h"
|
||||
#include "../Sensors.h"
|
||||
|
||||
//Интерфейс Single Wire
|
||||
typedef struct {
|
||||
//Порт подключения датчика
|
||||
const GPIO* gpio;
|
||||
} SingleWireSensor;
|
||||
|
||||
/* Датчики */
|
||||
extern const SensorType DHT11;
|
||||
extern const SensorType DHT12_SW;
|
||||
extern const SensorType DHT21;
|
||||
extern const SensorType DHT22;
|
||||
extern const SensorType AM2320_SW;
|
||||
|
||||
/**
|
||||
* @brief Инициализация датчика
|
||||
*
|
||||
* @param sensor Указатель на инициализируемый датчик
|
||||
* @return Истина если всё прошло успешно
|
||||
*/
|
||||
bool unitemp_singlewire_init(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация датчика
|
||||
*
|
||||
* @param sensor Указатель на инициализируемый датчик
|
||||
* @return Истина если всё прошло успешно
|
||||
*/
|
||||
bool unitemp_singlewire_deinit(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Получение данных с датчика по однопроводному интерфейсу DHTxx и AM2xxx
|
||||
*
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Статус опроса
|
||||
*/
|
||||
UnitempStatus unitemp_singlewire_update(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Установить порт датчика
|
||||
*
|
||||
* @param sensor Указатель на датчик
|
||||
* @param gpio Устанавливаемый порт
|
||||
* @return Истина если всё ок
|
||||
*/
|
||||
bool unitemp_singlewire_sensorSetGPIO(Sensor* sensor, const GPIO* gpio);
|
||||
|
||||
/**
|
||||
* @brief Получить порт датчика
|
||||
*
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Указатель на GPIO
|
||||
*/
|
||||
const GPIO* unitemp_singlewire_sensorGetGPIO(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Выделение памяти под датчик на линии One Wire
|
||||
*
|
||||
* @param sensor Указатель на датчик
|
||||
* @param args Указатель на массив с аргументами параметров датчка
|
||||
*/
|
||||
bool unitemp_singlewire_alloc(Sensor* sensor, char* args);
|
||||
|
||||
/**
|
||||
* @brief Высвобождение памяти инстанса датчика
|
||||
*
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_singlewire_free(Sensor* sensor);
|
||||
#endif
|
||||
Reference in New Issue
Block a user