-
Notifications
You must be signed in to change notification settings - Fork 0
Models
The OpenMppt.Model namespace contains the shared, serialization-friendly record types that every layer of the stack passes around. Codecs decode into them; drivers surface them via events; dashboards and loggers consume them. Because they are plain C# records with no internal I/O, they serialize cleanly to JSON with System.Text.Json or any compatible serializer — no custom converters required.
public interface ITelemetrySample
{
long TimestampMs { get; }
}A marker interface that tags any record carrying a timestamp. It exists solely so loggers, time-series stores, and dashboards can accept a uniform stream of samples without needing to know the concrete type.
MpptLive is the only type in the library that currently implements ITelemetrySample. Future record types — once more vendor protocols are decoded — will implement the same interface, letting a single ingestion pipeline handle all of them without modification.
MpptLive is the main live-telemetry snapshot. One instance is produced per successful poll cycle and surfaced on IDeviceDriver.LiveChanged.
public record MpptLive(
long TimestampMs,
double BatteryVoltage,
double ChargeCurrent,
double DischargeCurrent,
double TemperatureC,
int SolarStatusRaw,
int WorkStatusRaw,
int PowerStatusRaw,
double TotalAccumulatedAh,
ChargerState ChargerState,
double SocEstimate
) : ITelemetrySample| Field | Type | Description |
|---|---|---|
TimestampMs |
long |
Unix epoch milliseconds (UTC) |
BatteryVoltage |
double |
Terminal voltage in volts |
ChargeCurrent |
double |
Amps flowing into the battery |
DischargeCurrent |
double |
Amps flowing out of the battery (load side) |
TemperatureC |
double |
Controller temperature in °C |
SolarStatusRaw |
int |
Raw register value; input to ChargerStateLogic.FromRegisters
|
WorkStatusRaw |
int |
Raw register value; input to ChargerStateLogic.FromRegisters
|
PowerStatusRaw |
int |
Raw register value; input to ChargerStateLogic.FromRegisters
|
TotalAccumulatedAh |
double |
Lifetime Ah counter reported by the device |
ChargerState |
ChargerState |
Decoded charger state (see below) |
SocEstimate |
double |
State of charge, 0.0–1.0 |
Three watt figures are computed from the stored fields and are always available without any additional decode step:
| Property | Formula |
|---|---|
BatteryWatts |
BatteryVoltage × ChargeCurrent |
LoadWatts |
BatteryVoltage × DischargeCurrent |
ApproxPvWatts |
BatteryWatts + LoadWatts |
ApproxPvWatts is an estimate of PV harvest. It is accurate when the controller is actively charging and the battery current and load current are both sensed; it will be less accurate during idle or fault states.
double soc = MpptLive.EstimateSoc(batteryVoltage);A static 12 V lead-acid voltage-to-SoC lookup table. Use this as the socFromVoltage delegate when no MpptSettings or BatteryProfile is available yet — for example on the very first poll before settings have been read.
For any pack whose cutoff and charge setpoints are known, prefer MpptSettings.ComputeSoc or BatteryProfile.SocFromVoltage instead. EstimateSoc is a reasonable last resort, not the primary path.
MpptSettings holds the configurable setpoints read from the controller's holding registers. It is surfaced on IDeviceDriver.SettingsChanged after ReadSettingsAsync completes.
public record MpptSettings(
int BatteryType,
int TimerHours,
int TimerMinutes,
double ChargeVoltageSetpoint,
int OutputMode,
double CutoffVoltageSetpoint,
bool ManualLoadOn,
int VoltageMonitorMode,
double RecoveryVoltageSetpoint
)| Field | Type | Description |
|---|---|---|
BatteryType |
int |
Raw battery-type register value |
TimerHours |
int |
Load-timer hour setting |
TimerMinutes |
int |
Load-timer minute setting |
ChargeVoltageSetpoint |
double |
Bulk/absorption charge target (V); treated as 100 % SoC by ComputeSoc
|
OutputMode |
int |
Raw output-mode register value |
CutoffVoltageSetpoint |
double |
Low-voltage disconnect threshold (V); treated as 0 % SoC by ComputeSoc
|
ManualLoadOn |
bool |
Manual load override state |
VoltageMonitorMode |
int |
Raw voltage-monitor-mode register value |
RecoveryVoltageSetpoint |
double |
Low-voltage reconnect threshold (V) |
BatteryType bt = settings.BatteryTypeEnum; // BatteryType enum
OutputMode om = settings.OutputModeEnum; // OutputMode enumBatteryTypeEnum and OutputModeEnum decode the raw integer fields into the corresponding enums so you do not need to cast or pattern-match integers directly.
double? soc = settings.ComputeSoc(batteryVoltage);Linear interpolation between the two voltage setpoints the controller already stores:
-
CutoffVoltageSetpoint→ 0 % -
ChargeVoltageSetpoint→ 100 %
Returns null when the two setpoints are equal or in the wrong order (degenerate configuration). The result is not clamped — voltages outside the setpoint range return values below 0.0 or above 1.0, which callers can clamp or flag as out-of-range as appropriate.
// Typical usage after settings are first received
driver.SettingsChanged += settings =>
{
if (driver.Live is { } live)
{
double soc = settings.ComputeSoc(live.BatteryVoltage) ?? 0.0;
UpdateSocDisplay(soc);
}
};| Member | Value | Description |
|---|---|---|
Unknown |
0 | Register value not recognised |
SealedLead |
1 | Sealed lead-acid (AGM) |
GelLead |
2 | Gel lead-acid |
FloodedLead |
3 | Flooded/wet lead-acid |
Lithium |
4 | Lithium (generic; exact chemistry unspecified) |
| Member | Value | Description |
|---|---|---|
Manual |
0 | Load output controlled by ManualLoadOn
|
Auto |
1 | Controller manages load output automatically |
Timer |
2 | Load output follows the timer schedule |
Unknown |
255 | Register value not recognised |
ChargerState describes the controller's current charging or load-management phase.
public enum ChargerState
{
Bulk,
Boost,
Float,
Idle,
LoadOff,
Fault,
Unknown
}The enum value in MpptLive.ChargerState is decoded by the static heuristic:
ChargerState state = ChargerStateLogic.FromRegisters(
solarStatus: live.SolarStatusRaw,
workStatus: live.WorkStatusRaw,
powerStatus: live.PowerStatusRaw,
chargeCurrent: live.ChargeCurrent,
dischargeCurrent: live.DischargeCurrent,
batteryVoltage: live.BatteryVoltage);MpptRegisters.DecodeLive calls this internally, so the field is already decoded when MpptLive arrives on the event. FromRegisters is public for cases where you need to re-evaluate state from a stored raw snapshot.
string display = live.ChargerState.Label();
// → "Bulk", "Boost", "Float", "Idle", "Load off", "Fault", "Unknown"Label() is an extension method returning a short, human-readable string suitable for display in a UI or log line.
BatteryProfile carries the physical characteristics of the battery pack connected to the controller. It is constructed by the host application from user-entered values; no controller protocol surfaces these directly.
public record BatteryProfile(
double EmptyV,
double FullV,
double NominalV,
double CapacityAh
)| Field | Description |
|---|---|
EmptyV |
Terminal voltage at which the pack is considered fully discharged |
FullV |
Terminal voltage at which the pack is considered fully charged |
NominalV |
Nominal pack voltage (used for display and Wh conversions) |
CapacityAh |
Rated capacity in ampere-hours |
double wh = profile.CapacityWh; // CapacityAh × NominalVNominal energy capacity in watt-hours, derived from CapacityAh and NominalV.
double soc = profile.SocFromVoltage(batteryVoltage);Converts a measured terminal voltage to a state-of-charge fraction (0.0–1.0) by linear interpolation between EmptyV (0 %) and FullV (100 %). This is the delegate you pass to MpptRegisters.DecodeLive:
var profile = new BatteryProfile(EmptyV: 20.0, FullV: 25.5, NominalV: 22.1, CapacityAh: 105);
MpptLive live = MpptRegisters.DecodeLive(
regs,
DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
socFromVoltage: profile.SocFromVoltage);double delta = profile.DeltaWh(fromSoc, toSoc);Returns the energy transferred (Wh) for a state-of-charge change from fromSoc to toSoc. The sign is positive when the SoC increases (charging) and negative when it decreases (discharging). Used by energy loggers to accumulate daily yield and consumption figures.
double amps = profile.InferredNetCurrentA(fromSoc, toSoc, elapsedMs);Back-calculates the average net current (A) implied by an observed SoC change over elapsedMs milliseconds. This is the energy-inference path for cases where current is not directly sensed — for example, inferring net battery current from DeltaWh and elapsed time when only voltage is available.
// How much energy moved in the last poll interval?
double delta = profile.DeltaWh(previousSoc, currentSoc);
// What average net current does that imply?
double inferredA = profile.InferredNetCurrentA(previousSoc, currentSoc, elapsedMs: 5000);BatteryChemistry describes the electrochemical type of a pack independently of any controller register value. It is used by the host application's battery configuration UI; it is not decoded from any protocol.
public enum BatteryChemistry
{
Unknown,
LeadAcid,
Gel,
Flooded,
LiFePO4,
NMC,
NCA,
LTO,
Custom
}string name = BatteryChemistry.LiFePO4.DisplayName();
// → "LiFePO4"An extension method returning a human-readable label for each chemistry. Useful for populating a chemistry picker in a settings screen.
double? v = BatteryChemistry.NMC.TypicalNominalV();Returns the conventional nominal voltage per cell-stack for well-known chemistries, or null for Unknown and Custom. The host application can use this to pre-fill the NominalV field of a new BatteryProfile as a convenience default.
All model types are C# records with only primitive and enum fields. They serialize and deserialize cleanly with System.Text.Json using default settings:
string json = JsonSerializer.Serialize(live);
MpptLive? restored = JsonSerializer.Deserialize<MpptLive>(json);No custom converters, source generators, or attributes are required. This makes the types suitable for REST API payloads, SQLite JSON columns, message-queue envelopes, and flat file loggers without any additional ceremony.
⚡ OpenMPPT Wiki · Repository · GPL-3.0-or-later · contributions welcome