Drehstromzähler EBZ DD3 2R6 DTA zeigt beim Auslesen verkehrte Werte

Hallo zusammen
Mir wurde ein digitaler Stromzähler eingebaut, den ich mit einem ESP8266 NodeMCU auslese.
Ich hatte bisher nur sehr sehr wenig mit C++ gearbeitet.
Als Sketch verwende ich den von Rentner Faraday erstellten und veröffentlichten:
[SML Protokoll identifiziren und zerlegen - #379 by Rentner]

Der Sketch funktioniert erstklassig (dem kann man nur großen Respekt zollen)
Die Werte des Obis Code 1.0.7 werden richtig ausgegeben.
Die Werte für 1.8.0, 1.8.1, 2.8.0 werden verkehrt ausgegeben.
Ich vermute das die HEX Zahl zu groß ist und daher nicht richtig umgerechnet wird. Es wäre toll wenn jemand einen Lösungsvorschlag hätte.

Laut Datenblatt Zähler wird folgende Auflösung üertragen:
Die Auflösung des Zählers ist für den Code 1.0.7 Auflösung 0,01W (5 Vorkomma- und 2 Nachkommastellen)
Die Auflösung des Zählers ist für den Code 1.8.0 und 2.8.0 Auflösung 10 µWh (6 Vorkomma- und 8 Nachkommastellen)
Die Auflösung des Zählers ist für den Code 1.8.1, 2.8.1 und 2.8.2 Auflösung 1 W
h (6 Vorkomma- und 3 Nachkommastellen)

Gruß Neuling_68

Hier eine Kopie vom seriellen Monitor.
12:09:11.852 -> 1b 1b 1b 1b 01 01 01 01 76 05 01 1b f5 21 62 00 62 00 72 65
12:09:11.852 -> 00 00 01 01 76 01 01 07 65 42 5a 44 44 33 0b 09 01 45 42 5a
12:09:11.852 -> 01 00 xx xx xx 01 01 63 12 39 00 76 05 01 1b f5 22 62 00 62
12:09:11.852 -> 00 72 65 00 00 07 01 77 01 0b 09 01 45 42 5a 01 00 xx xx xx
12:09:11.899 -> 01 72 62 01 65 00 2f 54 a4 7f 77 07 01 00 60 32 01 01 01 01
12:09:11.899 -> 01 01 04 45 42 5a 01 77 07 01 00 60 01 00 ff 01 01 01 01 0f
12:09:11.899 -> 31 45 42 5a 30 31 30 33 36 32 36 31 32 38 01 77 07 01 00 01
12:09:11.899 -> 08 00 ff 65 00 1c 01 04 01 62 1e 52 fb 69 00 00 00 09 53 90
12:09:11.899 -> bc 1d 01 77 07 01 00 01 08 01 ff 01 01 62 1e 52 fb 69 00 00
12:09:11.899 -> 00 09 4d 9a db 1d 01 77 07 01 00 01 08 02 ff 01 01 62 1e 52
12:09:11.899 -> fb 69 00 00 00 00 05 f5 e1 00 01 77 07 01 00 02 08 00 ff 65
12:09:11.899 -> 00 1c 01 04 01 62 1e 52 fb 69 00 00 00 00 0d 35 59 00 01 77
12:09:11.899 -> 07 01 00 10 07 00 ff 01 01 62 1b 52 fe 55 00 00 62 1f 01 77
12:09:11.946 -> 07 01 00 24 07 00 ff 01 01 62 1b 52 fe 55 00 00 35 6a 01 77
12:09:11.946 -> 07 01 00 38 07 00 ff 01 01 62 1b 52 fe 55 00 00 1d a1 01 77
12:09:11.946 -> 07 01 00 4c 07 00 ff 01 01 62 1b 52 fe 55 00 00 0f 14 01 77
12:09:11.946 -> 07 01 00 20 07 00 ff 01 01 62 23 52 ff 63 09 16 01 77 07 01
12:09:11.946 -> 00 34 07 00 ff 01 01 62 23 52 ff 63 09 15 01 77 07 01 00 48
12:09:11.946 -> 07 00 ff 01 01 62 23 52 ff 63 09 12 01 77 07 01 00 00 02 00
12:09:11.946 -> 00 01 01 01 01 04 33 30 32 01 77 07 01 00 60 5a 02 01 01 01
12:09:11.946 -> 01 01 05 46 35 32 30 01 01 01 63 4e dc 00 76 05 01 1b f5 23
12:09:11.946 ->
12:09:11.946 -> code gefunden: 1b 1b 1b 1b 01 01 01 01
12:09:11.992 -> start mit elm 1 max Elemnt 0
12:09:11.992 -> 76 element 1 Liste erkannt plus 6 jetzt max 6
12:09:11.992 -> 05 01 1b f5 21 element 2
12:09:11.992 -> 62 00 element 3
12:09:11.992 -> 62 00 element 4
12:09:11.992 -> 72 element 5 Liste erkannt plus 2 jetzt max 8
12:09:11.992 -> 65 00 00 01 01 element 6
12:09:11.992 -> 76 element 7 Liste erkannt plus 6 jetzt max 14
12:09:11.992 -> 01 leres erkannt 8
12:09:11.992 -> 01 leres erkannt 9
12:09:11.992 -> 07 65 42 5a 44 44 33 element 10
12:09:11.992 -> 0b 09 01 45 42 5a 01 00 xx xx xx element 11 SML_ServerID
12:09:11.992 -> 01 leres erkannt 12
12:09:11.992 -> 01 leres erkannt 13
12:09:11.992 -> 63 12 39 element 14
12:09:11.992 ->
12:09:11.992 -> code gefunden: 01 00 01 08 00 ff
12:09:11.992 -> start mit elm 2 max Elemnt 7
12:09:12.039 -> 65 00 1c 01 04 element 2
12:09:12.039 -> 01 leres erkannt 3
12:09:12.039 -> 62 1e element 4
12:09:12.039 -> 52 fb element 5
12:09:12.039 -> 69 00 00 00 09 53 90 bc 1d element 6
12:09:12.039 -> SML_VALUE UINT 1401994269
12:09:12.039 -> 01 leres erkannt 7
12:09:12.039 ->
12:09:12.039 -> code gefunden: 01 00 01 08 01 ff
12:09:12.039 -> start mit elm 2 max Elemnt 7
12:09:12.039 -> 01 leres erkannt 2
12:09:12.039 -> 01 leres erkannt 3
12:09:12.039 -> 62 1e element 4
12:09:12.039 -> 52 fb element 5
12:09:12.039 -> 69 00 00 00 09 4d 9a db 1d element 6
12:09:12.039 -> SML_VALUE UINT 1301994269
12:09:12.039 -> 01 leres erkannt 7
12:09:12.039 ->
12:09:12.039 -> code gefunden: 01 00 02 08 00 ff
12:09:12.039 -> start mit elm 2 max Elemnt 7
12:09:12.039 -> 65 00 1c 01 04 element 2
12:09:12.039 -> 01 leres erkannt 3
12:09:12.039 -> 62 1e element 4
12:09:12.039 -> 52 fb element 5
12:09:12.039 -> 69 00 00 00 00 0d 35 59 00 element 6
12:09:12.086 -> SML_VALUE UINT 221600000
12:09:12.086 -> 01 leres erkannt 7
12:09:12.086 ->
12:09:12.086 -> code gefunden: 01 00 10 07 00 ff
12:09:12.086 -> start mit elm 2 max Elemnt 7
12:09:12.086 -> 01 leres erkannt 2
12:09:12.086 -> 01 leres erkannt 3
12:09:12.086 -> 62 1b element 4
12:09:12.086 -> 52 fe element 5
12:09:12.086 -> 55 00 00 62 1f element 6
12:09:12.086 -> SML_VALUE INT 25119
12:09:12.086 -> 01 leres erkannt 7
12:09:12.086 -> Zähler ID EBZ xxxxxxx
12:09:12.086 -> Zählwerk A+ 1401994KWh
12:09:12.086 -> Zählwerk T1 A+ 1301994KWh
12:09:12.086 -> Zählwerk A- 221600KWh
12:09:12.086 -> Wirkleistung 25119W
12:09:12.836 -> pause erkannt
12:09:12.836 -> 420 byte empfangen

Was steht auf dem Zählerdisplay, was da bei 1.8.0 (summe) 1.8.1 und 1.8.2? bzw. 2.8.x äquivalent?

Habs aufgelöst. Macht was draus. Das wird wieder nur eine Einzelfalllösung :laughing:

Liste der SML-Blöcke unkommentiert, da bekannt, wie das funktioniert

1b 1b 1b 1b 01 01 01 01 
76 
 05 01 1b f5 21 
 62 00 
 62 00 
 72 
  65 00 00 01 01 
  76 
   01 
   01 
   07 65 42 5a 44 44 33 
   0b 09 01 45 42 5a 01 00 xx xx xx 
   01 
   01 
 63 12 39 00 
 76 
 05 01 1b f5 22 
 62 00 
 62 00 
 72 
  65 00 00 07 01 
  77 
   01 
   0b 09 01 45 42 5a 01 00 xx xx xx 01 
   72 
    62 01 
    65 00 2f 54 a4 
   7f 
    77 
     07 01 00 60 32 01 01 
     01 
     01
     01 
     01 
     04 45 42 5a 
     01 
    77 
     07 01 00 60 01 00 ff 
     01 
     01 
     01 
     01 
     0f 31 45 42 5a 30 31 30 33 36 32 36 31 32 38 
     01 
    77 
     07 01 00 01 08 00 ff //**
     65 00 1c 01 04 
     01 
     62 1e // Wh 
     52 fb 
     69 00 00 00 09 53 90 bc 1d 
     01 
    77 
     07 01 00 01 08 01 ff //**
     01 
     01 
     62 1e 
     52 fb 
     69 00 00 00 09 4d 9a db 1d 
     01 
    77 
     07 01 00 01 08 02 ff  //** 
     01 
     01 
     62 1e 
     52 fb 
     69 00 00 00 00 05 f5 e1 00 
     01 
    77 
     07 01 00 02 08 00 ff 
     65 00 1c 01 04 
     01 
     62 1e 
     52 fb 
     69 00 00 00 00 0d 35 59 00 
     01 
    77
     07 01 00 10 07 00 ff 
     01 
     01 
     62 1b 
     52 fe 
     55 00 00 62 1f 
     01 
    77
     07 01 00 24 07 00 ff 
     01 
     01 
     62 1b 
     52 fe 
     55 00 00 35 6a 
     01
    77
     07 01 00 38 07 00 ff 
     01 
     01 
     62 1b 
     52 fe 
     55 00 00 1d a1 
     01 
    77
     07 01 00 4c 07 00 ff 
     01 
     01 
     62 1b 
     52 fe 
     55 00 00 0f 14 
     01 
    77
     07 01 00 20 07 00 ff 
     01 
     01 
     62 23 
     52 ff 
     63 09 16
     01 
    77 
     07 01 00 34 07 00 ff 
     01 
     01 
     62 23 
     52 ff 
     63 09 15 
     01 
    77 
     07 01 00 48 07 00 ff 
     01 
     01 
     62 23 
     52 ff 
     63 09 12 
     01 
    77 
     07 01 00 00 02 00 00 
     01 
     01 
     01 
     01 
     04 33 30 32 
     01 
    77 
     07 01 00 60 5a 02 01 
     01 
     01
     01 
     01 
     05 46 35 32 30 
01 
01 
01 
63 4e dc 
00 
76 
 05 01 1b f5 23

Hallo,
das ist ein Zweirichtungszähler welcher auf dem Display im 10 sekunden Wechsel den Code 1.8.0
und 2.8.0 anzeigt. Für beide Codes erfolgt die Anzeige in KWh ohne Nachkommastelle. Ich habe keine Solareinspeisung,
daher steht bei 2.8.0 nur 2 KWh, die sind wahrscheinlich bei der Fabrik Endprüfung entstanden.
Bei 1.8.0 stand zum Zeitpunkt der Messung 400 KWh.
In der 2 Zeile des Geräts wird die momentare Wirkleistung in W angezeigt diese betrug 251 W

Stimmt mit dem überein, was der Zähler ausgibt.

das sind 0x095390BC1D
Dezimal: 40056699933
Die Einheit ist Wh
40056699 wären Kilowattstunden, wenn.... da nicht der Teiler wäre.

Das 0xFB ist der scaler. Der ist aber ein int8_t, somit ist Dein Wert noch mit 10hoch-5 zu multiplizieren, oder durch 10000 zu dividieren.

Du brauchst ein unsigned long long (uint64_t) als Type.
Und das muss dann auch berücksichtigt werden, wenn die Zahl zusammengebaut wird.

/*
     number =  (uint64_t(myNumber[0]) << 56) + (uint64_t(myNumber[1]) << 48) + (uint64_t(myNumber[2]) << 40) + (uint64_t(myNumber[3]) << 32) +
               (uint64_t(myNumber[4]) << 24) + (uint64_t(myNumber[5]) << 16) + (uint64_t(myNumber[6]) << 8) + (uint64_t(myNumber[7]));
*/

Ich hab das vorausgesehen, das das so kommt. Du bist leider der erste, dem das passiert.

Hallo,
da fehlt noch eine Null
müsste 100000 sein

Gruß Heinz

:grinning_face_with_smiling_eyes: Bei sovielen Nullen kann man schon mal den Überblick verlieren.

Hallo
Ich habe den Parser Tab aktualisiert , auf 64 Bit Werte erweitert und die Auswertung der Teiler mit eingebaut. Ich hab das mit mehreren mir vorliegenden Hex Dumps unterschiedlicher Zählertypen getestet.

Denaktuellen Tab habe ich per PM an den TO geschickt. Wenn er das erfolgreich getestet hat werde ich das hier nochmal einstellen.

Grüße Heinz

Schönen guten Abend miteinander,
vielen Dank für den geänderten Code, welcher hundertprozentig funktioniert!
Nochmals meinen allergrößten Respekt!!
Und vielen Dank für die Änderung die ich nicht alleine geschafft hätte.

Viele Grüße
Neuling 68

Hallo,
da ich den Sketch jetzt mal wieder angepackt habe, habe ich noch gleich etwas geändert was ich schon länger mal machen wollte.
Die Anzeige der Messwerte für den Verbrauch wird jetzt mit zwei Nachkommastellen ausgegeben.

ich stelle jetzt hier nochmal den gesamten Sketch rein. Sicher wird es so sein das mit einer neuen Zählergeneration eine neu Variante innerhalb der SML Spezifikation entsteht die hier so nicht berücksichtigt ist. Dann muss ich mich halt noch mal reindenken. So kann ich mich erinnern das vor einiger Zeit ein User hier einen Zähler hatte der Kanal 1 anstelle des sonst üblichen Kanal 0 verwendet hat. Natürlich könnte man jetzt im parser genau diese Byte z.B. überspringen, damit würde es dann immer klappen.
Nun ist das Thema Stromzähler für uns Bastler sicher noch lange nicht zu Ende, letztlich wird es jedoch mit der allgemeinen Einführung von Smardmetern keinen Sinn mehr machen.

Jetzt hoffe ich noch das alles in einen Post passt :wink:

Also hier der code

HTML Seite
<!doctype html>
<html lang="de">
<!-- 
Einbindung der externen Chart Lib https://www.chartjs.org
Chart.js v2.9.4
 * https://www.chartjs.org
 * (c) 2020 Chart.js Contributors
 * Released under the MIT License
 */
-->

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stromzähler</title>

 <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js"></script>

	
<script>
document.addEventListener('DOMContentLoaded',()=>{
document.querySelector('#btn').addEventListener('click',sendData);

var P=[];

var xachse=[];
// objekt instanz erstellen
var config1=newConfig(); 
	
// Vorenstellung
config1.data.datasets[0].label="Leistung [w]";

//config1.data.datasets[0].borderColor="red";
//config1.data.datasets[1].borderColor="blue";

var ctx1 = document.getElementById("chart-1").getContext('2d');
window.myChart1=new Chart(ctx1,config1); 

	
function newConfig(){
	var config={
		type: 'line',
		data: {
			labels: xachse,
			datasets: [{
				label: 'Reihe1',
				borderColor: "red", 
				backgroundColor:"black", 
				borderWidth: 1,
				pointRadius:2,
				data:P,
				fill:false,
				}
				]				
			},
		options:{
			
			title: {
					display: false,
					text: 'Chart'
				},
			animation: {
            duration: 0 // general animation time
			},
					
			scales:{
				xAxes:[{
					display: true,
					scaleLabel: {
						display: true,
						labelString: 'Vergangene Minuten'
						}
					}],
				yAxes: [{
					ticks:{
					//precision:0.05
					
					},
					
					display: true,
					scaleLabel: {
						display: false,
						labelString: ''
						}		
					}]
			}
		} // options ende				
	};
	
	return config;
	}

async function loadData(){
	let resp = await fetch('/daten')
	let obj = await resp.json();
	document.getElementById("kennung").innerHTML=obj.kn;
	document.getElementById("kw180").innerHTML=obj.C180+" KWh";
	document.getElementById("kw181").innerHTML=obj.C181+" KWh";
	document.getElementById("kw280").innerHTML=obj.C280+" KWh";
	document.getElementById("P").innerHTML=obj.P+" W";	
	document.getElementById("logtime").value=obj.logtime;

	config1.data.labels = obj.time;
	config1.data.datasets[0].data = obj.PChart;
	window.myChart1.update();		
	
}

async function loadcycle(){
	let resp = await fetch('/cycle')
	let obj = await resp.json();
	document.getElementById("kennung").innerHTML = obj.kn;
	document.getElementById("kw180").innerHTML = obj.C180+" KWh";
	document.getElementById("kw181").innerHTML = obj.C181+" KWh";
	document.getElementById("kw280").innerHTML = obj.C280+" KWh";
	document.getElementById("P").innerHTML = obj.P+" W";	

	config1.data.labels = obj.time;
	config1.data.datasets[0].data = obj.PChart;
	window.myChart1.update();		
	
}



async function sendData(){
	let data = document.querySelector('form');

	let resp=await fetch('btnsend',{
		method:'post',
		body:new FormData(data)
	});
	let obj=await resp.json();
	document.getElementById("logtime").value = obj.logtime;

}




loadData();
setInterval(loadcycle, 10000);

});

</script>	
	
<style>
body {
	background-color: #bdb;
    display: flex;
    flex-flow: column; 
	font-size: 1.3em;
} 

.flex-container {
	display: flex;
	flex-direction:column;
	gap:1em;
}

.flex-item {
	border: 2px solid;
	margin: .5em;
	padding: .5em;
	min-width:20em;		
}	

.flex-item:nth-of-type(1) {
	background: #fdfcf3;	
}

.flex-item:nth-of-type(2) {
	background: #ffebeb;	
}

	/* große Viewports */
@media all and (min-width:45em) {
.flex-container {
    flex-direction:row;
}	
.flex-item:nth-of-type(2) {
	min-width:30em
	}			
}

div{
	width:auto
}

button{
	width:5em;
	height:1.5em;
	font-size:1em;
}  
input{
	width:4.5em;
	height:1em;
	font-size:1em;
}	

</style>	
	
  </head>

  <body>
	<h1>Stromzähler</h1>
	
	<main class="flex-container">
	
		<section class="flex-item">
		<h3>Zählerstand</h3>
		Zähler :<span id="kennung"></span>
		
		<table>
			<tr>
			<td>Bezug gesamt (1.8.0) </td><td><span id="kw180"></span></td>
			</tr>
			<tr>
			<td>Bezug T1 (1.8.1)  </td><td><span id="kw181"></span></td>
			</tr>
			<tr>
			<td>Einspeisung (2.8.0)  </td><td><span id="kw280"></span></td>
			</tr>
			<tr>
			<td>akt. Leistung   </td><td><span id="P"></span></td>
			</tr>		
		</table>
		
		<form>
		<p>
		Log Zyklus
		<input type="text" id="logtime" name="logtime">min	
		<button type="button" id="btn">send</button>
		</p>
		</form>
				
		</section>
		
		<section class="flex-item">
		<h3> Leistung Verlauf</h3>
		<div>
		<canvas id="chart-1"> </canvas> 
		</div>

		
		</section>		
	</main>
  </body>

</html>
Maintab
/*
   ESP8266 Wemos D1
   Okt 2025 V2.0
*/

#include <SoftwareSerial.h>
#include <LittleFS.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>

const char* ssid = "xxxxx";
const char* password = "yyyyy";


IPAddress staticIP(192, 168, 178, 14);  // eigene IP
IPAddress gateway(192, 168, 178, 1);  // Fritzbox Heimnetz
IPAddress subnet(255, 255, 255, 0);
// Server Instanz erstellen
ESP8266WebServer server(80);

//#define DEBUG 1
#ifdef DEBUG
#define printdbg(x) Serial.print(x)
#define printdbgn(x) Serial.println(x)
#else
#define printdbg(x)
#define printdbgn(x)
#endif

#define RX 13 // D7
#define TX 15 // D8
SoftwareSerial mySerial;
byte sml[420];

const byte sml_Kopf[] = {0x1b, 0x1b, 0x1b, 0x1b, 0x01, 0x01, 0x01, 0x01}; // Startsequenz
// OBIS Code 1.8.0
const byte code180[] = {0x01, 0x00, 0x01, 0x08, 0x00, 0xff};
// OBIS CODE 1.8.1
const byte code181[] = { 0x01, 0x00, 0x01, 0x08, 0x01, 0xff};
// OBIS CODE 1.8.2
const byte code182[] = { 0x01, 0x00, 0x01, 0x08, 0x02, 0xff};

// OBIS CODE 2.8.0
const byte code280[] = { 0x01, 0x00, 0x02, 0x08, 0x00, 0xff};
// OBIS CODE 2.8.1
const byte code281[] = { 0x01, 0x00, 0x02, 0x08, 0x01, 0xff};
// OBIS CODE 2.8.2
const byte code282[] = { 0x01, 0x00, 0x02, 0x08, 0x02, 0xff};
// OBIS Code 10.7.0
const byte code107[] = { 0x01, 0x00, 0x10, 0x07, 0x00, 0xff};

// Hier eventuell zusätzliche OBIS Codes eintragen

struct d {
  byte sml_octst[50];
  uint32_t sml_uint32;
  int32_t sml_int32;
} data;

float kw180, kw181, kw280;
long p;
long p_mittel, p_graf;
int n_mittel, n_graf;

long int ID;
char kennung[30];
uint16_t pos = 0;
uint16_t anzahl = 400;
uint32_t altzeit;
bool aktiv, wd;

// Ringpuffer
const byte maxbuffer = 100;
int logtime = 5;
byte wz = 0; // schreibzeiger
int b_wert[maxbuffer];
uint32_t b_zeit[maxbuffer];

void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);
  mySerial.begin(9600, SWSERIAL_8N1, RX, TX, false, 256);


  conectWIFI();
  LittleFS.begin(); // Filesystem starten

  listDir("/");       // Info anzeigen
  server_setup();

  ArduinoOTA.onStart([]() {
    // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
    LittleFS.end();
  });
  ArduinoOTA.begin();

}

