feat(firmware): IM1281C driver

This commit is contained in:
2026-04-19 23:20:50 +08:00
parent 2d48724e37
commit 696f2735ff
4 changed files with 232 additions and 2 deletions

View File

@@ -0,0 +1,125 @@
#include "IM1281C.h"
IM1281C::IM1281C()
: _serial(nullptr),
_slaveAddress(1),
_lastAResult(0xFF),
_lastBResult(0xFF)
{
}
bool IM1281C::begin(HardwareSerial &serial,
int8_t rxPin,
int8_t txPin,
uint8_t slaveAddress,
uint32_t baudRate,
uint32_t serialConfig)
{
_serial = &serial;
_slaveAddress = slaveAddress;
_a = IM1281CAData{};
_b = IM1281CBData{};
_lastAResult = 0xFF;
_lastBResult = 0xFF;
_serial->begin(baudRate, serialConfig, rxPin, txPin);
_node.begin(_slaveAddress, *_serial);
return true;
}
bool IM1281C::readA()
{
return readAll();
}
bool IM1281C::readB()
{
return readAll();
}
bool IM1281C::readAll()
{
if (_serial == nullptr)
{
return false;
}
// IM1281C datasheet addresses are effectively 32-bit data items.
// Requesting 0x0010 from 0x0048 yields 64 data bytes (0x40), which covers A/B block.
const uint8_t result = _node.readHoldingRegisters(0x0048, 16);
_lastAResult = result;
_lastBResult = result;
if (result != _node.ku8MBSuccess)
{
_a.valid = false;
_b.valid = false;
return false;
}
const uint32_t aVoltageRaw = combineWords(_node.getResponseBuffer(0), _node.getResponseBuffer(1));
const uint32_t aCurrentRaw = combineWords(_node.getResponseBuffer(2), _node.getResponseBuffer(3));
const uint32_t aPowerRaw = combineWords(_node.getResponseBuffer(4), _node.getResponseBuffer(5));
const uint32_t aEnergyRaw = combineWords(_node.getResponseBuffer(6), _node.getResponseBuffer(7));
const uint32_t aPfRaw = combineWords(_node.getResponseBuffer(8), _node.getResponseBuffer(9));
const uint32_t aCo2Raw = combineWords(_node.getResponseBuffer(10), _node.getResponseBuffer(11));
const uint32_t aTempRaw = combineWords(_node.getResponseBuffer(12), _node.getResponseBuffer(13));
const uint32_t aFreqRaw = combineWords(_node.getResponseBuffer(14), _node.getResponseBuffer(15));
_a.voltage = scaleValue(aVoltageRaw, 0.0001f);
_a.current = scaleValue(aCurrentRaw, 0.0001f);
_a.power = scaleValue(aPowerRaw, 0.0001f);
_a.energy = scaleValue(aEnergyRaw, 0.0001f);
_a.powerFactor = scaleValue(aPfRaw, 0.001f);
_a.co2 = scaleValue(aCo2Raw, 0.0001f);
_a.temperature = scaleValue(aTempRaw, 0.01f);
_a.frequency = scaleValue(aFreqRaw, 0.01f);
_a.valid = true;
const uint32_t bVoltageRaw = combineWords(_node.getResponseBuffer(16), _node.getResponseBuffer(17));
const uint32_t bCurrentRaw = combineWords(_node.getResponseBuffer(18), _node.getResponseBuffer(19));
const uint32_t bPowerRaw = combineWords(_node.getResponseBuffer(20), _node.getResponseBuffer(21));
const uint32_t bEnergyRaw = combineWords(_node.getResponseBuffer(22), _node.getResponseBuffer(23));
const uint32_t bPfRaw = combineWords(_node.getResponseBuffer(24), _node.getResponseBuffer(25));
const uint32_t bCo2Raw = combineWords(_node.getResponseBuffer(26), _node.getResponseBuffer(27));
_b.voltage = scaleValue(bVoltageRaw, 0.0001f);
_b.current = scaleValue(bCurrentRaw, 0.0001f);
_b.power = scaleValue(bPowerRaw, 0.0001f);
_b.energy = scaleValue(bEnergyRaw, 0.0001f);
_b.powerFactor = scaleValue(bPfRaw, 0.001f);
_b.co2 = scaleValue(bCo2Raw, 0.0001f);
_b.valid = true;
return true;
}
const IM1281CAData &IM1281C::a() const
{
return _a;
}
const IM1281CBData &IM1281C::b() const
{
return _b;
}
uint8_t IM1281C::lastAResult() const
{
return _lastAResult;
}
uint8_t IM1281C::lastBResult() const
{
return _lastBResult;
}
uint32_t IM1281C::combineWords(uint16_t highWord, uint16_t lowWord)
{
return (static_cast<uint32_t>(highWord) << 16) | static_cast<uint32_t>(lowWord);
}
float IM1281C::scaleValue(uint32_t raw, float scale)
{
return static_cast<float>(raw) * scale;
}

