Marstek CT002 / CT003 emulator

TinyC script for Tasmota · turns an ESP32 with a meter reading head into a Marstek CTTinyC-Script für Tasmota · macht aus einem ESP32 mit Lesekopf ein Marstek-CT

OverviewÜberblick

The script uses Marstek's native CT protocol on UDP port 12345, so a Venus / Jupiter / B2500 fleet can use your Tasmota device as its emulated CT002/CT003. Grid power comes from your electricity meter, exactly like the Shelly Pro 3EM and EcoTracker emulators — this is a third method to connect a battery to a emulated meter to enable zero-point feed-in.

What it adds over the Shelly emulation: coordination between several batteries. Every battery reports its own power and phase, and the answer carries the number of batteries per phase. Each battery divides the phase power by that count as its first step, so two batteries on one phase no longer fight each other. It also supports phase D, where a battery regulates against the sum of all three phases — usually what you want with a netting meter.

It runs in relay mode: the grid reading is passed through as measured, and each battery does its own regulation. There is no active steering or load balancing.

Requirements: ESP32 (ESP8266 is not supported), Tasmota with TinyC 1.6.64 or newer and USE_SML, and a meter reading head (examples on my blog). Older firmware refuses to load the program and says so — it does not fail silently.

Das Script verwendet Marsteks eigenes CT-Protokoll auf UDP-Port 12345, damit Venus-, Jupiter- oder B2500-Akkus dein Tasmota-Gerät als emulierten CT002/CT003 benutzen können. Die Netzleistung kommt von deinem Zähler, genau wie beim Shelly-Pro-3EM- und EcoTracker-Emulator — es ist die dritte Möglichkeit eine PV-Batterie mit einem emuliertem Zähler zu verbinden, um die Nulleinspeisung zu realisieren.

Der Vorteil gegenüber der Shelly-Emulation: Koordination mehrerer Akkus. Jeder Akku meldet seine eigene Leistung und Phase, und die Antwort enthält die Anzahl der Akkus je Phase. Jeder Akku teilt die Leistung als allerersten Schritt durch diese Zahl — zwei Akkus auf einer Phase schwingen sich damit nicht mehr gegenseitig auf (Regelschwingen). Dazu kommt Phase D, bei der ein Akku gegen die Summe aller drei Phasen regelt; bei einem saldierenden Zähler ist das meist das Richtige.

Betrieb im Relay-Modus: der Zählerwert wird unverändert durchgereicht, jeder Akku regelt selbst. Es gibt keine aktive Steuerung oder Lastverteilung.

Voraussetzungen: ESP32 (ESP8266 wird nicht unterstützt), Tasmota mit TinyC 1.6.64 oder neuer und USE_SML und natürlich ein Lesekopf (Beispiele auf meinem Blog). Ältere Firmware lädt das Programm nicht und sagt das auch — sie scheitert nicht still.