void loop() {
  // put your main code here, to run repeatedly:

  ArduinoOTA.handle();
  server.handleClient();// hier passiert sonst nichts
  MDNS.update();

  lesen();
  wd = watchdog(aktiv);

  if (logtime > 0)writebuffer(logtime * 60000UL);

  digitalWrite(LED_BUILTIN, aktiv);

}//----------- Loop ende ------------



bool watchdog(bool trigger) {
  static uint32_t altzeit = 0;
  bool merker = false;

  if (trigger != merker) {
    altzeit = millis();
    merker = trigger;
  }

  if (millis() - altzeit > 3500) {
    return false;
  }
  else return true;
}

void lesen() {
  while (mySerial.available()) {
    byte b = mySerial.read();
    //Serial.print(b,HEX);
    altzeit = millis();
    if (pos < sizeof(sml)) {    // abbruck bei max
      sml[pos] = b;
      pos++;
      aktiv = true;
    }
    yield();
  }
  // Pause erkennen
  if ((millis() - altzeit > 50) && aktiv == true) {
    printdbgn(F("Pause erkannt "));
    //Serial.println("pause erkannt");
    altzeit = 0;
    aktiv = false;
    anzahl = pos; // anzahl der gültigen byte 
    anzeige();    // anzeige und auswerten
    pMittel();
    reset();      // array zurück setzen und Zeiger auf 0 
  }
}
void pMittel() {

  if (n_mittel < 100) {
    p_mittel += p; // Mittelwert für numerische Anzeige
    n_mittel++;
  }
  else {
    p_mittel = p;
    n_mittel = 1;
  }
  p_graf += p;  // Mittelwert für Chart Anzeige
  n_graf ++;
}


