Menu
WiFi Мониторинг UPS smart APC

WiFi Мониторинг UPS smart APC

Плата WiFi Мониторинга UPS типа smart APC и похожих.

Основной используемый принцип  -запросы и обработка ответов по смарт-протоколу APC

Данная статья является продолжением предыдущей.

За исключением того, что плату мониторинга/управления собираем на основе WemosD1mini.

Дешевизна решения очевидна, с учетом того, что самая дешевая сетевая плата AP9640 (без внешних датчиков)  стоит более 500$

Плата собрана по традиции в коробочке из под Тик-Така.

Цель реализации:

  • мониторинг основных параметров UPS на WEB странице, а также в представлении XML и JSON
  • доступ к плате по WiFi. Сохранение параметров подключения
  • сигнализация аварии - отправка сообщения на PHP скрипт по ссылке
  • сбор информации о окружающей температуре и влажности
  • сохранение настроек в EEPROM  FLASH

Детали

  • Wemos D1 mini
  • max3232 module без DB9 разъема
  • разъем DB9 папа
  • DHT-21 датчик температуры и влажности (опционально)

Схема простого подключения APC smart к RS232 COM порту ПК:

Схема микроконтроллера в связке с UPS

Код программы

В коде применены нестандартные библиотеки.

Помимо основной esp8266, ESP_EEPROM и WiFiManager

ups_alarm_script_url - ссылка на скрипт, для передачи алерта пропадания/появления питания, на email или телеграмм, в формате http://server/mail=  или похожем.

Основные Команды

Y        Enter smart mode    SM

R        Exit smart mode    SM

^A        Model string        SMART-UPS 700

^N 1,5s ^N        Turn on UPS

K 1,5s K    Turn off      

L        Input line voltage 

P        Power load %        000.0

Q        Status flags        08

    08 = on line, battery OK

    10 = on battery, battery OK

    50 = on battery, battery low

Z        Shutdown immediately    n/a

f        Battery level        099.0

Сообщения

!!!        нет входного напряжения

$        появление входного напряжения

Скетч для Arduino IDE

Раскрыть Код

