Closed sjfaustino closed 6 years ago
Please try a delay(50); between each sdm.readVal. It never works without here.
A delay(1) between reading each value seems to be sufficient. Since 1ms is such a small delay, would it not be wise to add it on the library so that the user doesn't have to worry?
delay(1);
if (_dere_pin != NOT_A_PIN) //transmit to SDM -> DE Enable, /RE Disable (for control MAX485)
I can add this, but I never had such a problems I read all values one-by-one without any delay:
sdmval.voltage = sdm.readVal(SDM220T_VOLTAGE);
sdmval.current = sdm.readVal(SDM220T_CURRENT);
sdmval.power = sdm.readVal(SDM220T_POWER);
sdmval.frequency = sdm.readVal(SDM220T_FREQUENCY);
sdmval.energy = sdm.readVal(SDM220T_TOTAL_ACTIVE_ENERGY);
my config: SDM220 at 4800 baud
maybe baudrate affected this?
If I find some free time, then I insert some debug code with count wrong/nan reading
Not sure it's Baud related. I have 120c and 630 with 2400 and 9600 baud rate. They both show this behaviour.
After 1 hour of tests with error counter, I don't have any wrong readings.
this is my code (sorry but not usable as is because it uses my priv helper lib for wifi connection/ota/oled) as You see, I reading all values one-by-one without any delay between. I add errors counter variable (errorscnt), incremented every 1000ms loop, if any of reading values are NAN, but all readings from last hour (every 1000ms) are good!
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h>
#include <ThingSpeak.h>
#include "ESP8266r7Helper.h"
#include "SDM.h"
AsyncWebServer server(80);
WiFiClient client;
unsigned long myChannelNumber = xxxxxx;
const char * myWriteAPIKey = "XxXxXxXxXxXxXxXxXx";
unsigned long lastsdmreadtime = 0; //store time of last smd read
bool dtardy = false;
uint16_t errorscnt = 0; //error counter for debug
//------------------------------------------------------------------------------
SDM<> sdm;
struct sdm_values {
float voltage;
float current;
float power;
float frequency;
float energy;
} sdmval;
//------------------------------------------------------------------------------
void jsonrequest(AsyncWebServerRequest *request) {
if (dtardy) {
AsyncJsonResponse * response = new AsyncJsonResponse();
JsonObject& root = response->getRoot();
root["rp"] = String(sdmval.power,1);
root["kw"] = String(sdmval.energy,2);
root["rv"] = String(sdmval.voltage,1);
root["rc"] = String(sdmval.current,2);
root["rf"] = String(sdmval.frequency,2);
root["er"] = String(errorscnt);
response->setLength();
request->send(response);
request->client()->close();
} else {
request->send(404);
}
}
//------------------------------------------------------------------------------
void outrequest(AsyncWebServerRequest *request) {
int params = request->params();
for(int i=0;i<params;i++){
AsyncWebParameter* p = request->getParam(i);
Serial.printf("GET[%s]: %s\n", p->name().c_str(), p->value().c_str());
}
request->send(200, "text/plain", "OK");
}
//------------------------------------------------------------------------------
void setup() {
devicename = "PWRMETER";
sdmval.voltage = NAN;
sdmval.current = NAN;
sdmval.power = NAN;
sdmval.frequency = NAN;
sdmval.energy = NAN;
Serial.begin(115200);
oledinit();
wifiinit();
otainit();
sdm.begin();
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
request->send(200, "text/plain", String(ESP.getFreeHeap()));
});
server.on("/data.json", HTTP_GET, jsonrequest);
server.on("/out", HTTP_GET, outrequest);
server.onNotFound([](AsyncWebServerRequest *request){
Serial.println("404: not found");
request->send(404);
});
server.begin();
Serial.println(ESP.getFreeHeap());
oledprintline((String)ESP.getFreeHeap());
delay(2000);
}
//------------------------------------------------------------------------------
void loop() {
static unsigned long tsupload = 0;
static float lastenergy = -1.00;
static float sumpower = 0.00;
static float measurecnt = 0.00;
static uint16_t minutes = 0;
float tmpvoltage = NAN;
float tmpcurrent = NAN;
float tmppower = NAN;
float tmpfrequency = NAN;
float tmpenergy = NAN;
if ((!otalock) && (millis() >= (lastsdmreadtime + SDM_READ_EVERY))) {
lastsdmreadtime = millis();
tmpvoltage = sdm.readVal(SDM220T_VOLTAGE);
tmpcurrent = sdm.readVal(SDM220T_CURRENT);
tmppower = sdm.readVal(SDM220T_POWER);
tmpfrequency = sdm.readVal(SDM220T_FREQUENCY);
tmpenergy = sdm.readVal(SDM220T_TOTAL_ACTIVE_ENERGY);
if ((isnan(tmpvoltage)) || (isnan(tmpcurrent)) || (isnan(tmppower)) || (isnan(tmpfrequency)) || (isnan(tmpenergy))) { // if one of reading values is a NAN then set "dataready flag to false
dtardy = false;
errorscnt++; // increment errorscnt for debug
sdmval.voltage = 0.00;
sdmval.current = 0.00;
sdmval.power = 0.00;
sdmval.frequency = 0.00;
sdmval.energy = 0.00;
} else { // else -> every values != NAN
sdmval.voltage = tmpvoltage;
sdmval.current = tmpcurrent;
sdmval.power = tmppower;
sdmval.frequency = tmpfrequency;
sdmval.energy = tmpenergy;
dtardy = true;
if (lastenergy == -1.00) {
lastenergy = sdmval.energy;
}
sumpower += sdmval.power;
measurecnt = measurecnt + 1.00;
char buff[48] = {};
sprintf(buff, "V:%7.2f\nA:%7.2f\nP:%7.2f\nF:%7.2f\0", sdmval.voltage, sdmval.current, sdmval.power, sdmval.frequency);
oledprintline(buff, 0, true, Cousine_Bold_10, TEXT_ALIGN_CENTER);
if (lastsdmreadtime >= (tsupload + 60000)) {
char floatchr[12];
minutes++;
tsupload = lastsdmreadtime;
ThingSpeak.begin(client);
ThingSpeak.setField(1,dtostrf(sdmval.voltage,0,2,floatchr));
ThingSpeak.setField(2,dtostrf(sdmval.current,0,2,floatchr));
ThingSpeak.setField(3,dtostrf(float(sumpower / measurecnt),0,2,floatchr));
ThingSpeak.setField(4,dtostrf(sdmval.frequency,0,2,floatchr));
if (minutes == 59) {
ThingSpeak.setField(5,dtostrf((sdmval.energy - lastenergy),0,2,floatchr));
minutes = 0;
lastenergy = sdmval.energy;
ThingSpeak.setField(6,errorscnt); // every hour send errorscnt value to thingspeak for debug
errorscnt = 0; // errorscnt uploaded, set to 0
}
ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
measurecnt = 0.00;
sumpower = 0.00;
}
}
}
ArduinoOTA.handle() ; // Check for OTA
}
this is a prototype code but works ~129days without a problems
one more thing... how is Your connection between SDM and converter? two wire (A&B) or three wire (A&B&GND) I have 3 wire...
I'm using a Wemos D1 mini (esp8266) connected to this ttl2rs485: https://www.aliexpress.com/item/TTL-turn-RS485-module-485-to-serial-UART-level-mutual-conversion-hardware-automatic-flow-control/32709226383.html
Wemos mini to TTL2RS485: Wemos D1 to TX TTL2RS485 Wemos D2 to RX TTL2RS485 Wemos GND to GND TTL2S485 Wemos 3.3V to VCC TTL2RS485
TTL2RS485 to SDM120C B to pin9 A to pin10 GND not connected (don't know what is the gnd pin on the sdm120c, maybe 8?)
So, 2 wire. Also, no 120 ohm resistor in use ( I believe it is only necessary on long cable runs).
I sugest adding a ifdefine add_sdm_delay to the library and let the user do a #define add_sdm_delay if he wants.
I can't reproduce this error. On my hardware configuration, example from this lib and my other project works without problem, so...at this moment please add delays to Your main program or clone this lib and do this on Your copy.
GND...maybe 8: http://techfreak.pl/miernik-zuzycia-energii-elektrycznej-sdm120/sdm120-connection/ http://eastron.inf.ua/SDM120.html
after 14 hours still no errors
@beireken - do You check if this problem also exists when converter is connected via hardware serial?
I checked this problem whole weekend, code read all fourteen SDM220 registers at once in loop every 2sec and after 2 days I have only 37 errors where number of all reading = 48h 60m (60s/2) = 86400 readings
I must check if problem is a timeout/bad checksum/not enough bytes read
@reaper7 Do you mean changing the library to function with hardware serial and not softserial? Or just using the pins of tx and rx and not random other.?
no no, this lib is ready for use with hardware serial, user must set at top of sketch:
#define USE_HARDWARESERIAL
before:
#include <SDM.h>
If You not tested yet hardware serial functionality of this lib then no question :D
Today I added a few fix, maybe some reading errors disappear...
Hmm I did not know that. I will test this when I get the chance and report back.
ok, tnx. You can test software serial too, without any delay between each sdm read
Im trying to use your code from above but im missing this library ESP8266r7Helper.h Where can i find it?
@mariusz84pl - this is my private lib, only for connect to wifi, configure ota and print lines on oled display. You can replace my functions:
oledinit();
wifiinit();
otainit();
oledprintline("some text");
with Your own solution. I do not publish this lib because it contains many private data like passwords etc.
I'm trying to use hardwareserial cut can't get to work with it. TX working but no RX.
in sketch added #define USE_HARDWARESERIAL on top. I'm using nodemcu dev Lolin, connecting to TX, RX in it. Is there something else to be added or removed?
#define USE_HARDWARESERIAL
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <AsyncJson.h>
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
#include "SDM2.h"
added or removed? I don't know what is needed in Your application.
What kind of converter you are using, with or without DE/RE pins ? Maybe You swap tx / rx lines?
Tryed every combination of connection TX/RX and can't get any values
I use the same :)
so, I just tested this simple sketch with hw serial (my sdm is set to 9600b!!!):
#define USE_HARDWARESERIAL
#include <SDM.h>
SDM<9600> sdm;
void setup() {
pinMode(BUILTIN_LED, OUTPUT);
digitalWrite(BUILTIN_LED, HIGH);
sdm.begin();
}
void loop() {
float tmpval = NAN;
tmpval = sdm.readVal(SDM220T_VOLTAGE);
if (!isnan(tmpval)) {
digitalWrite(BUILTIN_LED, LOW);
}
delay(1000);
digitalWrite(BUILTIN_LED, HIGH);
}
led blinks, it's mean reading ok
this is my "hardware" ;)
I have WEMOS d1 mini. What does the error ets Jan 8 2013,rst cause:4, boot mode:(1,7)
wdt reset
watchdog timer reset
https://github.com/esp8266/Arduino/blob/4897e0006b5b0123a2fa31f67b14a3fff65ce561/doc/faq/a02-my-esp-crashes.md https://techtutorialsx.com/2017/01/21/esp8266-watchdog-functions/
I have to guess for what reason?
This happens to me when I want to test Sdm_live_page.
at start or whether during operation? example modified in some way? it connects to the access point?
Error appears at the beginning. With programming i do not have problem. It is, when the program start on esp. I used your DEMO Sdm_live_page
example sdm_simple works for You?
sorry, yes sdm_simple works. on esp works others examples with wifi
I removed some external libs(for display the time) from example, please download again and try.
You are using hardware or software serial? If softwareserial, then on which pins?
Try debuging code yourself by inserting Serial.print("something"); in different places, to detect the error location.
ets Jan 8 2013,rst cause:4, boot mode:(3,7)
sorry but I cant duplicate this issue, with core debug enabled I got on hardware serial console:
10f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v00000000
~ld
SDK:2.2.1(cfd48f3)/Core:win-2.5.0-dev/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1)
sta config unchangedscandone
scandone
state: 0 -> 2 (b0)
state: 2 -> 3 (0)
state: 3 -> 5 (10)
add 0
aid 3
cnt
connected with TP-LINK_XXXXXX, channel 11
dhcp client start...
ip:192.168.6.37,mask:255.255.255.0,gw:192.168.6.1
pm open,type:2 0
:urn 41
:urd 12, 41, 13
:urd 4, 41, 26
:urd 5, 41, 31
web page works
this is my code:
//sdm live page example by reaper7
//#define USE_HARDWARESERIAL
#define READSDMEVERY 2000 //read sdm every 2000ms
#define NBREG 6 //number of sdm registers to read
//#define USE_STATIC_IP
/* WEMOS D1 Mini
______________________________
| L T L T L T L T L T L T |
| |
RST| 1|TX
A0| 3|RX
D0|16 5|D1
D5|14 4|D2
D6|12 10kPUP_0|D3
RX SSer/HSer swap D7|13 LED_10kPUP_2|D4
TX_SSer/HSer swap D8|15 |GND
3V3|__ |5V
| |
|___________________________|
*/
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ESP8266mDNS.h>
#include <ArduinoOTA.h>
#include <ESPAsyncTCP.h> // https://github.com/me-no-dev/ESPAsyncTCP
#include <ESPAsyncWebServer.h> // https://github.com/me-no-dev/ESPAsyncWebServer
#include <SDM.h> // https://github.com/reaper7/SDM_Energy_Meter
#include "index_page.h"
//------------------------------------------------------------------------------
AsyncWebServer server(80);
#if !defined ( USE_HARDWARESERIAL ) // SOFTWARE SERIAL
SDM<4800, 13, 15, NOT_A_PIN> sdm; // baud, rx_pin, tx_pin, de/re_pin(not used in this example)
#else // HARDWARE SERIAL
SDM<4800, NOT_A_PIN, false> sdm; // baud, de/re_pin(not used in this example), uart0 pins 3/1(false) or 13/15(true)
#endif
//------------------------------------------------------------------------------
String devicename = "PWRMETER";
#if defined ( USE_STATIC_IP )
IPAddress ip(192, 168, 0, 130);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);
#endif
const char* wifi_ssid = "TP-LINK_XXXXXX";
const char* wifi_password = "12345678";
String lastresetreason = "";
unsigned long readtime;
volatile bool otalock = false;
//------------------------------------------------------------------------------
typedef volatile struct {
volatile float regvalarr;
const uint16_t regarr;
} sdm_struct;
volatile sdm_struct sdmarr[NBREG] = {
{0.00, SDM220T_VOLTAGE}, //V
{0.00, SDM220T_CURRENT}, //A
{0.00, SDM220T_POWER}, //W
{0.00, SDM220T_POWER_FACTOR}, //PF
{0.00, SDM220T_PHASE_ANGLE}, //DEGREE
{0.00, SDM220T_FREQUENCY}, //Hz
};
//------------------------------------------------------------------------------
void xmlrequest(AsyncWebServerRequest *request) {
String XML = F("<?xml version='1.0'?><xml>");
if(!otalock) {
for (int i = 0; i < NBREG; i++) {
XML += "<response" + (String)i + ">";
XML += String(sdmarr[i].regvalarr,2);
XML += "</response" + (String)i + ">";
}
}
XML += F("<freeh>");
XML += String(ESP.getFreeHeap());
XML += F("</freeh>");
XML += F("<rst>");
XML += lastresetreason;
XML += F("</rst>");
XML += F("</xml>");
request->send(200, "text/xml", XML);
}
//------------------------------------------------------------------------------
void indexrequest(AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_page);
}
//------------------------------------------------------------------------------
void ledOn() {
digitalWrite(LED_BUILTIN, LOW);
}
//------------------------------------------------------------------------------
void ledOff() {
digitalWrite(LED_BUILTIN, HIGH);
}
//------------------------------------------------------------------------------
void ledSwap() {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}
//------------------------------------------------------------------------------
void otaInit() {
ArduinoOTA.setHostname(devicename.c_str());
ArduinoOTA.onStart([]() {
otalock = true;
ledOn();
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
ledSwap();
});
ArduinoOTA.onEnd([]() {
ledOff();
otalock = false;
});
ArduinoOTA.onError([](ota_error_t error) {
ledOff();
otalock = false;
});
ArduinoOTA.begin();
}
//------------------------------------------------------------------------------
void serverInit() {
server.on("/", HTTP_GET, indexrequest);
server.on("/xml", HTTP_PUT, xmlrequest);
server.onNotFound([](AsyncWebServerRequest *request){
request->send(404);
});
server.begin();
}
//------------------------------------------------------------------------------
static void wifiInit() {
WiFi.persistent(false); // Do not write new connections to FLASH
WiFi.mode(WIFI_STA);
#if defined ( USE_STATIC_IP )
WiFi.config(ip, gateway, subnet); // Set fixed IP Address
#endif
WiFi.begin(wifi_ssid, wifi_password);
while( WiFi.status() != WL_CONNECTED ) { // Wait for WiFi connection
ledSwap();
delay(100);
}
}
//------------------------------------------------------------------------------
void sdmRead() {
float tmpval = NAN;
for (uint8_t i = 0; i < NBREG; i++) {
tmpval = sdm.readVal(sdmarr[i].regarr);
if (isnan(tmpval))
sdmarr[i].regvalarr = 0.00;
else
sdmarr[i].regvalarr = tmpval;
yield();
}
}
//------------------------------------------------------------------------------
void setup() {
#ifndef USE_HARDWARESERIAL
Serial.begin(115200);
#endif
pinMode(LED_BUILTIN, OUTPUT);
ledOn();
lastresetreason = ESP.getResetReason();
wifiInit();
otaInit();
serverInit();
sdm.begin();
readtime = millis();
ledOff();
}
//------------------------------------------------------------------------------
void loop() {
ArduinoOTA.handle();
if ((!otalock) && (millis() - readtime >= READSDMEVERY)) {
sdmRead();
readtime = millis();
}
yield();
}
Hello.
Thank you very much for all your help and informations. I did my web. But now i have another problem. :) In Simple Program is between datas NAN. I changed pin and changed delay and all other things. By every measure is ther one NAN. What i did wrong and where is the problem?
Best Regards, M. Rotovnik
all measurements in one loop run are NAN? or one incorrect reading in each loop (random, sometimes voltage, sometimes frequency etc...)?
what you changed? 7 days ago you wrote (https://github.com/reaper7/SDM_Energy_Meter/issues/7#issuecomment-375027078):
sorry, yes sdm_simple works. on esp works others examples with wifi
Example :sdm_simple change serial PIN
SDM<2400, 4, 5> sdm;
or
SDM<2400, 14, 16> sdm;
or
SDM<2400, 13, 15> sdm;
Change delay time: if (_dere_pin != NOT_A_PIN) digitalWrite(_dere_pin, HIGH); delay(2);
Change delay : ..... Serial.print(sdm.readVal(SDM220T_VOLTAGE), 2); delay(50); Serial.print(sdm.readVal(SDM220T_CURRENT), 2); delay(50); ......
Change SDM120 - SDM220 sdm.readVal(SDM120C_CURRENT sdm.readVal(SDM220T_CURRENT
............................ Random NAN [1;0HVoltage: 236.10V Current: 0.00A Power: 0.00W Frequency: 50.04Hz [1;0HVoltage: nanV Current: 0.00A Power: 0.00W Frequency: 50.09Hz [1;0HVoltage: 236.10V Current: 0.00A Power: nanW Frequency: 50.09Hz [1;0HVoltage: 236.10V Current: 0.00A Power: 0.00W Frequency: 50.09Hz [1;0HVoltage: 236.10V Current: 0.00A Power: 0.00W Frequency: 50.04Hz [1;0HVoltage: 236.10V Current: 0.00A Power: 0.00W Frequency: 50.09Hz [1;0HVoltage: 236.10V Current: nanA Power: 0.00W Frequency: nanHz [1;0HVoltage: 236.10V Current: 0.00A Power: 0.00W Frequency: nanHz
try to check which error causes a bad reading, add after every reading (before delay):
Serial.print("Last ERROR: ");
Serial.println(sdm.getErrCode(true));
Have you tried other speeds? 4800?
I still suggest switching to HardwareSerial which I personally use. I read all 14 SDM220 registers in loop every 3sec with only yeld() between every register(without any delay), what it takes ~470ms and after last 15hours I have only two errors. 14registers 20readings every minute 60min * 15h = 252000readings and only 2 errors...
Error 4. Speed 4800 to SDM120 does not work
[1;0HVoltage: 230.50V Last ERROR: 0 Current: nanA Last ERROR: 4 Power: 0.00W Last ERROR: 0 Frequency: 50.00Hz Last ERROR: 0 [1;0HVoltage: 230.40V Last ERROR: 0 Current: 0.00A Last ERROR: 0 Power: 0.00W Last ERROR: 0 Frequency: nanHz Last ERROR: 4 [1;0HVoltage: nanV Last ERROR: 4 Current: 0.00A Last ERROR: 0 Power: 0.00W Last ERROR: 0 Frequency: 50.00Hz Last ERROR: 0
error 4 -> SDM_ERR_TIMEOUT this means that at the declared time (in this case 500ms) esp serial did not get full reply frame (9 bytes) from sdm.
Try to increase MAX_MILLIS_TO_WAIT in line 18 https://github.com/reaper7/SDM_Energy_Meter/blob/master/SDM.h#L18 from 500 to e.g. 1000 maybe 500ms is too low for Your baud rate 2400
Thank you for all informations. I change all times, but nothing helps.
How can I change the speed from 2400 to 4800?
I would not yous Tx in Rx pin, because I allready use them for Serial.print.
Thank you for all informations. I change all times, but nothing helps.
How can I change the speed from 2400 to 4800?
I would not yous Tx in Rx pin, because I allready use them for Serial.print.
I have the very same setup as descibed here. I also changed the MAX_MILLIS_TO_WAIT to 1500 to be on the safe side. But I continue to receive the error code 4 as well. If possible I would like to stay with 2400 baud. Simply because I don't have the software to change it.
When using the code below with a SDM120C: `#include //import SDM template library
SDM<2400, D1, D2> sdm; //SDM120C baud, rx pin, tx pin
void setup() { Serial.begin(115200); //initialize serial sdm.begin(); //initalize SDM220 communication baudrate }
void loop() {
Serial.print("Frequency: "); Serial.print(sdm.readVal(SDM120C_FREQUENCY), 2); //display frequency Serial.println("Hz");
Serial.print("Voltage: "); Serial.print(sdm.readVal(SDM120C_VOLTAGE), 2); //display voltage Serial.println("V");
delay(1000); //wait a while before next loop }` I get:
If I swap the read order as on the code below: `#include //import SDM template library
SDM<2400, D1, D2> sdm; //SDM120C baud, rx pin, tx pin
void setup() { Serial.begin(115200); //initialize serial sdm.begin(); //initalize SDM220 communication baudrate }
void loop() {
Serial.print("Voltage: "); Serial.print(sdm.readVal(SDM120C_VOLTAGE), 2); //display voltage Serial.println("V");
Serial.print("Frequency: "); Serial.print(sdm.readVal(SDM120C_FREQUENCY), 2); //display frequency Serial.println("Hz");
delay(1000); //wait a while before next loop }` I get: