From 189117326b4d7690d8c110b664ab5fe71017270c Mon Sep 17 00:00:00 2001
From: MX <10697207+xMasterX@users.noreply.github.com>
Date: Sun, 1 Jan 2023 23:14:32 +0300
Subject: [PATCH] Update UniTemp
https://github.com/quen0n/unitemp-flipperzero/tree/dev
---
applications/plugins/unitemp/README.md | 9 +-
applications/plugins/unitemp/Sensors.c | 1 +
applications/plugins/unitemp/Sensors.h | 1 +
applications/plugins/unitemp/sensors/BMP180.c | 174 ++++++++++++++++++
applications/plugins/unitemp/sensors/BMP180.h | 62 +++++++
.../plugins/unitemp/sensors/Sensors.xlsx | Bin 12265 -> 12336 bytes
6 files changed, 245 insertions(+), 2 deletions(-)
create mode 100644 applications/plugins/unitemp/sensors/BMP180.c
create mode 100644 applications/plugins/unitemp/sensors/BMP180.h
diff --git a/applications/plugins/unitemp/README.md b/applications/plugins/unitemp/README.md
index 257e8a8ae..80a296e9b 100644
--- a/applications/plugins/unitemp/README.md
+++ b/applications/plugins/unitemp/README.md
@@ -1,9 +1,14 @@