SetupEinrichtung

  1. Open the TinyC IDE on the device (console: TinyCIde, then http://<ip>/tc).
  2. Load ct002_emu.tc, press Compile, then Upload into slot 0.
  3. Tick autoexec so it starts on boot, or run TinyCRun /ct002_emu.tcb.
  4. Set up the meter under Meter first — without readings the emulator answers with 0 W.
  5. Create the CT in your Marstek account and select it in the app.

Slot 0 only. The /ctreg page is a webOn endpoint, and Tasmota always dispatches those to slot 0.

  1. TinyC-IDE auf dem Gerät öffnen (Konsole: TinyCIde, dann http://<ip>/tc).
  2. ct002_emu.tc laden, Compile, dann Upload in Slot 0.
  3. Autoexec ankreuzen, damit es beim Booten startet — oder TinyCRun /ct002_emu.tcb.
  4. Zuerst den Zähler einrichten; ohne Messwerte antwortet der Emulator mit 0 W.
  5. Das CT im Marstek-Konto anlegen und in der App auswählen.

Nur Slot 0. Die Seite /ctreg ist ein webOn-Endpunkt, und die reicht Tasmota immer an Slot 0 weiter.

CT

TypeCT002 sends HME-4, CT003 sends HME-3 and four extra energy registers. Takes effect after a restart of the script.
CT MAC12 hex characters, the MAC of the CT in your Marstek account. When set, the emulator only answers requests for that MAC (or the wildcard 000000000000). Leave empty and it answers everything — handy while testing, wrong once several CTs exist.
UDP port12345 in every known firmware. Only change it if you know why.
RSSIWi-Fi signal strength reported to the battery.
Battery timeoutSeconds after which a offline battery is dropped from the table. 0 = automatic: about two of its own poll cycles, at least 5 s. That mirrors a real CT.
UDP rx/tx/badCounters below the settings, not a setting: received, answered, and frames rejected by the checksum. bad should stay at 0 — a rising value means the request did not arrive intact. They used to sit on the main page.
TypeCT002 sendet HME-4, CT003 sendet HME-3 und vier zusätzliche Energieregister. Wirkt erst nach einem Neustart des Scripts.
CT MAC12 Hex-Zeichen, die MAC des CT im Marstek-Konto. Ist sie gesetzt, antwortet der Emulator nur auf Anfragen für diese MAC (oder die Wildcard 000000000000). Leer bedeutet: antwortet auf alles — praktisch zum Testen, falsch sobald es mehrere CTs gibt.
UDP port12345 in jeder bekannten Firmware. Nur ändern, wenn du weißt warum.
RSSIWLAN-Signalstärke, die dem Akku gemeldet wird.
Akku-TimeoutSekunden, nach denen ein Akku, der nicht mehr antwortet, aus der Tabelle fällt. 0 = automatisch: etwa zwei Abfragezyklen, mindestens 5 s. So macht es ein echter CT auch.
UDP rx/tx/badZähler unter den Einstellungen, keine Einstellung: empfangen, beantwortet, und wegen Prüfsumme verworfen. bad sollte 0 bleiben — ein steigender Wert heißt, die Anfrage kam nicht heil an. Früher standen sie auf der Hauptseite.

BatteryAkku

Offset [W]Target grid power instead of zero. A small positive value keeps you slightly on the import side so nothing is fed back. Applied per phase, because each battery regulates its own phase — with three batteries on three phases the household total ends up at three times the value.
Active controlOFF relays the meter reading as it is and lets the battery decide. ON runs the control loop described below. Predictor, damper and freshness check start straight away; the three settings underneath add to them and are off at 0.
Import trim [W]Recommended 15. Every Marstek firmware stops a few watts short on the import side: its own input deadband. Household load the battery could cover keeps being bought at the retail tariff. This adds a fixed nudge past that deadband, but only once the grid has held between 0 and 120 W of import for six fresh meter readings. The dwell keeps it out of a load step; the upper limit keeps it away from an empty or full battery, where the import is not the firmware's doing.
Ramp step [W]Recommended 30. Caps the size of the correction sent per second, measured from zero — not the step away from the last reported value, which would trail the battery instead of bounding it. The cap doubles towards three times this value while the batteries demonstrably follow, falls back with the deviation, and returns to the base step the moment the direction reverses.
Min DC output [W]Default 0 = off, otherwise 20 or more. A Marstek B2500 switches its inverter off at 0 W and falls asleep under high PV surplus. This never reports less than that much import, so the battery keeps discharging and stays awake. It costs exactly that many watts of export while the sun is out — only worth it for a DC battery that actually falls asleep. AC batteries (Venus) do not need it.
3 phasesON: L1/L2/L3 come from the meter individually (not all meter supports this). OFF: the total goes onto L1 and B/C stay at 0 — then set every battery to phase A or D, otherwise a battery on B or C would read 0 W forever.

Offset and import trim pull in opposite directions on purpose. The offset is the margin you choose and holds at all times; the trim closes the gap the battery firmware leaves once things are calm, and it aims at the offset, not at zero. Setting an offset of 50 W and a trim of 15 therefore means: hold 50 W of import, and let the trim make sure those 50 are actually reached rather than 65.

Three further parts always run with active control and have no settings of their own. They follow AstraMeter's CT002 control loop at its field-tested values:

  • A grid predictor subtracts what the batteries report having already delivered, so a correction still on its way to the meter is not issued a second time. That double issue is the usual cause of hunting.
  • An oscillation damper counts how often the correction reverses sign and bleeds up to 95 % of the gain while that is happening. A real load step keeps its sign and passes at full strength.
  • A freshness check ignores a repeated meter value, so a meter slower than one second is not counted several times over.

All the settings on this page sit on their own page in the device: the 🔋 CT button in Tasmota's main menu, or the link at the top of the settings page. They outgrew the main page when the control settings arrived.

Phase selection

The phase is set on the battery, in the Marstek app — not here:

  • A / B / C: the battery regulates against that phase and shares it with other batteries on the same phase.
  • D: combined mode. The battery reads the total of all three phases. With a netting meter this is normally the right choice.
  • x in the web UI means the battery has not decided yet and is still probing. It changes its own output and watches which phase moves. With a simulator that produces constant values it can never conclude — give it a real meter, or set the phase manually in the app.
  • While a battery is probing, the emulator answers it with the raw meter reading: active control and the offset are bypassed for that battery alone. They would otherwise defeat the diagnosis — the grid predictor exists precisely to subtract the battery's own output change, so it could open up to 800 W and still be shown 0 W. The log says in inspection mode while this lasts and settled on phase when it is over. Every other battery keeps being steered normally.
Offset [W]Zielwert am Netz statt null. Ein kleiner positiver Wert hält dich knapp über der Bezugsseite, damit nichts eingespeist wird. Wirkt pro Phase, weil jeder Akku seine eigene Phase regelt — bei drei Akkus auf drei Phasen liegt der Hausanschluss am Ende beim Dreifachen.
Aktive RegelungAUS reicht den Zählerwert unverändert weiter, der Akku entscheidet selbst. EIN schaltet die unten beschriebene Regelung ein. Prädiktor, Dämpfung und Frischeprüfung laufen sofort mit; die drei Einstellungen darunter kommen obendrauf und sind bei 0 aus.
Import-Trim [W]Empfohlen 15. Jede Marstek-Firmware bleibt ein paar Watt auf der Bezugsseite stehen — ihr eigenes Totband am Eingang. Haushaltslast, die der Akku decken könnte, wird also weiter zum Arbeitspreis gekauft. Das schiebt mit einem festen Wert über dieses Totband hinweg, aber erst wenn das Netz sechs frische Zählerwerte lang zwischen 0 und 120 W Bezug lag. Die Wartezeit hält es aus Lastsprüngen heraus, die Obergrenze von einem leeren oder vollen Akku fern — dort liegt der Bezug nicht an der Firmware.
Schrittweite [W]Empfohlen 30. Begrenzt die Größe der Korrektur je Sekunde, gemessen ab null — nicht den Schritt weg vom zuletzt gemeldeten Wert, denn der würde dem Akku hinterherlaufen statt ihn zu begrenzen. Die Grenze verdoppelt sich auf bis zum Dreifachen, solange die Akkus nachweislich folgen, fällt mit der Abweichung zurück und springt bei jedem Richtungswechsel sofort auf den Grundwert.
Mindestabgabe DC [W]Standard 0 = aus, sonst 20 oder mehr. Ein Marstek B2500 schaltet bei 0 W seinen Wechselrichter ab und schläft bei viel PV-Überschuss ein. Hier wird nie weniger als dieser Bezug gemeldet, der Akku entlädt also weiter und bleibt wach. Das kostet bei Sonne genau diese Watt an Einspeisung — lohnt sich nur bei einem DC-Akku, der wirklich einschläft. AC-Akkus (Venus) brauchen es nicht.
3 phasesEIN: L1/L2/L3 kommen einzeln vom Zähler (nicht alle Stromzähler unterstützten das). AUS: die Gesamtleistung liegt auf L1, B und C bleiben 0 — dann alle Akkus auf Phase A oder D stellen, sonst liest ein Akku auf B oder C dauerhaft 0 W.

Offset und Import-Trim ziehen absichtlich in verschiedene Richtungen. Der Offset ist der Abstand, den du willst, und gilt immer; der Trim schließt die Lücke, die die Akku-Firmware lässt, sobald Ruhe herrscht — und er zielt auf den Offset, nicht auf null. Offset 50 W und Trim 15 heißt also: 50 W Bezug halten, und der Trim sorgt dafür, dass es wirklich 50 werden und nicht 65.

Drei weitere Teile laufen bei aktiver Regelung immer mit und haben keine eigene Einstellung. Sie sind der CT002-Regelung von AstraMeter nachempfunden und stehen auf deren erprobten Werten:

  • Ein Netz-Prädiktor zieht ab, was die Akkus bereits geliefert melden. Eine Korrektur, die noch auf dem Weg zum Zähler ist, wird so kein zweites Mal ausgegeben — genau diese Doppelausgabe ist die übliche Ursache fürs Pendeln.
  • Eine Schwingungsdämpfung zählt, wie oft die Korrektur ihr Vorzeichen wechselt, und nimmt währenddessen bis zu 95 % der Verstärkung weg. Ein echter Lastsprung behält sein Vorzeichen und geht ungebremst durch.
  • Eine Frischeprüfung übergeht einen wiederholten Zählerwert, damit ein Zähler, der langsamer als eine Sekunde ist, nicht mehrfach zählt.

Alle Einstellungen dieser Seite liegen im Gerät auf einer eigenen Seite: der Knopf 🔋 CT in Tasmotas Hauptmenü, oder der Link oben auf der Einstellungsseite. Sie sind der Hauptseite entwachsen, als die Regelungsoptionen dazukamen.

Phasenwahl

Die Phase wird am Akku eingestellt, in der Marstek-App — nicht hier:

  • A / B / C: der Akku regelt gegen diese Phase und teilt sie sich mit anderen Akkus auf derselben Phase.
  • D: kombiniert. Der Akku liest die Summe aller drei Phasen. Bei saldierendem Zähler normalerweise die richtige Wahl.
  • x in der Weboberfläche heißt: der Akku hat sich noch nicht entschieden und sucht. Er ändert dazu seine eigene Leistung und schaut, welche Phase sich mitbewegt. Bei einem Simulator mit konstanten Werten kommt er nie zu einem Schluss — also echten Zähler anschließen oder die Phase in der App fest vorgeben.
  • Solange ein Akku sucht, bekommt er den rohen Zählerwert: aktive Regelung und Offset sind für genau diesen Akku ausgesetzt. Sie würden die Diagnose sonst unmöglich machen — der Netz-Prädiktor ist ja gerade dazu da, die eigene Leistungsänderung des Akkus herauszurechnen, also könnte er auf 800 W aufmachen und würde trotzdem 0 W sehen. Im Log steht währenddessen in inspection mode und danach settled on phase. Alle anderen Akkus werden normal weitergeregelt.

Several batteriesMehrere Akkus

All of this only matters with two or more batteries on the same phase. With one battery every setting here can stay off.

Fair distribution is a correction, not a refinement. Without it every battery is told the whole deviation of its phase, so two of them each pull the full amount and the pair overshoots by a factor of two. If you run more than one battery, switch it on.

Share the loadDivides the phase deviation by the number of participating batteries and adds the balance term below. Off = the old behaviour, every battery hears the full value.
Saturation detectionTracks whether a battery follows what it is told. One that is full or empty stops moving; its share shrinks and the others pick it up automatically. Also suspends the minimum output below, which an empty battery cannot deliver anyway.

A deprioritised battery fades out by 15 % per cycle instead of dropping to zero at once, so the promoted one has time to ramp and no gap opens at the handover.

The B2500 start floor

A B2500 (device types HMA, HMJ, HMK) feeds a separate inverter and each of its DC channels is a hard on/off below about 40 W — so the unit cannot answer a command under roughly 80 W at all. Two of them sharing a 100 W deviation would each be told 50 W and both stay asleep.

The emulator therefore reads the device type every battery sends with its request and, for that family, does three things by itself — nothing to set:

  • Concentrates the load so each active battery gets at least 80 W, rather than splitting it below the floor.
  • Does not score saturation below that figure. "It did not move" says nothing about a command the hardware cannot execute, and scoring it there is self-reinforcing — it eventually pushes a healthy battery out of the pool (AstraMeter issue 624).
  • Applies the minimum output only here. A Venus or a Jupiter carries its own inverter and cannot fall asleep, so holding it awake would export for nothing.

Fixed values

Three things were settings and are now constants, at AstraMeter's field-tested values. tomquist advised it himself — too many knobs make support harder, and these are ones nobody could tune without measurements:

Balance gain0.2. How hard a battery doing less than its peers is pushed.
Rotation900 s between turns at being the active battery, so wear is shared.
Efficiency thresholdGone entirely. For a B2500 the 80 W floor above now applies by itself; for an AC battery it was polish.

Where this comes from, and where it differs

The loop follows AstraMeter's CT002 control loop, whose constants are field-tested by many users: deadband 25 W, at most 80 W of correction per cycle, gain boosted above 150 W of imbalance. Those are fixed here rather than exposed.

Two things are deliberately done differently, both because of open reports there:

  • No gentle nudges. AstraMeter softens corrections when the imbalance is small. A Marstek HMJ ignores anything under about 10 W per channel, so those corrections change nothing while the balancer believes it acted — in issue 600 a battery sat at 4 W for hours receiving them. Here: below 25 W of imbalance nothing is sent at all, above it at least 20 W so the correction clears the battery's own deadband.
  • Minimum output is per battery. Applied to the phase total it would be halved by the fair split, and half of 20 W lands back inside that deadband — the lock-in described in the same report.

What is not implemented is AstraMeter's probe handover with a fallback battery and a timeout. The fade above serves the same purpose with a fraction of the state.

Das alles betrifft nur zwei oder mehr Akkus auf derselben Phase. Bei einem einzelnen kann hier alles aus bleiben.

Die faire Verteilung ist eine Korrektur, keine Feinabstimmung. Ohne sie bekommt jeder Akku die volle Abweichung seiner Phase gemeldet — zwei Akkus ziehen also beide den ganzen Betrag und schießen gemeinsam um das Doppelte über. Wer mehr als einen Akku hat, sollte sie einschalten.

Last verteilenTeilt die Phasenabweichung durch die Zahl der teilnehmenden Akkus und rechnet den Ausgleich unten dazu. Aus = das bisherige Verhalten, jeder Akku hört den vollen Wert.
Sättigung erkennenVerfolgt, ob ein Akku dem folgt, was ihm gesagt wird. Ein voller oder leerer bewegt sich nicht mehr; sein Anteil sinkt, die anderen übernehmen ihn von selbst. Setzt außerdem die Mindestabgabe aus, die ein leerer Akku ohnehin nicht liefern kann.

Ein zurückgestellter Akku blendet mit 15 % je Zyklus aus statt sofort auf null zu fallen — so hat der übernehmende Zeit hochzufahren und es entsteht keine Lücke.

Die Anlaufschwelle des B2500

Ein B2500 (Gerätetypen HMA, HMJ, HMK) speist einen separaten Wechselrichter, und jeder seiner DC-Kanäle ist unterhalb von etwa 40 W ein hartes Ein/Aus — das Gerät kann einen Befehl unter rund 80 W also gar nicht ausführen. Teilen sich zwei davon eine Abweichung von 100 W, bekommt jeder 50 W gesagt und beide bleiben schlafen.

Der Emulator liest deshalb den Gerätetyp aus, den jeder Akku mit jeder Anfrage mitschickt, und macht für diese Familie drei Dinge von allein — nichts davon ist einzustellen:

  • Er bündelt die Last, sodass jeder aktive Akku mindestens 80 W bekommt, statt sie unter die Schwelle zu teilen.
  • Er bewertet Sättigung unterhalb dieses Werts nicht. „Hat sich nicht bewegt" sagt nichts über einen Befehl, den die Hardware nicht ausführen kann, und ihn dort zu bewerten ist selbstverstärkend — am Ende fliegt ein gesunder Akku aus dem Pool (AstraMeter-Meldung 624).
  • Er wendet die Mindestabgabe nur hier an. Eine Venus oder ein Jupiter hat einen eigenen Wechselrichter und kann nicht einschlafen — sie wach zu halten hieße, umsonst einzuspeisen.

Feste Werte

Drei Dinge waren einmal Einstellungen und sind jetzt Konstanten, auf AstraMeters erprobten Werten. Tomquist hat selbst dazu geraten — zu viele Stellschrauben erschweren den Support, und diese hier kann ohne Messungen ohnehin niemand sinnvoll drehen:

Ausgleich0,2. Wie kräftig ein Akku angeschoben wird, der weniger tut als die anderen.
Rotation900 s zwischen den Wechseln, damit der Verschleiß wandert.
EffizienzschwelleGanz entfallen. Beim B2500 greift die 80-W-Schwelle oben jetzt von selbst, bei AC-Akkus war es Feinschliff.

Woher das kommt und wo es abweicht

Die Regelung ist der CT002-Regelung von AstraMeter nachempfunden, deren Konstanten von vielen Nutzern erprobt sind: Totband 25 W, höchstens 80 W Korrektur je Zyklus, verstärkt oberhalb von 150 W Ungleichgewicht. Die stehen hier fest und sind nicht einstellbar.

Zwei Dinge sind bewusst anders gelöst, beide wegen dortiger offener Meldungen:

  • Keine sanften Stupser. AstraMeter mildert Korrekturen bei kleinem Ungleichgewicht ab. Ein Marstek HMJ ignoriert aber alles unter etwa 10 W je Kanal, solche Korrekturen bewirken also nichts, während der Regler glaubt, gehandelt zu haben — in Meldung 600 saß ein Akku deshalb stundenlang bei 4 W. Hier gilt: unterhalb von 25 W Ungleichgewicht gar nichts, darüber mindestens 20 W, damit die Korrektur das geräteeigene Totband verlässt.
  • Die Mindestabgabe wirkt je Akku. Auf die Phasensumme angewandt würde die faire Teilung sie halbieren, und die Hälfte von 20 W liegt wieder im Totband — genau der Verklemmungs-Kreislauf aus derselben Meldung.

Nicht übernommen ist AstraMeters Probe-Übergabe mit Rückfall-Akku und Zeitgrenze. Das Ausblenden oben erfüllt denselben Zweck mit einem Bruchteil des Zustands.

MeterZähler

SML activeSwitches the SML driver on. Without it there are no readings and the emulator answers 0 W.
DescriptorPicks a meter definition from the script repository and writes it to /sml_meter.def. Use Edit descriptor for manual changes.
RX / TX pinGPIOs of the reading head. Common: RX 3 / TX 1 (Hichi, Stromleser, Wattwächter), RX 5 / TX 4 (bitShake).
Median filterSmooths single outliers. Costs reaction time — leave it off for zero feed-in, the batteries filter on their own.

Value order matters: 1 power, 2 energy import, 3 energy export, 4/5/6 the three phase powers. Only meters that publish per-phase values can drive the 3 phases option.

SML activeSchaltet den SML-Treiber ein. Ohne ihn gibt es keine Messwerte und der Emulator antwortet mit 0 W.
DescriptorWählt eine Zählerdefinition aus dem Script-Repository und schreibt sie nach /sml_meter.def. Für Handarbeit Edit descriptor.
RX / TX pinGPIOs des Lesekopfs (wo ist die Sende- und Empfangsdiode angeschlossen). Häufig: RX 3 / TX 1 (Hichi, Stromleser, Wattwächter), RX 5 / TX 4 (bitShake).
Median filterGlättet einzelne Ausreißer, kostet aber Reaktionszeit — für Nulleinspeisung besser aus lassen, die Akkus filtern selbst.

Die Reihenfolge der Werte zählt: 1 Leistung, 2 Bezug, 3 Einspeisung, 4/5/6 die drei Phasenleistungen. Nur Zähler, die Einzelphasen liefern, können die Option 3 phases bedienen.

TelemetryTelemetrie

Publish power hands two values to the outside: power2, the heavily smoothed grid power, and cpwr, what this CT reports to the batteries. Both go out over MQTT every 5 s and are served as JSON.

MQTT

stat/<topic>/script/power2
stat/<topic>/script/cpwr

Same topics the .tas emulators use, so existing Home Assistant sensors keep working unchanged.

HTTP

http://<ip>/pwr
{"power2":-694,"cpwr":-620,"L1":-620,"L2":0,"L3":0}

For a second battery that polls a meter over HTTP (e.g. opendtu-onbattery DPL): enter that URL and use the JSON path power2. No password parameter needed. With the checkbox off the endpoint answers {"enabled":0} and nothing is published.

power2 is an exponential average, 0.9 × old + 0.1 × reading every 5 s — about a 50 s time constant. It is meant for a slow consumer that must not chase every spike; use cpwr or L1..L3 if you want the live value.

Publish power gibt zwei Werte nach außen: power2, die stark geglättete Netzleistung, und cpwr, das was dieses CT den Akkus meldet. Beide gehen alle 5 s per MQTT raus und stehen als JSON bereit.

MQTT

stat/<topic>/script/power2
stat/<topic>/script/cpwr

Dieselben Topics wie in den .tas-Emulatoren, vorhandene Home-Assistant-Sensoren laufen also unverändert weiter.

HTTP

http://<ip>/pwr
{"power2":-694,"cpwr":-620,"L1":-620,"L2":0,"L3":0}

Für einen zweiten Akku, der einen Zähler per HTTP abfragt (z. B. opendtu-onbattery DPL): diese URL eintragen und als JSON-Pfad power2 verwenden. Ohne Passwort-Parameter. Ist die Checkbox aus, antwortet der Endpunkt {"enabled":0} und es wird nichts publiziert.

power2 ist ein exponentieller Mittelwert, 0,9 × alt + 0,1 × Messwert alle 5 s — rund 50 s Zeitkonstante. Gedacht für einen trägen Verbraucher, der nicht jeder Spitze hinterherlaufen soll; wer den Live-Wert will, nimmt cpwr oder L1..L3.

Marstek accountMarstek-Konto

A battery only talks to a CT that exists in the owner's Marstek account. The emulator can create such an entry — a managed device whose id and MAC start with 02b250, the same record AstraMeter creates.

  1. Press ☁️ Marstek in Tasmota's main menu (opens /ctreg) and enter mail and password.
  2. Log in + check account fetches a token and adopts an existing fake CT if there is one.
  3. If none was found: Create fake CT and confirm the dialog. The status line updates by itself and turns green on success.
  4. In the Marstek app refresh the CT list (log out and in if needed), select the new CT, switch the battery to automatic.

Create is disabled while a CT MAC is set — Clear CT MAC at the bottom of the same page frees it again. That only clears the MAC on the device; the CT in your Marstek account stays and has to be deleted in the app.

Notes

  • The fake CT shows as offline in the app, and its live values stay at --. That is expected and does not affect regulation: the battery reads the CT locally over UDP, while the app's display comes from Marstek's cloud over MQTT, which a fake CT never connects to.
  • The setCtReporting HTTP push does not fix that. It was implemented and measured: the endpoint accepts the data ({"code":1,"msg":"success"}), but after 776 accepted reports the app still showed --. It was removed again.
  • Create only one fake CT. If several exist, the app may be pointed at a different one than your device answers for.

The password travels as plain text over your LAN from your browser to the ESP with Tasmota. It is never written to flash, is kept in RAM for 10 minutes so step 2 does not need it again, and is discarded after a successful create.

Creating the fake CT talks to an undocumented Marstek API with your own account. That may go against Marstek's terms of use — it is your account and your risk. In the worst case Marstek removes the device or acts against the account.

Ein Akku spricht nur mit einem CT, das im Marstek-Konto des Besitzers existiert. Der Emulator kann so einen Eintrag anlegen — ein verwaltetes Gerät, dessen Id und MAC mit 02b250 beginnen, derselbe Datensatz, den auch AstraMeter erzeugt.

  1. Im Hauptmenü von Tasmota auf ☁️ Marstek (öffnet /ctreg), Mail und Passwort eintragen.
  2. Log in + check account holt ein Token und übernimmt ein vorhandenes Fake-CT, falls eines da ist.
  3. Wurde keines gefunden: Create fake CT und den Dialog bestätigen. Die Statuszeile aktualisiert sich von selbst und wird bei Erfolg grün.
  4. In der Marstek-App die CT-Liste aktualisieren (notfalls ab- und wieder anmelden), das neue CT auswählen, den Akku auf Automatik stellen.

Create ist gesperrt, solange eine CT-MAC gesetzt ist — Clear CT MAC unten auf derselben Seite gibt es wieder frei. Das löscht nur die MAC auf dem Gerät; das CT im Marstek-Konto bleibt und muss in der App gelöscht werden.

Hinweise

  • Das Fake-CT erscheint in der App als offline, seine Live-Werte bleiben auf --. Das ist so erwartet und hat auf die Regelung keinen Einfluss: der Akku liest das CT lokal per UDP, während die Anzeige der App aus Marsteks Cloud per MQTT kommt — und dorthin verbindet sich ein Fake-CT nie.
  • Der setCtReporting-Push ändert daran nichts. Er war eingebaut und wurde gemessen: der Endpunkt nimmt die Daten an ({"code":1,"msg":"success"}), aber nach 776 akzeptierten Reports zeigte die App weiter --. Er wurde wieder entfernt.
  • Lege nur ein Fake-CT an. Gibt es mehrere, zeigt die App womöglich auf ein anderes, als dein Gerät bedient.

Das Passwort geht unverschlüsselt durch dein lokales Netzwerk vom Browser an den ESP mit Tasmota. Es wird nicht in den Flash-Speicher geschrieben, bleibt 10 Minuten im Arbeitsspeicher, damit Schritt 2 es nicht erneut braucht, und wird nach erfolgreichem Anlegen verworfen.

Das Anlegen des Fake-CT spricht eine undokumentierte Marstek-Schnittstelle mit deinem eigenen Konto an. Das kann gegen Marsteks Nutzungsbedingungen verstoßen — es ist dein Konto und dein Risiko. Im schlimmsten Fall entfernt Marstek das Gerät oder geht gegen das Konto vor.

Console commandsKonsolen-Kommandos

CTSTAT            rx/tx/bad counters, one log line per battery
CTCLR             clear the battery table
CTMAC <hex12>     set the CT MAC (empty = answer any)
CTMAIL <address>  set the Marstek account mail
CTPASS <password> set the password (RAM only)
CTLOGIN           run step 1
CTCREATE JA       run step 2 (the JA is the confirmation)

Log lines are always English, whatever else is on screen — mixed-language logs make support harder.

Log-Zeilen sind immer englisch, unabhängig vom Rest — gemischtsprachige Logs machen die Fehlersuche schwerer.

TroubleshootingFehlersuche

rx stays 0Nothing is arriving. The battery has not been pointed at this CT, or it lost it. Toggle the operating mode in the app (automatic off and on again). A script restart drops the socket for a moment, and the battery does not always come back on its own.
bad counts upPackets arrive but fail the checksum. Something on the wire does not match the protocol — keep the log with weblog 4.
Battery shows Ph xIt is still probing its phase. See Battery.
All values 0 WNo meter readings. Check SML active, pins and descriptor under Meter.
App shows --Expected. See Marstek account.
rx bleibt 0Es kommt nichts an. Der Akku wurde nicht auf dieses CT gestellt oder hat es verloren. In der App den Betriebsmodus umschalten (Automatik aus und wieder ein). Ein Script-Neustart schließt den Socket kurz, und der Akku findet nicht immer von allein zurück.
bad zählt hochPakete kommen an, scheitern aber an der Prüfsumme. Auf dem Draht passt etwas nicht zum Protokoll — Log mit weblog 4 mitschreiben.
Akku zeigt Ph xEr sucht noch seine Phase. Siehe Akku.
Alle Werte 0 WKeine Zählerwerte. SML active, Pins und Descriptor unter Zähler prüfen.
App zeigt --Erwartet. Siehe Marstek-Konto.