). Got bored with all the skype/zoom/teams/bigblueblob stuff from today, so I took the liberty to add a bit of (pseudo) code to your arduino code.
Each added section is commented with something along these lines: // add: .... // end addition
The main bits are inside the routine getdata(), plus some declarations up front, hope it's useful.
Code: Select all
/******************************************************************************************
* SolarBridge by Oepi-Loepi
* All rights reserved
* Do not use for commercial purposes
*
* Solar Bridge for GroWatt solar converters
* This bridge will get the data from the GroWatt web interface (API) and create S0 pulses and led flashes
* Each pulse/flash will suggest 1 Watt
*
* On GPIO 4 a resistor (330 ohm and red LED are connected in series
* PIN D2 ---- Resistor 33Ohm) ---- (Long LED lead ---- LED ---- Short LED Lead) ----- GND
*
* On GPIO 5 a PC817 optocoupler is connected
* PIN D1 ---- PC817 (anode, pin 1, spot)
* GND ------- PC817 (cathode, pin 2)
*
* Pin 3 and 4 of the PC817 will be a potential free contact
*
* After uploading the sketch to the Wemos D1 mini, connect to the AutoConnectAP wifi.
* Goto 192.168.4.1 in a webbrowser and fill in all data including GroWatt credentials.
*
*/
#include <FS.h> //this needs to be first, or it all crashes and burns...
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
//needed for library
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include "ESP8266HTTPClient.h"
#include <ArduinoJson.h> //https://github.com/bblanchon/ArduinoJson
//define your default values here, if there are different values in config.json, they are overwritten.
char GroWattPass[40] = "password";
char GroWattName[40] = "login";
char static_ip[18] = "192.168.10.2";
char static_gw[18] = "192.168.10.1";
char static_sn[18] = "255.255.255.0";
char static_dn[18] = "8.8.8.8";
bool dhcp = false;
bool ConnectionPossible=false;
char* wifiHostname = "SolarBridge";
String JSESSIONID;
String SERVERID;
bool cookiestep= false;
bool reset1 = false;
bool reset2 = false;
int led = 4;
int meteradapter = 5;
// Add: Need to do floating point arithmetic, integer is not accurate enough.
double today_value = 0.0;
double last_value = 0.0;
double remainder = 0.0;
double freq_out = 0.0;
double timestep = 0.0; //needs to be your data acquisition time interval, in seconds
double energy_per_pulse = 3600.0; // in Joule (assuming 1000 pulses/kWh)
//end addition.
int CurrentValue = 0;
String todayval = "Not Connected Yet";
String monthval= "Not Connected Yet";
unsigned long startMillis; //some global variables available anywhere in the program
unsigned long currentMillis;
unsigned long period = 2000; //time between pulses, initial 2000 ms
unsigned long startMillis2; //some global variables available anywhere in the program
unsigned long currentMillis2;
unsigned long InitialGetdatafrequency = 20000; //only first time to get data from Growatt, then time will be set to Getdatafrequency)
unsigned long Getdatafrequency = 60000; //time between data transfers from GroWatt
unsigned long Getdatafrequencyset = 0; //time between data transfers from GroWatt
unsigned long timebetweenpulses = 2000; //time between pulses calculated (initial)
bool shouldSaveConfig = false;
WiFiServer server(80);
String header_web = "";
//callback notifying us of the need to save config
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(led, OUTPUT);
digitalWrite(led, HIGH);
pinMode(meteradapter, OUTPUT);
digitalWrite(meteradapter, HIGH);
startMillis = millis(); //initial start time
startMillis2 = millis(); //initial start time
Getdatafrequencyset = InitialGetdatafrequency;
//read configuration from FS json
Serial.println("mounting FS...");
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
StaticJsonBuffer<200> jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.printTo(Serial);
if (json.success()) {
Serial.println("\nparsed json");
strcpy(GroWattName, json["GroWattName"]);
strcpy(GroWattPass, json["GroWattPass"]);
if(json["dhcp"]) {
Serial.println("Setting up wifi from dhcp config");
dhcp=true;
} else{
Serial.println("Setting up wifi from Static IP config");
}
if(json["ip"]) {
Serial.println("Last known ip from config");
strcpy(static_ip, json["ip"]);
strcpy(static_gw, json["gateway"]);
strcpy(static_sn, json["subnet"]);
Serial.println(static_ip);
} else {
Serial.println("no custom ip in config");
}
} else {
Serial.println("failed to load json config");
}
}
}
} else {
Serial.println("failed to mount FS");
}
//end read
WiFiManagerParameter custom_GroWattName("GroWattName", "GroWattName", GroWattName, 40);
WiFiManagerParameter custom_GroWattPass("GroWattPass", "GroWattPass", GroWattPass, 40);
WiFiManagerParameter custom_text("<p>Select Checkbox for DHCP ");
WiFiManagerParameter custom_text2("</p><p>DHCP will be effective after reset (power off/on)</p>");
WiFiManagerParameter custom_text3("</p><p>So first wait for a minute after saving and then reboot by removing power.</p>");
WiFiManagerParameter custom_dhcp("dhcp", "dhcp on", "T", 2, "type=\"checkbox\" ");
WiFiManager wifiManager;
WiFi.hostname(wifiHostname);
wifiManager.setSaveConfigCallback(saveConfigCallback);
if (!dhcp){
IPAddress _ip,_gw,_sn;
_ip.fromString(static_ip);
_gw.fromString(static_gw);
_sn.fromString(static_sn);
wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
}else{
wifiManager.autoConnect("AutoConnectAP");
}
wifiManager.addParameter(&custom_GroWattName);
wifiManager.addParameter(&custom_GroWattPass);
wifiManager.addParameter(&custom_text);
wifiManager.addParameter(&custom_dhcp);
wifiManager.addParameter(&custom_text2);
wifiManager.addParameter(&custom_text3);
wifiManager.setMinimumSignalQuality();
if (!wifiManager.autoConnect("AutoConnectAP")) {
Serial.println("failed to connect and hit timeout");
delay(3000);
//reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(5000);
}
Serial.println("connected...yeey :)");
if (!dhcp){
IPAddress _dn;
_dn.fromString(static_dn);
WiFi.hostname(wifiHostname);
WiFi.mode(WIFI_STA);
WiFi.config(WiFi.localIP(), WiFi.gatewayIP(), WiFi.subnetMask(), _dn);
WiFi.begin();
}
delay(2000);
Serial.println("local ip");
Serial.println(WiFi.localIP());
Serial.println(WiFi.gatewayIP());
Serial.println(WiFi.subnetMask());
Serial.println(WiFi.dnsIP());
//Serial.print("custom_dhcp.getValue(): ");
//Serial.println(custom_dhcp.getValue());
dhcp = (strncmp(custom_dhcp.getValue(), "T", 1) == 0);
//Serial.print("cdhcp: ");
//Serial.println(dhcp);
strcpy(GroWattName, custom_GroWattName.getValue());
strcpy(GroWattPass, custom_GroWattPass.getValue());
//save the custom parameters to FS
if (shouldSaveConfig) {
Serial.println("saving config");
StaticJsonBuffer<200> jsonBuffer;
JsonObject& json = jsonBuffer.createObject();
json["GroWattName"] = GroWattName;
json["GroWattPass"] = GroWattPass;
json["dhcp"] = dhcp;
json["ip"] = WiFi.localIP().toString();
json["gateway"] = WiFi.gatewayIP().toString();
json["subnet"] = WiFi.subnetMask().toString();
File configFile = SPIFFS.open("/config.json", "w");
if (!configFile) {
Serial.println("failed to open config file for writing");
}
json.prettyPrintTo(Serial);
json.printTo(configFile);
configFile.close();
//end save
}
delay(1000);
WiFi.begin();
server.begin();
}
void webserver(){
// Set web server port number to 80
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header_web += c;
if (c == '\n') { // if the byte is a newline character
if (currentLine.length() == 0) {
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<meta http-equiv=\"refresh\" content=\"300\">");
client.println("<title>SolarBridge</title>");
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #195B6A; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}</style>");
client.println("</head>");
client.println("<body><h1>SolarBridge</h1>");
client.println("<hr>");
client.println("<p></p>");
if ((header_web.indexOf("GET /") >= 0) && (header_web.indexOf("GET /reset/") <0)) {
reset1 = false;
reset2 = false;
client.println("<p>IP: " +WiFi.localIP().toString() + "<br>");
client.println("Gateway: " +WiFi.gatewayIP().toString()+ "<br>");
client.println("Subnet: " +WiFi.subnetMask().toString() + "</p>");
client.println("<p> </p>");
client.println("<p>User: " + String(GroWattName) + "<br>");
client.println("PW: " + String(GroWattPass) + "</p>");
String YesNo = "No";
if (ConnectionPossible){
YesNo = "Yes";
}else{
YesNo = "No";
};
client.println("Connection Posssible: " + YesNo + "</p>");
client.println("<p> </p>");
client.println("<p>1000 imp/kW</p>");
String curval = String(CurrentValue);
client.println("<hr>");
client.println("<h1>Current: " + curval + " Watt<br>");
client.println("Today: " + todayval + "<br>");
client.println("Month: " + monthval + "</h1>");
client.println("<hr>");
client.println("<p> </p>");
client.println("<p> </p>");
client.println("<p> </p>");
client.println("<p> </p>");
client.println("<p><a href=\"/reset/req\"><button class=\"button\">Reset</button></a></p>");
client.println("<p>When reset is pressed, all settings are deleted and AutoConnect is restarted<br>");
client.println("Please connect to wifi network AutoConnectAP and after connect go to: 192.168.4.1 for configuration page </p>");
client.println("</body></html>");
}
if (header_web.indexOf("GET /reset/req") >= 0) {
reset1 = true;
reset2 = false;
Serial.print("Reset1 :");
Serial.print(reset1);
Serial.print(", Reset2 :");
Serial.println(reset2);
client.println("<p>Weet u zeker ?</p>");
client.println("<p> </p>");
client.println("<p><a href=\"/reset/ok\"><button class=\"button\">Yes</button></a><a href=\"/../..\"><button class=\"button\">No</button></a></p>");
client.println("</body></html>");
}
if (header_web.indexOf("GET /reset/ok") >= 0) {
reset2 = true;
Serial.print("Reset1 :");
Serial.print(reset1);
Serial.print(", Reset2 :");
Serial.println(reset2);
client.println("<p>Reset carried out</p>");
client.println("<p>Please connect to wifi network AutoConnectAP and goto 192.168.4.1 for configuration</p>");
client.println("</body></html>");
}
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header_web = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
void getdata(){
MD5Builder md5;
md5.begin();
md5.add(GroWattPass); // md5 of the user:realm:user
md5.calculate();
String password = md5.toString();
//Serial.print("password : ");
//Serial.println(password);
HTTPClient http;
const char * headerkeys[] = {"User-Agent","Set-Cookie","Cookie","Date","Content-Type","Connection"} ;
size_t headerkeyssize = sizeof(headerkeys)/sizeof(char*);
http.begin("http://server.growatt.com/LoginAPI.do");
http.setReuse(true);
http.setUserAgent("Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A6003 Build/PKQ1.180716.001");
http.addHeader("Content-type", "application/x-www-form-urlencoded");
http.collectHeaders(headerkeys,headerkeyssize);
int code = http.POST("password=" + password + "&userName="+GroWattName); //Send the request
if ((code=200) || (code = 301) || (code = 302)){
JSESSIONID = "";
SERVERID = "";
//Serial.printf("[HTTP] POST... code: %d\r\n", code);
String res = http.getString();
//Serial.println(res);
//Serial.printf("Header count: %d\r\n", http.headers());
for (int i=0; i < http.headers(); i++) {
//Serial.printf("%s = %s\r\n", http.headerName(i).c_str(), http.header(i).c_str());
}
//Serial.printf("Cookie: %s\r\n", http.header("Cookie").c_str());
//Serial.printf("Set-Cookie: %s\r\n", http.header("Set-Cookie").c_str());
String totCookie = http.header("Set-Cookie").c_str();
//Serial.println(totCookie);
int isteken = totCookie.indexOf("JSESSIONID=") + 11;
int puntkommateken = totCookie.indexOf(";",isteken);
JSESSIONID = totCookie.substring(isteken, puntkommateken);
//Serial.println(JSESSIONID);
isteken = totCookie.indexOf("SERVERID=", puntkommateken ) + 9;
puntkommateken = totCookie.indexOf(";",isteken);
SERVERID = totCookie.substring(isteken, puntkommateken);
//Serial.println(SERVERID);
if ((JSESSIONID != "")&&(SERVERID != "")){
cookiestep= true;
ConnectionPossible = true;
}
}
http.begin("http://server-api.growatt.com/newPlantAPI.do?action=getUserCenterEnertyData");
//http.begin("http://server.growatt.com/PlantDetailAPI.do?type=4&plantId=DJE2A02071");
http.addHeader("Cookie", "JSESSIONID=" + JSESSIONID + ";" + "SERVERID=" + SERVERID);
//http.addHeader("Set-Cookie", "JSESSIONID=" + JSESSIONID + ";" + "SERVERID=" + SERVERID);
http.setReuse(true);
http.setUserAgent("Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A6003 Build/PKQ1.180716.001");
http.addHeader("Content-type", "application/x-www-form-urlencoded");
http.addHeader("Referer", "http://server.growatt.com/LoginAPI.do");
http.collectHeaders(headerkeys,headerkeyssize);
code = http.POST("language=1"); //Send the request
//code = http.GET();
//Serial.printf("[HTTP] POST... code: %d\r\n", code);
if ((code=200) || (code = 301) || (code = 302)){
String payload = http.getString();; //Get the request response payload
Serial.println(payload); //Print the response payload
const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
DynamicJsonBuffer jsonBuffer(capacity);
//Serial.println("JSON covert..");
JsonObject& root = jsonBuffer.parseObject(payload);
if (!root.success()) {
Serial.println("parseObject() failed");
}
else {
String subroot = root["powerValue"];
String DayValue = root["todayStr"];
String MonthValue = root["monthStr"];
// Need to add something like this:
last_value = today_value; // keep track of the previous datum
// then continue in floating point,
today_value = (double)DayValue; // Need to do a conversion of DayValue first.
// Not sure how to do that, don't have the json data.
// and add:
increment = today_value - last_value + remainder; // not sure about the units, need to check that.
// Need to add the last bit of energy from the last pulse,
// since that has not been accounted for yet.
number_of_pulses = floor( increment / energy_per_pulse ); // do we have a complete math library including floor()?
remainder = increment % number_of_pulses; // does arduino support the modulo operator?
power = increment / 60 ; // From energy in the last timestep to power.
// (assuming a 60s interval, and energy in Joule).
freq_out = number_of_pulses / timestep; // in Hz
// end addition
monthval = MonthValue;
subroot.trim();
CurrentValue = subroot.toInt();
// replace
//if (CurrentValue > 1) {
//timebetweenpulses = 3600000/CurrentValue;
// with:
if ( number_of_pulses > 0 )
timebetweenpulses = (int)( freq_out * 60000 ); // assuming 60k millisecond time intervals
// end replacement.
}
else{
timebetweenpulses = 0;
}
Getdatafrequencyset = Getdatafrequency;
}
}
http.end(); //Close connection
}
void blinkled() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
digitalWrite(meteradapter, HIGH);
delay(100); // wait for some time
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
digitalWrite(meteradapter, LOW);
}
void loop() {
//all reset pages have been acknowledged and reset parameters have been set
webserver();
if ((reset1) && (reset2)) {
delay(2000);
Serial.println("Reset required");
Serial.println("saving config");
SPIFFS.remove("/config.json");
WiFiManager wifiManager;
wifiManager.resetSettings();
delay(500);
ESP.reset();
}
currentMillis = millis(); //get the current "time" (actually the number of milliseconds since the program started)
//if (currentMillis - startMillis >= 2000) { //test at 2 s interval (=1800 Watt/hr)
if (currentMillis - startMillis >= period) { //test whether the period has elapsed
Serial.print("Current Value :");
Serial.print(CurrentValue);
Serial.print(", Pulse at time :");
Serial.println(timebetweenpulses);
blinkled();
period = timebetweenpulses;
startMillis = currentMillis; //IMPORTANT to save the start time of the current LED state.
}
currentMillis2 = millis(); //get the current "time" (actually the number of milliseconds since the program started)
//if (currentMillis - startMillis >= 2000) { //test at 2 s interval (=1800 Watt/hr)
if (currentMillis2 - startMillis2 >= Getdatafrequencyset) { //test whether the period has elapsed
//get new data from server
if (WiFi.status() == WL_CONNECTED) { //Check WiFi connection status
getdata();
}
startMillis2 = currentMillis2; //IMPORTANT to save the start time of the current LED state.
}
}