From 2e2dfaa62292aa0b1c82c2f535e594acffb2b81c Mon Sep 17 00:00:00 2001 From: Markus Kirberg Date: Fri, 23 Aug 2024 17:06:36 +0200 Subject: [PATCH] WIP: BL0942 powermeter --- mos.yml | 1 + src/BL0942/shelly_pm_bl0942.cpp | 108 ++++++++++++++++++++++++++ src/BL0942/shelly_pm_bl0942.hpp | 46 +++++++++++ src/ShellyMini1PMGen3/shelly_init.cpp | 4 +- 4 files changed, 157 insertions(+), 2 deletions(-) create mode 100644 src/BL0942/shelly_pm_bl0942.cpp create mode 100644 src/BL0942/shelly_pm_bl0942.hpp diff --git a/mos.yml b/mos.yml index 3ecaa93c..235963fa 100644 --- a/mos.yml +++ b/mos.yml @@ -10,6 +10,7 @@ sources: - src - src/mock - src/BL0937 + - src/BL0942 - src/${build_vars.MODEL} includes: diff --git a/src/BL0942/shelly_pm_bl0942.cpp b/src/BL0942/shelly_pm_bl0942.cpp new file mode 100644 index 00000000..ae5a2b8f --- /dev/null +++ b/src/BL0942/shelly_pm_bl0942.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) Shelly-HomeKit Contributors + * All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "shelly_pm_bl0942.hpp" + +#include + +#include "mgos.hpp" + +namespace shelly { + +BL0942PowerMeter::BL0942PowerMeter(int id, int tx_pin, int rx_pin, + int meas_time, int uart_no) + : PowerMeter(id), + tx_pin_(tx_pin), + rx_pin_(rx_pin), + meas_time_(meas_time), + uart_no_(uart_no), + meas_timer_(std::bind(&BL0942PowerMeter::MeasureTimerCB, this)) { +} + +BL0942PowerMeter::~BL0942PowerMeter() { +} + +Status BL0942PowerMeter::Init() { + if (rx_pin_ < 0 && tx_pin_ < 0) { + return mgos::Errorf(STATUS_INVALID_ARGUMENT, "no valid pins"); + } + + struct mgos_uart_config ucfg; + + ucfg.baud_rate = 4800; + + ucfg.dev.rx_gpio = rx_pin_; + ucfg.dev.tx_gpio = tx_pin_; + + if (!mgos_uart_configure(uart_no_, &ucfg)) { + LOG(LL_ERROR, ("Failed to configure UART%d", uart_no_)); + } + + meas_timer_.Reset(meas_time_ * 1000, MGOS_TIMER_REPEAT); + LOG(LL_INFO, ("BL0942 @ %d/%d", rx_pin_, tx_pin_)); + return Status::OK(); +} + +StatusOr BL0942PowerMeter::GetPowerW() { + return apa_; +} + +StatusOr BL0942PowerMeter::GetEnergyWH() { + return aea_; +} + +#define BL_READ 0x58 +#define BL_ADDR 0x3 + +#define BL_WATT 0x6 + +bool BL0942PowerMeter::ReadReg(uint8_t reg, uint8_t *rx_buf, size_t len) { + uint8_t tx_buf[2] = {BL_READ | BL_ADDR, reg}; + mgos_uart_write(uart_no_, tx_buf, 1); + mgos_uart_flush(uart_no_); + + // Delay to allow data to be available + int baud = 4800; + mgos_msleep(roundf(len * 8 / baud) * 1e3); + + int read_len = mgos_uart_read(uart_no_, rx_buf, len); + + uint8_t chksum = tx_buf[0] + tx_buf[1]; + for (int i = 0; i < len; i++) { + chksum += rx_buf[i]; + } + chksum ^= 0xFF; + + if (read_len != len || rx_buf[len - 1] != chksum) { + return false; + } + return true; +} + +void BL0942PowerMeter::MeasureTimerCB() { + int len = 4; // including 1 checksum byte + uint8_t rx_buf[len]; + if (this->ReadReg(BL_WATT, rx_buf, len)) { + uint32_t d = rx_buf[2] << 16 | rx_buf[1] << 8 | rx_buf[0]; + if (d & (1 << 23)) { + d |= 0xFF000000; + } + apa_ = d; + } +} + +} // namespace shelly diff --git a/src/BL0942/shelly_pm_bl0942.hpp b/src/BL0942/shelly_pm_bl0942.hpp new file mode 100644 index 00000000..22b8ea23 --- /dev/null +++ b/src/BL0942/shelly_pm_bl0942.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) Shelly-HomeKit Contributors + * All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "shelly_pm.hpp" + +#include "mgos_timers.hpp" + +namespace shelly { + +class BL0942PowerMeter : public PowerMeter { + public: + BL0942PowerMeter(int id, int tx_pin, int rx_pin, int meas_time, int uart_no); + virtual ~BL0942PowerMeter(); + + Status Init() override; + StatusOr GetPowerW() override; + StatusOr GetEnergyWH() override; + + private: + void MeasureTimerCB(); + + const int tx_pin_, rx_pin_, meas_time_, uart_no_; + + float apa_ = 0; // Last active power reading, W. + float aea_ = 0; // Accumulated active energy, Wh. + // + bool ReadReg(uint8_t reg, uint8_t *rx_buf, size_t len); + + mgos::Timer meas_timer_; +}; + +} // namespace shelly diff --git a/src/ShellyMini1PMGen3/shelly_init.cpp b/src/ShellyMini1PMGen3/shelly_init.cpp index b737541e..45f67e06 100644 --- a/src/ShellyMini1PMGen3/shelly_init.cpp +++ b/src/ShellyMini1PMGen3/shelly_init.cpp @@ -19,6 +19,7 @@ #include "shelly_hap_input.hpp" #include "shelly_input_pin.hpp" #include "shelly_main.hpp" +#include "shelly_pm_bl0942.hpp" #include "shelly_sys_led_btn.hpp" #include "shelly_temp_sensor_ntc.hpp" @@ -34,12 +35,11 @@ void CreatePeripherals(std::vector> *inputs, in->Init(); inputs->emplace_back(in); -// not yet compatible #ifdef MGOS_HAVE_ADC sys_temp->reset(new TempSensorSDNT1608X103F3950(3, 3.3f, 10000.0f)); #endif - // std::unique_ptr pm() + std::unique_ptr pm(new BL0942PowerMeter(1, 6, 7, 100, 1)); // BL0942 GPIO6 TX GPIO7 RX // const Status &st = pm->Init(); // if (st.ok()) {