void anzeige() {

  Serial.print(anzahl); Serial.println(F(" byte empfangen"));
  printdump(anzahl);

  // die ersten 3 byte sind ascii Zeichen
  findcode(sml_Kopf, sizeof(sml_Kopf));
  for (byte i = 0; i < 3; i++) {
    kennung[i] = data.sml_octst[i + 2];
    kennung[i + 1] = 0;
  }
  // der rest ergibt eine Zahl
  for (byte i = 0; i < 10; i++) {
    ID = ID << 8;
    ID += data.sml_octst[i];
  }

  // hier ist eventuell zu prüfen ob der Zähler die Daten als int oder uint
  // ausgibt um den vollen Zahlenumfang zu haben
  // normal solle int reichen

  findcode(code180, sizeof(code180));
  if (data.sml_uint32 > 0 ) {
    kw180 = data.sml_uint32 / 1000.0;
    findcode(code181, sizeof(code181)); kw181 = data.sml_uint32 / 1000.0;
    findcode(code280, sizeof(code280)); kw280 = data.sml_uint32 / 1000.0;
    findcode(code107, sizeof(code107));
    if (abs(data.sml_int32) < 16000) p = data.sml_int32;
  }


#ifdef DEBUG
  Serial.print(F("Zähler ID ")); Serial.print(kennung);
  Serial.print(" "); Serial.print(ID); Serial.println();
  Serial.print(F("Zählwerk    A+ ")); Serial.print(kw180); Serial.println("KWh");
  Serial.print(F("Zählwerk T1 A+ ")); Serial.print(kw181); Serial.println("KWh");
  Serial.print(F("Zählwerk    A- ")); Serial.print(kw280); Serial.println("KWh");
  Serial.print(F("Wirkleistung   ")); Serial.print(p); Serial.println("W");
#endif

}


void reset() {
  // buffer löschen
  for (uint16_t i = 0; i < sizeof(sml); i++) {
    sml[i] = 0;
    pos = 0;
  }
}

void send_daten() {
  /* alle daten senden beim laden der Seite

  */
  char s[50];
  int pm = 0;
  if (n_mittel > 0) pm = p_mittel / n_mittel;

  if (wd == true)sprintf(s, "ID %s %ld", kennung, ID);
  else strcpy(s, "Keine Daten");

  String buff;
  buff.reserve(maxbuffer * 14 + 100);
  buff = "{";

  buff += "\"kn\":[\"";
  buff += s;
  buff += "\"],";

  buff += "\"C180\":[\"";
  buff += kw180;
  buff += "\"],";

  buff += "\"C181\":[\"";
  buff += kw181;
  buff += "\"],";

  buff += "\"C280\":[\"";
  buff += kw280;
  buff += "\"],";

  buff += "\"P\":[\"";
  buff += pm;
  buff += "\"],";

  buff += "\"logtime\":[\"";
  buff += logtime;
  buff += "\"],";

  buff += "\"PChart\"";
  buff += ":";
  buff += intbufferToString(b_wert);

  buff += ",\"time\"";
  buff += ":";
  buff += timebufferToString(b_zeit);
  buff += "}";

  //Serial.println(buff);
  server.send(200, "application/json", buff);

}