// wemosD1mini + max3232 + DHT21 // version 23/02/15 #include "DHT.h" #include <Ticker.h> #include <ESP8266WiFi.h> #include <ESP_EEPROM.h> // https://github.com/jwrw/ESP_EEPROM // for WEB #include <WiFiClient.h> #include <ESP8266WebServer.h> #include <WiFiManager.h> // https://github.com/tzapu/WiFiManager #include <SoftwareSerial.h> #define RXD2 D7 #define TXD2 D8 SoftwareSerial SerialCO2(RXD2, TXD2); const char* host = "gardaricafm.ru"; // remote page for sync time String url = "/gpio/themp.php"; // GET password String url_get_password = "12345"; String last_url = ""; String device_ip; String inString; String ups_desc[] = { "Y", "Bat_volt", "Int_temp", "Input_volt", "Power_loadPr", "Bat_levelPr", "Status"}; String ups_sent[] = { "Y", "B", "C", "L", "P", "f", "Q"}; float ups_reciv[20]; int ups_count = 0; int ups_maxcount; String ups_alarm_script_url; bool ups_alarm = false; float h_max_comfort=60; //WiFiServer server(80); ESP8266WebServer server ( 80 ); Ticker dht_get; Ticker tim; Ticker key_r; //#define RST_OLED 16 #define DHTPIN 14 #define DHTTYPE DHT21 DHT dht(DHTPIN, DHTTYPE); #define button12 12 #define relay5 5 float humi = 0; float temp = 0; String chip_id = ""; int temp_max = 20; int temp_delta = 1; float temp_real = 0; bool temp_auto = true; float temp_arr[4]; bool temp_arr_full = false; int temp_index = 0; int fail_connect =0; int time_update =10; int analogval =0; int hour=0; int minu=0; int secu=0; int clnts=0; int point=0; bool but_state = false; bool but_last_state = false; int but_count = 0; int led_delay = 1; int str2_count = 0; int str2_state = 0; float refr_cnt=883; String datas="Init"; bool dht_get_flag = false; bool tm_flag = false; bool key_flag = false; bool updated = false; String tm_capt = ""; void http_get_finc( int operation, String sens_id, float value) { fail_connect= millis(); if((WiFi.status() == WL_CONNECTED)) { // Use WiFiClient class to create TCP connections WiFiClient client; String line = ""; if(operation==1) { if (client.connect(host, 80)) { // This will send the request to the server line = url + "?act=Hi" + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"; last_url = line; client.print(String("GET ") + line); // Read all the lines of the reply from server and print them to Serial line = ""; while (client.connected()) { if (client.available()) { line = client.readStringUntil('\n'); if(line.indexOf('now')>0) { String st= line.substring(3,5); hour = st.toInt(); st= line.substring(5,7); minu = st.toInt(); st= line.substring(7,9); secu = st.toInt(); Serial.println(line); } } if(millis()-fail_connect > 5000) client.stop(); } client.stop(); last_url += line; // Serial.println("disconnect"); } else { Serial.println("connection failed!]"); client.stop(); } } if(operation==2 && sizeof(ups_alarm_script_url)>1) // ОТПРАВКА { String u_host = ""; String u_url=""; int slsh=0; for (int i = 1; i < ups_alarm_script_url.length(); i++) { if(ups_alarm_script_url[i]=='/') slsh++; if(slsh==2 && ups_alarm_script_url[i]!='/') u_host += ups_alarm_script_url[i]; if(slsh>2 ) u_url += ups_alarm_script_url[i]; } Serial.print(ups_alarm_script_url.length()); Serial.println(ups_alarm_script_url); Serial.println("Try server:" + u_host + " url:" + u_url); if (client.connect(host, 80)) { line = u_url + sens_id + " HTTP/1.1\r\n" + "Host: " + u_host + "\r\n" + "Connection: close\r\n\r\n"; last_url = line; client.print(String("GET ") + line); line = ""; while (client.connected()) { if (client.available()) { line = client.readStringUntil('\n'); if(line.indexOf('saved')>0) Serial.println(line); } if(millis()-fail_connect > 5000) client.stop(); } client.stop(); last_url += line; } else { Serial.println("connection failed!]"); client.stop(); } } if(operation==3) // ПОЛУЧЕНИЕ { if (client.connect(host, 80)) { // This will send the request to the server line = url + "?act=simple&sens_id=" + sens_id + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"; last_url = line; client.print(String("GET ") + line); // Read all the lines of the reply from server and print them to Serial line = ""; while (client.connected()) { if (client.available()) { line = client.readStringUntil('\n'); // bool variables String st= line.substring(0,1); bool state = false; if(st=="0" || st=="") state = false; if(st=="1" || st=="5") state = true; } if(millis()-fail_connect > 5000) client.stop(); } client.stop(); last_url += line; } else { Serial.println("connection failed!]"); client.stop(); } } } Serial.println(last_url); dht_get_flag = false; } void time_finc() { secu=secu+30; if(secu>59) { secu=0; minu++; } if(minu>59) { minu=0; hour++; } if(hour>23) hour=0; if(secu==0) { Serial.print(hour); Serial.print(":"); Serial.print(minu); Serial.print(":"); Serial.print(secu); Serial.print(": IP "); Serial.println(WiFi.localIP().toString() ); device_ip = WiFi.localIP().toString(); } if(updated==false && WiFi.status() == WL_CONNECTED) { http_get_finc( 1, "", 0); // sync time updated=true; } ups_count = -1; Serial.println(""); tm_flag=false; } void key_reader() { bool but = digitalRead(button12); if(but==but_last_state && but==false) { but_count++; digitalWrite(2, HIGH ); delay(60); digitalWrite(2, LOW ); delay(60); digitalWrite(2, HIGH ); delay(60); digitalWrite(2, LOW ); delay(60); led_delay=0; Serial.println(but_count); } if(but!=but_last_state) { Serial.print("But: "); Serial.println(but); if(but==true) // действие { if(but_count>2 && but_count<8) { Serial.println("Manual mode. Invert control"); temp_auto=false; digitalWrite(relay5, !digitalRead(relay5)); } if(but_count>9) { Serial.println("Reset"); ESP.reset(); } but_count=0; } } but_last_state = but; digitalWrite(2, LOW ); if(temp_auto==false && led_delay>6) { digitalWrite(2, HIGH ); led_delay=0; } if(temp_auto==true && led_delay>3){ digitalWrite(2, HIGH ); led_delay=0; } led_delay++; if(ups_count < ups_maxcount-1) { ups_count++; SerialCO2.println(ups_sent[ups_count]); } key_flag = false; } void handleRoot() { char temp[550]; snprintf ( temp, 550, "<html>\ <head>\ <meta http-equiv='refresh' content='5'/>\ <meta charset=\"utf-8\">\ <title>ESP d1mini module - UPS version</title>\ <style>\ body { background-color: #cfc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\ </style>\ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> \ </head>\ <body>\ <h1>ESP d1mini module </h1>\ <a href=\"index.php\">main page</a> <br /> \n \ <a href=index.xml>XML page</a> <br />\n \ <a href=index.json>JSON page</a> <br />\n \ </body></html>" ); server.send ( 200, "text/html", temp ); } void handleNotFound() { String s = " "; char footer[400]; snprintf ( footer, 400, "<html> <head> \ <meta charset=\"utf-8\">\ <title>ESP d1mini module</title>\ <style> \ body { background-color: #cfc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\ </style>\ <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> \ </head><body>\ <h1>ESP d1mini module - UPS version</h1>"); char button[30]; snprintf ( button, 30, "</body> </html>" ); // Serial.println(s); if(server.uri()=="/index.php" && server.args()>0) { String temp_max1; String temp_delta1; String temp_auto1; for ( uint8_t i = 0; i < server.args(); i++ ) { // settings if(server.argName ( i ) == "ups_alarm_script_url" ) { ups_alarm_script_url = server.arg ( i ); Serial.println("Settings> ups_alarm_script_url:" + ups_alarm_script_url ); int str_sz = ups_alarm_script_url.length(); EEPROM.put(0, str_sz); for (int i = 0; i < ups_alarm_script_url.length(); i++) EEPROM.put(i+4, ups_alarm_script_url[i]); boolean res1 = EEPROM.commit(); Serial.print("Write to FLASH ups_alarm_script_url:" + ups_alarm_script_url + " Size:"); Serial.print(str_sz); if(res1==true) Serial.println(" OK"); else Serial.println(" FALSE"); } if(server.argName(i)=="control") { ups_count=10; if(server.arg(i)=="test") { SerialCO2.println("W"); Serial.println("comment test"); } if(server.arg(i)=="on") { Serial.println("comment ON"); SerialCO2.println("^N"); delay(1700); SerialCO2.println("^N"); } if(server.arg(i)=="off") { Serial.println("comment OFF"); SerialCO2.println("Z"); delay(1700); SerialCO2.println("Z"); } } // reset if(server.argName(i) == "reset" && server.arg(i) == "run") ESP.reset(); //ESP.restart() } // arg for } // index String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><params>"; String json = "{"; String s_tmp = ""; s += "<form action=\"/index.php\" method=\"post\"> \ Alarm URL: <input type=\"text\" name=\"ups_alarm_script_url\" value=\""+ups_alarm_script_url+"\" > \ <input type=\"submit\" value=\"Set\"> \ </form>\n"; s += "<form action=\"/index.php\" method=\"post\"><input type=\"hidden\" name=\"control\" value=\"test\"><input type=\"submit\" value=\"test\"> </form>\n"; s += "<form action=\"/index.php\" method=\"post\"><input type=\"hidden\" name=\"control\" value=\"on\"><input type=\"submit\" value=\"on\"> </form>\n"; s += "<form action=\"/index.php\" method=\"post\"><input type=\"hidden\" name=\"control\" value=\"off\"><input type=\"submit\" value=\"off\"> </form>\n"; if(digitalRead(relay5)==1) s_tmp="off"; else s_tmp="on"; s += "<form action=\"/index.php\" method=\"post\">Relay 5<input type=\"hidden\" name=\"r5\" value=\""+s_tmp+"\"><input type=\"submit\" value=\""+s_tmp+"\"> </form>\n"; xml += "<out5>"; xml +=digitalRead(relay5); xml +="</out5>"; json += "\"out5\": \""; json +=digitalRead(relay5); json +="\","; for (int i = 1; i < ups_maxcount; i++) { s += ups_desc[i]; s += i; s += " "; s += ups_reciv[i]; s += "<br />\n"; xml += "<"; xml += ups_desc[i]; xml +=">"; xml += ups_reciv[i]; xml += "</"; xml += ups_desc[i]; xml +=">"; json += "\""; json += ups_desc[i]; json += "\": "; json += "\""; json += ups_reciv[i]; json += "\","; } switch (int(round(ups_reciv[6]))) { case 0: s += "OFF line"; break; case 8: s += "ON line"; break; case 10: s += "on battery - OK"; break; case 50: s += "on battery - LOW"; break; default: s += "Bad Status"; } s += "<br />\n"; s += "UPS_alarm: "; s += ups_alarm; s += " <br /> \n"; xml += "<UPS_alarm>"; xml+=ups_alarm; xml += "</UPS_alarm>"; json += "\"UPS_alarm\": \""; json +=ups_alarm; json +="\","; s += "UPS_alarm_URL: "; s += ups_alarm_script_url; s += " <br /> \n"; json += "\"UPS_alarm_URL\": \""; json +=ups_alarm_script_url; json +="\","; s += "Temperature = "; s += temp; s += "<br />\n"; xml += "<temperature>"; xml+= temp; xml += "</temperature>"; json += "\"temperature\": \""; json +=temp; json +="\","; s += "Humidity = "; s += humi; s += "<br />\n"; xml += "<humidity>"; xml += humi; xml += "</humidity>"; json += "\"humidity\": \""; json +=humi; json +="\","; s += "FreeMem = "; s += round(ESP.getFreeHeap()/1024); s += " kB<br /> \n"; s += "Uptime = "; s += round(millis() / (1000 * 60 * 60)); s += " hours <br />\n"; xml += "<Uptime>"; xml += round(millis() / (1000 * 60 * 60)); xml+="</Uptime>"; json += "\"Uptime\": \""; json +=round(millis() / (1000 * 60 * 60)); json +="\","; s += "MAC: "; s += chip_id; s += " <br /> \n"; xml += "<MAC>"; xml+=chip_id; xml+="</MAC>"; json += "\"MAC\": \""; json +=chip_id; json +="\""; s += "Available input Params: temp_max[int], temp_delta[int], temp_auto[on/off], r5[on/off] <br />\n"; s += "URI: "; s += server.uri(); s += "<br />\n"; s += " Method: "; s += ( server.method() == HTTP_GET ) ? "GET" : "POST"; s += "\n"; for ( uint8_t i = 0; i < server.args(); i++ ) { s += " " + server.argName ( i ) + ": " + server.arg ( i ) + "<br />\n"; } s += " Arguments: "; s += server.args(); s += "<br />\n"; s += "<a href=index.php>Index page</a> <br /> \n \ <a href=index.xml>XML page</a> <br />\n \ <a href=index.json>JSON page</a> <br />\n"; xml += "</params>"; json += "}"; if(server.uri()=="/index.php" ) server.send ( 200, "text/html", footer + s + button ); if(server.uri()=="/index.xml") server.send ( 200, "text/xml", xml ); if(server.uri()=="/index.json") server.send ( 200, "application/json", json ); } void key_event() { if(!tm_flag && !dht_get_flag) key_flag = true; } void dht_get_event() { if(!tm_flag ) dht_get_flag = true; } void tm_event() { tm_flag=true; } void setup() { Serial.begin(115200); SerialCO2.begin(2400); Serial.println("System inits"); pinMode(5, OUTPUT); digitalWrite(relay5, LOW); pinMode(2, OUTPUT); // BUILTLED pinMode(12, INPUT); // timers in sec: dht_get.attach(302, dht_get_event); // 937 tim.attach(30.0, tm_event); key_r.attach(1.0, key_event); //0.3 //chip_id = ESP.getChipId(); chip_id = WiFi.macAddress(); dht.begin(); //WiFiManager, Local intialization. Once its business is done, there is no need to keep it around WiFiManager wm; wm.setConfigPortalTimeout(30); // auto close configportal after n seconds bool res; res = wm.autoConnect("ESP-AP","12344321"); // password protected ap if(!res) { Serial.println("Failed to connect"); // ESP.restart(); } else { //if you get here you have connected to the WiFi Serial.println("connected...yeey :)"); } // Start the server server.on ( "/", handleRoot ); server.onNotFound ( handleNotFound ); server.begin(); Serial.println(chip_id +"Server started"); // Print the IP address Serial.println(WiFi.localIP()); humi = dht.readHumidity(); Serial.print(" H: "); Serial.print(humi); temp = dht.readTemperature(); Serial.print(" T: "); Serial.println(temp); ups_maxcount = sizeof(ups_desc)/sizeof(ups_desc[0]); /////////////////// eeprom EEPROM.begin(150); // looks like 16 bytes is the minimum Serial.print("reading ups_alarm_script_url.. Size:"); int str_sz2; EEPROM.get(0, str_sz2); Serial.print(str_sz2); char t; for (int i = 0; i < str_sz2; i++) { EEPROM.get(i+4, t); ups_alarm_script_url += t; } Serial.println(ups_alarm_script_url); } void loop() { if(tm_flag) time_finc(); if(key_flag && !dht_get_flag && !tm_flag) key_reader(); if(!key_flag && !dht_get_flag && !tm_flag) {// ArduinoOTA.handle(); server.handleClient();// http_server(); } if (Serial.available()) { int inByte = Serial.read(); SerialCO2.write(inByte); } while (SerialCO2.available()) { char inChar2 = SerialCO2.read(); if(isdigit(inChar2)) inString += inChar2; if(inChar2 == '.') inString += ","; if (inChar2 == '!') { Serial.println("No Input voltage"); if(ups_alarm != true) http_get_finc( 2, "No_Input_voltage_on_" + device_ip, 0); ups_alarm = true; } if (inChar2 == '$') { ups_alarm = false; Serial.println("Return from Fail"); http_get_finc( 2, "Return_from_Fail_on_" + device_ip, 0); } if (inChar2 == '\n' && inString.length()>0) { Serial.print(ups_desc[ups_count]); Serial.print(": "); ups_reciv[ups_count] = inString.toFloat(); Serial.print(ups_reciv[ups_count]); Serial.print("; "); inString = ""; } } if (!key_flag && !tm_flag && dht_get_flag) { Serial.println("dht_get_flag"); humi = dht.readHumidity(); Serial.print(" H: "); Serial.print(humi); temp = dht.readTemperature(); Serial.print(" T: "); Serial.print(temp); // check wifi if(WiFi.status() != WL_CONNECTED) { Serial.println("Not connected"); } else { time_update++; if(time_update>10) { http_get_finc( 1, "", 0); // sync time time_update=0; } } dht_get_flag = false; } }

Lisolog / 2023