Hallo,
ich habe schon das eine oder andere Mal kleine Projekte gebaut bei dem ich
serielle Daten aufbereiten und an einen MQTT Server geschickt habe. Bisher
war ich da recht zufrieden mit den ESP8266 da diese schon klein sind und
alles wichtige an Bord haben
Verwende Softwareserial an D7 und D8.
Ich habe ein DT24 DC Energy Meter von Atorch und möchte die an der Seriellen
anliegenden Daten abgreifen und ebenfalls verarbeiten. Hier stosse ich auf
meine Grenzen. Bin da mit Sicherheit erst auf dem Level Beginner 
Es werden jede Sekunde Daten geschickt ... nach meiner Recherche soll das
im binary Format sein. Eine Protokollbeschreibung für eine Elektronische
Last des Herstellers, gibt es hier
-> atorch-console/protocol-design.md at master · NiceLabs/atorch-console · GitHub
Ich habe aktuell so gar keinen Ansatz wie ich da am besten vorgehe und wäre
für eine Tip dankbar. Denke aber das ich die fixe länge des Datenpakets in eine
Struktur packen und in Zahlenwerte umwandeln muss.
Danke schon mal an der Stelle für das lesen und eventuell gibt es ja einen
Ansatz.
Ja, Zuerst musst Du die 2 Startbytes erkennen, damit Du weißt, das das Datagramm beginnt. Dann das Datagramm-Byte auswerten, damit kannst Du die Länge des Payload ermitteln.
Dann alles bis zur Länge erst mal in ein Byte-Array schreiben und dann auswerten.
Gruß Tommy
hehehe so in etwa habe ich mir das auch gedacht, habe aber das Problem die Theorie in funktionierende Code umzusetzen. Gerne lese ich mich da auch weiter ein wenn es einen sachdienlichen Link gibt oder Codebeispiele.
Gruß Stefan
Finde Magic Header
Magic Header gefunden: FF 55 (Byte 1, Byte 2)
Wenn gefunden, finde MessageType an Position (Byte 3) - Inhalt ist ein byte
Wenn MessageType-Inhalt (war Byte 3) == 01, sind die nächsten 32 byte der report
Der Inhalt ist in AC Meter Report für jedes Byte dargestellt.
Wenn Byte 3 nicht 01 schmeiss den Rest weg und warte auf Magic Header.
Wo klemmt es?
zum einlesen in das Thema "Serielle Schnittstelle lesen" rate ich dir zu den "Serial Input Basics"
https://forum.arduino.cc/t/serial-input-basics-updated/382007
edit
hab mal rumgespielt *)
/*
Atorch
https://github.com/NiceLabs/atorch-console/blob/master/docs/protocol-design.md
Serial Receive Example for two byte magic header, Message Type, Variable Payload, Checksum
Valid telegram
FF55010100090400000E0000040000000000006401F40085002F00000A093C0000000039
FF55010200011A00003C0004D40000002000006400000000002600590D363C00000000F0
FF55010200011A0000000004D40000002000006400000000002A00590E343C000000003F
FF55010200011A00003C0004D40000002000006400000000002600590D363C00000000F0
FF5501030001F3000000000638000003110007000A000000122E333C000000000000004E
by noiasca
2022-04-01
*/
class Atorch {
protected:
static const byte numChars = 36; // receiver buffer size
byte receivedChars[numChars]; // an array to store the received data
byte ndx = 0; // length of received message
boolean newData = false; // flag to indicate when new data is complete
Stream &stream; // a reference to the serial interface
void (*cbOnNewData)(); // gets called after we received a full message
void delBuffer()
{
memcpy(receivedChars, "", sizeof(receivedChars));
ndx = 0;
}
void debugDataHex()
{
for (size_t i = 0; i < ndx; i++)
{
if (receivedChars[i] < 0x10) Serial.print('0');
Serial.print(receivedChars[i], HEX);
Serial.print(' ');
}
Serial.println();
}
void parseData() // parse data and store to internal variables
{
deviceType = receivedChars[3];
if (deviceType == 1 || deviceType == 2 || deviceType == 3)
{
voltage = ((uint32_t)receivedChars[4] << 16 ) + (receivedChars[5] << 8) + receivedChars[6];
amp = ((uint32_t)receivedChars[7] << 16 ) + (receivedChars[8] << 8) + receivedChars[9];
wh = ((uint32_t)receivedChars[0x0D] << 24 ) + ((uint32_t)receivedChars[0x0E] << 16 ) + (receivedChars[0x0F] << 8) + receivedChars[0x10];
}
if (deviceType == 1)
{
price = ((uint32_t)receivedChars[0x11] << 16 ) + (receivedChars[0x12] << 8) + receivedChars[0x13];
frequency = (receivedChars[0x14] << 8) + receivedChars[0x15];
}
if (deviceType == 1 || deviceType == 2)
{
watt = ((uint32_t)receivedChars[0x0A] << 16 ) + (receivedChars[0x0B] << 8) + receivedChars[0x0c];
powerfactor = (receivedChars[0x16] << 8) + receivedChars[0x17];
temperature = (receivedChars[0x18] << 8) + receivedChars[0x19];
hour = (receivedChars[0x1A] << 8) + receivedChars[0x1B];
minute = receivedChars[0x1C];
second = receivedChars[0x1D];
backlight = receivedChars[0x1E];
}
if (deviceType == 3)
{
ah = ((uint32_t)receivedChars[0x0A] << 16 ) + (receivedChars[0x0B] << 8) + receivedChars[0x0c];
usbdminus = (receivedChars[0x11] << 8) + receivedChars[0x12];
usbdplus = (receivedChars[0x13] << 8) + receivedChars[0x14];
//temperature = ((uint32_t)receivedChars[0x15] << 16 ) + (receivedChars[0x16] << 8) + receivedChars[0x17];
temperature = (receivedChars[0x15] << 8) + receivedChars[0x16];
hour = (receivedChars[0x17] << 8) + receivedChars[0x18];
minute = receivedChars[0x19];
second = receivedChars[0x1A];
backlight = receivedChars[0x1B];
}
}
bool validateChecksum() // validate if checksum of telegram is correct
{
return true; // todo: implement validate
}
void parseLine() // process a telegram
{
//Serial.print(F("I64: This is just in:")); Serial.println(receivedChars); // output all
Serial.print(F("I65: This is just in:")); debugDataHex(); // output all
if (validateChecksum())
{
parseData();
if (cbOnNewData) cbOnNewData();
}
}
public:
byte messageType = 0;
// payload data
byte deviceType = 0;
uint32_t voltage = 0;
uint32_t amp = 0;
uint32_t ah = 0; // deviceType 3
uint32_t watt = 0;
uint32_t wh = 0;
uint32_t price = 0;
uint16_t frequency = 0; // deviceType 1
uint16_t powerfactor = 0; // deviceType 1
uint16_t usbdminus = 0; // deviceType 3
uint16_t usbdplus = 0; // deviceType 3
int16_t temperature = 0;
uint16_t hour = 0;
uint8_t minute = 0;
uint8_t second = 0;
uint8_t backlight = 0;
Atorch (Stream &stream) : stream(stream)
{
delBuffer();
}
void setOnNewData(void (*cbOnNewData)()) // set a callback function. Gets called when new data was received
{
(*this).cbOnNewData = cbOnNewData;
}
void update() // run this member function in loop()
{
constexpr byte startMarker = 0xFF; // start flag
constexpr byte secondMarker = 0x55;
if (stream.available() > 0) {
char rc = stream.read();
// restart on FF 55
if (ndx > 0 && receivedChars[ndx - 1] == startMarker && rc == secondMarker)
{
delBuffer();
receivedChars[0] = startMarker;
receivedChars[1] = rc;
ndx = 2;
}
else
{
receivedChars[ndx] = rc;
if (ndx == 2) messageType = rc;
ndx++;
}
// parse data on reached packet length
if (messageType == 1 && ndx == (2 + 1 + 32 + 1)) parseLine();
// sanity check
if (ndx >= numChars) delBuffer();
}
}
};
// create the sensor object and hand over the Serial interface to use:
HardwareSerial &mySerial = Serial;
Atorch device(mySerial); // use Hardware Serial (for example for testing with the PC)
//#include <SoftwareSerial.h> // on an Uno you can use SoftSerial
//SoftwareSerial mySerial(2, 3); // RX, TX
//Atorch device(mySerial); // on an UNO you will need SoftSerial to connect to the device
// On a Mega you can simply use
// a Reference to an existing HW Serial:
//HardwareSerial &mySerial = Serial1;
//Atorch device(mySerial);
void output() // simple output of data
{
Serial.print(F("deviceType= ")); Serial.print(device.deviceType); Serial.println();
if (device.deviceType == 1 || device.deviceType == 2)
{
Serial.print(F("voltage= ")); Serial.print(device.voltage / 10.0); Serial.println('V');
Serial.print(F("amp= ")); Serial.print(device.amp / 1000.0); Serial.println('A');
Serial.print(F("watt= ")); Serial.print(device.watt / 10.0); Serial.println('W');
Serial.print(F("wh= ")); Serial.print(device.wh / 100.0); Serial.println(F("Wh"));
Serial.print(F("price= ")); Serial.print(device.price / 100.0); Serial.println(F("/KWh"));
}
if (device.deviceType == 1)
{
Serial.print(F("powerfactor=")); Serial.print(device.powerfactor / 1000.0); Serial.println();
Serial.print(F("frequency= ")); Serial.print(device.frequency / 10.0); Serial.println(F("Hz"));
}
if (device.deviceType == 3)
{
Serial.print(F("voltage= ")); Serial.print(device.voltage / 100.0); Serial.println('V');
Serial.print(F("amp= ")); Serial.print(device.amp / 100.0); Serial.println('A');
Serial.print(F("ah= ")); Serial.print(device.ah / 1000.0); Serial.println(F("Ah"));
Serial.print(F("wh= ")); Serial.print(device.wh / 100.0); Serial.println(F("Wh"));
Serial.print(F("usbdminus= ")); Serial.print(device.usbdminus / 100.0); Serial.println();
Serial.print(F("usbdplus= ")); Serial.print(device.usbdplus / 100.0); Serial.println();
}
Serial.print(F("temperature=")); Serial.print(device.temperature); Serial.println('C');
Serial.print(F("durance= ")); Serial.print(device.hour); Serial.print(':'); Serial.print(device.minute); Serial.print(':'); Serial.println(device.second);
Serial.print(F("backlight= ")); Serial.print(device.backlight); Serial.println();
}
void timerOutput() // a timer to output data to serial
{
static uint32_t previousMillis = 0; // time management
const uint16_t interval = 3000; // interval to display data
if (millis() - previousMillis >= interval)
{
previousMillis = millis();
output();
}
}
void setup() {
Serial.begin(9600);
mySerial.begin(9600);
//device.setOnNewData(output); // register a callback function which gets called when a new telegram was received
}
void loop() {
device.update(); // you must call the .update() method in your loop()
timerOutput();
}
ergibt für
FF55010200011A00003C0004D40000002000006400000000002600590D363C00000000F0
voltage= 28.20V
amp= 0.06A
watt= 98.00W
wh= 0.32Wh
price= 1.00/KWh
temperature=38C
durance= 89:13:54
*) vieles noch zu tun!(!!!)
kein copy/paste sketch!
Ausgangsbasis war mein Sketch von https://werner.rothschopf.net/microcontroller/202201_a02yyuw_ultrasonic_sensor_en.htm
edit 20220401 15:26 noch mal kleines update. Sollte jetzt für alle 3 deviceType funktionieren.
Hey noiasca,
cool, das funktioniert schon mal und bietet mir eine ideale Basis um es für meine Bedürfnisse anzupassen. Watt wird auf den Display berechnet und der serielle Wert unter Watt wird auf dem Display als Cap angezeigt, aber das biege ich mir hin
ich werde mir noch eine Indikation (LED bei Datenpacket) dazu bauen, WIFI und MQTT einbinden und das ganze dann laufen lassen bevor ich den ESP8266 auf die Platine brate
Wünscht Du Feedback ?
Gruß Stefan
ja berichte.
Was mich auch interessieren würde, was du da genau hast, ist es so etwas?
DT24 HD
das LED geblinke würde ich übrigens über die Callback Funktion antoßen,
device.setOnNewData(blinkOnce); // register a callback function which gets called when a new telegram was received
Ja. genau so ein Energy Meter ist es, ich habe es in ein Gehäuse mit XT60 Buchsen/Stecker gebaut um so meine Verbrauchs oder auch Balkonkraftwerk Erzeugungswerte per MQTT in Elastic zu speichern. Per MQTT und Node-RED geht es dann auch auf ein kleines Dashboard das von iPad im Flur angezeigt wird
Wie würde meine Frau sagen, das braucht doch keiner, aber ist definitiv schick
Da ich nur das TX Signal abgreife kann die Bluetooth Funktion des DT24 sogar erhalten bleiben.
@stefankde
Ich habe jetzt auch die Checksummenberechnung und überhaupt ein paar Detail-Erklärungen dazu gemacht:
https://werner.rothschopf.net/microcontroller/202204_atorch_dt24hd_power_sensor_en.htm
Hehehe cool, hoffe mal das ich heute Abend dazu komme weiter damit rum zu spielen. Habe Device Type 2 (DC Meter) da wird Watt zur Anzeige berechnet und für den Wert Watt die Capacity geliefert … schaue ich mir aber noch mal an … Die Protokoll Beschreibung die ich da hatte ist wohl auch nicht vollständig …
Hallo, kleines Update. Der Softwareteil läuft super. Allerdings musste ich feststellen, das die Atorch Platine an keiner Stelle genug Strom liefert um den ESP8266 zu betreiben, ohne das diese zusammenbricht und das Display dann nicht mehr geht, oder der ESP nicht durch bootet. Ich werde mir überlegen ob ich einen. extra Spannungswandler verbaue, der nich zu groß ist, aber bis 100v mit geht. Ich lasse von mir hören 