# Unitemp - Universal temperature sensor reader
[](https://github.com/quen0n/unitemp-flipperzero/releases/)
-[](https://github.com/quen0n/unitemp-flipperzero/blob/dev/LICENSE.md)
+[](https://github.com/quen0n/unitemp-flipperzero/blob/dev/LICENSE.md)
+[](https://github.com/quen0n/unitemp-flipperzero/actions/workflows/build_dev.yml)
[Flipper Zero](https://flipperzero.one/) application for reading temperature, humidity and pressure sensors using Onewire, Singlewire, I2C protocols.
## List of supported sensors (supplemented)
-
+
## Installation
Copy the contents of the repository to the `applications/plugins/unitemp` folder and build the project. Flash FZ along with resources. [More...](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md)
+## Some community photos
+
+
+
diff --git a/applications/plugins/unitemp/Sensors.c b/applications/plugins/unitemp/Sensors.c
index 9740b9887..33f62b201 100644
--- a/applications/plugins/unitemp/Sensors.c
+++ b/applications/plugins/unitemp/Sensors.c
@@ -83,6 +83,7 @@ static const SensorType* sensorTypes[] = {
&SHT30,
&GXHT30,
&LM75,
+ &BMP180,
&BMP280,
&BME280};
diff --git a/applications/plugins/unitemp/Sensors.h b/applications/plugins/unitemp/Sensors.h
index 0643ffb1f..2193ce466 100644
--- a/applications/plugins/unitemp/Sensors.h
+++ b/applications/plugins/unitemp/Sensors.h
@@ -323,4 +323,5 @@ const GPIO*
#include "./sensors/AM2320.h"
#include "./sensors/DHT20.h"
#include "./sensors/SHT30.h"
+#include "./sensors/BMP180.h"
#endif
diff --git a/applications/plugins/unitemp/sensors/BMP180.c b/applications/plugins/unitemp/sensors/BMP180.c
new file mode 100644
index 000000000..6fd5f2fa5
--- /dev/null
+++ b/applications/plugins/unitemp/sensors/BMP180.c
@@ -0,0 +1,174 @@
+/*
+ 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 .
+*/
+#include "BMP180.h"
+#include "../interfaces/I2CSensor.h"
+
+typedef struct {
+ int16_t AC1;
+ int16_t AC2;
+ int16_t AC3;
+ uint16_t AC4;
+ uint16_t AC5;
+ uint16_t AC6;
+ int16_t B1;
+ int16_t B2;
+ int16_t MB;
+ int16_t MC;
+ int16_t MD;
+} BMP180_cal;
+
+typedef struct {
+ //Калибровочные значения
+ BMP180_cal bmp180_cal;
+} BMP180_instance;
+
+const SensorType BMP180 = {
+ .typename = "BMP180",
+ .interface = &I2C,
+ .datatype = UT_TEMPERATURE | UT_PRESSURE,
+ .pollingInterval = 1000,
+ .allocator = unitemp_BMP180_I2C_alloc,
+ .mem_releaser = unitemp_BMP180_I2C_free,
+ .initializer = unitemp_BMP180_init,
+ .deinitializer = unitemp_BMP180_I2C_deinit,
+ .updater = unitemp_BMP180_I2C_update};
+
+bool unitemp_BMP180_I2C_alloc(Sensor* sensor, char* args) {
+ UNUSED(args);
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+
+ //Адреса на шине I2C (7 бит)
+ i2c_sensor->minI2CAdr = 0x77 << 1;
+ i2c_sensor->maxI2CAdr = 0x77 << 1;
+
+ BMP180_instance* bmx280_instance = malloc(sizeof(BMP180_instance));
+ i2c_sensor->sensorInstance = bmx280_instance;
+ return true;
+}
+
+bool unitemp_BMP180_I2C_free(Sensor* sensor) {
+ UNUSED(sensor);
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ free(i2c_sensor->sensorInstance);
+ return true;
+}
+
+bool unitemp_BMP180_init(Sensor* sensor) {
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+
+ //Перезагрузка
+ if(!unitemp_i2c_writeReg(i2c_sensor, 0xE0, 0xB6)) return false;
+ furi_delay_ms(100);
+
+ //Проверка ID
+ uint8_t id = unitemp_i2c_readReg(i2c_sensor, 0xD0);
+ if(id != 0x55) {
+ FURI_LOG_E(
+ APP_NAME, "Sensor %s returned wrong ID 0x%02X, expected 0x55", sensor->name, id);
+ return false;
+ }
+
+ BMP180_instance* bmp180_instance = i2c_sensor->sensorInstance;
+
+ uint8_t buff[22] = {0};
+
+ //Чтение калибровочных значений
+ if(!unitemp_i2c_readRegArray(i2c_sensor, 0xAA, 22, buff)) return false;
+ bmp180_instance->bmp180_cal.AC1 = (buff[0] << 8) | buff[1];
+ bmp180_instance->bmp180_cal.AC2 = (buff[2] << 8) | buff[3];
+ bmp180_instance->bmp180_cal.AC3 = (buff[4] << 8) | buff[5];
+ bmp180_instance->bmp180_cal.AC4 = (buff[6] << 8) | buff[7];
+ bmp180_instance->bmp180_cal.AC5 = (buff[8] << 8) | buff[9];
+ bmp180_instance->bmp180_cal.AC6 = (buff[10] << 8) | buff[11];
+ bmp180_instance->bmp180_cal.B1 = (buff[12] << 8) | buff[13];
+ bmp180_instance->bmp180_cal.B2 = (buff[14] << 8) | buff[15];
+ bmp180_instance->bmp180_cal.MB = (buff[16] << 8) | buff[17];
+ bmp180_instance->bmp180_cal.MC = (buff[18] << 8) | buff[19];
+ bmp180_instance->bmp180_cal.MD = (buff[20] << 8) | buff[21];
+
+#ifdef UNITEMP_DEBUG
+ FURI_LOG_D(
+ APP_NAME,
+ "Sensor BMP180 (0x%02X) calibration values: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d",
+ i2c_sensor->currentI2CAdr,
+ bmp180_instance->bmp180_cal.AC1,
+ bmp180_instance->bmp180_cal.AC2,
+ bmp180_instance->bmp180_cal.AC3,
+ bmp180_instance->bmp180_cal.AC4,
+ bmp180_instance->bmp180_cal.AC5,
+ bmp180_instance->bmp180_cal.AC6,
+ bmp180_instance->bmp180_cal.B1,
+ bmp180_instance->bmp180_cal.B2,
+ bmp180_instance->bmp180_cal.MB,
+ bmp180_instance->bmp180_cal.MC,
+ bmp180_instance->bmp180_cal.MD);
+#endif
+ return true;
+}
+
+bool unitemp_BMP180_I2C_deinit(Sensor* sensor) {
+ //Нечего деинициализировать
+ UNUSED(sensor);
+ return true;
+}
+
+UnitempStatus unitemp_BMP180_I2C_update(Sensor* sensor) {
+ I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance;
+ BMP180_instance* bmp180_instance = i2c_sensor->sensorInstance;
+
+ //Чтение температуры
+ if(!unitemp_i2c_writeReg(i2c_sensor, 0xF4, 0x2E)) return UT_SENSORSTATUS_TIMEOUT;
+ furi_delay_ms(5);
+ uint8_t buff[3] = {0};
+ if(!unitemp_i2c_readRegArray(i2c_sensor, 0xF6, 2, buff)) return UT_SENSORSTATUS_TIMEOUT;
+ int32_t UT = ((uint16_t)buff[0] << 8) + buff[1];
+ int32_t X1 = (UT - bmp180_instance->bmp180_cal.AC6) * bmp180_instance->bmp180_cal.AC5 >> 15;
+ int32_t X2 = (bmp180_instance->bmp180_cal.MC << 11) / (X1 + bmp180_instance->bmp180_cal.MD);
+ int32_t B5 = X1 + X2;
+ sensor->temp = ((B5 + 8) / 16) * 0.1f;
+
+ //Чтение давления
+ if(!unitemp_i2c_writeReg(i2c_sensor, 0xF4, 0x34 + (0b11 << 6))) return UT_SENSORSTATUS_TIMEOUT;
+ furi_delay_ms(26);
+ if(!unitemp_i2c_readRegArray(i2c_sensor, 0xF6, 3, buff)) return UT_SENSORSTATUS_TIMEOUT;
+ uint32_t UP = ((buff[0] << 16) + (buff[1] << 8) + buff[2]) >> (8 - 0b11);
+
+ int32_t B6, X3, B3, P;
+ uint32_t B4, B7;
+ B6 = B5 - 4000;
+ X1 = (bmp180_instance->bmp180_cal.B2 * ((B6 * B6) >> 12)) >> 11;
+ X2 = (bmp180_instance->bmp180_cal.AC2 * B6) >> 11;
+ X3 = X1 + X2;
+ B3 = (((bmp180_instance->bmp180_cal.AC1 * 4 + X3) << 0b11) + 2) >> 2;
+ X1 = (bmp180_instance->bmp180_cal.AC3 * B6) >> 13;
+ X2 = (bmp180_instance->bmp180_cal.B1 * ((B6 * B6) >> 12)) >> 16;
+ X3 = ((X1 + X2) + 2) >> 2;
+ B4 = (bmp180_instance->bmp180_cal.AC4 * (unsigned long)(X3 + 32768)) >> 15;
+ B7 = ((unsigned long)UP - B3) * (50000 >> 0b11);
+ if(B7 < 0x80000000)
+ P = (B7 * 2) / B4;
+ else
+ P = (B7 / B4) * 2;
+ X1 = (P >> 8) * (P >> 8);
+ X1 = (X1 * 3038) >> 16;
+ X2 = (-7357 * (P)) >> 16;
+ P = P + ((X1 + X2 + 3791) >> 4);
+ sensor->pressure = P;
+
+ return UT_SENSORSTATUS_OK;
+}
diff --git a/applications/plugins/unitemp/sensors/BMP180.h b/applications/plugins/unitemp/sensors/BMP180.h
new file mode 100644
index 000000000..4237fb57a
--- /dev/null
+++ b/applications/plugins/unitemp/sensors/BMP180.h
@@ -0,0 +1,62 @@
+/*
+ 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 .
+*/
+#ifndef UNITEMP_BMP180
+#define UNITEMP_BMP180
+
+#include "../unitemp.h"
+#include "../Sensors.h"
+extern const SensorType BMP180;
+/**
+ * @brief Выделение памяти и установка начальных значений датчика BMP180
+ *
+ * @param sensor Указатель на создаваемый датчик
+ * @return Истина при успехе
+ */
+bool unitemp_BMP180_I2C_alloc(Sensor* sensor, char* args);
+
+/**
+ * @brief Инициализации датчика BMP180
+ *
+ * @param sensor Указатель на датчик
+ * @return Истина если инициализация упспешная
+ */
+bool unitemp_BMP180_init(Sensor* sensor);
+
+/**
+ * @brief Деинициализация датчика
+ *
+ * @param sensor Указатель на датчик
+ */
+bool unitemp_BMP180_I2C_deinit(Sensor* sensor);
+
+/**
+ * @brief Обновление значений из датчика
+ *
+ * @param sensor Указатель на датчик
+ * @return Статус обновления
+ */
+UnitempStatus unitemp_BMP180_I2C_update(Sensor* sensor);
+
+/**
+ * @brief Высвободить память датчика
+ *
+ * @param sensor Указатель на датчик
+ */
+bool unitemp_BMP180_I2C_free(Sensor* sensor);
+
+#endif
\ No newline at end of file
diff --git a/applications/plugins/unitemp/sensors/Sensors.xlsx b/applications/plugins/unitemp/sensors/Sensors.xlsx
index b139b1b00722c1c505ebb46541ba194caeddd262..9983aeb6ea1201003aa27a9ca54218e19e5f20c7 100644
GIT binary patch
delta 3980
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
W4TletR0hJ~_*O|i$^*pTZ~p;vEoOuO
delta 3935
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