P1 poort slimme meter – Data naar emonWeb.org (Arduino)
Bron: dovadi @ Github
Dit is een voorbeeldscript om P1  gegevens weg te schrijven naar emonWeb.org
ReadP1
Reading P1 Companion Standard of Dutch Smart Meter through external RX with an Arduino (or in my case a Nanode 5) and relay data to emonWeb.org. EmonWeb is a Ruby on Rails port of emonCMS.
Library is tested in the field with:
- A Iskra MT382 (see docs and (manuals)) delivered by Liander.
- A Kamstrup 162 Kamstrup.nl
Explanation
P1 port uses a baudrate of 9600, 7 bits, E parity and 1 stop bit. Use a 7404 to invert data signal. Every 10 seconds you get an output something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 |
/* _______ _________ _ _ _______ _______ ( ____ \\__ __/( ( /|| \ /\( ____ \( ___ ) | ( \/ ) ( | \ ( || \ / /| ( \/| ( ) | | | | | | \ | || (_/ / | | | | | | | | ____ | | | (\ \) || _ ( | | ____ | | | | | | \_ ) | | | | \ || ( \ \ | | \_ )| | | | | (___) |___) (___| ) \ || / \ \| (___) || (___) | (_______)\_______/|/ )_)|_/ \/(_______)(_______) _____ __ __ _ ____ _ | ____|_ __ ___ ___ _ _\ \ / /__| |__ | _ \/ | | _| | '_ ` _ \ / _ \| '_ \ \ /\ / / _ \ '_ \ | |_) | | | |___| | | | | | (_) | | | \ V V / __/ |_) | | __/| | |_____|_| |_| |_|\___/|_| |_|\_/\_/ \___|_.__/ |_| |_| Reading P1 Companion Standard of Dutch Smart Meter through external RX with an Arduino. You can't use SoftwareSerial because you need to change the default settings of the USART like partity and stop bits. EN_TX should be enabled during reading of data, but disabled if you want to upload your sketch! You need to convert the data signal with an 7404. Based on emonBase - Documentation: http://openenergymonitor.org/emon/emonbase Original authors: Trystan Lea and Glyn Hudson Adjusted for reading P1 port and posting to emonWeb.org: Frank Oxener Part of the: openenergymonitor.org project Licenced under GNU GPL V3 http://openenergymonitor.org/emon/license EtherCard Library by Jean-Claude Wippler and Andrew Lindsay */ // #define DEBUG //comment out to disable serial printing to increase long term stability #define UNO //anti crash wachdog reset only works with Uno (optiboot) bootloader, comment out the line if using delianuova #define START_PARAMETERS "auth_token=qqbUzX2V8UKUDwxZyBhR&P1=" #include <avr/wdt.h> #include <EtherCard.h> //https://github.com/jcw/ethercard #include <SRAM9.h> // ethernet interface mac address, must be unique on the LAN byte mymac[6] = { 0x00,0x04,0xA3,0x21,0xC8,0x56}; byte sd; byte free_stash_memory; byte Ethernet::buffer[700]; char id_token[] PROGMEM = "qqbUzX2V8UKUDwxZyBhR"; //Domain name of remote webserver - leave blank if posting to IP address char website[] PROGMEM = "emonweb.org"; // static byte hisip[] = { 192, 168, 2, 211}; // un-comment for posting to static IP server (no domain name) const int redLED = A1; const int requestPin = 6; int ethernet_error = 0; // Etherent (controller/DHCP) error flag int rf_error = 0; // RF error flag - high when no data received int ethernet_requests = 0; // count ethernet requests without reply int dhcp_status = 0; int dns_status = 0; int serial_input = 0; int data_ready = 0; // Used to signal that emontx data is ready to be sent int reset = 0; int nof_times = 0; static byte session; String stringReply; boolean start_p1_record; Stash stash; //Use the RAM inside the ENC28J60 Ethernet controller to post data from P1 port //See http://jeelabs.org/2012/04/11/ethercard-improvements/ //********************************************************************************************************************** // SETUP //********************************************************************************************************************** void setup () { pinMode(redLED, OUTPUT); digitalWrite(redLED,LOW); delay(100); digitalWrite(redLED,HIGH); // turn off redLED Serial.begin(9600); UCSR0C = (2<<UPM00)|(0<<USBS0)|(2<<UCSZ00)|(0<<UCPOL0); // 7-E-1 needed for reading P1 Serial.println("\n[webClient]"); if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) { Serial.println( "Failed to access Ethernet controller"); ethernet_error = 1; } dhcp_status = 0; dns_status = 0; reset = 1; #ifdef UNO wdt_enable(WDTO_8S); #endif; pinMode(requestPin, OUTPUT); digitalWrite(requestPin, HIGH); } //********************************************************************************************************************** // LOOP //********************************************************************************************************************** void loop () { stringReply = ""; //----------------------------------------------------------------------------------------------------------------- // Receive response from server //----------------------------------------------------------------------------------------------------------------- const char* reply = ether.tcpReply(session); if (reply != 0){ int size = strlen(reply); #ifdef DEBUG Serial.println(size); Serial.println(">>>> RESPONSE RECEIVED -----"); Serial.println(reply); #endif } dhcp_dns(); // handle dhcp and dns setup - see dhcp_dns tab // Display error states on status LED if (ethernet_error == 1 || ethernet_requests > 0) digitalWrite(redLED,LOW); else digitalWrite(redLED,HIGH); #ifdef UNO wdt_reset(); #endif //----------------------------------------------------------------------------------------------------------------- // Receive data from P1 //----------------------------------------------------------------------------------------------------------------- serial_input = 0; while (Serial.available() > 0) { digitalWrite(redLED, LOW); char inChar = (char)Serial.read(); serial_input += 1; #ifdef DEBUG Serial.write(inChar); #endif if (inChar == '/') { start_p1_record = true; } if (start_p1_record == true) { stash.print(inChar); } if (inChar == '!') { stash.save(); digitalWrite(redLED, HIGH); data_ready = 1; start_p1_record = false; } if (serial_input > 800) { delay(10000); //Unable to determine the end of P1 datagram } free_stash_memory = Stash::freeCount(); if (free_stash_memory < 20){ delay(10000); } } //----------------------------------------------------------------------------------------------------------------- // Send data via ethernet //----------------------------------------------------------------------------------------------------------------- ether.packetLoop(ether.packetReceive()); if (data_ready) { nof_times += 1; //Connection needs time to build up, so no post RST on the first few times if (reset == 1 && nof_times > 2) { #ifdef DEBUG Serial.print("Reset true!!!!"); #endif stash.print("&RST=1&REA=UNK"); reset = 0; } stash.save(); Stash::prepare(PSTR("POST http://$F/p1 HTTP/1.0" "\r\n" "Host: $F" "\r\n" "User-Agent: Ginkgo P1 reader 0.3" "\r\n" "Content-Length: $D" "\r\n" "ID_token: $F" "\r\n" "\r\n" "$H"), website, website, stash.size(), id_token, sd); // send the packet - this also releases all stash buffers once done session = ether.tcpSend(); #ifdef DEBUG Serial.println("=SESSION"); Serial.println(session); #endif free_stash_memory = Stash::freeCount(); #ifdef DEBUG Serial.println(); Serial.print("Number of bytes free in Stash memory: "); Serial.println(free_stash_memory, DEC); Serial.println(); Serial.println(); #endif //No response from server consumes memory from the Stash, //so if no memory left, reset the arduino (delay is longer than WDTO_8S) if (free_stash_memory == 0){ storeState(0x03); delay(10000); } data_ready =0; sd = stash.create(); stash.print(START_PARAMETERS); } } void storeState (byte state){ SRAM9.writestream(0); SRAM9.RWdata(state); SRAM9.closeRWstream(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
void dhcp_dns() { //----------------------------------------------------------------------------------- // Get DHCP address // Putting DHCP setup and DNS lookup in the main loop allows for: // powering nanode before ethernet is connected //----------------------------------------------------------------------------------- if (ether.dhcpExpired()) dhcp_status = 0; // if dhcp expired start request for new lease by changing status if (!dhcp_status){ #ifdef UNO wdt_disable(); #endif dhcp_status = ether.dhcpSetup(); // DHCP setup #ifdef UNO wdt_enable(WDTO_8S); #endif Serial.print("DHCP status: "); // print Serial.println(dhcp_status); // dhcp status if (dhcp_status){ // on success print out ip's ether.printIp("IP: ", ether.myip); ether.printIp("GW: ", ether.gwip); static byte dnsip[] = {8,8,8,8}; ether.copyIp(ether.dnsip, dnsip); ether.printIp("DNS: ", ether.dnsip); //ether.copyIp(ether.hisip, hisip); // un-comment for posting to static IP server (no domain name) //ether.hisport = 3000; //dns_status = 1; // un-comment for posting to static IP server (no domain name) } else { ethernet_error = 1; } } //----------------------------------------------------------------------------------- // Get server address via DNS //----------------------------------------------------------------------------------- if (dhcp_status && !dns_status){ #ifdef UNO wdt_disable(); #endif dns_status = ether.dnsLookup(website); // Attempt DNS lookup #ifdef UNO wdt_enable(WDTO_8S); #endif; Serial.print("DNS status: "); // print Serial.println(dns_status); // dns status if (dns_status){ ether.printIp("SRV: ", ether.hisip); // server ip } else { ethernet_error = 1; } } } |