feat(firmware): IM1281C driver
This commit is contained in:
125
hardware/firmware/lib/IM1281C/src/IM1281C.cpp
Normal file
125
hardware/firmware/lib/IM1281C/src/IM1281C.cpp
Normal 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;
|
||||
}
|
||||
65
hardware/firmware/lib/IM1281C/src/IM1281C.h
Normal file
65
hardware/firmware/lib/IM1281C/src/IM1281C.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user