void send_cycle() {
  /*alle daten senden jedoch ohne das Input feld
     damt dieses nicht zyklisch überschrieben wird
     Mittelwert aktualisieren und Rücksetzen
  */
  char s[50];
  int pm = 0;
  if (n_mittel > 0) {
    pm = p_mittel / n_mittel;
    p_mittel = 0; // reset Mittelwert
    n_mittel = 0;
  }

  if (wd == true)sprintf(s, "ID %s %ld", kennung, ID);
  else strcpy(s, "Keine Daten");

  String buff;
  buff.reserve(maxbuffer * 14 + 100);
  buff = "{";

  buff += "\"kn\":[\"";
  buff += s;
  buff += "\"],";

  buff += "\"C180\":[\"";
  buff += kw180;
  buff += "\"],";

  buff += "\"C181\":[\"";
  buff += kw181;
  buff += "\"],";

  buff += "\"C280\":[\"";
  buff += kw280;
  buff += "\"],";

  buff += "\"P\":[\"";
  buff += pm;
  buff += "\"],";

  buff += "\"PChart\"";
  buff += ":";
  buff += intbufferToString(b_wert);

  buff += ",\"time\"";
  buff += ":";
  buff += timebufferToString(b_zeit);
  buff += "}";

  //Serial.println(buff);
  server.send(200, "application/json", buff);


}

