mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2024-12-19 03:11:52 +03:00
Unitemp SCD40 support
by divinebird
This commit is contained in:
parent
b92f7c669b
commit
fef90f1ec5
2
applications/external/unitemp/Sensors.c
vendored
2
applications/external/unitemp/Sensors.c
vendored
@ -79,7 +79,7 @@ static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT
|
||||
&Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10,
|
||||
&SHT30, &GXHT30, &LM75, &HDC1080, &BMP180,
|
||||
&BMP280, &BME280, &BME680, &MAX31855, &MAX6675,
|
||||
&SCD30};
|
||||
&SCD30, &SCD40};
|
||||
|
||||
const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
|
||||
if(index > SENSOR_TYPES_COUNT) return NULL;
|
||||
|
60
applications/external/unitemp/interfaces/endianness.h
vendored
Normal file
60
applications/external/unitemp/interfaces/endianness.h
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
//
|
||||
// Created by Avilov Vasily on 10.06.2023.
|
||||
//
|
||||
|
||||
#ifndef FLIPPERZERO_FIRMWARE_ENDIANNESS_H
|
||||
#define FLIPPERZERO_FIRMWARE_ENDIANNESS_H
|
||||
|
||||
inline static void store16(uint8_t* b, uint16_t i) {
|
||||
memcpy(b, &i, 2);
|
||||
}
|
||||
|
||||
inline static void store32(uint8_t* b, uint32_t i) {
|
||||
memcpy(b, &i, 4);
|
||||
}
|
||||
|
||||
inline static uint16_t load16(uint8_t* b) {
|
||||
uint16_t x;
|
||||
memcpy(&x, b, 2);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline static uint32_t load32(uint8_t* b) {
|
||||
uint32_t x;
|
||||
memcpy(&x, b, 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define htobe16(x) (x)
|
||||
#define htobe32(x) (x)
|
||||
#define htole16(x) __builtin_bswap16(x)
|
||||
#define htole32(x) __builtin_bswap32(x)
|
||||
#define be16toh(x) (x)
|
||||
#define be32toh(x) (x)
|
||||
#define le16toh(x) __builtin_bswap16(x)
|
||||
#define le32toh(x) __builtin_bswap32(x)
|
||||
#elif BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define htole16(x) (x)
|
||||
#define htole32(x) (x)
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define le16toh(x) (x)
|
||||
#define le32toh(x) (x)
|
||||
#else
|
||||
#error "What kind of system is this?"
|
||||
#endif
|
||||
|
||||
#define load16_le(b) (le16toh(load16(b)))
|
||||
#define load32_le(b) (le32toh(load32(b)))
|
||||
#define store16_le(b, i) (store16(b, htole16(i)))
|
||||
#define store32_le(b, i) (store32(b, htole32(i)))
|
||||
|
||||
#define load16_be(b) (be16toh(load16(b)))
|
||||
#define load32_be(b) (be32toh(load32(b)))
|
||||
#define store16_be(b, i) (store16(b, htobe16(i)))
|
||||
#define store32_be(b, i) (store32(b, htobe32(i)))
|
||||
|
||||
#endif //FLIPPERZERO_FIRMWARE_ENDIANNESS_H
|
53
applications/external/unitemp/sensors/SCD30.c
vendored
53
applications/external/unitemp/sensors/SCD30.c
vendored
@ -21,60 +21,9 @@
|
||||
|
||||
#include "SCD30.h"
|
||||
#include "../interfaces/I2CSensor.h"
|
||||
#include "../interfaces/endianness.h"
|
||||
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
|
||||
|
||||
inline static uint16_t load16(uint8_t* b) {
|
||||
uint16_t x;
|
||||
memcpy(&x, b, 2);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline static uint32_t load32(uint8_t* b) {
|
||||
uint32_t x;
|
||||
memcpy(&x, b, 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline static void store16(uint8_t* b, uint16_t i) {
|
||||
memcpy(b, &i, 2);
|
||||
}
|
||||
|
||||
inline static void store32(uint8_t* b, uint32_t i) {
|
||||
memcpy(b, &i, 4);
|
||||
}
|
||||
|
||||
#if BYTE_ORDER == BIG_ENDIAN
|
||||
#define htobe16(x) (x)
|
||||
#define htobe32(x) (x)
|
||||
#define htole16(x) __builtin_bswap16(x)
|
||||
#define htole32(x) __builtin_bswap32(x)
|
||||
#define be16toh(x) (x)
|
||||
#define be32toh(x) (x)
|
||||
#define le16toh(x) __builtin_bswap16(x)
|
||||
#define le32toh(x) __builtin_bswap32(x)
|
||||
#elif BYTE_ORDER == LITTLE_ENDIAN
|
||||
#define htobe16(x) __builtin_bswap16(x)
|
||||
#define htobe32(x) __builtin_bswap32(x)
|
||||
#define htole16(x) (x)
|
||||
#define htole32(x) (x)
|
||||
#define be16toh(x) __builtin_bswap16(x)
|
||||
#define be32toh(x) __builtin_bswap32(x)
|
||||
#define le16toh(x) (x)
|
||||
#define le32toh(x) (x)
|
||||
#else
|
||||
#error "What kind of system is this?"
|
||||
#endif
|
||||
|
||||
#define load16_le(b) (le16toh(load16(b)))
|
||||
#define load32_le(b) (le32toh(load32(b)))
|
||||
#define store16_le(b, i) (store16(b, htole16(i)))
|
||||
#define store32_le(b, i) (store32(b, htole32(i)))
|
||||
|
||||
#define load16_be(b) (be16toh(load16(b)))
|
||||
#define load32_be(b) (be32toh(load32(b)))
|
||||
#define store16_be(b, i) (store16(b, htobe16(i)))
|
||||
#define store32_be(b, i) (store32(b, htobe32(i)))
|
||||
|
||||
typedef union {
|
||||
uint16_t array16[2];
|
||||
uint8_t array8[4];
|
||||
|
291
applications/external/unitemp/sensors/SCD40.c
vendored
Normal file
291
applications/external/unitemp/sensors/SCD40.c
vendored
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
Unitemp - Universal temperature reader
|
||||
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
|
||||
Contributed by divinebird (https://github.com/divinebird)
|
||||
|
||||
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/>.
|
||||
*/
|
||||
|
||||
// Some information may be seen on https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library
|
||||
|
||||
#include "SCD30.h"
|
||||
#include "../interfaces/I2CSensor.h"
|
||||
#include "../interfaces/endianness.h"
|
||||
//#include <3rdparty/everest/include/everest/kremlin/c_endianness.h>
|
||||
|
||||
bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
|
||||
bool unitemp_SCD40_init(Sensor* sensor);
|
||||
bool unitemp_SCD40_deinit(Sensor* sensor);
|
||||
UnitempStatus unitemp_SCD40_update(Sensor* sensor);
|
||||
bool unitemp_SCD40_free(Sensor* sensor);
|
||||
|
||||
const SensorType SCD40 = {
|
||||
.typename = "SCD40",
|
||||
.interface = &I2C,
|
||||
.datatype = UT_DATA_TYPE_TEMP_HUM_CO2,
|
||||
.pollingInterval = 5000,
|
||||
.allocator = unitemp_SCD40_alloc,
|
||||
.mem_releaser = unitemp_SCD40_free,
|
||||
.initializer = unitemp_SCD40_init,
|
||||
.deinitializer = unitemp_SCD40_deinit,
|
||||
.updater = unitemp_SCD40_update};
|
||||
|
||||
#define SCD40_ID 0x62
|
||||
|
||||
#define COMMAND_START_PERIODIC_MEASUREMENT 0X21B1
|
||||
#define COMMAND_READ_MEASUREMENT 0XEC05
|
||||
#define COMMAND_STOP_PERIODIC_MEASUREMENT 0X3F86
|
||||
|
||||
#define COMMAND_PERSIST_SETTINGS 0X3615
|
||||
#define COMMAND_GET_SERIAL_NUMBER 0X3682
|
||||
#define COMMAND_PERFORM_SELF_TEST 0X3639
|
||||
#define COMMAND_PERFORM_FACTORY_RESET 0X3632
|
||||
#define COMMAND_REINIT 0X3646
|
||||
|
||||
#define COMMAND_SET_TEMPERATURE_OFFSET 0X241D
|
||||
#define COMMAND_GET_TEMPERATURE_OFFSET 0X2318
|
||||
#define COMMAND_SET_SENSOR_ALTITUDE 0X2427
|
||||
#define COMMAND_GET_SENSOR_ALTITUDE 0X2322
|
||||
#define COMMAND_SET_AMBIENT_PRESSURE 0XE000
|
||||
#define COMMAND_PERFORM_FORCED_RECALIBRATION 0X362F
|
||||
#define COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2416
|
||||
#define COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED 0X2313
|
||||
|
||||
static bool readMeasurement(Sensor* sensor) __attribute__((unused));
|
||||
static void reset(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) __attribute__((unused));
|
||||
static bool getAutoSelfCalibration(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) __attribute__((unused));
|
||||
|
||||
static float getTemperatureOffset(Sensor* sensor) __attribute__((unused));
|
||||
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused));
|
||||
|
||||
static bool beginMeasuring(Sensor* sensor) __attribute__((unused));
|
||||
static bool stopMeasurement(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
bool unitemp_SCD40_alloc(Sensor* sensor, char* args) {
|
||||
UNUSED(args);
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
|
||||
i2c_sensor->minI2CAdr = SCD40_ID << 1;
|
||||
i2c_sensor->maxI2CAdr = SCD40_ID << 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD40_free(Sensor* sensor) {
|
||||
//Нечего высвобождать, так как ничего не было выделено
|
||||
UNUSED(sensor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD40_init(Sensor* sensor) {
|
||||
return beginMeasuring(sensor);
|
||||
}
|
||||
|
||||
bool unitemp_SCD40_deinit(Sensor* sensor) {
|
||||
return stopMeasurement(sensor);
|
||||
}
|
||||
|
||||
UnitempStatus unitemp_SCD40_update(Sensor* sensor) {
|
||||
readMeasurement(sensor);
|
||||
return UT_SENSORSTATUS_OK;
|
||||
}
|
||||
|
||||
#define CRC8_POLYNOMIAL 0x31
|
||||
#define CRC8_INIT 0xFF
|
||||
|
||||
static uint8_t computeCRC8(uint8_t* message, uint8_t len) {
|
||||
uint8_t crc = CRC8_INIT; // Init with 0xFF
|
||||
for(uint8_t x = 0; x < len; x++) {
|
||||
crc ^= message[x]; // XOR-in the next input byte
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
if((crc & 0x80) != 0)
|
||||
crc = (uint8_t)((crc << 1) ^ CRC8_POLYNOMIAL);
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc; // No output reflection
|
||||
}
|
||||
|
||||
// Sends a command along with arguments and CRC
|
||||
static bool sendCommandWithCRC(Sensor* sensor, uint16_t command, uint16_t arguments) {
|
||||
static const uint8_t cmdSize = 5;
|
||||
|
||||
uint8_t bytes[cmdSize];
|
||||
uint8_t* pointer = bytes;
|
||||
store16_be(pointer, command);
|
||||
pointer += 2;
|
||||
uint8_t* argPos = pointer;
|
||||
store16_be(pointer, arguments);
|
||||
pointer += 2;
|
||||
*pointer = computeCRC8(argPos, pointer - argPos);
|
||||
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
|
||||
}
|
||||
|
||||
// Sends just a command, no arguments, no CRC
|
||||
static bool sendCommand(Sensor* sensor, uint16_t command) {
|
||||
static const uint8_t cmdSize = 2;
|
||||
|
||||
uint8_t bytes[cmdSize];
|
||||
store16_be(bytes, command);
|
||||
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
return unitemp_i2c_writeArray(i2c_sensor, cmdSize, bytes);
|
||||
}
|
||||
|
||||
static uint16_t readRegister(Sensor* sensor, uint16_t registerAddress) {
|
||||
static const uint8_t regSize = 2;
|
||||
|
||||
if(!sendCommand(sensor, registerAddress)) return 0; // Sensor did not ACK
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
uint8_t bytes[regSize];
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, regSize, bytes)) return 0;
|
||||
|
||||
return load16_be(bytes);
|
||||
}
|
||||
|
||||
static bool loadWord(uint8_t* buff, uint16_t* val) {
|
||||
uint16_t tmp = load16_be(buff);
|
||||
uint8_t expectedCRC = computeCRC8(buff, 2);
|
||||
if(buff[2] != expectedCRC) return false;
|
||||
*val = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool getSettingValue(Sensor* sensor, uint16_t registerAddress, uint16_t* val) {
|
||||
static const uint8_t respSize = 3;
|
||||
|
||||
if(!sendCommand(sensor, registerAddress)) return false; // Sensor did not ACK
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
uint8_t bytes[respSize];
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) return false;
|
||||
|
||||
return loadWord(bytes, val);
|
||||
}
|
||||
|
||||
// Get 18 bytes from SCD30
|
||||
// Updates global variables with floats
|
||||
// Returns true if success
|
||||
static bool readMeasurement(Sensor* sensor) {
|
||||
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
|
||||
return false; // Sensor did not ACK
|
||||
}
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
static const uint8_t respSize = 9;
|
||||
uint8_t buff[respSize];
|
||||
uint8_t* bytes = buff;
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
|
||||
FURI_LOG_E(APP_NAME, "Error while read measures");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t tmpValue;
|
||||
|
||||
bool error = false;
|
||||
if(loadWord(bytes, &tmpValue)) {
|
||||
sensor->co2 = tmpValue;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing CO2");
|
||||
error = true;
|
||||
}
|
||||
|
||||
bytes += 3;
|
||||
if(loadWord(bytes, &tmpValue)) {
|
||||
sensor->temp = (float)tmpValue * 175.0f / 65535.0f - 45.0f;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing temp");
|
||||
error = true;
|
||||
}
|
||||
|
||||
bytes += 3;
|
||||
if(loadWord(bytes, &tmpValue)) {
|
||||
sensor->hum = (float)tmpValue * 100.0f / 65535.0f;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing humidity");
|
||||
error = true;
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
static void reset(Sensor* sensor) {
|
||||
sendCommand(sensor, COMMAND_REINIT);
|
||||
}
|
||||
|
||||
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) {
|
||||
return sendCommandWithCRC(
|
||||
sensor, COMMAND_SET_AUTOMATIC_SELF_CALIBRATION_ENABLED, enable); // Activate continuous ASC
|
||||
}
|
||||
|
||||
// Get the current ASC setting
|
||||
static bool getAutoSelfCalibration(Sensor* sensor) {
|
||||
return 1 == readRegister(sensor, COMMAND_GET_AUTOMATIC_SELF_CALIBRATION_ENABLED);
|
||||
}
|
||||
|
||||
// Unfinished
|
||||
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) {
|
||||
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
|
||||
return false; // Sensor did not ACK
|
||||
}
|
||||
|
||||
static const uint8_t respSize = 9;
|
||||
uint8_t buff[respSize];
|
||||
uint8_t* bytes = buff;
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
if(!unitemp_i2c_readArray(i2c_sensor, respSize, bytes)) {
|
||||
FURI_LOG_E(APP_NAME, "Error while read measures");
|
||||
return false;
|
||||
}
|
||||
|
||||
*val = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool beginMeasuring(Sensor* sensor) {
|
||||
return sendCommand(sensor, COMMAND_START_PERIODIC_MEASUREMENT);
|
||||
}
|
||||
|
||||
// Stop continuous measurement
|
||||
static bool stopMeasurement(Sensor* sensor) {
|
||||
return sendCommand(sensor, COMMAND_READ_MEASUREMENT);
|
||||
}
|
||||
|
||||
static float getTemperatureOffset(Sensor* sensor) {
|
||||
uint16_t curOffset;
|
||||
if(!getSettingValue(sensor, COMMAND_GET_TEMPERATURE_OFFSET, &curOffset)) return 0.0;
|
||||
return (float)curOffset * 175.0f / 65536.0f;
|
||||
}
|
||||
|
||||
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) {
|
||||
uint16_t newOffset = tempOffset * 65536.0 / 175.0 + 0.5f;
|
||||
return sendCommandWithCRC(
|
||||
sensor, COMMAND_SET_TEMPERATURE_OFFSET, newOffset); // Activate continuous ASC
|
||||
}
|
59
applications/external/unitemp/sensors/SCD40.h
vendored
Normal file
59
applications/external/unitemp/sensors/SCD40.h
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Unitemp - Universal temperature reader
|
||||
Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n)
|
||||
Contributed by divinebird (https://github.com/divinebird)
|
||||
|
||||
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_SCD40
|
||||
#define UNITEMP_SCD40
|
||||
|
||||
#include "../unitemp.h"
|
||||
#include "../Sensors.h"
|
||||
|
||||
extern const SensorType SCD40;
|
||||
/**
|
||||
* @brief Выделение памяти и установка начальных значений датчика SCD40
|
||||
* @param sensor Указатель на создаваемый датчик
|
||||
* @return Истина при успехе
|
||||
*/
|
||||
bool unitemp_SCD40_alloc(Sensor* sensor, char* args);
|
||||
|
||||
/**
|
||||
* @brief Инициализации датчика SCD40
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Истина если инициализация упспешная
|
||||
*/
|
||||
bool unitemp_SCD40_init(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_SCD40_deinit(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Обновление значений из датчика
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Статус опроса датчика
|
||||
*/
|
||||
UnitempStatus unitemp_SCD40_update(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Высвободить память датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_SCD40_free(Sensor* sensor);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user