mirror of
https://github.com/DarkFlippers/unleashed-firmware
synced 2024-11-10 06:54:19 +00:00
Merge branch 'dev' into Fuzzer
This commit is contained in:
commit
bd9d831b56
12 changed files with 587 additions and 17 deletions
|
@ -48,8 +48,9 @@ bool save_signal(ProtoViewApp* app, const char* filename) {
|
|||
for(int j = 0; regs[j]; j += 2) {
|
||||
furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]);
|
||||
}
|
||||
size_t len = furi_string_size(file_content);
|
||||
furi_string_set_char(custom, len - 1, '\n');
|
||||
//size_t len = furi_string_size(file_content);
|
||||
//furi_string_set_char(custom, len - 1, '\n');
|
||||
furi_string_cat(custom, "\n");
|
||||
furi_string_cat(file_content, custom);
|
||||
furi_string_free(custom);
|
||||
}
|
||||
|
|
3
applications/external/unitemp/Sensors.c
vendored
3
applications/external/unitemp/Sensors.c
vendored
|
@ -78,7 +78,8 @@ const Interface SPI = {
|
|||
static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT21, &DHT22,
|
||||
&Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10,
|
||||
&SHT30, &GXHT30, &LM75, &HDC1080, &BMP180,
|
||||
&BMP280, &BME280, &BME680, &MAX31855, &MAX6675};
|
||||
&BMP280, &BME280, &BME680, &MAX31855, &MAX6675,
|
||||
&SCD30};
|
||||
|
||||
const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) {
|
||||
if(index > SENSOR_TYPES_COUNT) return NULL;
|
||||
|
|
5
applications/external/unitemp/Sensors.h
vendored
5
applications/external/unitemp/Sensors.h
vendored
|
@ -24,6 +24,7 @@
|
|||
#define UT_TEMPERATURE 0b00000001
|
||||
#define UT_HUMIDITY 0b00000010
|
||||
#define UT_PRESSURE 0b00000100
|
||||
#define UT_CO2 0b00001000
|
||||
|
||||
//Статусы опроса датчика
|
||||
typedef enum {
|
||||
|
@ -31,6 +32,7 @@ typedef enum {
|
|||
UT_DATA_TYPE_TEMP_HUM = UT_TEMPERATURE | UT_HUMIDITY,
|
||||
UT_DATA_TYPE_TEMP_PRESS = UT_TEMPERATURE | UT_PRESSURE,
|
||||
UT_DATA_TYPE_TEMP_HUM_PRESS = UT_TEMPERATURE | UT_HUMIDITY | UT_PRESSURE,
|
||||
UT_DATA_TYPE_TEMP_HUM_CO2 = UT_TEMPERATURE | UT_HUMIDITY | UT_CO2,
|
||||
} SensorDataType;
|
||||
|
||||
//Типы возвращаемых данных
|
||||
|
@ -121,6 +123,8 @@ typedef struct Sensor {
|
|||
float hum;
|
||||
//Атмосферное давление
|
||||
float pressure;
|
||||
// Концентрация CO2
|
||||
float co2;
|
||||
//Тип датчика
|
||||
const SensorType* type;
|
||||
//Статус последнего опроса датчика
|
||||
|
@ -329,4 +333,5 @@ const GPIO*
|
|||
#include "./sensors/HDC1080.h"
|
||||
#include "./sensors/MAX31855.h"
|
||||
#include "./sensors/MAX6675.h"
|
||||
#include "./sensors/SCD30.h"
|
||||
#endif
|
||||
|
|
BIN
applications/external/unitemp/assets/co2_11x14.png
vendored
Normal file
BIN
applications/external/unitemp/assets/co2_11x14.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 176 B |
438
applications/external/unitemp/sensors/SCD30.c
vendored
Normal file
438
applications/external/unitemp/sensors/SCD30.c
vendored
Normal file
|
@ -0,0 +1,438 @@
|
|||
/*
|
||||
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 <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];
|
||||
float value;
|
||||
} ByteToFl;
|
||||
|
||||
bool unitemp_SCD30_alloc(Sensor* sensor, char* args);
|
||||
bool unitemp_SCD30_init(Sensor* sensor);
|
||||
bool unitemp_SCD30_deinit(Sensor* sensor);
|
||||
UnitempStatus unitemp_SCD30_update(Sensor* sensor);
|
||||
bool unitemp_SCD30_free(Sensor* sensor);
|
||||
|
||||
const SensorType SCD30 = {
|
||||
.typename = "SCD30",
|
||||
.interface = &I2C,
|
||||
.datatype = UT_DATA_TYPE_TEMP_HUM_CO2,
|
||||
.pollingInterval = 2000,
|
||||
.allocator = unitemp_SCD30_alloc,
|
||||
.mem_releaser = unitemp_SCD30_free,
|
||||
.initializer = unitemp_SCD30_init,
|
||||
.deinitializer = unitemp_SCD30_deinit,
|
||||
.updater = unitemp_SCD30_update};
|
||||
|
||||
#define SCD30_ID 0x61
|
||||
|
||||
#define COMMAND_CONTINUOUS_MEASUREMENT 0x0010
|
||||
#define COMMAND_SET_MEASUREMENT_INTERVAL 0x4600
|
||||
#define COMMAND_GET_DATA_READY 0x0202
|
||||
#define COMMAND_READ_MEASUREMENT 0x0300
|
||||
#define COMMAND_AUTOMATIC_SELF_CALIBRATION 0x5306
|
||||
#define COMMAND_SET_FORCED_RECALIBRATION_FACTOR 0x5204
|
||||
#define COMMAND_SET_TEMPERATURE_OFFSET 0x5403
|
||||
#define COMMAND_SET_ALTITUDE_COMPENSATION 0x5102
|
||||
#define COMMAND_RESET 0xD304 // Soft reset
|
||||
#define COMMAND_STOP_MEAS 0x0104
|
||||
#define COMMAND_READ_FW_VER 0xD100
|
||||
|
||||
static bool dataAvailable(Sensor* sensor) __attribute__((unused));
|
||||
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 bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration)
|
||||
__attribute__((unused));
|
||||
static uint16_t getAltitudeCompensation(Sensor* sensor) __attribute__((unused));
|
||||
static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) __attribute__((unused));
|
||||
static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) __attribute__((unused));
|
||||
|
||||
static float getTemperatureOffset(Sensor* sensor) __attribute__((unused));
|
||||
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) __attribute__((unused));
|
||||
|
||||
static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset)
|
||||
__attribute__((unused));
|
||||
static bool beginMeasuring(Sensor* sensor) __attribute__((unused));
|
||||
static bool stopMeasurement(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) __attribute__((unused));
|
||||
static uint16_t getMeasurementInterval(Sensor* sensor) __attribute__((unused));
|
||||
|
||||
bool unitemp_SCD30_alloc(Sensor* sensor, char* args) {
|
||||
UNUSED(args);
|
||||
I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
|
||||
|
||||
i2c_sensor->minI2CAdr = SCD30_ID << 1;
|
||||
i2c_sensor->maxI2CAdr = SCD30_ID << 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD30_free(Sensor* sensor) {
|
||||
//Нечего высвобождать, так как ничего не было выделено
|
||||
UNUSED(sensor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD30_init(Sensor* sensor) {
|
||||
if(beginMeasuring(sensor) == true) { // Start continuous measurements
|
||||
setMeasurementInterval(sensor, SCD30.pollingInterval / 1000);
|
||||
setAutoSelfCalibration(sensor, true);
|
||||
setAmbientPressure(sensor, 0);
|
||||
} else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unitemp_SCD30_deinit(Sensor* sensor) {
|
||||
return stopMeasurement(sensor);
|
||||
}
|
||||
|
||||
UnitempStatus unitemp_SCD30_update(Sensor* sensor) {
|
||||
readMeasurement(sensor);
|
||||
return UT_SENSORSTATUS_OK;
|
||||
}
|
||||
|
||||
static uint8_t computeCRC8(uint8_t* message, uint8_t len) {
|
||||
uint8_t crc = 0xFF; // 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) ^ 0x31);
|
||||
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);
|
||||
}
|
||||
|
||||
static bool loadFloat(uint8_t* buff, float* val) {
|
||||
// ByteToFl tmp;
|
||||
size_t cntr = 0;
|
||||
uint8_t floatBuff[4];
|
||||
for(size_t i = 0; i < 2; i++) {
|
||||
floatBuff[cntr++] = buff[0];
|
||||
floatBuff[cntr++] = buff[1];
|
||||
uint8_t expectedCRC = computeCRC8(buff, 2);
|
||||
if(buff[2] != expectedCRC) return false;
|
||||
buff += 3;
|
||||
}
|
||||
uint32_t tmpVal = load32_be(floatBuff);
|
||||
memcpy(val, &tmpVal, sizeof(float));
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get 18 bytes from SCD30
|
||||
// Updates global variables with floats
|
||||
// Returns true if success
|
||||
static bool readMeasurement(Sensor* sensor) {
|
||||
// Verify we have data from the sensor
|
||||
if(!dataAvailable(sensor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!sendCommand(sensor, COMMAND_READ_MEASUREMENT)) {
|
||||
FURI_LOG_E(APP_NAME, "Sensor did not ACK");
|
||||
return false; // Sensor did not ACK
|
||||
}
|
||||
|
||||
float tempCO2 = 0;
|
||||
float tempHumidity = 0;
|
||||
float tempTemperature = 0;
|
||||
|
||||
furi_delay_ms(3);
|
||||
|
||||
static const uint8_t respSize = 18;
|
||||
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;
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
if(loadFloat(bytes, &tempCO2)) {
|
||||
sensor->co2 = tempCO2;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing CO2");
|
||||
error = true;
|
||||
}
|
||||
|
||||
bytes += 6;
|
||||
if(loadFloat(bytes, &tempTemperature)) {
|
||||
sensor->temp = tempTemperature;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing temp");
|
||||
error = true;
|
||||
}
|
||||
|
||||
bytes += 6;
|
||||
if(loadFloat(bytes, &tempHumidity)) {
|
||||
sensor->hum = tempHumidity;
|
||||
} else {
|
||||
FURI_LOG_E(APP_NAME, "Error while parsing humidity");
|
||||
error = true;
|
||||
}
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
static void reset(Sensor* sensor) {
|
||||
sendCommand(sensor, COMMAND_RESET);
|
||||
}
|
||||
|
||||
static bool setAutoSelfCalibration(Sensor* sensor, bool enable) {
|
||||
return sendCommandWithCRC(
|
||||
sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION, enable); // Activate continuous ASC
|
||||
}
|
||||
|
||||
// Get the current ASC setting
|
||||
static bool getAutoSelfCalibration(Sensor* sensor) {
|
||||
return 1 == readRegister(sensor, COMMAND_AUTOMATIC_SELF_CALIBRATION);
|
||||
}
|
||||
|
||||
static bool getFirmwareVersion(Sensor* sensor, uint16_t* val) {
|
||||
return getSettingValue(sensor, COMMAND_READ_FW_VER, val);
|
||||
}
|
||||
|
||||
// Set the forced recalibration factor. See 1.3.7.
|
||||
// The reference CO2 concentration has to be within the range 400 ppm ≤ cref(CO2) ≤ 2000 ppm.
|
||||
static bool setForcedRecalibrationFactor(Sensor* sensor, uint16_t concentration) {
|
||||
if(concentration < 400 || concentration > 2000) {
|
||||
return false; // Error check.
|
||||
}
|
||||
return sendCommandWithCRC(sensor, COMMAND_SET_FORCED_RECALIBRATION_FACTOR, concentration);
|
||||
}
|
||||
|
||||
// Get the temperature offset. See 1.3.8.
|
||||
static float getTemperatureOffset(Sensor* sensor) {
|
||||
union {
|
||||
int16_t signed16;
|
||||
uint16_t unsigned16;
|
||||
} signedUnsigned; // Avoid any ambiguity casting int16_t to uint16_t
|
||||
signedUnsigned.unsigned16 = readRegister(sensor, COMMAND_SET_TEMPERATURE_OFFSET);
|
||||
|
||||
return ((float)signedUnsigned.signed16) / 100.0;
|
||||
}
|
||||
|
||||
static bool setTemperatureOffset(Sensor* sensor, float tempOffset) {
|
||||
// Temp offset is only positive. See: https://github.com/sparkfun/SparkFun_SCD30_Arduino_Library/issues/27#issuecomment-971986826
|
||||
//"The SCD30 offset temperature is obtained by subtracting the reference temperature from the SCD30 output temperature"
|
||||
// https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/9.5_CO2/Sensirion_CO2_Sensors_SCD30_Low_Power_Mode.pdf
|
||||
|
||||
if(tempOffset < 0.0) return false;
|
||||
|
||||
uint16_t value = tempOffset * 100;
|
||||
|
||||
return sendCommandWithCRC(sensor, COMMAND_SET_TEMPERATURE_OFFSET, value);
|
||||
}
|
||||
|
||||
// Get the altitude compenstation. See 1.3.9.
|
||||
static uint16_t getAltitudeCompensation(Sensor* sensor) {
|
||||
return readRegister(sensor, COMMAND_SET_ALTITUDE_COMPENSATION);
|
||||
}
|
||||
|
||||
// Set the altitude compenstation. See 1.3.9.
|
||||
static bool setAltitudeCompensation(Sensor* sensor, uint16_t altitude) {
|
||||
return sendCommandWithCRC(sensor, COMMAND_SET_ALTITUDE_COMPENSATION, altitude);
|
||||
}
|
||||
|
||||
// Set the pressure compenstation. This is passed during measurement startup.
|
||||
// mbar can be 700 to 1200
|
||||
static bool setAmbientPressure(Sensor* sensor, uint16_t pressure_mbar) {
|
||||
if(pressure_mbar != 0 || pressure_mbar < 700 || pressure_mbar > 1200) {
|
||||
return false;
|
||||
}
|
||||
return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressure_mbar);
|
||||
}
|
||||
|
||||
// Begins continuous measurements
|
||||
// Continuous measurement status is saved in non-volatile memory. When the sensor
|
||||
// is powered down while continuous measurement mode is active SCD30 will measure
|
||||
// continuously after repowering without sending the measurement command.
|
||||
// Returns true if successful
|
||||
static bool beginMeasuringWithSettings(Sensor* sensor, uint16_t pressureOffset) {
|
||||
return sendCommandWithCRC(sensor, COMMAND_CONTINUOUS_MEASUREMENT, pressureOffset);
|
||||
}
|
||||
|
||||
// Overload - no pressureOffset
|
||||
static bool beginMeasuring(Sensor* sensor) {
|
||||
return beginMeasuringWithSettings(sensor, 0);
|
||||
}
|
||||
|
||||
// Stop continuous measurement
|
||||
static bool stopMeasurement(Sensor* sensor) {
|
||||
return sendCommand(sensor, COMMAND_STOP_MEAS);
|
||||
}
|
||||
|
||||
// Sets interval between measurements
|
||||
// 2 seconds to 1800 seconds (30 minutes)
|
||||
static bool setMeasurementInterval(Sensor* sensor, uint16_t interval) {
|
||||
if(interval < 2 || interval > 1800) return false;
|
||||
if(!sendCommandWithCRC(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, interval)) return false;
|
||||
uint16_t verInterval = readRegister(sensor, COMMAND_SET_MEASUREMENT_INTERVAL);
|
||||
if(verInterval != interval) {
|
||||
FURI_LOG_E(APP_NAME, "Measure interval wrong! Val: %02x", verInterval);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Gets interval between measurements
|
||||
// 2 seconds to 1800 seconds (30 minutes)
|
||||
static uint16_t getMeasurementInterval(Sensor* sensor) {
|
||||
uint16_t interval = 0;
|
||||
getSettingValue(sensor, COMMAND_SET_MEASUREMENT_INTERVAL, &interval);
|
||||
return interval;
|
||||
}
|
||||
|
||||
// Returns true when data is available
|
||||
static bool dataAvailable(Sensor* sensor) {
|
||||
return 1 == readRegister(sensor, COMMAND_GET_DATA_READY);
|
||||
}
|
59
applications/external/unitemp/sensors/SCD30.h
vendored
Normal file
59
applications/external/unitemp/sensors/SCD30.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_SCD30
|
||||
#define UNITEMP_SCD30
|
||||
|
||||
#include "../unitemp.h"
|
||||
#include "../Sensors.h"
|
||||
|
||||
extern const SensorType SCD30;
|
||||
/**
|
||||
* @brief Выделение памяти и установка начальных значений датчика SCD30
|
||||
* @param sensor Указатель на создаваемый датчик
|
||||
* @return Истина при успехе
|
||||
*/
|
||||
bool unitemp_SCD30_alloc(Sensor* sensor, char* args);
|
||||
|
||||
/**
|
||||
* @brief Инициализации датчика SCD30
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Истина если инициализация упспешная
|
||||
*/
|
||||
bool unitemp_SCD30_init(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Деинициализация датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_SCD30_deinit(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Обновление значений из датчика
|
||||
* @param sensor Указатель на датчик
|
||||
* @return Статус опроса датчика
|
||||
*/
|
||||
UnitempStatus unitemp_SCD30_update(Sensor* sensor);
|
||||
|
||||
/**
|
||||
* @brief Высвободить память датчика
|
||||
* @param sensor Указатель на датчик
|
||||
*/
|
||||
bool unitemp_SCD30_free(Sensor* sensor);
|
||||
|
||||
#endif
|
2
applications/external/unitemp/unitemp.h
vendored
2
applications/external/unitemp/unitemp.h
vendored
|
@ -40,7 +40,7 @@
|
|||
//Имя приложения
|
||||
#define APP_NAME "Unitemp"
|
||||
//Версия приложения
|
||||
#define UNITEMP_APP_VER "1.2"
|
||||
#define UNITEMP_APP_VER "1.3"
|
||||
//Путь хранения файлов плагина
|
||||
#define APP_PATH_FOLDER "/ext/unitemp"
|
||||
//Имя файла с настройками
|
||||
|
|
|
@ -162,6 +162,35 @@ static void _draw_pressure(Canvas* canvas, Sensor* sensor) {
|
|||
}
|
||||
}
|
||||
|
||||
static void _draw_co2(Canvas* canvas, Sensor* sensor, Color color) {
|
||||
const uint8_t x = 29, y = 39;
|
||||
//Рисование рамки
|
||||
canvas_draw_rframe(canvas, x, y, 75, 20, 3);
|
||||
if(color == ColorBlack) {
|
||||
canvas_draw_rbox(canvas, x, y, 75, 19, 3);
|
||||
canvas_invert_color(canvas);
|
||||
} else {
|
||||
canvas_draw_rframe(canvas, x, y, 75, 19, 3);
|
||||
}
|
||||
|
||||
//Рисование иконки
|
||||
canvas_draw_icon(canvas, x + 3, y + 3, &I_co2_11x14);
|
||||
|
||||
int16_t concentration_int = sensor->co2;
|
||||
// int8_t concentration_dec = (int16_t)(sensor->co2 * 10) % 10;
|
||||
|
||||
//Целая часть
|
||||
if(concentration_int > 9999) {
|
||||
snprintf(app->buff, BUFF_SIZE, "MAX ");
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
} else {
|
||||
snprintf(app->buff, BUFF_SIZE, "%d", concentration_int);
|
||||
canvas_set_font(canvas, FontBigNumbers);
|
||||
}
|
||||
|
||||
canvas_draw_str_aligned(canvas, x + 70, y + 10, AlignRight, AlignCenter, app->buff);
|
||||
}
|
||||
|
||||
static void _draw_singleSensor(Canvas* canvas, Sensor* sensor, const uint8_t pos[2], Color color) {
|
||||
canvas_set_font(canvas, FontPrimary);
|
||||
|
||||
|
@ -320,6 +349,17 @@ static void _draw_carousel_values(Canvas* canvas) {
|
|||
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]);
|
||||
_draw_pressure(canvas, unitemp_sensor_getActive(generalview_sensor_index));
|
||||
break;
|
||||
case UT_DATA_TYPE_TEMP_HUM_CO2:
|
||||
_draw_temperature(
|
||||
canvas,
|
||||
unitemp_sensor_getActive(generalview_sensor_index),
|
||||
temp_positions[2][0],
|
||||
temp_positions[2][1],
|
||||
ColorWhite);
|
||||
_draw_humidity(
|
||||
canvas, unitemp_sensor_getActive(generalview_sensor_index), hum_positions[1]);
|
||||
_draw_co2(canvas, unitemp_sensor_getActive(generalview_sensor_index), ColorWhite);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -211,7 +211,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
|||
|
||||
flipper_format_free(temp_fm_preset3);
|
||||
|
||||
// # HND - FM presets
|
||||
// # HND - FM preset
|
||||
FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc();
|
||||
flipper_format_write_string_cstr(
|
||||
temp_fm_preset4,
|
||||
|
@ -221,16 +221,6 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) {
|
|||
subghz_setting_load_custom_preset(setting, (const char*)"HND_1", temp_fm_preset4);
|
||||
|
||||
flipper_format_free(temp_fm_preset4);
|
||||
|
||||
FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc();
|
||||
flipper_format_write_string_cstr(
|
||||
temp_fm_preset5,
|
||||
(const char*)"Custom_preset_data",
|
||||
(const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00");
|
||||
flipper_format_rewind(temp_fm_preset5);
|
||||
subghz_setting_load_custom_preset(setting, (const char*)"HND_2", temp_fm_preset5);
|
||||
|
||||
flipper_format_free(temp_fm_preset5);
|
||||
}
|
||||
// custom presets loading - end
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
from typing import Callable, List, Optional, Tuple, Union
|
||||
from typing import Callable, ClassVar, List, Optional, Tuple, Union
|
||||
|
||||
|
||||
class FlipperManifestException(Exception):
|
||||
|
@ -23,6 +24,8 @@ class FlipperAppType(Enum):
|
|||
|
||||
@dataclass
|
||||
class FlipperApplication:
|
||||
APP_ID_REGEX: ClassVar[re.Pattern] = re.compile(r"^[a-z0-9_]+$")
|
||||
|
||||
@dataclass
|
||||
class ExternallyBuiltFile:
|
||||
path: str
|
||||
|
@ -84,6 +87,10 @@ class FlipperApplication:
|
|||
def __post_init__(self):
|
||||
if self.apptype == FlipperAppType.PLUGIN:
|
||||
self.stack_size = 0
|
||||
if not self.APP_ID_REGEX.match(self.appid):
|
||||
raise FlipperManifestException(
|
||||
f"Invalid appid '{self.appid}'. Must match regex '{self.APP_ID_REGEX}'"
|
||||
)
|
||||
if isinstance(self.fap_version, str):
|
||||
try:
|
||||
self.fap_version = tuple(int(v) for v in self.fap_version.split("."))
|
||||
|
|
|
@ -3,6 +3,29 @@
|
|||
# Requiremets:
|
||||
# cxxfilt==0.3.0
|
||||
|
||||
# Most part of this code written by Lars-Dominik Braun <lars@6xq.net> https://github.com/PromyLOPh/linkermapviz
|
||||
# and distributes under MIT licence
|
||||
|
||||
# Copyright (c) 2017 Lars-Dominik Braun <lars@6xq.net>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
|
|
|
@ -75,7 +75,7 @@ from fbt.util import (
|
|||
wrap_tempfile,
|
||||
path_as_posix,
|
||||
)
|
||||
from fbt.appmanifest import FlipperAppType
|
||||
from fbt.appmanifest import FlipperAppType, FlipperApplication
|
||||
from fbt.sdk.cache import SdkCache
|
||||
|
||||
# Base environment with all tools loaded from SDK
|
||||
|
@ -410,6 +410,12 @@ dist_env.Alias("vscode_dist", vscode_dist)
|
|||
# Creating app from base template
|
||||
|
||||
dist_env.SetDefault(FBT_APPID=appenv.subst("$APPID") or "template")
|
||||
if fbt_appid := dist_env.subst("$FBT_APPID"):
|
||||
if not FlipperApplication.APP_ID_REGEX.match(fbt_appid):
|
||||
raise UserError(
|
||||
f"Invalid app id '{fbt_appid}'. App id must match {FlipperApplication.APP_ID_REGEX.pattern}"
|
||||
)
|
||||
|
||||
app_template_dir = project_template_dir.Dir("app_template")
|
||||
app_template_dist = []
|
||||
for template_file in app_template_dir.glob("*"):
|
||||
|
|
Loading…
Reference in a new issue