void get_daten() {
  /*  Eingabe von Form lesen
       und antworten
  */

  String buff;
  buff.reserve( 50);

  buff = server.arg(0); logtime = buff.toInt();

  if (logtime < 1 ) logtime = 1;
  if (logtime > 60) logtime = 60;
  //Serial.print ("Daten angekommen"); Serial.println(logtime);

  buff = "{";

  buff += "\"logtime\":[\"";
  buff += logtime;
  buff += "\"]";

  buff += "}";

  //Serial.println(buff);
  server.send(200, "application/json", buff);


}

//------------- Ringpuffer beschreiben
void writebuffer(uint32_t t) {

  uint32_t static altzeit = 0;

  if (millis() - altzeit >= t) {
    altzeit = millis();

    if (n_graf > 0) {
      b_wert[wz] = p_graf / n_graf;
      b_zeit[wz] = millis();
    }
    p_graf = 0; // reset Mittelwert für Chart Anzeige
    n_graf = 0;

    wz++; // schreibzeiger erhöhen
    if (wz > maxbuffer - 1) wz = 0;
    //  for (byte i = 0; i < maxbuffer; i++) {
    //   Serial.print(b_temp[i]); Serial.print("   ");
    // }
    //  Serial.println(wz);

  }
}

String intbufferToString(int * arr) {
  String buff;
  buff.reserve(maxbuffer * 4 + 5); byte lz;
  lz = wz;
  buff = "[";
  for (byte i = 0; i < maxbuffer; i++) {
    if (b_zeit[lz] > 0) {  // Eintrag enthalten
      buff = buff += arr[lz];
      buff = buff += ",";
    }
    lz++;
    if (lz >= maxbuffer)lz = 0;
  }
  uint16_t l = buff.length();
  if (l >= 2) buff.remove(l - 1);
  buff = buff += "]";
  //Serial.print(buff);Serial.println(l);
  return buff;

}

String timebufferToString(uint32_t* arr) {
  String buff ;
  buff.reserve(maxbuffer * 4 + 5);
  uint32_t actms = millis();
  byte lz;
  lz = wz;
  buff = "[";
  for (byte i = 0; i < maxbuffer; i++) {
    if (b_zeit[lz] > 0) {  // Eintrag enthalten
      buff = buff += (actms - arr[lz]) / 60000UL; // Differenz in Minuten
      buff = buff += ",";
    }
    lz++;
    if (lz >= maxbuffer)lz = 0;
  }
  uint16_t l = buff.length();
  if (l >= 2) buff.remove(l - 1);
  buff = buff += "]";
  //Serial.print(buff);Serial.println(l);
  return buff;
}
Parser tab
void findcode(const byte *code, uint16_t anz) {
  bool found = false;
  bool sta = false;
  bool kopfdaten = false;
  unsigned int index = 0;
  unsigned int startindex = 0;
  unsigned int i, ii;
  unsigned int n = 0;

  int dazu = 0;
  int64_t erg = 0;
  uint64_t uerg = 0;
  byte maxelm = 0;
  byte elm = 0;
  byte maxloop = 0;

  data.sml_uint32 = 0;
  data.sml_int32 = 0;

  printdbgn();

  // Start abfragen
  if (sml[0] == 0x1B && sml[1] == 0x1B &&
      sml[2] == 0x1B && sml[3] == 0x1B) sta = true;
  if (!sta) return; // abbrechen Anfang falsch

  for ( i = 0; i <= sizeof(sml) - anz; i++) {
    if (sml[i] == code[0]) { // Anfang gefunden
      found = true;
      for (ii = 1; ii < anz; ii++) { // restliche prüfen
        if (sml[i + ii] != code[ii]) found = false;
      }
      index = i + ii;
      startindex = i;

      if (found) { // OBIS Code stimmt

        printdbg(F("code gefunden: "));
        for (byte i = 0; i < anz; i++ ) {
          printhex(code[i]);
        }
        printdbgn();
        break; // for schleife verlassen
      }
    }
  }
  // hier gehts weiter
  // Abfrage geht es um die Kopfdaten
  int s = 0;
  for (i = 0; i < anz; i++) {
    s += code[i];
  }
  //printdbg(F("summe")); printdbgn(s);
  if (s == 112) {
    kopfdaten = true;
    //printhex(sml[index]);
    maxelm = 0;// es gab noch kein Listen-Element
    elm = 1;
  }
  else {
    kopfdaten = false;
    //printhex(sml[index]);
    maxelm = sml[startindex - 2] - 0x70; // Anzahl der Elemnte aus dem zugeh. Listen-Elemen
    elm = 2; // das nächste Element ist 2
  }

  printdbg(F("start mit elm  ")); printdbg( elm);
  printdbg(F("  max Elemnt ")); printdbgn(maxelm);

  do {
    maxloop++;
    if (maxloop > 30) { // Notausstieg :-)
      printdbgn(F("kein Elemt erkannt"));
      return;
    }
    // --------- 0x01 auswerten
    switch (sml[index]) {
      case 0x01://leres Element
        printhex(sml[index]);
        printdbg(F("leres erkannt "));
        printdbgn(elm);
        index++;
        elm++;
        break;

      // ---------- Liste auswerten
      case 0x70 ... 0x7f: // Liste
        dazu = sml[index] - 0x70; // neue Elemente aus Liste
        maxelm = maxelm + dazu;
        printhex(sml[index]);

        printdbg(F("element ")); printdbg(elm);
        printdbg(F(" Liste erkannt plus ")); printdbg(dazu);
        printdbg(F(" jetzt max ")); printdbgn(maxelm);

        index++;
        elm++;
        break;
      //----------- Integer auswerten
      case 0x52 ... 0x59:// integer
        n = sml[index] - 0x50;
        for ( uint16_t i =  0; i < n; i++) {
          printhex(sml[index + i]);
        }
        printdbg(F(" element ")); printdbgn(elm);
        if (elm == maxelm - 1) {
          erg = 0;
          for (uint16_t i =  1; i < n; i++ ) {
            erg = erg << 8;
            erg += sml[index + i];
          }
          // Vorzeichen für int8,int16,int32 berücksichtigen
          if ( n == 2 && erg > 127) erg = erg - 256;
          else if (n == 3 && erg > 32768 ) erg = erg - 65536;
          else if (n == 5 && erg > 2147483648 ) erg = erg - 4294967296;
         
          // scaler berücksichtigen
          if (sml[index - 1] == 0x02) erg = erg * 100;
          else if (sml[index - 1] == 0x01) erg = erg * 10;
          else if (sml[index - 1] == 0xff) erg = erg / 10;
          else if (sml[index - 1] == 0xfe) erg = erg / 100;   //*10^-2
          else if (sml[index - 1] == 0xfd) erg = erg / 1000;
          else if (sml[index - 1] == 0xfc) erg = erg / 10000;
          else if (sml[index - 1] == 0xfb) erg = erg / 100000;//*10^-5
          data.sml_int32 = erg;
          printdbg(F("SML_VALUE INT ")); printdbgn(erg);
        }
        elm++;
        index += n;
        break;
      // ------------unsigned Integer auswrten
      case 0x62 ... 0x69:// unsigned integer
        n = sml[index] - 0x60;
        for (uint16_t i = 0; i < n; i++) {
          printhex(sml[index + i]);
        }
        printdbg(F(" element ")); printdbgn(elm);
        if (elm == maxelm - 1) { // hier sollte SML_VALUE sein
          uerg = 0;
          for (uint16_t i = 1; i < n; i++ ) {
            uerg = uerg << 8;
            uerg += sml[index + i];
          }

          if (sml[index - 1] == 0x02) uerg = uerg * 100;
          else if (sml[index - 1] == 0x01) uerg = uerg * 10;
          else if (sml[index - 1] == 0xff) uerg = uerg / 10;
          else if (sml[index - 1] == 0xfe) uerg = uerg / 100;
          else if (sml[index - 1] == 0xfd) uerg = uerg / 1000;
          else if (sml[index - 1] == 0xfc) uerg = uerg / 10000;
          else if (sml[index - 1] == 0xfb) uerg = uerg / 100000;
          data.sml_uint32 = uerg;
          printdbg(F("SML_VALUE UINT ")); printdbgn(uerg);
        }

        elm++;
        index += n;
        break;
      //----------- octal String auswerten
      case 0x02 ... 0x0f: // octal String
        n = sml[index];
        for (uint16_t i = 0; i < n; i++) {
          printhex(sml[index + i]);
        }

        if (n > sizeof(data.sml_octst)) n = sizeof(data.sml_octst);
        if ( kopfdaten == true && elm == maxelm - 3) { // Kopfdaten ID abfragen  // das ist noch Q&D

          printdbg(F("element ")); printdbg(elm);
          printdbgn(F(" SML_ServerID "));

          for (uint16_t i = 1; i < n; i++) {
            //printhex(sml[index + i]);
            data.sml_octst[i - 1] = sml[index + i];

          }

          //return;
        }
        else {

          for (uint16_t i = 1; i < n; i++) {
            //printhex(sml[index + i]);
            //Serial.println(i);
            data.sml_octst[i - 1] = sml[index + i];
            data.sml_octst[i] = 0;
          }
          printdbg(F("element ")); printdbgn(elm);
        }
        elm++;
        index += n;
        break;

      //----------- octal String länger 15 Zeichen
      case 0x80 ...0x8f:// octalstring länger 14 Zeichen
        n = (sml[index] - 0x80) * 16;
        n = n + sml[index + 1];
        for (uint16_t i = 0; i < n; i++) {
          printhex(sml[index]);
        }
        printdbg(F("element ")); printdbgn(elm);
        elm++;
        index += n;
        break;
    }
  } while (elm <= maxelm);

  return;
}

void printdump(uint16_t n) {

  for (unsigned int i = 0; i < n; i++) {
    if (i % 20 == 0) printdbgn();
    printhex(sml[i]);
  }
  printdbgn();
}

void printhex(byte wert) {

  char hexstring[10];
  if (wert < 16)sprintf(hexstring, "0%x ", wert);
  else sprintf(hexstring, "%x ", wert);
  printdbg(hexstring);
}
Webserver Tab
/*Web Server tab

*/

void server_setup() {

  // --Server Handle einrichten--;
  server.serveStatic("/", LittleFS, "/flexbox.html");
  server.on("/daten", send_daten);
  server.on("/btnsend", get_daten);
  server.on("/cycle", send_cycle);

  server.onNotFound(handleNotFound);   // Fehler bearbeiten
  server.begin();
  Serial.println("HTTP server started");
}


void conectWIFI() {
  byte maxzeit = 0;
  WiFi.disconnect();
  //WiFi.persistent(true);   // daten in EEprom
  WiFi.mode(WIFI_STA);
  Serial.printf("Connecting to %s ", ssid);

  WiFi.config(staticIP, gateway, subnet);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    maxzeit++;
    if (maxzeit > 60)return;
  }


  WiFi.setAutoReconnect(true);
  Serial.print(F(" connected...locale IP:"));
  Serial.println(WiFi.localIP());
  if (MDNS.begin("Stromzähler")) {
    Serial.println("MDNS responder started");
  }

}

// ------------ Fehler Seite
void handleNotFound() {
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
}



// --------------- Fillesystem anzeigen ---------------------
void listDir(const char *dirname) {
  FSInfo fs_info;
  LittleFS.info(fs_info);
  float fileTotalKB = fs_info.totalBytes / 1024.0;
  float fileUsedKB = fs_info.usedBytes / 1024.0;
  Serial.print("\nFilesystem Total KB "); Serial.print(fileTotalKB);
  Serial.print(" benutzt KB "); Serial.println(fileUsedKB);


  Serial.printf("Listing directory: %s\n", dirname);
  Dir root = LittleFS.openDir(dirname);

  while (root.next()) {
    File file = root.openFile("r");
    Serial.print(root.fileName());
    Serial.print('\t');
    Serial.print(file.size());
    Serial.println(" byte");
    file.close();

  }
}

Da muss ich Dich enttäuschen.
Gerade deswegen wird es ganz viel Sinn machen.

A) Die SM-Gateways dürfen nur aggregierte Daten im 15Minuten-Intervall verarbeiten
B) Mit der "Cloudifizierung" steigt das Ausfallrisiko.

Und nachdem ich grad durch habe, dass zwischen Messstellenbetreiber und -abrechner nicht mal die Schnittstellen für dynamische Tarifierung funktioniert (und auch noch so bleben wird, mit dem Segen der BNetza), wird es weiterhin für die Leute die es sinnvoll einsetzen einen Markt geben.

Darf ich das noch ergänzen

C) die derzeitige technische Lösung mittels LTE führt dazu das aus einem nicht unerheblichen Anteil deutscher Keller keine Verbindung zu Stande kommt.

Aber das sind doch alles nur kleine Anfangsprobleme , die lassen sich bestimmt in den nächsten 50 Jahren lösen. Die DB schafft das doch auch.

Gruß Heinz

:rofl:

PS: Die Stadtwerke haben mittlerweile ein LORAWAN-Gateway am Heizhausschornstein...