View File

@@ -0,0 +1,65 @@
#ifndef HELIOS_IM1281C_H
#define HELIOS_IM1281C_H
#include <Arduino.h>
#include <ModbusMaster.h>
struct IM1281CAData
{
float voltage = 0.0f;
float current = 0.0f;
float power = 0.0f;
float energy = 0.0f;
float powerFactor = 0.0f;
float co2 = 0.0f;
float temperature = 0.0f;
float frequency = 0.0f;
bool valid = false;
};
struct IM1281CBData
{
float voltage = 0.0f;
float current = 0.0f;
float power = 0.0f;
float energy = 0.0f;
float powerFactor = 0.0f;
float co2 = 0.0f;
bool valid = false;
};
class IM1281C
{
public:
IM1281C();
bool begin(HardwareSerial &serial,
int8_t rxPin,
int8_t txPin,
uint8_t slaveAddress = 1,
uint32_t baudRate = 4800,
uint32_t serialConfig = SERIAL_8N1);
bool readA();
bool readB();
bool readAll();
const IM1281CAData &a() const;
const IM1281CBData &b() const;
uint8_t lastAResult() const;
uint8_t lastBResult() const;
private:
static uint32_t combineWords(uint16_t highWord, uint16_t lowWord);
static float scaleValue(uint32_t raw, float scale);
ModbusMaster _node;
HardwareSerial *_serial;
uint8_t _slaveAddress;
uint8_t _lastAResult;
uint8_t _lastBResult;
IM1281CAData _a;
IM1281CBData _b;
};
#endif // HELIOS_IM1281C_H

View File

@@ -18,5 +18,6 @@ lib_deps =
miguelbalboa/MFRC522@^1.4.12
tzapu/WiFiManager@^2.0.17
adafruit/Adafruit SSD1306@^2.5.16
4-20ma/ModbusMaster@^2.0.1
build_flags = -DMO_PLATFORM=MO_PLATFORM_ARDUINO -DMO_MG_USE_VERSION=MO_MG_V715 -DMO_NUMCONNECTORS=3
board_build.partitions = partitions.csv

View File

@@ -15,6 +15,7 @@
#include "esp_system.h"
#include "config.h"
#include "IM1281C.h"
#include "pins.h"
/* LED State Enum */
@@ -95,6 +96,7 @@ Adafruit_SSD1306 display(128, 64, &Wire, -1);
SmartLed leds(LED_WS2812B, LED_COUNT, LED_PIN, 0, DoubleBuffer);
MFRC522 rfid(PIN_RC_CS, PIN_RC_RST);
IM1281C im1281c;
static bool isConnectorPlugged(unsigned int connectorId)
{
@@ -303,6 +305,42 @@ static void expireAuthWaitIfNeeded()
}
}
static void pollIm1281c()
{
static unsigned long s_last_poll_ms = 0;
const unsigned long now = millis();
if ((now - s_last_poll_ms) < 1000)
{
return;
}
s_last_poll_ms = now;
if (!im1281c.readAll())
{
Serial.printf("[IM1281C] read failed: A=%u B=%u\n", im1281c.lastAResult(), im1281c.lastBResult());
return;
}
const IM1281CAData &a = im1281c.a();
const IM1281CBData &b = im1281c.b();
Serial.printf("[IM1281C] A: U=%.4fV I=%.4fA P=%.4fW E=%.4fkWh PF=%.3f CO2=%.4fkg T=%.2fC F=%.2fHz\n",
a.voltage,
a.current,
a.power,
a.energy,
a.powerFactor,
a.co2,
a.temperature,
a.frequency);
Serial.printf("[IM1281C] B: U=%.4fV I=%.4fA P=%.4fW E=%.4fkWh PF=%.3f CO2=%.4fkg\n",
b.voltage,
b.current,
b.power,
b.energy,
b.powerFactor,
b.co2);
}
/* LED Control Functions */
void updateLED()
{
@@ -435,8 +473,8 @@ void setup()
// Initialize I2C for OLED (from schematic pin map)
Wire.begin(PIN_OLED_SDA, PIN_OLED_SCL);
// Initialize UART2 for IM1281C (U2)
Serial2.begin(BAUD_IM1281C, SERIAL_8N1, PIN_U2RXD, PIN_U2TXD);
// Initialize IM1281C over UART2 (default: address 1, 4800bps, 8N1)
im1281c.begin(Serial2, PIN_U2RXD, PIN_U2TXD);
// Initialize SPI bus for RC522
SPI.begin(PIN_RC_SCK, PIN_RC_MISO, PIN_RC_MOSI, PIN_RC_CS);
@@ -773,6 +811,7 @@ void loop()
stopIfUnplugged();
pollRfidCard();
expireAuthWaitIfNeeded();
pollIm1281c();
mg_mgr_poll(&mgr, 10);
mocpp_loop();