diff --git a/HomeDicator/core/config/common/esphome.yaml b/HomeDicator/core/config/common/esphome.yaml index 1fdb8f1..290d6a3 100755 --- a/HomeDicator/core/config/common/esphome.yaml +++ b/HomeDicator/core/config/common/esphome.yaml @@ -10,6 +10,8 @@ esphome: platformio_options: build_unflags: -Werror=all + lib_deps: + - ArduinoJson on_boot: - priority: 800.0 diff --git a/HomeDicator/core/config/common/fonts.yaml b/HomeDicator/core/config/common/fonts.yaml index dd9162a..8ddcb83 100755 --- a/HomeDicator/core/config/common/fonts.yaml +++ b/HomeDicator/core/config/common/fonts.yaml @@ -246,6 +246,7 @@ font: "\U000F0F91", # mdi:wardrobe-outline "\U000F111C", # mdi:window-shutter "\U000F111E", # mdi:window-shutter-open + "\U000F058E", # mdi:WaterPercent ] diff --git a/HomeDicator/core/config/device/seeed-sensecap-indicator/hardware.yaml b/HomeDicator/core/config/device/seeed-sensecap-indicator/hardware.yaml index d07071f..50ef9df 100755 --- a/HomeDicator/core/config/device/seeed-sensecap-indicator/hardware.yaml +++ b/HomeDicator/core/config/device/seeed-sensecap-indicator/hardware.yaml @@ -4,15 +4,13 @@ esp32: flash_size: 8MB framework: type: esp-idf - version: 5.2.2 - platform_version: 6.7.0 sdkconfig_options: CONFIG_ESPTOOLPY_FLASHSIZE_8MB: y CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240: y CONFIG_ESP32S3_DATA_CACHE_64KB: y CONFIG_SPIRAM_FETCH_INSTRUCTIONS: y CONFIG_SPIRAM_RODATA: y - + CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG: n # Disable to use PIN 19,20 as UART to RP2040 psram: mode: octal speed: 80MHz diff --git a/components/json_uart/README.md b/components/json_uart/README.md new file mode 100644 index 0000000..3ebe121 --- /dev/null +++ b/components/json_uart/README.md @@ -0,0 +1,30 @@ +This implements reading the sensors from the SenseCAP Indicator D1S via the UART connection from the hard-wired RP2040 2nd MCU. + +This doesn't work with the stock firmware on the RP2040, you've to flash https://github.com/HomeDicator/RP2040-Firmware to the RP2040 using https://platformio.org/. + +```yaml +# example configuration: + +sensor: + - platform: json_uart + name: "CO2 Level" + key: "scd4x.co2" + unit_of_measurement: "ppm" + icon: "mdi:molecule-co2" + accuracy_decimals: 0 + + uart_id: uart_rp2040 + - platform: json_uart + name: "Temperature" + key: "scd4x.temp" + unit_of_measurement: "°C" + icon: "mdi:thermometer" + + uart_id: uart_rp2040 + - platform: json_uart + name: "Humidity" + key: "scd4x.humidity" + unit_of_measurement: "%" + icon: "mdi:water-percent" + uart_id: uart_rp2040 +``` diff --git a/components/json_uart/__init__.py b/components/json_uart/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/components/json_uart/json_uart.cpp b/components/json_uart/json_uart.cpp new file mode 100644 index 0000000..b71e973 --- /dev/null +++ b/components/json_uart/json_uart.cpp @@ -0,0 +1,94 @@ +#include "json_uart.h" +#include "esphome/core/log.h" +#include "esphome/core/application.h" +#include "esphome/core/helpers.h" +#include +#include +#include + +namespace esphome { +namespace json_uart { + +static const char *TAG = "json_uart.sensor"; +std::string json_data; + + +void JsonUartSensor::setup() { +} + +void JsonUartSensor::loop() { + // Read JSON data from UART + std::string raw_data; + while (this->available()) { + uint8_t c; + this->read_byte(&c); + if (c == '\n') { + // Store the latest line + json_data = raw_data; + raw_data.clear(); // Clear the buffer after storing the line + } else { + raw_data += static_cast(c); // Cast to char before appending + } + } +} + +void JsonUartSensor::update() { + if (json_data.empty()) { + ESP_LOGW(TAG, "No data received from UART yet"); + return; + } + + ESP_LOGD(TAG, "Updating sensor with JSON data: %s", json_data.c_str()); + parse_json_data(json_data); +} + +void JsonUartSensor::dump_config() { + ESP_LOGCONFIG(TAG, "JSON UART custom sensor"); + ESP_LOGCONFIG(TAG, " Key: %s", key_.c_str()); + ESP_LOGCONFIG(TAG, " Unit of Measurement: %s", unit_of_measurement_.c_str()); +} + +std::vector split(const std::string &s, char delimiter) { + std::vector tokens; + std::string token; + std::istringstream tokenStream(s); + while (std::getline(tokenStream, token, delimiter)) { + tokens.push_back(token); + } + return tokens; +} + +void JsonUartSensor::parse_json_data(const std::string &json_data) { + // Parse the JSON data and extract the value for the given key + ESP_LOGD(TAG, "Parsing JSON data: %s", json_data.c_str()); + StaticJsonDocument<1024> doc; + auto error = deserializeJson(doc, json_data); + if (error) { + ESP_LOGW(TAG, "Failed to parse JSON data: %s", error.c_str()); + return; + } + + JsonObject root = doc.as(); + std::vector keys = split(key_, '.'); + JsonObject current = root; + for (size_t i = 0; i < keys.size(); ++i) { + if (!current[keys[i].c_str()].is() && i != keys.size() - 1) { + ESP_LOGW(TAG, "Key %s not found in JSON data", key_.c_str()); + return; + } + if (i == keys.size() - 1) { + if (!current[keys[i].c_str()].is()) { + ESP_LOGW(TAG, "Key %s does not contain a float value", key_.c_str()); + return; + } + float value = current[keys[i].c_str()].as(); + ESP_LOGD(TAG, "Parsed value for key %s: %f", key_.c_str(), value); + this->publish_state(value); + } else { + current = current[keys[i].c_str()].as(); + } + } +} + +} // namespace json_uart +} // namespace esphome diff --git a/components/json_uart/json_uart.h b/components/json_uart/json_uart.h new file mode 100644 index 0000000..a604480 --- /dev/null +++ b/components/json_uart/json_uart.h @@ -0,0 +1,26 @@ +#pragma once + +#include "esphome/components/sensor/sensor.h" +#include "esphome/core/component.h" +#include "esphome/components/uart/uart.h" // Correct header for UARTDevice + +namespace esphome { +namespace json_uart { + +class JsonUartSensor : public sensor::Sensor, public PollingComponent, public uart::UARTDevice { + public: + void setup() override; + void loop() override; + void update() override; + void dump_config() override; + void set_key(const std::string &key) { key_ = key; } + void set_unit_of_measurement(const std::string &unit) { unit_of_measurement_ = unit; } + + protected: + std::string key_; + std::string unit_of_measurement_; + void parse_json_data(const std::string &json_data); +}; + +} // namespace json_uart +} // namespace esphome diff --git a/components/json_uart/sensor.py b/components/json_uart/sensor.py new file mode 100644 index 0000000..3fd8850 --- /dev/null +++ b/components/json_uart/sensor.py @@ -0,0 +1,21 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor as sensor_component, uart +from esphome.const import UNIT_EMPTY, ICON_EMPTY + +json_uart_ns = cg.esphome_ns.namespace("json_uart") + +JsonUartSensor = json_uart_ns.class_("JsonUartSensor", sensor_component.Sensor, cg.PollingComponent, uart.UARTDevice) + +CONFIG_SCHEMA = sensor_component.sensor_schema( + JsonUartSensor, unit_of_measurement=cv.string, icon=ICON_EMPTY, accuracy_decimals=1 +).extend({ + cv.Required("key"): cv.string, +}).extend(cv.polling_component_schema("10s")).extend(uart.UART_DEVICE_SCHEMA) + +async def to_code(config): + var = await sensor_component.new_sensor(config) + await cg.register_component(var, config) + await uart.register_uart_device(var, config) + cg.add(var.set_key(config["key"])) + cg.add(var.set_unit_of_measurement(config["unit_of_measurement"]))