cjcharles

Arduino Powermax code

Apr 1st, 2017
375
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #include <pmax.h>
  2. #include <ESP8266WiFi.h>
  3. #include <WiFiClient.h>
  4. #include <WiFiUdp.h>
  5. #include <ESP8266WebServer.h>
  6. #include <ArduinoOTA.h>
  7. #include <EEPROM.h>
  8.  
  9. //////////////////// IMPORTANT DEFINES, ADJUST TO YOUR NEEDS //////////////////////
  10. //////////////////// COMMENT DEFINE OUT to disable a feature  /////////////////////
  11. //Telnet allows to see debug logs via network, it allso allows CONTROL of the system if PM_ALLOW_CONTROL is defined
  12. #define PM_ENABLE_TELNET_ACCESS
  13.  
  14. //This enables control over your system, when commented out, alarm is 'read only' (esp will read the state, but will never ardm/disarm)
  15. #define PM_ALLOW_CONTROL
  16.  
  17. //This enables flashing of ESP via OTA (WiFI)
  18. #define PM_ENABLE_OTA_UPDATES
  19.  
  20. //This enables ESP to send a multicast UDP packet on LAN with notifications
  21. //Any device on your LAN can register to listen for those messages, and will get a status of the alarm
  22. //#define PM_ENABLE_LAN_BROADCAST
  23.  
  24. //Those settings control where to send lan broadcast, clients need to use the same value. Note IP specified here has nothing to do with ESP IP, it's only a destination for multicast
  25. //Read more about milticast here: http://en.wikipedia.org/wiki/IP_multicast
  26. IPAddress PM_LAN_BROADCAST_IP(192, 168, 32, 12);
  27. #define   PM_LAN_BROADCAST_PORT  23127
  28.  
  29. //Specify your WIFI settings:
  30. #define WIFI_SSID ""
  31. #define WIFI_PASS ""
  32. #define IP_FOR_ST "192.168.1.199"
  33. #define PORT_FOR_ST "39500"
  34. const int httpPort = 39500;
  35.  
  36. #define ALARM_STATE_CHANGE 0
  37. #define ZONE_STATE_CHANGE 1
  38.  
  39.  
  40. void SendJSONMessage(const char* ZoneOrEvent, const char* WhoOrState, const unsigned char zoneID, int zone_or_system_update) {
  41.   char message_text[600];
  42.   message_text[0] = '\0';
  43.  
  44.   //Convert zone ID to text
  45.   char zoneIDtext[10];
  46.   itoa(zoneID, zoneIDtext, 10);
  47.  
  48.   DEBUG(LOG_NOTICE,"Creating JSON string");
  49.   //Build key JSON headers and structure
  50.   strncpy(message_text, "POST / HTTP/1.1\r\nHost: ", 600);
  51.   strcat(message_text, IP_FOR_ST);
  52.   strcat(message_text, ":");
  53.   strcat(message_text, PORT_FOR_ST);
  54.   strcat(message_text, "\r\nContent-Type: application/json;charset=utf-8\r\nServer: Visonic Alarm\r\nConnection: close\r\n\r\n");
  55.   //strncpy(message_text, " ", 500);
  56.  
  57.   if (zone_or_system_update == ALARM_STATE_CHANGE) {
  58.     //Here we have an alarm status change (zone 0) so put the status into JSON
  59.     strcat(message_text, "{\"stat_str\": \"");
  60.     strcat(message_text, ZoneOrEvent);
  61.     strcat(message_text, "\",\r\n\"stat_update_from\": \"");
  62.     strcat(message_text, WhoOrState);
  63.     strcat(message_text, "\"");
  64.   }
  65.   else if (zone_or_system_update == ZONE_STATE_CHANGE) {
  66.     //Here we have a zone status change so put this information into JSON
  67.     strcat(message_text, "{\"zone_id\": \"");
  68.     strcat(message_text, zoneIDtext);
  69.     strcat(message_text, "\",\r\n\"zone_name\": \"");
  70.     strcat(message_text, ZoneOrEvent);
  71.     strcat(message_text, "\",\r\n\"zone_status\": \"");
  72.     strcat(message_text, WhoOrState);
  73.     strcat(message_text, "\"");
  74.   }
  75.   //Close the JSON string
  76.   strcat(message_text, "}\r\n");
  77.  
  78.   //Useful debug log below, but slows things down so disabled when not needed
  79.   //DEBUG(LOG_NOTICE, message_text);
  80.  
  81.   //If not connected (we shouldnt be) then connect to ST
  82.   WiFiClient client;
  83.   if (!client.connect(IP_FOR_ST, httpPort)) {
  84.     Serial.println("connection failed");
  85.     return;
  86.   }
  87.  
  88.   //Finally send the JSON to ST
  89.   client.print(message_text);
  90. }
  91.  
  92.  
  93. //////////////////////////////////////////////////////////////////////////////////
  94. //NOTE: PowerMaxAlarm class should contain ONLY functionality of Powerlink
  95. //If you want to (for example) send an SMS on arm/disarm event, don't add it to PowerMaxAlarm
  96. //Instead create a new class that inherits from PowerMaxAlarm, and override required function
  97. class MyPowerMax : public PowerMaxAlarm
  98. {
  99. public:
  100.     //Need access to the struct Zone {} here
  101.    
  102.     virtual void OnStatusChange(const PlinkBuffer  * Buff)
  103.     {
  104.         //call base class implementation first, this will send ACK back and upate internal state.
  105.         PowerMaxAlarm::OnStatusChange(Buff);
  106.  
  107.         //Now send update to ST and use zone 0 as system state not zone
  108.         const unsigned char zoneId = 0;
  109.         SendJSONMessage(GetStrPmaxLogEvents(Buff->buffer[4]), GetStrPmaxEventSource(Buff->buffer[3]), zoneId, ALARM_STATE_CHANGE);
  110.         //now our customization:
  111.         switch(Buff->buffer[4])
  112.         {
  113.         case 0x51: //"Arm Home"
  114.         case 0x53: //"Quick Arm Home"
  115.             //OnSytemArmed(Buff->buffer[4], GetStrPmaxLogEvents(Buff->buffer[4]), Buff->buffer[3], GetStrPmaxEventSource(Buff->buffer[3]));
  116.             break;
  117.  
  118.         case 0x52: //"Arm Away"
  119.         case 0x54: //"Quick Arm Away"
  120.             //OnSytemArmed(Buff->buffer[4], GetStrPmaxLogEvents(Buff->buffer[4]), Buff->buffer[3], GetStrPmaxEventSource(Buff->buffer[3]));
  121.             break;
  122.  
  123.         case 0x55: //"Disarm"
  124.             //OnSytemDisarmed(Buff->buffer[3], GetStrPmaxEventSource(Buff->buffer[3]));
  125.             break;
  126.         }        
  127.     }
  128.     virtual void OnStatusUpdatePanel(const PlinkBuffer  * Buff)
  129.     {
  130.         //call base class implementation first, to log the event and update states.
  131.         PowerMaxAlarm::OnStatusUpdatePanel(Buff);
  132.  
  133.         //Now if it is a zone event then send it to SmartThings
  134.         if (this->isZoneEvent()) {
  135.               const unsigned char zoneId = Buff->buffer[5];
  136.               ZoneEvent eventType = (ZoneEvent)Buff->buffer[6];
  137.              
  138.               SendJSONMessage(this->getZoneName(zoneId), GetStrPmaxZoneEventTypes(Buff->buffer[6]), zoneId, ZONE_STATE_CHANGE);  
  139.         }
  140.     }
  141. };
  142. //////////////////////////////////////////////////////////////////////////////////
  143.  
  144. MyPowerMax pm;
  145. ESP8266WebServer server(80);
  146.  
  147. int telnetDbgLevel = LOG_NO_FILTER; //by default only NO FILTER messages are logged to telnet clients
  148.  
  149. #ifdef PM_ENABLE_LAN_BROADCAST
  150. WiFiUDP udp;
  151. unsigned int packetCnt = 0;
  152. #endif
  153.  
  154. #ifdef PM_ENABLE_TELNET_ACCESS
  155. WiFiServer telnetServer(23); //telnet server
  156. WiFiClient telnetClient;
  157. #endif
  158.  
  159. #define PRINTF_BUF 512 // define the tmp buffer size (change if desired)
  160. void LOG(const char *format, ...)
  161. {
  162.   char buf[PRINTF_BUF];
  163.   va_list ap;
  164.  
  165.   va_start(ap, format);
  166.   vsnprintf(buf, sizeof(buf), format, ap);
  167. #ifdef PM_ENABLE_TELNET_ACCESS  
  168.   if(telnetClient.connected())
  169.   {
  170.     telnetClient.write((const uint8_t *)buf, strlen(buf));
  171.   }
  172. #endif  
  173.   va_end(ap);
  174. }
  175.  
  176. void handleRoot() {
  177.   //This returns the 'homepage' with links to each other main page
  178.   unsigned long days = 0, hours = 0, minutes = 0;
  179.   unsigned long val = os_getCurrentTimeSec();
  180.  
  181.   days = val / (3600*24);
  182.   val -= days * (3600*24);
  183.  
  184.   hours = val / 3600;
  185.   val -= hours * 3600;
  186.  
  187.   minutes = val / 60;
  188.   val -= minutes*60;
  189.  
  190.   char szTmp[PRINTF_BUF*2];
  191.   sprintf(szTmp, "<html>Dashboard for esp8266 controlled Visonic Powermax.<br>Uptime: %02d:%02d:%02d.%02d<br>free heap: %u<br><br>Web Commands<br><a href='/armaway' target='_blank'>Arm Away</a><br><a href='/armhome' target='_blank'>Arm Home</a><br><a href='/disarm' target='_blank'>Disarm</a><br><br>JSON Endpoints<br><a href='/status'>Alarm Status</a></html>", (int)days, (int)hours, (int)minutes, (int)val, ESP.getFreeHeap());
  192.   server.send(200, "text/html", szTmp);
  193. }
  194.  
  195. //writes to webpage without storing large buffers, only small buffer is used to improve performance
  196. class WebOutput : public IOutput
  197. {
  198.   WiFiClient* c;
  199.   char buffer[PRINTF_BUF+1];
  200.   int bufferLen;
  201.  
  202. public:
  203.   WebOutput(WiFiClient* a_c)
  204.   {
  205.     c = a_c;
  206.     bufferLen = 0;
  207.     memset(buffer, 0, sizeof(buffer));
  208.   }
  209.  
  210.   void write(const char* str)
  211.   {
  212.     int len = strlen(str);
  213.     if(len < (PRINTF_BUF/2))
  214.     {
  215.       if(bufferLen+len < PRINTF_BUF)
  216.       {
  217.         strcat(buffer, str);
  218.         bufferLen += len;
  219.         return;
  220.       }
  221.     }
  222.    
  223.     flush();
  224.     c->write(str, len);
  225.        
  226.     yield();
  227.   }
  228.  
  229.   void flush()
  230.   {
  231.     if(bufferLen)
  232.     {
  233.       c->write((const uint8_t *)buffer, bufferLen);
  234.       buffer[0] = 0;
  235.       bufferLen = 0;
  236.     }  
  237.   }
  238. };
  239.  
  240. void handleStatus() {
  241.   //This sends full system status including status/panel PIN codes/zones/... etc
  242.   WiFiClient client = server.client();
  243.  
  244.   client.print("HTTP/1.1 200 OK\r\n");
  245.   client.print("Content-Type: text/plain\r\n");
  246.   client.print("Connection: close\r\n");
  247.   client.print("\r\n");
  248.  
  249.   WebOutput out(&client);
  250.   pm.dumpToJson(&out);
  251.   out.flush();
  252.  
  253.   client.stop();
  254. }
  255.  
  256. void handleRefresh() {
  257.   //This returns the current arm state in case it gets out of sync somehow
  258.   WiFiClient client = server.client();
  259.  
  260.   client.print("HTTP/1.1 200 OK\r\n");
  261.   client.print("Content-Type: text/plain\r\n");
  262.   client.print("Connection: close\r\n");
  263.   client.print("\r\n{\"stat_str\":\"");
  264.  
  265.   client.print(pm.GetStrPmaxSystemStatus(pm.GetSystemStatus()));
  266.  
  267.   client.print("\"}\r\n");
  268.  
  269.   client.stop();
  270. }
  271.  
  272. void handleGetZoneNames() {
  273.   //This returns all zone names and types
  274.   WiFiClient client = server.client();
  275.  
  276.   client.print("HTTP/1.1 200 OK\r\n");
  277.   client.print("Content-Type: text/plain\r\n");
  278.   client.print("Connection: close\r\n\r\n{\"namedzonesenrolled\":");
  279.   client.print(MAX_ZONE_COUNT);
  280.   client.print("\r\n");
  281.   int addedCnt=0;
  282.   for(int ix=1; ix<MAX_ZONE_COUNT; ix++) {
  283.     if(pm.zone[ix].enrolled) {
  284.       if(addedCnt >0) {
  285.         client.print(",\r\n");
  286.       }
  287.       client.print("\"zone");
  288.       client.print(ix);
  289.       client.print("name\":\"");
  290.       //client.print(pm.zone[ix].name);
  291.       client.print("\",\r\n\"zone");
  292.       client.print(ix);
  293.       client.print("type\":\"");
  294.       //client.print(pm.zone[ix].zoneTypeStr);
  295.       client.print("\"");
  296.       addedCnt++;
  297.     }
  298.     client.print("}");
  299.   }
  300. }
  301.  
  302. void handleConfig() {
  303.  
  304.   //Parse the response into different value
  305.   String payload = server.arg("test");
  306.  
  307.   //First respond to the poster
  308.   WiFiClient client = server.client();
  309.   client.print("HTTP/1.1 200 OK\r\n");
  310.   client.print("Content-Type: text/plain\r\n");
  311.   client.print("Connection: close\r\n");
  312.   client.print("\r\n{\"testbob\":\"");
  313.   client.print(payload.c_str());
  314.   client.print("\"}\r\n");
  315.   client.stop();
  316.  
  317.   //Then set up a new connection to post to SmartThings
  318.   WiFiClient client2;
  319.   if (!client2.connect(IP_FOR_ST, httpPort)) {
  320.     Serial.println("connection failed");
  321.     return;
  322.   }
  323.   client2.print("POST / HTTP/1.1\r\n");
  324.   client2.print("Content-Type: text/plain\r\n");
  325.   client2.print("Connection: close\r\n");
  326.   client2.print("\r\n{\"testbob\":\"");
  327.   client2.print(payload.c_str());
  328.   client2.print("\"}\r\n");
  329.   client2.stop();
  330. }
  331.  
  332. void handleArmAway() {
  333.   DEBUG(LOG_NOTICE,"Arm Away Command received from Web");
  334.   pm.sendCommand(Pmax_ARMAWAY);
  335. }
  336.  
  337. void handleArmHome() {
  338.   DEBUG(LOG_NOTICE,"Arm Home command received from Web");
  339.   pm.sendCommand(Pmax_ARMHOME);
  340. }
  341.  
  342. void handleDisarm() {
  343.   DEBUG(LOG_NOTICE,"Disarm command received from Web");
  344.   pm.sendCommand(Pmax_DISARM);
  345. }
  346.  
  347. void handleNotFound(){
  348.   String message = "File Not Found\n\n";
  349.   message += "URI: ";
  350.   message += server.uri();
  351.   message += "\nMethod: ";
  352.   message += (server.method() == HTTP_GET)?"GET":"POST";
  353.   message += "\nArguments: ";
  354.   message += server.args();
  355.   message += "\n";
  356.   for (uint8_t i=0; i<server.args(); i++){
  357.     message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  358.   }
  359.   server.send(404, "text/plain", message);
  360. }
  361.  
  362. void setup(void){
  363.  
  364.   Serial.begin(9600); //connect to PowerMax
  365.  
  366.   WiFi.mode(WIFI_STA);
  367.   WiFi.begin(WIFI_SSID, WIFI_PASS);
  368.  
  369.   // Wait for connection
  370.   while (WiFi.status() != WL_CONNECTED) {
  371.     delay(500);
  372.   }
  373.  
  374.   server.on("/", handleRoot);
  375.   server.on("/status", handleStatus);
  376.   server.on("/refresh", handleRefresh);
  377.   server.on("/getzonenames", handleGetZoneNames);
  378.   server.on("/config", HTTP_POST, handleConfig);
  379.   server.on("/armaway", [](){
  380.     handleArmAway();
  381.     server.send(200, "text/html", " {} <body> window.onload = <script> window.close() </script>; </body>");
  382.   });
  383.   server.on("/armhome", [](){
  384.     handleArmHome();
  385.     server.send(200, "text/html", " {} <body> window.onload = <script> window.close() </script>; </body>");
  386.   });
  387.   server.on("/disarm", [](){
  388.     handleDisarm();
  389.     server.send(200, "text/html", " {} <body> window.onload = <script> window.close() </script>; </body>");
  390.   });
  391.   server.on("/ping", [](){
  392.     server.send(200, "text/plain", "{\"ping_alive\": true}");
  393.   });
  394.  
  395.   server.onNotFound(handleNotFound);
  396.   server.begin();
  397.  
  398. #ifdef PM_ENABLE_OTA_UPDATES
  399.   ArduinoOTA.begin();
  400. #endif
  401.  
  402. #ifdef PM_ENABLE_TELNET_ACCESS
  403.   telnetServer.begin();
  404.   telnetServer.setNoDelay(true);
  405. #endif
  406.  
  407.   //if you have a fast board (like PowerMax Complete) you can pass 0 to init function like this: pm.init(0);
  408.   //this will speed up the boot process, keep it as it is, if you have issues downloading the settings from the board.
  409.   pm.init();
  410. }
  411.  
  412. #ifdef PM_ENABLE_TELNET_ACCESS
  413. void handleNewTelnetClients()
  414. {
  415.   if(telnetServer.hasClient())
  416.   {
  417.     if(telnetClient.connected())
  418.     {
  419.       //no free/disconnected spot so reject
  420.       WiFiClient newClient = telnetServer.available();
  421.       newClient.stop();
  422.     }
  423.     else
  424.     {
  425.       telnetClient = telnetServer.available();
  426.       LOG("Connected to %s, type '?' for help.\r\n", WIFI_SSID);
  427.     }
  428.   }
  429. }
  430.  
  431. void runDirectRelayLoop()
  432. {
  433.   while(telnetClient.connected())
  434.   {
  435.     bool wait = true;
  436.    
  437.     //we want to read/write in bulk as it's much faster than read one byte -> write one byte (this is known to create problems with PowerMax)
  438.     unsigned char buffer[256];
  439.     int readCnt = 0;
  440.     while(readCnt < 256)
  441.     {
  442.       if(Serial.available())
  443.       {
  444.         buffer[readCnt] = Serial.read();
  445.         readCnt++;
  446.         wait = false;
  447.       }
  448.       else
  449.       {
  450.         break;
  451.       }
  452.     }
  453.    
  454.     if(readCnt > 0)
  455.     {
  456.       telnetClient.write((const uint8_t *)buffer, readCnt );
  457.     }
  458.  
  459.     if(telnetClient.available())
  460.     {
  461.       Serial.write( telnetClient.read() );
  462.       wait = false;
  463.     }
  464.  
  465.     if(wait)
  466.     {
  467.       delay(10);
  468.     }
  469.   }
  470. }
  471.  
  472. void handleTelnetRequests(PowerMaxAlarm* pm) {
  473.   char c;
  474.   if ( telnetClient.connected() && telnetClient.available() )  {
  475.     c = telnetClient.read();
  476.  
  477.     if(pm->isConfigParsed() == false &&
  478.        (c == 'h' || //arm home
  479.         c == 'a' || //arm away
  480.         c == 'd'))  //disarm
  481.     {
  482.         //DEBUG(LOG_NOTICE,"EPROM is not download yet (no PIN requred to perform this operation)");
  483.         return;
  484.     }
  485.  
  486. #ifdef PM_ALLOW_CONTROL
  487.     if ( c == 'h' ) {
  488.       DEBUG(LOG_NOTICE,"Arming home");
  489.       pm->sendCommand(Pmax_ARMHOME);
  490.     }
  491.     else if ( c == 'd' ) {
  492.       DEBUG(LOG_NOTICE,"Disarm");
  493.       pm->sendCommand(Pmax_DISARM);
  494.     }  
  495.     else if ( c == 'a' ) {
  496.       DEBUG(LOG_NOTICE,"Arming away");
  497.       pm->sendCommand(Pmax_ARMAWAY);
  498.     }
  499.     else if ( c == 'D' ) {
  500.       DEBUG(LOG_NO_FILTER,"Direct relay enabled, disconnect to stop...");
  501.       runDirectRelayLoop();
  502.     }
  503. #endif
  504.  
  505.     if ( c == 'g' ) {
  506.       DEBUG(LOG_NOTICE,"Get Event log");
  507.       pm->sendCommand(Pmax_GETEVENTLOG);
  508.     }
  509.     else if ( c == 't' ) {
  510.       DEBUG(LOG_NOTICE,"Restore comms");
  511.       pm->sendCommand(Pmax_RESTORE);
  512.     }
  513.     else if ( c == 'v' ) {
  514.       DEBUG(LOG_NOTICE,"Exit Download mode");
  515.       pm->sendCommand(Pmax_DL_EXIT);
  516.     }
  517.     else if ( c == 'r' ) {
  518.       DEBUG(LOG_NOTICE,"Request Status Update");
  519.       pm->sendCommand(Pmax_REQSTATUS);
  520.     }
  521.     else if ( c == 'j' ) {
  522.         ConsoleOutput out;
  523.         pm->dumpToJson(&out);
  524.     }
  525.     else if ( c == 'c' ) {
  526.         DEBUG(LOG_NOTICE,"Exiting...");
  527.         telnetClient.stop();
  528.     }    
  529.     else if( c == 'C' ) {
  530.       DEBUG(LOG_NOTICE,"Reseting...");
  531.       ESP.reset();
  532.     }    
  533.     else if ( c == 'p' ) {
  534.       telnetDbgLevel = LOG_DEBUG;
  535.       DEBUG(LOG_NOTICE,"Debug Logs enabled type 'P' (capital) to disable");
  536.     }    
  537.     else if ( c == 'P' ) {
  538.       telnetDbgLevel = LOG_NO_FILTER;
  539.       DEBUG(LOG_NO_FILTER,"Debug Logs disabled");
  540.     }      
  541.     else if ( c == 'H' ) {
  542.       DEBUG(LOG_NO_FILTER,"Free Heap: %u", ESP.getFreeHeap());
  543.     }  
  544.     else if ( c == '?' )
  545.     {
  546.         DEBUG(LOG_NO_FILTER,"Allowed commands:");
  547.         DEBUG(LOG_NO_FILTER,"\t c - exit");
  548.         DEBUG(LOG_NO_FILTER,"\t C - reset device");        
  549.         DEBUG(LOG_NO_FILTER,"\t p - output debug messages");
  550.         DEBUG(LOG_NO_FILTER,"\t P - stop outputing debug messages");
  551. #ifdef PM_ALLOW_CONTROL        
  552.         DEBUG(LOG_NO_FILTER,"\t h - Arm Home");
  553.         DEBUG(LOG_NO_FILTER,"\t d - Disarm");
  554.         DEBUG(LOG_NO_FILTER,"\t a - Arm Away");
  555.         DEBUG(LOG_NO_FILTER,"\t D - Direct mode (relay all bytes from client to PMC and back with no processing, close connection to exit");  
  556. #endif        
  557.         DEBUG(LOG_NO_FILTER,"\t g - Get Event Log");
  558.         DEBUG(LOG_NO_FILTER,"\t t - Restore Comms");
  559.         DEBUG(LOG_NO_FILTER,"\t v - Exit download mode");
  560.         DEBUG(LOG_NO_FILTER,"\t r - Request Status Update");
  561.         DEBUG(LOG_NO_FILTER,"\t j - Dump Application Status to JSON");  
  562.         DEBUG(LOG_NO_FILTER,"\t H - Get free heap");  
  563.  
  564.        
  565.     }
  566.   }
  567. }
  568. #endif
  569.  
  570. #ifdef PM_ENABLE_LAN_BROADCAST
  571. void broadcastPacketOnLan(const PlinkBuffer* commandBuffer, bool packetOk)
  572. {
  573.   udp.beginPacketMulticast(PM_LAN_BROADCAST_IP, PM_LAN_BROADCAST_PORT, WiFi.localIP());
  574.   udp.write("{ data: [");
  575.   for(int ix=0; ix<commandBuffer->size; ix++)
  576.   {
  577.     char szDigit[20];
  578.     //sprintf(szDigit, "%d", commandBuffer->buffer[ix]);
  579.     sprintf(szDigit, "%02x", commandBuffer->buffer[ix]); //IZIZTODO: temp
  580.  
  581.     if(ix+1 != commandBuffer->size)
  582.     {
  583.       strcat(szDigit, ", ");
  584.     }
  585.  
  586.     udp.write(szDigit);
  587.   }
  588.  
  589.   udp.write("], ok: ");
  590.   if(packetOk)
  591.   {
  592.     udp.write("true");
  593.   }
  594.   else
  595.   {
  596.     udp.write("false");
  597.   }
  598.  
  599.   char szTmp[50];
  600.   sprintf(szTmp, ", seq: %u }", packetCnt++);
  601.   udp.write(szTmp);
  602.  
  603.   udp.endPacket();
  604. }
  605. #endif
  606.  
  607. bool serialHandler(PowerMaxAlarm* pm) {
  608.  
  609.   bool packetHandled = false;
  610.  
  611.   PlinkBuffer commandBuffer ;
  612.   memset(&commandBuffer, 0, sizeof(commandBuffer));
  613.  
  614.   char oneByte = 0;  
  615.   while (  (os_pmComPortRead(&oneByte, 1) == 1)  )
  616.   {    
  617.     if (commandBuffer.size<(MAX_BUFFER_SIZE-1))
  618.     {
  619.       *(commandBuffer.size+commandBuffer.buffer) = oneByte;
  620.       commandBuffer.size++;
  621.    
  622.       if(oneByte == 0x0A) //postamble received, let's see if we have full message
  623.       {
  624.         if(PowerMaxAlarm::isBufferOK(&commandBuffer))
  625.         {
  626.           DEBUG(LOG_INFO,"--- new packet %d ----", millis());
  627.  
  628. #ifdef PM_ENABLE_LAN_BROADCAST
  629.           broadcastPacketOnLan(&commandBuffer, true);
  630. #endif
  631.  
  632.           packetHandled = true;
  633.           pm->handlePacket(&commandBuffer);
  634.           commandBuffer.size = 0;
  635.           break;
  636.         }
  637.       }
  638.     }
  639.     else
  640.     {
  641.       DEBUG(LOG_WARNING,"Packet too big detected");
  642.     }
  643.   }
  644.  
  645.   if(commandBuffer.size > 0)
  646.   {
  647. #ifdef PM_ENABLE_LAN_BROADCAST
  648.     broadcastPacketOnLan(&commandBuffer, false);
  649. #endif
  650.    
  651.     packetHandled = true;
  652.     //this will be an invalid packet:
  653.     DEBUG(LOG_WARNING,"Passing invalid packet to packetManager");
  654.     pm->handlePacket(&commandBuffer);
  655.   }
  656.  
  657.   return packetHandled;
  658. }
  659.  
  660. void loop(void){
  661. #ifdef PM_ENABLE_OTA_UPDATES
  662.   ArduinoOTA.handle();
  663. #endif
  664.  
  665.   server.handleClient();
  666.  
  667.   static unsigned long lastMsg = 0;
  668.   if(serialHandler(&pm) == true)
  669.   {
  670.     lastMsg = millis();
  671.   }
  672.  
  673.   if(millis() - lastMsg > 300 || millis() < lastMsg) //we ensure a small delay between commands, as it can confuse the alarm (it has a slow CPU)
  674.   {
  675.     pm.sendNextCommand();
  676.   }
  677.  
  678.   if(pm.restoreCommsIfLost()) //if we fail to get PINGs from the alarm - we will attempt to restore the connection
  679.   {
  680.       DEBUG(LOG_WARNING,"Connection lost. Sending RESTORE request.");  
  681.   }  
  682.  
  683. #ifdef PM_ENABLE_TELNET_ACCESS
  684.   handleNewTelnetClients();
  685.   handleTelnetRequests(&pm);
  686. #endif  
  687. }
  688.  
  689. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  690. //This file contains OS specific implementation for ESP8266 used by PowerMax library
  691. //If you build for other platrorms (like Linux or Windows, don't include this file, but provide your own)
  692.  
  693. int log_console_setlogmask(int mask)
  694. {
  695.   int oldmask = telnetDbgLevel;
  696.   if(mask == 0)
  697.     return oldmask; /* POSIX definition for 0 mask */
  698.   telnetDbgLevel = mask;
  699.   return oldmask;
  700. }
  701.  
  702. void os_debugLog(int priority, bool raw, const char *function, int line, const char *format, ...)
  703. {
  704.   if(priority <= telnetDbgLevel)
  705.   {
  706.     char buf[PRINTF_BUF];
  707.     va_list ap;
  708.    
  709.     va_start(ap, format);
  710.     vsnprintf(buf, sizeof(buf), format, ap);
  711.    
  712.   #ifdef PM_ENABLE_TELNET_ACCESS
  713.     if(telnetClient.connected())
  714.     {
  715.       telnetClient.write((const uint8_t *)buf, strlen(buf));
  716.       if(raw == false)
  717.       {
  718.         telnetClient.write((const uint8_t *)"\r\n", 2);
  719.       }    
  720.     }
  721.   #endif
  722.    
  723.     va_end(ap);
  724.  
  725.     yield();
  726.     }
  727. }
  728.  
  729. void os_usleep(int microseconds)
  730. {
  731.     delay(microseconds / 1000);
  732. }
  733.  
  734. unsigned long os_getCurrentTimeSec()
  735. {
  736.   static unsigned int wrapCnt = 0;
  737.   static unsigned long lastVal = 0;
  738.   unsigned long currentVal = millis();
  739.  
  740.   if(currentVal < lastVal)
  741.   {
  742.     wrapCnt++;
  743.   }
  744.  
  745.   unsigned long seconds = currentVal/1000;
  746.  
  747.   //millis will wrap each 50 days, as we are interested only in seconds, let's keep the wrap counter
  748.   return (wrapCnt*4294967) + seconds;
  749. }
  750.  
  751. int os_pmComPortRead(void* readBuff, int bytesToRead)
  752. {
  753.     int dwTotalRead = 0;
  754.     while(bytesToRead > 0)
  755.     {
  756.         for(int ix=0; ix<10; ix++)
  757.         {
  758.           if(Serial.available())
  759.           {
  760.             break;
  761.           }
  762.           delay(5);
  763.         }
  764.        
  765.         if(Serial.available() == false)
  766.         {
  767.             break;
  768.         }
  769.  
  770.         *((char*)readBuff) = Serial.read();
  771.         dwTotalRead ++;
  772.         readBuff = ((char*)readBuff) + 1;
  773.         bytesToRead--;
  774.     }
  775.  
  776.     return dwTotalRead;
  777. }
  778.  
  779. int os_pmComPortWrite(const void* dataToWrite, int bytesToWrite)
  780. {
  781.     Serial.write((const uint8_t*)dataToWrite, bytesToWrite);
  782.     return bytesToWrite;
  783. }
  784.  
  785. bool os_pmComPortClose()
  786. {
  787.     return true;
  788. }
  789.  
  790. bool os_pmComPortInit(const char* portName) {
  791.     return true;
  792. }
  793.  
  794. void os_strncat_s(char* dst, int dst_size, const char* src)
  795. {
  796.     strncat(dst, src, dst_size);
  797. }
  798.  
  799. int os_cfg_getPacketTimeout()
  800. {
  801.     return PACKET_TIMEOUT_DEFINED;
  802. }
  803.  
  804. //see PowerMaxAlarm::setDateTime for details of the parameters, if your OS does not have a RTC clock, simply return false
  805. bool os_getLocalTime(unsigned char& year, unsigned char& month, unsigned char& day, unsigned char& hour, unsigned char& minutes, unsigned char& seconds)
  806. {
  807.     return false; //IZIZTODO
  808. }
  809. //////End of OS specific part of PM library/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
RAW Paste Data