For my inverter control unit I was thinking of a web interface, so first you need an easy way to connect the device to the WiFi network.
For this purpose exist a simple library that I explain in “How to manage dynamic WIFI configuration on esp8266 or esp32“, this allows you to start the control unit starts in AP mode and from here you can connect and configure your wifi.
When you connect to the AP, automatically you are redirected to the WIFI configuration page, here you con select your AP and insert your password.
Now you can connect to the url http://InverterCentraline but in some network configuration not work mDNS (Multicast DNS) so you must check what IP you have received, you can connect to the Serial port for debug or use program (for Android phone or Desktop) like Fing that scan all device in your network.
If no configuration is specified, the command that starts the whole process is
wifiManager.autoConnect("InverterCentralineConfiguration");
otherwise if a configuration is found, the data will be set using this code
DEBUG_PRINT(F("Set static data..."));
if (isDNS){
wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn, _dns1, _dns2);
}else{
wifiManager.setSTAStaticIPConfig(_ip, _gw, _sn);
}
But be careful, WiFiManager by default does not support DNS configuration, so you will have to use my library
https://github.com/xreef/WiFiManager
I added DNS management and it allows us to use esp8266 as a client and make requests to the outside.
When the device restarts, hopefully :D, you can always use that ip to manage the inverter.
When you restart the device, If all It’s ok :D, you can use that ip to manage the Inverter.
REST Server
I separate REST server from Web Server, and I implemented a series of API to control the device in all part.
You can have more information about REST server on esp8266 on these articles “How to create a REST server on esp8266 or esp32“
This is the code where I census the rest REST end-points.
void restServerRouting() {
// httpRestServer.header("Access-Control-Allow-Headers: Authorization, Content-Type");
//
httpRestServer.on("/", HTTP_GET, []() {
httpRestServer.send(200, F("text/html"),
F("Welcome to the Inverter Centraline REST Web Server"));
});
httpRestServer.on(F("/production"), HTTP_GET, getProduction);
httpRestServer.on(F("/productionTotal"), HTTP_GET, getProductionTotal);
httpRestServer.on(F("/monthly"), HTTP_GET, getMontlyValue);
httpRestServer.on(F("/historical"), HTTP_GET, getHistoricalValue);
httpRestServer.on(F("/config"), HTTP_OPTIONS, sendCrossOriginHeader);
httpRestServer.on(F("/config"), HTTP_POST, postConfigFile);
httpRestServer.on(F("/config"), HTTP_GET, getConfigFile);
httpRestServer.on(F("/inverterInfo"), HTTP_GET, getInverterInfo);
httpRestServer.on(F("/inverterState"), HTTP_GET, getInverterLastState);
httpRestServer.on(F("/inverterDayWithProblem"), HTTP_GET, inverterDayWithProblem);
httpRestServer.on(F("/inverterDayState"), HTTP_GET, getInverterDayState);
httpRestServer.on(F("/serverState"), HTTP_GET, getServerState);
httpRestServer.on(F("/battery"), HTTP_GET, getBatteryInfo);
httpRestServer.on(F("/reset"), HTTP_GET, getReset);
}
All endpoints can operate in CORS (check REST server article).
You can find all end point with example, documentation and how to call these in various language at this link.
Here the documentation of the API.
Post config file
POST
http://192.168.1.15:8080/config
BODY raw
{
"server": {
"hostname": "invertercentraline",
"isStatic": true,
"address": "192.168.1.15",
"gatway": "192.168.1.1",
"netMask": "255.255.255.0",
"dns1": "8.8.8.8",
"dns2": "85.37.17.51"
},
"serverSMTP": {
"server": "smtp.gmail.com",
"port": 465,
"login": "account@gmail.com",
"password": "yourpasswd",
"from": "emailfrom@gmail.com"
},
"emailNotification": {
"isNotificationEnabled": true,
"subject": "Notifica inverter",
"messageProblem": "Un problema è stato rilevato sull'inverter:",
"messageNoProblem": "E' stato ripristinato lo stato del tuo inverter:",
"emailList": [
{
"email": "notificationemail@gmail.com",
"name": "Renzo Mischianti",
"alarm": "on_problem",
"ch1": "on_problem",
"ch2": "none",
"state": "on_problem"
}
]
},
"preferences": {
"GTM": {
"timeZoneId": 31,
"gmtAdjustment": "GMT+01:00",
"useDaylightTime": 1,
"value": 1,
"description": "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"
},
"DST": {
"id": 2,
"code": "CET",
"description": "Central European Time (Frankfurt, Paris)"
},
"adminEmail": "adminemail@gmail.com",
"undefined": {
"id": 0,
"code": "GMT",
"description": "Standard GMT"
}
}
}
JS
var data = JSON.stringify({"server":{"hostname":"invertercentraline","isStatic":true,"address":"192.168.1.15","gatway":"192.168.1.1","netMask":"255.255.255.0","dns1":"8.8.8.8","dns2":"85.37.17.51"},"serverSMTP":{"server":"smtp.gmail.com","port":465,"login":"account@gmail.com","password":"miapassword","from":"emailfrom@gmail.com"},"emailNotification":{"isNotificationEnabled":true,"subject":"Notifica inverter","messageProblem":"Un problema è stato rilevato sull'inverter:","messageNoProblem":"E' stato ripristinato lo stato del tuo inverter:","emailList":[{"email":"emailsend@gmail.com","name":"Renzo Mischianti","alarm":"on_problem","ch1":"on_problem","ch2":"none","state":"on_problem"}]},"preferences":{"GTM":{"timeZoneId":31,"gmtAdjustment":"GMT+01:00","useDaylightTime":1,"value":1,"description":"(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"},"DST":{"id":2,"code":"CET","description":"Central European Time (Frankfurt, Paris)"},"adminEmail":"adminemail@gmail.com","undefined":{"id":0,"code":"GMT","description":"Standard GMT"}}});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("POST", "http://192.168.1.15:8080/config");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
Get config file
GET
http://192.168.1.11:8080/config
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/config");
xhr.send();
RESPONSE
{
"server": {
"hostname": "invertercentraline",
"isStatic": true,
"address": "192.168.1.11",
"gatway": "192.168.1.1",
"netMask": "255.255.255.0",
"dns1": "85.37.17.12",
"dns2": "8.8.8.8"
},
"serverSMTP": {
"server": "smtp.gmail.com",
"port": 465,
"login": "account@gmail.com",
"password": "yourpasswd",
"from": "emailfrom@gmail.com"
},
"emailNotification": {
"isNotificationEnabled": true,
"subject": "Notifica inverter",
"messageProblem": "Un problema è stato rilevato sull'inverter:",
"messageNoProblem": "E' stato ripristinato lo stato del tuo inverter:",
"emailList": [
{
"email": "adminemail@gmail.com",
"name": "Renzo Mischianti",
"alarm": "on_problem",
"ch1": "none",
"ch2": "none",
"state": "on_problem"
}
]
},
"preferences": {
"GTM": {
"timeZoneId": 31,
"gmtAdjustment": "GMT+01:00",
"useDaylightTime": 1,
"value": 1,
"description": "(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna"
}
}
}
Get production data
GET
http://192.168.1.11:8080/production?day=20181020&type=power
PARAMS
day | 20181020 | YYYYMMDD |
type | power |
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/production?day=20181130&type=power");
xhr.send();
RESPONSE
{
"lastUpdate": "30/11/2018 16:25:49",
"data": [
{
"h": "1520",
"val": 76.6
},
{
"h": "1525",
"val": 87.3
},
{
"h": "1530",
"val": 92.2
},
{
"h": "1535",
"val": 113.8
},
{
"h": "1540",
"val": 112.3
},
{
"h": "1545",
"val": 135.3
},
{
"h": "1550",
"val": 122.1
},
{
"h": "1555",
"val": 87.3
},
{
"h": "1600",
"val": 60.9
},
{
"h": "1605",
"val": 48.2
},
{
"h": "1610",
"val": 44.7
},
{
"h": "1615",
"val": 54
},
{
"h": "1620",
"val": 27.5
},
{
"h": "1625",
"val": 7.2
}
]
}
Get stats for month
http://192.168.1.11:8080/monthly?month=201810
PARAMS
month | 201801 | YYYYMM |
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/monthly?month=201810");
xhr.send();
RESPONSE
{
"lastUpdate": "21/01/2020 23:58:21",
"data": {
"2235": 4.036887,
"2255": 4.012887,
"2258": 4.009645,
"2318": 3.996532,
"2338": 3.973693,
"2358": 3.963484
}
}
Inverter state
http://192.168.1.11:8080/inverterState
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/inverterState");
xhr.send();
RESPONSE
{
"lastUpdate": "01/12/2018 17:06:08",
"alarmStateParam": 0,
"alarmState": "No Alarm",
"channel1StateParam": 2,
"channel1State": "MPPT",
"channel2StateParam": 7,
"channel2State": "Input Low",
"inverterStateParam": 2,
"inverterState": "Run"
}
Inverter day where problem occurs
http://192.168.1.11:8080/inverterDayWithProblem
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/inverterDayWithProblem");
xhr.send();
Inverter day state
http://192.168.1.11:8080/inverterDayState?day=20181023
PARAMS
day | 20181023 |
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/inverterDayState?day=20181023");
xhr.send();
RESPONSE
{
"lastUpdate": null,
"data": [
{
"h": "0753",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "0958",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1035",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1041",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1045",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1050",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1052",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1056",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1058",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1100",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1102",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1104",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1106",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1109",
"asp": 0,
"c1sp": 2,
"c2sp": 7,
"isp": 2
},
{
"h": "1111",
"asp": 2,
"c1sp": 2,
"c2sp": 7,
"isp": 2
}
]
}
Production total
http://192.168.1.11:8080/productionTotal
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.11:8080/productionTotal");
xhr.send();
RESPONSE
{
"lastUpdate": "01/12/2018 17:05:07",
"energyLifetime": 20213214,
"energyYearly": 3142014,
"energyMonthly": 6150,
"energyWeekly": 26081,
"energyDaily": 6151,
"W": "201848",
"M": "201812",
"Y": "2018"
}
Historical data
GET
http://192.168.1.15:8080/historical?frequence=years&year=2018
PARAMS
frequence | years |
year | 2018 |
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "192.168.1.15:8080/historical?frequence=years&year=2018");
xhr.send();
RESPONSE
{
"2020": 155011
}
Server state
http://192.168.1.15:8080/serverState
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "192.168.1.15:8080/serverState");
xhr.send();
RESPONSE
{
"network": {
"ip": "192.168.1.15",
"gw": "192.168.1.1",
"nm": "255.255.255.0",
"dns1": "8.8.8.8",
"dns2": "85.37.17.51",
"signalStrengh": -41
},
"lastUpdate": "27/01/2020 08:46:49",
"chip": {
"chipId": 8644639,
"flashChipId": 1458208,
"flashChipSize": 4194304,
"flashChipRealSize": 4194304,
"freeHeap": 28096,
"batteryVoltage": 0.059468
}
}
Battery state
http://192.168.1.15:8080/battery?day=20200121
PARAMS
day | 20200121 |
JS
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function() {
if(this.readyState === 4) {
console.log(this.responseText);
}
});
xhr.open("GET", "http://192.168.1.15:8080/battery?day=20200121");
xhr.send();
RESPONSE
{
"lastUpdate": "21/01/2020 23:58:21",
"data": {
"2235": 4.036887,
"2255": 4.012887,
"2258": 4.009645,
"2318": 3.996532,
"2338": 3.973693,
"2358": 3.963484
}
}
Thanks
- ABB Aurora Web Inverter Monitor (WIM): project introduction
- ABB Aurora Web Inverter Monitor (WIM): wiring Arduino to RS-485
- ABB Aurora Web Inverter Monitor (WIM): storage devices
- ABB Aurora Web Inverter Monitor (WIM): debug and notification
- ABB Aurora Web Inverter Monitor (WIM): set time and UPS
- ABB Aurora Web Inverter Monitor (WIM): WIFI configuration and REST Server
- ABB Aurora Web Inverter Monitor (WIM): WebSocket and Web Server
- ABB Aurora Web Inverter Monitor (WIM): Wiring and PCB soldering
- ABB Aurora Web Inverter Monitor (WIM): upload the sketch and front end
- ABB Aurora web inverter Monitor (WIM): 3D printed case to complete project
- ABB Aurora web inverter monitor (WIM): repair E013 error
GitHub repository with all code BE and FE transpiled