Advertisement
Guest User

Untitled

a guest
Apr 4th, 2020
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.28 KB | None | 0 0
  1. #include <ArduinoJson.h>
  2. #include <ESP8266WiFi.h>
  3. #include <ArduinoOTA.h>
  4. #include <ModbusMaster.h>
  5. #include <PubSubClient.h>
  6. #include "configuration.h"
  7. #if SERIAL == SERIAL_SOFTWARE
  8. #include <SoftwareSerial.h>
  9. #endif
  10.  
  11. #define SERIAL_SOFTWARE 1
  12. #define SERIAL_HARDWARE 2
  13.  
  14. #define HOST "NilanGW-%s" // Change this to whatever you like.
  15. #define MAXREGSIZE 30
  16. #define SENDINTERVAL 30000 // normally set to 180000 milliseconds = 3 minutes. Define as you like
  17. #define VENTSET 1003
  18. #define RUNSET 1001
  19. #define MODESET 1002
  20. #define TEMPSET 1004
  21.  
  22. #if SERIAL == SERIAL_SOFTWARE
  23. SoftwareSerial SSerial(SERIAL_SOFTWARE_RX, SERIAL_SOFTWARE_TX); // RX, TX
  24. #endif
  25.  
  26. const char* ssid = WIFISSID;
  27. const char* password = WIFIPASSWORD;
  28. char chipid[12];
  29. const char* mqttserver = MQTTSERVER;
  30. const char* mqttusername = MQTTUSERNAME;
  31. const char* mqttpassword = MQTTPASSWORD;
  32. WiFiServer server(80);
  33. WiFiClient client;
  34. PubSubClient mqttclient(client);
  35. static long lastMsg = -SENDINTERVAL;
  36. static int16_t rsbuffer[MAXREGSIZE];
  37. ModbusMaster node;
  38.  
  39. String req[4]; //operation, group, address, value
  40. enum reqtypes
  41. {
  42.   reqtemp = 0,
  43.   reqalarm,
  44.   reqtime,
  45.   reqcontrol,
  46.   reqspeed,
  47.   reqairtemp,
  48.   reqairflow,
  49.   reqairheat,
  50.   requser,
  51.   requser2,
  52.   reqinfo,
  53.   reqinputairtemp,
  54.   reqapp,
  55.   reqoutput,
  56.   reqdisplay1,
  57.   reqdisplay2,
  58.   reqdisplay,
  59.   reqmax
  60. };
  61.  
  62. String groups[] = {"temp", "alarm", "time", "control", "speed", "airtemp", "airflow", "airheat", "user", "user2", "info", "inputairtemp", "app", "output", "display1", "display2", "display"};
  63. byte regsizes[] = {23, 10, 6, 8, 6, 9, 6, 0, 6, 6, 17, 7, 4, 28, 4, 4, 1};
  64. int regaddresses[] = {200, 400, 300, 1000, 200, 1200, 1100, 0, 600, 610, 100, 1200, 0, 100, 2002, 2007, 3000};
  65. byte regtypes[] = {8, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 2, 1, 4, 4, 8};
  66. char *regnames[][MAXREGSIZE] = {
  67.     //temp - Input register 200
  68.     {"T0_Controller", "T1_Intake", "T2_Inlet", "T3_Exhaust", "T4_Outlet", "T5_Cond", "T6_Evap", "T7_Inlet", "T8_Outdoor", "T9_Heater", "T10_Extern", "T11_Top", "T12_Bottom", "T13_Return", "T14_Supply", "T15_Room", "T16", "T17_PreHeat", "T18_PresPibe", "pSuc", "pDis", "RH", "CO2"},
  69.     //alarm - Input register 400
  70.     {"Status", "List_1_ID ", "List_1_Date", "List_1_Time", "List_2_ID ", "List_2_Date", "List_2_Time", "List_3_ID ", "List_3_Date", "List_3_Time"},
  71.     //time - Holding register 300
  72.     {"Second", "Minute", "Hour", "Day", "Month", "Year"},
  73.     //control - Holding register 1000
  74.     {"Type", "RunSet", "ModeSet", "VentSet", "TempSet", "ServiceMode", "ServicePct", "Preset"},
  75.     //speed - Holding register 200
  76.     {"ExhaustSpeed", "InletSpeed","AirHeatCap","CenHeatCap","CprCap","PreHeatCap"},
  77.     //airtemp - Holding register 1200
  78.     {"CoolSet", "TempMinSum", "TempMinWin", "TempMaxSum", "TempMaxWin", "TempSummer","NightDayLim","NightSet","SensorSelect"},
  79.     //airflow - Holding register 1100
  80.     {"AirExchMode", "CoolVent","TestSelect","LastTestDay","TestState","FiltAlmType"},
  81.     //airheat  - Display register 1400
  82.     {},
  83.     //program.user  - Holding register 600
  84.     {"UserFuncAct", "UserFuncSet", "UserTimeSet", "UserVentSet", "UserTempSet", "UserOffsSet"},
  85.     //program.user2 - Holding register 610
  86.     {"User2FuncAct", "User2FuncSet", "User2TimeSet", "User2VentSet", "UserTempSet", "UserOffsSet"},
  87.     //info - Input register 100
  88.     {"UserFunc", "AirFilter", "DoorOpen", "Smoke", "MotorThermo", "Frost_overht", "AirFlow", "P_Hi", "P_Lo", "Boil", "3WayPos", "DefrostHG", "Defrost", "UserFunc_2","DamperClosed","DamperOpened","FCorThermoAl"},
  89.     //inputairtemp - Input register 1200
  90.     {"IsSummer", "TempInletSet", "TempControl", "TempRoom", "EffPct", "CapSet", "CapAct"},
  91.     //app - Input register 0
  92.     {"Bus.Version", "VersionMajor", "VersionMinor", "VersionRelease"},
  93.     //output - Holding register 100
  94.     {"AirFlap", "SmokeFlap", "BypassOpen", "BypassClose", "AirCircPump", "AirHeatAllow", "AirHeat_1", "AirHeat_2", "AirHeat_3", "Compressor", "Compressor_2", "4WayCool", "HotGasHeat", "HotGasCool", "CondOpen", "CondClose", "WaterHeat", "3WayValve", "CenCircPump", "CenHeat_1", "CenHeat_2", "CenHeat_3", "CenHeatExt", "UserFunc", "UserFunc_2", "Defrosting","AlarmRelay","PreHeat"},
  95.     //display1
  96.     {"Text_1_2", "Text_3_4", "Text_5_6", "Text_7_8"},
  97.     //display2
  98.     {"Text_9_10", "Text_11_12", "Text_13_14", "Text_15_16"},
  99.     //airbypass
  100.     {"AirBypass/IsOpen"}};
  101.  
  102. char *getName(reqtypes type, int address)
  103. {
  104.   if (address >= 0 && address <= regsizes[type])
  105.   {
  106.     return regnames[type][address];
  107.   }
  108.   return NULL;
  109. }
  110.  
  111. JsonObject HandleRequest(JsonDocument& doc)
  112. {
  113.   JsonObject root = doc.to<JsonObject>();
  114.   reqtypes r = reqmax;
  115.   char type = 0;
  116.   if (req[1] != "")
  117.   {
  118.     for (int i = 0; i < reqmax; i++)
  119.     {
  120.       if (groups[i] == req[1])
  121.       {
  122.         r = (reqtypes)i;
  123.       }
  124.     }
  125.   }
  126.   type = regtypes[r];
  127.   if (req[0] == "read")
  128.   {
  129.     int address = 0;
  130.     int nums = 0;
  131.     char result = -1;
  132.     address = regaddresses[r];
  133.     nums = regsizes[r];
  134.  
  135.     result = ReadModbus(address, nums, rsbuffer, type & 1);
  136.     if (result == 0)
  137.     {
  138.       root["status"] = "Modbus connection OK";
  139.       for (int i = 0; i < nums; i++)
  140.       {
  141.         char *name = getName(r, i);
  142.         if (name != NULL && strlen(name) > 0)
  143.         {
  144.           if ((type & 2 && i > 0) || type & 4)
  145.           {
  146.             String str = "";
  147.             str += (char)(rsbuffer[i] & 0x00ff);
  148.             str += (char)(rsbuffer[i] >> 8);
  149.             root[name] = str;
  150.           }
  151.           else if (type & 8)
  152.           {
  153.             root[name] = rsbuffer[i] / 100.0;
  154.           }
  155.           else
  156.           {
  157.             root[name] = rsbuffer[i];
  158.           }
  159.         }
  160.       }
  161.     }
  162.     else {
  163.       root["status"] = "Modbus connection failed";
  164.     }
  165.     root["requestaddress"] = address;
  166.     root["requestnum"] = nums;
  167.   }
  168.   if (req[0] == "set" && req[2] != "" && req[3] != "")
  169.   {
  170.     int address = atoi(req[2].c_str());
  171.     int value = atoi(req[3].c_str());
  172.     char result = WriteModbus(address, value);
  173.     root["result"] = result;
  174.     root["address"] = address;
  175.     root["value"] = value;
  176.   }
  177.   if (req[0] == "help")
  178.   {
  179.     for (int i = 0; i < reqmax; i++)
  180.     {
  181.       root[groups[i]] = 0;
  182.     }
  183.   }
  184.   root["operation"] = req[0];
  185.   root["group"] = req[1];
  186. }
  187.  
  188. void setup()
  189. {
  190.   if(USE_WIFI_LED) pinMode(WIFI_LED, OUTPUT);
  191.   char host[64];
  192.   sprintf(chipid, "%08X", ESP.getChipId());
  193.   sprintf(host, HOST, chipid);
  194.   delay(500);
  195.   if(CUSTOM_HOSTNAME)
  196.   {
  197.     WiFi.hostname(CUSTOM_HOSTNAME);
  198.     ArduinoOTA.setHostname(CUSTOM_HOSTNAME);
  199.   } else
  200.   {
  201.     WiFi.hostname(host);
  202.     ArduinoOTA.setHostname(host);
  203.   }
  204.   WiFi.mode(WIFI_STA);
  205.   WiFi.begin(ssid, password);
  206.  
  207.   while (WiFi.waitForConnectResult() != WL_CONNECTED)
  208.   {
  209.     if(USE_WIFI_LED) digitalWrite(WIFI_LED, !digitalRead(WIFI_LED));
  210.     delay(5000);
  211.     ESP.restart();
  212.   }
  213.   if (WiFi.status() == WL_CONNECTED && USE_WIFI_LED)
  214.   {
  215.     digitalWrite(WIFI_LED, 0);
  216.   }
  217.   ArduinoOTA.onStart([]() {
  218.   });
  219.   ArduinoOTA.onEnd([]() {
  220.   });
  221.   ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
  222.   });
  223.   ArduinoOTA.onError([](ota_error_t error) {
  224.   });
  225.   ArduinoOTA.begin();
  226.   server.begin();
  227.  
  228.   #if SERIAL == SERIAL_SOFTWARE
  229.     #warning Compiling for software serial
  230.     SSerial.begin(19200); // SERIAL_8E1
  231.     node.begin(30, SSerial);
  232.   #elif SERIAL == SERIAL_HARDWARE
  233.     #warning Compiling for hardware serial
  234.     Serial.begin(19200, SERIAL_8E1);
  235.     node.begin(30, Serial);
  236.   #else
  237.     #error hardware og serial serial port?
  238.   #endif
  239.  
  240.   mqttclient.setServer(mqttserver, 1883);
  241.   mqttclient.setCallback(mqttcallback);
  242. }
  243.  
  244. void mqttcallback(char *topic, byte *payload, unsigned int length)
  245. {
  246.   if (strcmp(topic, "ventilation/ventset") == 0)
  247.   {
  248.     if (length == 1 && payload[0] >= '0' && payload[0] <= '4')
  249.     {
  250.       int16_t speed = payload[0] - '0';
  251.       WriteModbus(VENTSET, speed);
  252.     }
  253.   }
  254.   if (strcmp(topic, "ventilation/modeset") == 0)
  255.   {
  256.     if (length == 1 && payload[0] >= '0' && payload[0] <= '4')
  257.     {
  258.       int16_t mode = payload[0] - '0';
  259.       WriteModbus(MODESET, mode);
  260.     }
  261.   }
  262.   if (strcmp(topic, "ventilation/runset") == 0)
  263.   {
  264.     if (length == 1 && payload[0] >= '0' && payload[0] <= '1')
  265.     {
  266.       int16_t run = payload[0] - '0';
  267.       WriteModbus(RUNSET, run);
  268.     }
  269.   }
  270.   if (strcmp(topic, "ventilation/tempset") == 0)
  271.   {
  272.     if (length == 4 && payload[0] >= '0' && payload[0] <= '2')
  273.     {
  274.       String str;
  275.       for (int i = 0; i < length; i++)
  276.       {
  277.         str += (char)payload[i];
  278.       }
  279.       WriteModbus(TEMPSET, str.toInt());
  280.     }
  281.   }
  282.   lastMsg = -SENDINTERVAL;
  283. }
  284.  
  285. bool readRequest(WiFiClient &client)
  286. {
  287.   req[0] = "";
  288.   req[1] = "";
  289.   req[2] = "";
  290.   req[3] = "";
  291.  
  292.   int n = -1;
  293.   bool readstring = false;
  294.   while (client.connected())
  295.   {
  296.     if (client.available())
  297.     {
  298.       char c = client.read();
  299.       if (c == '\n')
  300.       {
  301.         return false;
  302.       }
  303.       else if (c == '/')
  304.       {
  305.         n++;
  306.       }
  307.       else if (c != ' ' && n >= 0 && n < 4)
  308.       {
  309.         req[n] += c;
  310.       }
  311.       else if (c == ' ' && n >= 0 && n < 4)
  312.       {
  313.         return true;
  314.       }
  315.     }
  316.   }
  317.  
  318.   return false;
  319. }
  320.  
  321. void writeResponse(WiFiClient& client, const JsonDocument& doc)  
  322. {
  323.   client.println("HTTP/1.1 200 OK");
  324.   client.println("Content-Type: application/json");
  325.   client.println("Connection: close");
  326.   client.println();
  327.   serializeJsonPretty(doc,client);
  328. }
  329.  
  330. char ReadModbus(uint16_t addr, uint8_t sizer, int16_t *vals, int type)
  331. {
  332.   char result = 0;
  333.   switch (type)
  334.   {
  335.   case 0:
  336.     result = node.readInputRegisters(addr, sizer);
  337.     break;
  338.   case 1:
  339.     result = node.readHoldingRegisters(addr, sizer);
  340.     break;
  341.   }
  342.   if (result == node.ku8MBSuccess)
  343.   {
  344.     for (int j = 0; j < sizer; j++)
  345.     {
  346.       vals[j] = node.getResponseBuffer(j);
  347.     }
  348.     return result;
  349.   }
  350.   return result;
  351. }
  352. char WriteModbus(uint16_t addr, int16_t val)
  353. {
  354.   node.setTransmitBuffer(0, val);
  355.   char result = 0;
  356.   result = node.writeMultipleRegisters(addr, 1);
  357.   return result;
  358. }
  359.  
  360. void mqttreconnect()
  361. {
  362.   int numretries = 0;
  363.   while (!mqttclient.connected() && numretries < 3)
  364.   {
  365.     if (mqttclient.connect(chipid, mqttusername, mqttpassword))
  366.     {
  367.       mqttclient.subscribe("ventilation/ventset");
  368.       mqttclient.subscribe("ventilation/modeset");
  369.       mqttclient.subscribe("ventilation/runset");
  370.       mqttclient.subscribe("ventilation/tempset");
  371.     }
  372.     else
  373.     {
  374.       delay(1000);
  375.     }
  376.     numretries++;
  377.   }
  378. }
  379.  
  380. void loop()
  381. {
  382. #ifdef DEBUG_TELNET
  383.   // handle Telnet connection for debugging
  384.   handleTelnet();
  385. #endif
  386.  
  387.   ArduinoOTA.handle();
  388.   WiFiClient client = server.available();
  389.   if (client)
  390.   {
  391.     bool success = readRequest(client);
  392.     if (success)
  393.     {
  394.       StaticJsonDocument<500> doc;
  395.       HandleRequest(doc);
  396.  
  397.       writeResponse(client, doc);
  398.     }
  399.     client.stop();
  400.   }
  401.  
  402.   if (!mqttclient.connected())
  403.   {
  404.     mqttreconnect();
  405.   }
  406.  
  407.   if (mqttclient.connected())
  408.   {
  409.     mqttclient.loop();
  410.     long now = millis();
  411.     if (now - lastMsg > SENDINTERVAL)
  412.     {
  413.       reqtypes rr[] = {reqtemp, reqcontrol, reqoutput, reqspeed, reqalarm, reqinputairtemp, requser, reqdisplay, reqinfo}; // put another register in this line to subscribe
  414.       for (int i = 0; i < (sizeof(rr)/sizeof(rr[0])); i++)
  415.       {
  416.         reqtypes r = rr[i];
  417.         char result = ReadModbus(regaddresses[r], regsizes[r], rsbuffer, regtypes[r] & 1);
  418.         if (result == 0)
  419.         {
  420.           mqttclient.publish("ventilation/error/modbus/", "0"); //no error when connecting through modbus
  421.           for (int i = 0; i < regsizes[r]; i++)
  422.           {
  423.             char *name = getName(r, i);
  424.             char numstr[8];
  425.             if (name != NULL && strlen(name) > 0)
  426.             {
  427.               String mqname = "temp/";
  428.               switch (r)
  429.               {
  430.               case reqcontrol:
  431.                 mqname = "ventilation/control/"; // Subscribe to the "control" register
  432.                 itoa((rsbuffer[i]), numstr, 10);
  433.                 break;
  434.               case reqoutput:
  435.                 mqname = "ventilation/output/"; // Subscribe to the "output" register
  436.                 itoa((rsbuffer[i]), numstr, 10);
  437.                 break;
  438.               case reqdisplay:
  439.                 mqname = "ventilation/display/"; // Subscribe to the "input display" register
  440.                 itoa((rsbuffer[i]), numstr, 10);
  441.                 break;
  442.               case reqspeed:
  443.                 mqname = "ventilation/speed/"; // Subscribe to the "speed" register
  444.                 itoa((rsbuffer[i]), numstr, 10);
  445.                 break;
  446.               case reqalarm:
  447.                 mqname = "ventilation/alarm/"; // Subscribe to the "alarm" register
  448.                 itoa((rsbuffer[i]), numstr, 10);
  449.                 break;
  450.               case reqinputairtemp:
  451.                 mqname = "ventilation/inputairtemp/"; // Subscribe to the "inputairtemp" register
  452.                 itoa((rsbuffer[i]), numstr, 10);
  453.                 break;
  454.               case requser:
  455.                 mqname = "ventilation/user/"; // Subscribe to the "user" register
  456.                 itoa((rsbuffer[i]), numstr, 10);
  457.                 break;
  458.               case reqinfo:
  459.                 mqname = "ventilation/info/"; // Subscribe to the "info" register
  460.                 itoa((rsbuffer[i]), numstr, 10);
  461.                 break;                    
  462.               case reqtemp:
  463.                 if (strncmp("RH", name, 2) == 0) {
  464.                   mqname = "ventilation/moist/"; // Subscribe to moisture-level
  465.                 } else {
  466.                   mqname = "ventilation/temp/"; // Subscribe to "temp" register
  467.                 }
  468.                 dtostrf((rsbuffer[i] / 100.0), 5, 2, numstr);
  469.                 break;
  470.               }
  471.               mqname += (char *)name;
  472.               mqttclient.publish(mqname.c_str(), numstr);
  473.             }
  474.           }
  475.         }
  476.         else {
  477.           mqttclient.publish("ventilation/error/modbus/", "1"); //error when connecting through modbus
  478.         }      
  479.       }
  480.  
  481.       // Handle text fields
  482.       reqtypes rr2[] = {reqdisplay1, reqdisplay2}; // put another register in this line to subscribe
  483.       for (int i = 0; i < (sizeof(rr2)/sizeof(rr2[0])); i++) // change value "5" to how many registers you want to subscribe to
  484.       {
  485.         reqtypes r = rr2[i];
  486.  
  487.         char result = ReadModbus(regaddresses[r], regsizes[r], rsbuffer, regtypes[r] & 1);
  488.         if (result == 0)
  489.         {
  490.           String text = "";
  491.           String mqname = "ventilation/text/";
  492.  
  493.           for (int i = 0; i < regsizes[r]; i++)
  494.           {
  495.               char *name = getName(r, i);
  496.  
  497.               if ((rsbuffer[i] & 0x00ff) == 0xDF) {
  498.                 text += (char)0x20; // replace degree sign with space
  499.               } else {
  500.                 text += (char)(rsbuffer[i] & 0x00ff);
  501.               }
  502.               if ((rsbuffer[i] >> 8) == 0xDF) {
  503.                 text += (char)0x20; // replace degree sign with space
  504.               } else {
  505.                 text += (char)(rsbuffer[i] >> 8);
  506.               }
  507.               mqname += (char *)name;
  508.           }
  509.           mqttclient.publish(mqname.c_str(), text.c_str());
  510.         }
  511.       }
  512.       lastMsg = now;
  513.     }
  514.   }
  515. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement