20221231 P1 Monitor met ESP32 (NodeMCU)

- under construction

Missie:

We gaan het telegram van de digitale meter ophalen.

Het telegram van de digitale meter (ook wel slimme meter genoemd) wordt via de P1 poort aan de buitenwereld beschikbaar gesteld.
De P1 poort van de digitale meter werkt volgens het zgn. Dutch Smart Meter Requirements protocal, in het kort DSMR.
De versie van 2016 vind je hier.

Hoofdstukken:
Stap 0 - De route van begin tot het eind.
Stap 1 - Een geschikte microprocessor uitzoeken geschikt voor de programmeertaal C.
Stap 2 - Onderzoeken of we de juiste pinnen op de ESP32 benutten.
Stap 3 - Het telegram binnen halen en decoderen.

p1-telegram-met-uitleg.png

Stap 0 - De route van begin tot het eind.

  1. Een geschikte microprocessor uitzoeken geschikt voor de programmeertaal C.
  2. De juiste pinnen op de ESP32 zoeken.
  3. Een telegram aanbieden
  4. Het telegram decoderen met de microprocessor en programmeertaal C.
  5. Het telegram in het terminal venster zichtbaar maken.
  6. Een stukje elektronica als buffer in elkaar knutselen.
  7. De schakeling ontwerpen en bouwen.
  8. De uitkomsten van het gedecodeerde signaal versturen via MQTT.
  9. Het MQTT signaal ontvangen op een Raspberry Pi (gebruik NodeRED).
  10. Met NodeRed het gedecodeerde resultaat naar een InfluxDB database sturen.
  11. De data die in InfluxDB is opgeslagen, zichtbaar maken op een Grafana dashboard.

Stap 1 - Een geschikte microprocessor uitzoeken geschikt voor de programmeertaal C.

Omdat we een NodeMCU bord al eerder hebben gebruikt, gaan we hiermee beginnen.
Een lijst met meest gebruikte ESP32 varianten vinden we hier.
Blijkt op een later tijdstip dat bijvoorbeeld er onvoldoende geheugen is, dan zitten we in ieder geval op de "Arduino-trein" en hoeven we niet van voren af aan te beginnen.
De Arduino wordt in C geprogrammeerd. Dat is veel universeler dan bijvoorbeeld Python (waar ik toch al geen fan van ben).

Stap 2 - Onderzoeken of we de juiste pinnen op de ESP32 benutten.

Om te onderzoeken of we de juiste pinnen (voor UART2) op de ESP32 gebruiken gaan we het toestel uit de vorige blog gebruiken.
Allereerst gaan we de informatie die dan van het Sparkfun bordje komt op de ESP (UART2) binnen brengen en echoëen naar het terminal venster. Als dat lukt kunnen we de UART2 recieve pin als gevonden beschouwen.
Hier is meer informatie over de pinbezetting van de UARTS op een ESP32 te vinden. We moeten het volgende doen:

  • De pin GND van de Artemis verbinden met de GND pin van de ESP32.
  • De TXD pin van de Artemis verbinden met de RXD pin van de ESP32.
    Op de Artemis is pin 1 (telling begint bij 0) de Transmit pin, op de ESP is GPIO16 de Recieve pin.
  • De code uit de vorige blog en het telegram op de Artemis laden.
  • Onderstaande code op het ESP32 bord laden.
  • Beide bordjes opstarten.
  • Het resultaat in CoolTerm bekijken.

P1MonitorTestUART2.ino

						#define BAUDRATE0 9600            // Baudrate for the terminal program
						#define CONFIG0   SERIAL_8N1      // Configuration for the terminal program
						
						// define UART 2 (see this documentation for the pins)
						// https://microcontrollerslab.com/esp32-uart-communication-pins-example/
						#define BAUDRATE2 9600            // Baudrate for UART2
						#define CONFIG2   SERIAL_7E1      // Configuration for UART2
						#define RXD2      16              // RXD pin for UART2 = GPIO16
						#define TXD2      17              // TXD pin for UART2 = GPIO17
						
						#define SWVERSION "v0.1"          // Software version
						
						// --- Constants will NOT change ----------------------------------------------
						
						// --- Variables will change --------------------------------------------------
						uint16_t ledState = LOW;          // ledState used to set the LED
						uint32_t prevBlinkMillis = 0;     // will store last time LED was updated
						
						// === setup ==================================================================
						// setup the system
						void setup() {
						// Initialize LED_BUILTIN and switch it off
						pinMode(LED_BUILTIN, OUTPUT);
						digitalWrite(LED_BUILTIN, LOW);
						
						// Start serial0 as terminal
						Serial.begin(BAUDRATE0, CONFIG0);
						while (!Serial)
						{ // wait for serial-0 ready
							delay(100); 
						}
						Serial.println("Setup: +--- Terminal window started (UART0).");
						Serial.print("Setup: +--- Software version : ");
						Serial.println(SWVERSION);
						
						// Start serial2 as input, if the signal is inverterd, set the last parameter on true
						Serial2.begin(BAUDRATE2, CONFIG2, RXD2, TXD2, false);
						while (!Serial2)
						{ // wait for serial2 ready
							delay(100);
							Serial.println("Setup: +--- Waiting for UART2 to start.");
						}
						Serial.println("Setup: +--- Serial IO started (UART2).");
						} // void setup() ends
						
						//--- void blink() blink the internal LED routine -----------------------------
						void blink(uint16_t pinNr, uint16_t interval) {
						//--- blinking the LED ------------------------------------------------------
						// This is just to see that the program is running
						// check to see if it's time to blink the LED; that is, if the difference
						// between the current time and last time you blinked the LED is bigger than
						// the interval at which you want to blink the LED.
						uint32_t curBlinkMillis = millis();
						// check if blink time elapsed
						if (curBlinkMillis - prevBlinkMillis >= interval) {
							// save the last time you blinked the LED
							prevBlinkMillis = curBlinkMillis;
							// if the LED is off turn it on and vice-versa:
							ledState = !(digitalRead(pinNr));
							digitalWrite(pinNr, ledState);
						} // if blink millis elapsed
						} // void blink()
						
						// === main ===================================================================
						// main program
						void loop() {
						if (Serial2.available()) {      
							Serial.println("Loop: === Serial2 is available");
							while (Serial2.available()) { // echo Serial2 to Serial0
							char ch = Serial2.read();
							Serial.print(ch);  
							}
							Serial.println("\n");
						}
						blink(LED_BUILTIN, 500);
						} // void loop() ends
						
					
Download

Het CoolTerm vernster ziet er dan uit als hier beneden.
Niet helemaal foutloos, maar we hebben in elk geval de pin voor de juiste UART te pakken.

P1MonitorTestUART2.jpg

En nu verder,

Stap 3 - Het telegram binnen halen en decoderen met de microprocessor en programmeertaal C.

3.1 - Opbouw van het telegram.

Zoals te zien in de eerste figuur van deze post heeft het telegram een gedefinieerde opbouw.
Het telegram begint altijd met een forward slash "/" gevolgd door het merk en serienummer van de meter.
Elke regel wordt afgesloten met "\n" en bevat de code, de waarde en de eenheid van de opname.
Het telegram eindigt met een ampersand "!", gevolgd door de CRC16 code. De opbouw van het telegram is ook in de vorige blog onder TELEGRAM.TXT te zien.
We gaan veel van deze github gebruiken voor het binnenhalen en het decoderen.

De verschillende P1 specificaties zijn hier beneden in tabel vorm opgenomen:

parameter DSMR 2.0 DSMR 4.2 ESMR 5.0
Baud rate 9600 115200 115200
Data bits 7 7 8
Parity Even Even None
Stop bits 1 1 1
Documentatie
Slimme_meter_DSMR22.pdf
Slimme_meter_DSMR42.pdf
Slimme_meter_ESMR50.pdf
Back to top