RS485 MODBUS to UDP & Thingspeak gateway, issues with program timing

Hello,

I have a program running on a Nano IoT 33 which runs through a STRUCT array of slave device addresses, registers and lengths and then records the result from the poll in the array. I then send the array by UDP on the LAN and also some posted to my Thinkspeak account. Main reason for wanting UDP on the LAN is so displays of the MODBUS data are live and fast responding.

What's happening just now i see are two issues related to timing;

  1. One of the slave devices (a solar PV inverter) does not respond when its dark, so the MODBUS library 1000ms timeout slows everything down. As far as i can see there's not a way to change this timeout.

  2. Even when the solar inverter is responding, the MODBUS loop takes quite a period of time. The baud rate is 9600 I've attached the comms protocol for one inverter here, it suggests a 300ms time required between requests, it looks like its taking about 90-100ms.
    RS485_MODBUS Communication Protocol_Solis Inverters.pdf (592.3 KB)

I have a couple of thoughts but need some forum assistance;

  1. Can the library be edited and any suggestions for how to go about this?

  2. a. I could do a smaller UDP transmission after each MODBUS poll, that puts more onus on the receiver to then file the received UDP data for use by those end points

  3. b. The devices permit sets of 50 registers to be read as a range, i would think this has less overhead as its one request as opposed to 50, and the reply will have less overhead too. Given some registers are single and others are double, i am not sure how to receive these 50 and then parse into a 16bit or 32bit values

  4. c. Some registers done change very often, so i had looked to poll a smaller set of values quickly for the real time display, then perhaps every minute, poll the full table. This is partially in place in the code below before i realised the issues would then be present and it may be better to make things more efficient first.

I would appreciate any suggestions and ideas to help.

The full code as follows, at this time all libraries are current versions with no changes;

/*

  Solis RAI inverter/charger unit and Solis MINI Solar PV inverter RS485 MODBUS to network interface

  S. Graham May/June 2023
     based around the excellent work of;
       R.A.Lincoln     July 2022, SolisComms https://github.com/RichardL64

  Hardware: Arduino Nano 33 IOT

SolisComms_SGr_
  V1
  V2
  V3            These versions added in polling a second address for the solar PV and send by HTTP/UDP etc - too many issues with mods
  V1_1_Cutback  Start a fresh - remove the things i dont need, eg the webserver
  V1_2_Cutback  Bit more work done
  V4 from V1.2  Got this working, polling a long list of registers on both addresses, posting to thingspeak and also doing a UDP broadcast
                Issues with timing:
                      When theres no PV, the MODBUS library 1000ms timeout is too long - not fussed; need perhaps 20ms perhaps
                      UDP and Thingspeak held up by MODBUS polling, need to do the UDP quicker, thingspeak probably OK.
                      
  V5            Lets try and fix things!
  


  /////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////
  //******************* SYSTEM STUFF ******************* //  //******************* SYSTEM STUFF ******************* //
  /////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //******************* LIBRARIES  ******************* //

*/
#include <SPI.h>
#include <WiFiNINA.h>                           // (note _generic version locks up on closed connections)
#include <ArduinoModbus.h>
#include <ModbusClient.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <NTPClient.h>
#include "ThingSpeak.h" // always include thingspeak header file after other header files and custom macros


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************* WiFi HEADER  ******************* //

#define SECRET_SSID "removed"
#define SECRET_PASS "removed"

// Various objects which will use WiFi/data comms
WiFiClient  client;  // used for thingspeak
WiFiUDP ntpUDP;  // used for NTP receie server
WiFiUDP lanUDP;  // used for LAN transmission

int statusTx = 0;  //transmission status

IPAddress ip(192, 168, 1, 61);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8);   //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

IPAddress destIp(192, 168, 1, 255);


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************* TIMERS  ******************* //

unsigned long currentMicros = 0;
unsigned long currentMillis = 0;

//2Hz / 500ms loop
unsigned long prevMillis2Hz = 0;
unsigned long millisPer2Hz = 500; // 500ms

//1Hz / 1000ms loop
unsigned long prevMillis1Hz = 0;
unsigned long millisPer1Hz = 1000; // 1000ms

// program timer
int progTime = 0;
int timeEnd = 0;
int loopDelay = 5;  // delay in main loop to allow background processes
int counter = 0;

//display timer
unsigned long delaytime = 500;
unsigned long delayprint = 100;


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************* NTP TIME HEADER  ******************* //

NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", (60 * 60), 60000);  // update ever minute, add 1 hour for BST

int liveDays = 0;
int liveHours = 0;
int liveMinutes = 0;
int liveSeconds = 0;

int liveMinutesRunning = 0;

unsigned long unix = 0;


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************  THINGSPEAK HEADER  ******************* //

unsigned long solarBattery = removed;
const char * solarBatteryStatusWriteAPIKey = "removed";
String solarBatteryStatus = "";


int updatePeriod = 2; // number of 1Hz loops between transmissions
int periods = 0; // counter for the updatePeriod for
unsigned long pktTxTspk = 0; // Transmit Packet counter


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************  UDP  ******************* //

char packetBuffer[255];
unsigned int localPort = 1912;
unsigned int remotePort = 1912;
unsigned long pktTxUDP = 0; // Transmit Packet counter
unsigned long pktRxUDP = 0; // Receive Packet counter


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************  MODBUS  ******************* //

#define MODBUS_DELAY 5

struct database
{
  int addr;
  int command;
  int registerNum;
  int length;
  long returnedValue;
};

const int storagePVSize = 5;
database storagePV[storagePVSize] = {
  2, 4, 3004, 2, 0,  //PV inverter active AC power
  1, 4, 33079, 2, 0,  //storage inverter active AC power (unsigned)
  1, 4, 33135, 1, 0,  //Battery current direction
  1, 4, 33139, 1, 0,  //Battery capacity
  1, 4, 33130, 2, 0,  //grid meter active power (signed, negative as import)
};

const int fullSystemSize = 50;
database fullSystem[fullSystemSize] = {
  2, 4, 3035, 1, 0,  //AC Voltage
  2, 4, 3038, 1, 0,  //AC Current
  2, 4, 3042, 1, 0,  //AC Frequency
  2, 4, 3021, 1, 0,  //DC Voltage
  2, 4, 3022, 1, 0,  //DC Current
  2, 4, 3014, 1, 0,  //Today energy
  2, 4, 3041, 1, 0,  //Inverter Temperature
  1, 4, 33073, 1, 0,  //AB line voltage /
  1, 4, 33076, 1, 0,  //Phase A current
  1, 4, 33081, 2, 0,  //Reactive power
  1, 4, 33083, 2, 0,  //Apparent power
  1, 4, 33094, 1, 0,  //Grid frequency
  1, 4, 33137, 1, 0,  //Bypass AC voltage
  1, 4, 33138, 1, 0,  //Bypass AC current
  1, 4, 33148, 1, 0,  //Bypass load power
  1, 4, 33057, 2, 0,  //Total DC output power
  1, 4, 33133, 1, 0,  //Battery voltage
  1, 4, 33134, 1, 0,  //Battery current
  1, 4, 33136, 1, 0,  //LLCbus voltage
  1, 4, 33149, 2, 0,  //Battery power
  1, 4, 33161, 2, 0,  //Total battery charge
  1, 4, 33163, 1, 0,  //Battery charge today
  1, 4, 33164, 1, 0,  //Battery charge yesterday
  1, 4, 33165, 2, 0,  //Total battery discharge
  1, 4, 33167, 1, 0,  //Battery discharge capacity
  1, 4, 33168, 1, 0,  //Battery discharge power yesterday
  1, 4, 33169, 2, 0,  //Total power imported from Grid
  1, 4, 33171, 1, 0,  //Grid power imported today
  1, 4, 33172, 1, 0,  //Grid power imported yesterday
  1, 4, 33173, 2, 0,  //Total power exported to Grid
  1, 4, 33175, 1, 0,  //Power imported from Grid today
  1, 4, 33176, 1, 0,  //Power imported from Grid yesterday
  1, 4, 33141, 1, 0,  //BMS Battery Voltage
  1, 4, 33142, 1, 0,  //BMS Battery Current
  1, 4, 33126, 2, 0,  //Electricity meter total active power generation
  1, 4, 33128, 1, 0,  //Meter voltage
  1, 4, 33129, 1, 0,  //Meter current
  1, 4, 33281, 1, 0,  //Meter power factor
  1, 4, 33282, 1, 0,  //Meter Grid frequency
  1, 4, 33283, 1, 0,  //Meter total active energy imported from Grid
  1, 4, 33285, 2, 0,  //Meter total active energy exported to Grid
  1, 4, 33022, 1, 0,  //System time year
  1, 4, 33023, 1, 0,  //System time month
  1, 4, 33024, 1, 0,  //System time day
  1, 4, 33025, 1, 0,  //System time
  1, 4, 33026, 1, 0,  //System time
  1, 4, 33027, 1, 0,  //System time second
  1, 4, 33071, 1, 0,  //DC bus voltage
  1, 4, 33093, 1, 0,  //Inverter
  1, 4, 33095, 1, 0  //Current state of the inverter
};


//================================================================================================================================
  /////////////////////////////////////////////////////////
  //*********************** SETUP ********************** //

void setup() {
  Serial.begin(9600);                               // initialize serial communication

  pinMode(LED_BUILTIN, OUTPUT);                     // LED control
  digitalWrite(LED_BUILTIN, HIGH);                  // LED lit during setup - should go out if sucessful

//  if (!WiFi.config(ip, gateway, subnet, primaryDNS, secondaryDNS)) {
//    Serial.println("STA Failed to configure");
//  }
  setupWiFi();                                      // Bring WiFi and mDNS up
  digitalWrite(LED_BUILTIN, HIGH);                  // LED lit during setup - should go out if sucessful

  Serial.println(F("Modbus begin"));                // Bring Modbus up

  if (!ModbusRTUClient.begin(9600)) {
    Serial.println(F("Modbus RTU Client start failed"));
    while (true);                                   // lockup
  }

  lanUDP.begin(localPort);  // Initialise local LAN UDP
  ThingSpeak.begin(client);  // Initialise ThingSpeak
  digitalWrite(LED_BUILTIN, LOW);
}

//================================================================================================================================
// Wifi per     R.A.Lincoln | July 2022 >>

void setupWiFi() {

  digitalWrite(LED_BUILTIN, HIGH);                  // LED lit during setup - should go out if sucessful

  Serial.println(F("WiFi begin"));                  // Bring WiFi up
  while (WiFi.status() != WL_CONNECTED) {
    WiFi.begin(SECRET_SSID, SECRET_PASS);           
    delay(4000);
  }

  printWifiStatus();

  digitalWrite(LED_BUILTIN, LOW);
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print(F("SSID: "));
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print(F("IP Address: "));
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print(F("Signal strength (RSSI):"));
  Serial.print(rssi);
  Serial.println(F(" dBm"));
}

//================================================================================================================================
void loop() {

  /////////////////////////////////////?///////////////////////
  //******************* MAIN LOOP TIMER  ******************* //
  delay(loopDelay);

  currentMillis = millis();  // use the same Millis for all timing each loop, i.e. dont end up with many Millisses, apart from...
  currentMicros = micros();  // where we use micros to do the program time
  progTime = currentMicros - timeEnd;  // will use the current micros at the end of the loop



  //******************* REAL TIME LOOP  ******************* //
  if (WiFi.status() != WL_CONNECTED) setupWiFi();            // re-connect if disconnected

  static unsigned long lastCollect;

  for ( int j = 0; j < storagePVSize; ++j )  // just use the numbers just now, will need to be more dynamic
  {
    delay(MODBUS_DELAY);
    Serial.print("StoragePVPoll ");
    Serial.print(j);

    digitalWrite(LED_BUILTIN, HIGH);

    int pollAddr = storagePV[j].addr;
    int pollCommand = storagePV[j].command;
    int pollRegisterNum = storagePV[j].registerNum;
    int pollLength = storagePV[j].length;
    long pollReturnedValue = 0;

    Serial.print(", ");
    Serial.print(pollAddr);
    Serial.print(", ");
    Serial.print(pollCommand);
    Serial.print(", ");
    Serial.print(pollRegisterNum);
    Serial.print(", ");
    Serial.print(pollLength);
    Serial.print(", ");
    Serial.print(pollReturnedValue);
    Serial.print(", ");


    if (!ModbusRTUClient.requestFrom(pollAddr, INPUT_REGISTERS, pollRegisterNum, pollLength))
    {
      Serial.println(F(" = Data error"));
    }
    else
    {
      if (pollLength == 2) pollReturnedValue = ModbusRTUClient.read() << 16; // 32 bit High 16
      pollReturnedValue |=  ModbusRTUClient.read();                   // Low 16 bits

      storagePV[j].returnedValue = pollReturnedValue;
      Serial.print(pollReturnedValue);
      Serial.println(", ");
    }
    digitalWrite(LED_BUILTIN, LOW);
  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //******************* 2Hz TIMER LOOP  ******************* //


  if ((currentMillis - prevMillis2Hz) >= millisPer2Hz)
  {
    prevMillis2Hz = currentMillis;


    lanUDP.beginPacket(destIp, remotePort);
    lanUDP.write((byte*)storagePV, sizeof(storagePV));
    lanUDP.endPacket();


  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //******************* 1Hz TIMER LOOP  ******************* //

  if ((currentMillis - prevMillis1Hz) >= millisPer1Hz)
  {
    prevMillis1Hz = currentMillis;
    periods = periods + 1;
    Serial.print("1Hz| ");
    Serial.println(periods);

    for ( int j = 0; j < fullSystemSize; ++j )  // just use the numbers just now, will need to be more dynamic
    {
      delay(MODBUS_DELAY);
      Serial.print("SystemPoll ");
      Serial.print(j);

      digitalWrite(LED_BUILTIN, HIGH);

      int pollAddr = fullSystem[j].addr;
      int pollCommand = fullSystem[j].command;
      int pollRegisterNum = fullSystem[j].registerNum;
      int pollLength = fullSystem[j].length;
      long pollReturnedValue = 0;

      Serial.print(", ");
      Serial.print(pollAddr);
      Serial.print(", ");
      Serial.print(pollCommand);
      Serial.print(", ");
      Serial.print(pollRegisterNum);
      Serial.print(", ");
      Serial.print(pollLength);
      Serial.print(", ");
      Serial.print(pollReturnedValue);
      Serial.print(", ");


      if (!ModbusRTUClient.requestFrom(pollAddr, INPUT_REGISTERS, pollRegisterNum, pollLength))
      {
        Serial.println(F(" = Data error"));
      }
      else
      {
        if (pollLength == 2) pollReturnedValue = ModbusRTUClient.read() << 16; // 32 bit High 16
        pollReturnedValue |=  ModbusRTUClient.read();                   // Low 16 bits

        fullSystem[j].returnedValue = pollReturnedValue;
        Serial.print(pollReturnedValue);
        Serial.println(", ");
      }
      digitalWrite(LED_BUILTIN, LOW);
    }

    /////////////////////////////////////////////////////////////////////
    //******************* THINGSPEAK TRANSMISSION  ******************* //

    if (periods >= updatePeriod)
    {
      periods = 0;  // reset the periods counter
      pktTxTspk = pktTxTspk + 1;  // transmission loops counter

      // SolarStorage values transmission
      ThingSpeak.setField(1, storagePV[0].returnedValue); //time between gas pulses
      ThingSpeak.setField(2, storagePV[1].returnedValue); //
      ThingSpeak.setField(3, storagePV[2].returnedValue); //
      ThingSpeak.setField(4, storagePV[3].returnedValue); //
      ThingSpeak.setField(5, storagePV[4].returnedValue); //

      ThingSpeak.setStatus(solarBatteryStatus);

      // write to the ThingSpeak channel
      int solarStoragex = ThingSpeak.writeFields(solarBattery, solarBatteryStatusWriteAPIKey);
      if (solarStoragex == 200) {
        Serial.println("Solar Storage Channel update successful.");
      }
      else {
        Serial.println("Solar Storage Problem updating channel; " + String(solarStoragex));
      }
    }

    
  } // ====== END OF 1Hz LOOP ++++++
}   // ====== END OF MAIN LOOP ++++++



//================================================================================================================================

The ModbusClient class of your library has the method setTimeout() exactly for this purpose.

What kind of processes (foreground or background) do you expect? Your Arduino isn't running a multi-tasking OS. Delaying the loop doesn't make any sense.

Another delay which is not necessary as the Modbus library should handle necessary wait times. As your choice of library fails to do that you might include that here but the correct wait time is 3.5 characters, so at 9600 baud this would be about 4ms. As you have several serial output in each loop and the serial interface runs at the same speed (9600 baud) you probably can eliminate that delay() call without any problems.

No, it suggest at minimum frame interval of 300ms. So you should wait 300ms from the start of one frame until you start the next one.

It can, but that's by far the most complex and the biggest (by number of code lines) of all Modbus libraries for the Arduino. If you want to adapt a library I strongly suggest to use a smaller one (p.e. ModbusMaster).

Although I don't see why sending 50 register values in one response is that less load on the device than sending 10 values in 10 responses the manual isn't clear about that. One could interpret the "interval" such that you shouldn't ask for values more often than once in 300ms but given the slow speed on the Modbus interface that doesn't make that sense.

A Modbus register has always 16 bit values, combining two of them to a 32bit value is quite easy.

Every minute? I guess polling the complete set isn't necessary more than every 10 minutes and your real time display is happy with an update every other second or so. You probably have no benefit of more often updates.

1 Like

Hello, - many thanks for your detailed feedback, it was very usefull.

Regarding the MODBUS timeout, i had not realised this function existed! I done some trial and error found 100ms stopped any false timeouts. May be if i make larger requests, ill need to increase this, but it certainly has had a huge impact.

This is my first time using this MCU, for the last few months ive been using ESP8266 and found for the WiFi to be stable, this benefited from a few ms of delay. Ive removed this delay.

The delay for the MODBUS came from the original source by R. A. Lincoln, ive removed this delay too and it still works well.


Noted the point on reading 1 or 2, or 50 registers, my thought was each request looks like 8 bytes for 1 or 50, and a reply is 7 for 1 register, to read 6 is 17 bytes - this was where i was thinking there was an increase in efficiency but this is pushing my understanding of these things so i appreciate your understanding.

Looking at reading a wider address pool, i had thought i could read the reply into an array "reply" (type long of size 28);


  if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33057, 28))
  {
    Serial.println(F(" = Data error"));
  }
  else
  {
    reply = ModbusRTUClient.read();
    Serial.println(reply);
    
    long storDCpwrDC  = (reply[0] << 16) | reply[1];
    long storSyDCvBus = reply[14];
    long storACv      = reply[16];
    long storACi      = reply[19];
    long storACpwrAct = (reply[20] << 16) | reply[21];
    long storACpwrRea = (reply[22] << 16) | reply[23];
    long storACpwrApp = (reply[26] << 16) | reply[27];

  } 

When i compile i get an error "incompatible types in assignment of 'long int' to 'long int [28]' " at the line "reply = ModbusRTUClient.read();"

Im thinking the reply is perhaps a string?

It's hard to believe that making the timeout shorter stops false timeouts. Maybe I misunderstood something.

This is correct, the number of bytes transmitted decreases if you have to request consecutive register values. But if you request 50 registers but need only 6 of it's not efficient anymore.

From the description in the manual I got the impression that for the device the interruption to handle a request is more expensive than having to send many values in response to one request. But that's not clear in the manual.

No, using read() you get one value back. You can read your result like this:

uint8_t ind = 0;
while (ModbusRTUClient.available()) {
  reply[ind++] = ModbusRTUClient.read();
}
1 Like

Hello,

Again - thanks for your help, i have things working reliably and very much faster than before - i need to do a good bit of tidy up as i just wanted to get something reliably polling and receiving which can be improved upon.

Full code as follows:

/*

  Solis RAI inverter/charger unit and Solis MINI Solar PV inverter RS485 MODBUS to network interface

  S. Graham May/June 2023
     based around the excellent work of;
       R.A.Lincoln     July 2022, SolisComms https://github.com/RichardL64

  Hardware: Arduino Nano 33 IOT

  SolisComms_SGr_
  V1
  V2
  V3            These versions added in polling a second address for the solar PV and send by HTTP/UDP etc - too many issues with mods
  V1_1_Cutback  Start a fresh - remove the things i dont need, eg the webserver
  V1_2_Cutback  Bit more work done
  V4 from V1.2  Got this working, polling a long list of registers on both addresses, posting to thingspeak and also doing a UDP broadcast
                Issues with timing:
                      When theres no PV, the MODBUS library 1000ms timeout is too long - not fussed; need perhaps 20ms perhaps
                      UDP and Thingspeak held up by MODBUS polling, need to do the UDP quicker, thingspeak probably OK.

  V5            Lets try and fix things!
    V5.1        Spin off to read a range of MODBUS registers in one go
                      Much improved response speed etc!
                      TODO:  Split them out into timed elements, eg only read some registers so often
                             Add more things to ThingSpeak
                             LAN UDP (perhaps do it with each poll for many smaller UDP packets as opposed to a big one?)
                             Solar group time out - eg dont poll for so many reads following a couple of timeouts (dark)
                             Move repeated things into functions - code is messy!

  V6            Working on the above...



  /////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////
  //******************* SYSTEM STUFF ******************* //  //******************* SYSTEM STUFF ******************* //
  /////////////////////////////////////////////////////////  /////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //******************* LIBRARIES  ******************* //

*/
#include <SPI.h>
#include <WiFiNINA.h>                           // (note _generic version locks up on closed connections)
#include <ArduinoModbus.h>
#include <ModbusClient.h>
#include <WiFiUdp.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <NTPClient.h>
#include "ThingSpeak.h" // always include thingspeak header file after other header files and custom macros


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************* WiFi HEADER  ******************* //

#define SECRET_SSID "removed"
#define SECRET_PASS "removed"

// Various objects which will use WiFi/data comms
WiFiClient  client;  // used for thingspeak
WiFiUDP ntpUDP;  // used for NTP receie server
WiFiUDP lanUDP;  // used for LAN transmission

int statusTx = 0;  //transmission status

IPAddress ip(192, 168, 1, 61);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8);   //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional

IPAddress destIp(192, 168, 1, 255);


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************* TIMERS  ******************* //

unsigned long currentMicros = 0;
unsigned long currentMillis = 0;

//2Hz / 500ms loop
unsigned long prevMillis2Hz = 0;
unsigned long millisPer2Hz = 500; // 500ms

//1Hz / 1000ms loop
unsigned long prevMillis1Hz = 0;
unsigned long millisPer1Hz = 1000; // 1000ms

// program timer
int progTime = 0;
int timeEnd = 0;
int loopDelay = 5;  // delay in main loop to allow background processes
int counter = 0;

//display timer
unsigned long delaytime = 500;
unsigned long delayprint = 100;


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//******************* NTP TIME HEADER  ******************* //

NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", (60 * 60), 60000);  // update ever minute, add 1 hour for BST

int liveDays = 0;
int liveHours = 0;
int liveMinutes = 0;
int liveSeconds = 0;

int liveMinutesRunning = 0;

unsigned long unix = 0;


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************  THINGSPEAK HEADER  ******************* //

unsigned long solarBattery = removed;
const char * solarBatteryStatusWriteAPIKey = "removed";
String solarBatteryStatus = "";


int updatePeriod = 2; // number of 1Hz loops between transmissions
int periods = 0; // counter for the updatePeriod for
unsigned long pktTxTspk = 0; // Transmit Packet counter


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************  UDP  ******************* //

char packetBuffer[255];
unsigned int localPort = 1912;
unsigned int remotePort = 1912;
unsigned long pktTxUDP = 0; // Transmit Packet counter
unsigned long pktRxUDP = 0; // Receive Packet counter


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//*******************  MODBUS  ******************* //

#define MODBUS_DELAY 5
long reply[100];

int slowUpdate = 20;  // number of 2Hz loops between updates
int slowUpdateCount = 0;  //


// solarGroup1
long pvACpwrAct = 0;   // Active power
long pvEntdy = 0;   // Today energy
long pvDCv = 0;   // DC Voltage
long pvDCi = 0;   // DC Current
long pvACv = 0;   // AC Voltage
long pvACi = 0;   // AC Current
long pvSyt = 0;   // Inverter Temperature
long pvACf = 0;   // AC Frequency


// storageGroup1
long storSyyy = 0;   // System time year
long storSymomo = 0;   // System time month
long storSydd = 0;   // System time day
long storSyhh = 0;   // System time hour
long storSymm = 0;   // System time minute
long storSyss = 0;   // System time second


// storageGroup2
long storSyDCvBus = 0;   // DC bus voltage
long storACv = 0;   // AB line voltage /
long storACi = 0;   // Phase A current
long storACpwrAct = 0;   // Active power
long storACpwrRea = 0;   // Reactive power
long storACpwrApp = 0;   // Apparent power


// storageGroup3
long storSyt = 0;   // Inverter
long storACf = 0;   // Grid frequency
long storSystate = 0;   // Current state of the inverter


// storageGroup4
long storgACpwrGenActTtl = 0;   // Electricity meter total active power generation
long storgACv = 0;   // Meter voltage
long storgACi = 0;   // Meter current
long storgACpwrActImpExp = 0;   // Meter active power
long storDCv = 0;   // Battery voltage
long storDCi = 0;   // Battery current
long storDCdir = 0;   // Battery current direction
long storDCvLLC = 0;   // LLCbus voltage
long storACvBypass = 0;   // Bypass AC voltage
long storACiBypass = 0;   // Bypass AC current


// storageGroup5
long storDCsoc = 0;   // Battery capacity
long storDCsoh = 0;   // Battery health SOH
long storBMSv = 0;   // Battery voltage (BMS)
long storBMSi = 0;   // Battery current (BMS)
long storBMSchgLim = 0;   // Battery charge limit (BMS)
long storBMSdisChg = 0;   // Battery discharge limit (BMS)
long storBMSfail1 = 0;   // Battery failure 1 (BMS)
long storBMSfail2 = 0;   // Battery failure 2 (BMS)
long storAChlp = 0;   // House load power
long storACblp = 0;   // Bypass load power
long storDCpwrBat = 0;   // Battery power


// storageGroup6
long storEnbatChgTtl = 0;   // Total battery charge
long storEnbatChgTdy = 0;   // Battery charge today
long storEnbatChgYdy = 0;   // Battery charge yesterday
long storEnbatDisTtl = 0;   // Total battery discharge
long storEnbatDisTdy = 0;   // Battery discharge capacity
long storEnbatDisYdy = 0;   // Battery discharge power yesterday
long storEngridImpTtl = 0;   // Total power imported from Grid
long storEngridImpTdy = 0;   // Grid power imported today
long storEngridImpYdy = 0;   // Grid power imported yesterday
long storEngridExpTtl = 0;   // Total power exported to Grid
long storEngridExpTdy = 0;   // Power imported from Grid today
long storEngridExpYdy = 0;   // Power imported from Grid yesterday


// storageGroup7
long storgACpf = 0;   // Meter power factor
long storgACf = 0;   // Meter Grid frequency
long storgACgridActImp = 0;   // Meter total active energy imported from Grid
long storgACgridActExp = 0;   // Meter total active energy exported to Grid



// Calculated
int storACpwrActChgDis = 0;  // storage inverter active power with direction

//================================================================================================================================
/////////////////////////////////////////////////////////
//*********************** SETUP ********************** //

void setup() {
  Serial.begin(9600);                               // initialize serial communication

  pinMode(LED_BUILTIN, OUTPUT);                     // LED control
  digitalWrite(LED_BUILTIN, HIGH);                  // LED lit during setup - should go out if sucessful

  //  if (!WiFi.config(ip, gateway, subnet, primaryDNS, secondaryDNS)) {
  //    Serial.println("STA Failed to configure");
  //  }
  setupWiFi();                                      // Bring WiFi and mDNS up
  digitalWrite(LED_BUILTIN, HIGH);                  // LED lit during setup - should go out if sucessful

  Serial.println(F("Modbus begin"));                // Bring Modbus up

  if (!ModbusRTUClient.begin(9600)) {
    Serial.println(F("Modbus RTU Client start failed"));
    while (true);                                   // lockup
  }

  ModbusRTUClient.setTimeout(350);

  lanUDP.begin(localPort);  // Initialise local LAN UDP
  ThingSpeak.begin(client);  // Initialise ThingSpeak
  digitalWrite(LED_BUILTIN, LOW);
}

//================================================================================================================================
// Wifi per     R.A.Lincoln | July 2022 >>

void setupWiFi() {

  digitalWrite(LED_BUILTIN, HIGH);                  // LED lit during setup - should go out if sucessful

  Serial.println(F("WiFi begin"));                  // Bring WiFi up
  while (WiFi.status() != WL_CONNECTED) {
    WiFi.begin(SECRET_SSID, SECRET_PASS);
    delay(4000);
  }

  printWifiStatus();

  digitalWrite(LED_BUILTIN, LOW);
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print(F("SSID: "));
  Serial.println(WiFi.SSID());

  // print your board's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print(F("IP Address: "));
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print(F("Signal strength (RSSI):"));
  Serial.print(rssi);
  Serial.println(F(" dBm"));
}

//================================================================================================================================
void loop() {

  /////////////////////////////////////?///////////////////////
  //******************* MAIN LOOP TIMER  ******************* //

  currentMillis = millis();  // use the same Millis for all timing each loop, i.e. dont end up with many Millisses, apart from...
  currentMicros = micros();  // where we use micros to do the program time
  progTime = currentMicros - timeEnd;  // will use the current micros at the end of the loop


  if (WiFi.status() != WL_CONNECTED) setupWiFi();            // re-connect if disconnected


  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //******************* 2Hz TIMER LOOP  ******************* //


  if ((currentMillis - prevMillis2Hz) >= millisPer2Hz)
  {
    prevMillis2Hz = currentMillis;


    
        //****************** Poll with the loop *************** //

        // = = = = = = solarGroup1   2  3004  39
        Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(2, INPUT_REGISTERS, 3004, 39))
    {
      Serial.println(F(" = Data error - solarGroup1"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("solarGroup1 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
          }

          pvACpwrAct  = (reply[0] << 16) | reply[1];
          pvEntdy     = reply[10];
          pvDCv       = reply[17];
          pvDCi       = reply[18];
          pvACv       = reply[31];
          pvACi       = reply[34];
          pvSyt       = reply[37];
          pvACf       = reply[38];
        }
    

    // = = = = = = storageGroup1   33022  6
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33022, 6))
    {
      Serial.println(F(" = Data error - storageGroup1"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup1 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storSyyy    = reply[0];
      storSymomo  = reply[1];
      storSydd    = reply[2];
      storSyhh    = reply[3];
      storSymm    = reply[4];
      storSyss    = reply[5];
    }


    // = = = = = = storageGroup2   33071 14
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33071, 14))
    {
      Serial.println(F(" = Data error - storageGroup2"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup2 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storSyDCvBus = reply[0];
      storACv      = reply[2];
      storACi      = reply[4];
      storACpwrAct = (reply[8] << 16) | reply[9];
      storACpwrRea = (reply[10] << 16) | reply[11];
      storACpwrApp = (reply[12] << 16) | reply[13];
    }


    // = = = = = = storageGroup3  ** Tested and working **
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33093, 2))
    {
      Serial.println(F(" = Data error - storageGroup3"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup3 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storSyt     = reply[0];
      storACf     = reply[1];
      storSystate = reply[2];

    }



    // = = = = = = storageGroup4   33126  13
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33126, 13))
    {
      Serial.println(F(" = Data error - storageGroup4"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup4 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storgACpwrGenActTtl = (reply[0] << 16) | reply[1];
      storgACv            = reply[2];
      storgACi            = reply[3];
      storgACpwrActImpExp = (reply[4] << 16) | reply[5];
      storDCv             = reply[7];
      storDCi             = reply[8];
      storDCdir           = reply[9];
      storDCvLLC          = reply[10];
      storACvBypass       = reply[11];
      storACiBypass       = reply[12];
    }


    // = = = = = = storageGroup5  33139  12
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33139, 12))
    {
      Serial.println(F(" = Data error - storageGroup5"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup5 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storDCsoc     = reply[0];
      storDCsoh     = reply[1];
      storBMSv      = reply[2];
      storBMSi      = reply[3];
      storBMSchgLim = reply[4];
      storBMSdisChg = reply[5];
      storBMSfail1  = reply[6];
      storBMSfail2  = reply[7];
      storAChlp     = reply[8];
      storACblp     = reply[9];
      storDCpwrBat  = (reply[10] << 16) | reply[11];
    }


    // = = = = = = storageGroup6   33161  16
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33161, 16))
    {
      Serial.println(F(" = Data error - storageGroup6"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup6 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storEnbatChgTtl  = (reply[1] << 16) | reply[1];
      storEnbatChgTdy  = reply[2];
      storEnbatChgYdy  = reply[3];
      storEnbatDisTtl  = (reply[4] << 16) | reply[5];
      storEnbatDisTdy  = reply[6];
      storEnbatDisYdy  = reply[7];
      storEngridImpTtl = (reply[8] << 16) | reply[9];
      storEngridImpTdy = reply[10];
      storEngridImpYdy = reply[11];
      storEngridExpTtl = (reply[12] << 16) | reply[13];
      storEngridExpTdy = reply[14];
      storEngridExpYdy = reply[15];
    }


    // = = = = = = storageGroup7   33281  6
    Serial.println(millis());
    if (!ModbusRTUClient.requestFrom(1, INPUT_REGISTERS, 33281, 6))
    {
      Serial.println(F(" = Data error - storageGroup7"));
    }
    else
    {
      uint8_t ind = 0;
      while (ModbusRTUClient.available())
      {
        int reading = ModbusRTUClient.read();

        Serial.print("storageGroup7 while: ");
        Serial.print(reading);
        Serial.print("  | Ind: ");
        Serial.println(ind);

        reply[ind++] = reading;
      }

      storgACpf         = reply[0];
      storgACf          = reply[1];
      storgACgridActImp = reply[2];
      storgACgridActExp = (reply[4] << 16) | reply[5];
    }
    Serial.println(millis());


    // solarGroup1
    Serial.print(pvACpwrAct);  Serial.print(", ");
    Serial.print(pvEntdy);  Serial.print(", ");
    Serial.print(pvDCv);  Serial.print(", ");
    Serial.print(pvDCi);  Serial.print(", ");
    Serial.print(pvACv);  Serial.print(", ");
    Serial.print(pvACi);  Serial.print(", ");
    Serial.print(pvSyt);  Serial.print(", ");
    Serial.print(pvACf);  Serial.print(", ");

    // storageGroup1
    Serial.print(storSyyy);  Serial.print(", ");
    Serial.print(storSymomo);  Serial.print(", ");
    Serial.print(storSydd);  Serial.print(", ");
    Serial.print(storSyhh);  Serial.print(", ");
    Serial.print(storSymm);  Serial.print(", ");
    Serial.print(storSyss);  Serial.print(", ");

    // storageGroup2
    Serial.print(storSyDCvBus);  Serial.print(", ");
    Serial.print(storACv);  Serial.print(", ");
    Serial.print(storACi);  Serial.print(", ");
    Serial.print(storACpwrAct);  Serial.print(", ");
    Serial.print(storACpwrRea);  Serial.print(", ");
    Serial.print(storACpwrApp);  Serial.print(", ");

    // storageGroup3
    Serial.print(storSyt);  Serial.print(", ");
    Serial.print(storACf);  Serial.print(", ");
    Serial.print(storSystate);  Serial.print(", ");

    // storageGroup4
    Serial.print(storgACpwrGenActTtl);  Serial.print(", ");
    Serial.print(storgACv);  Serial.print(", ");
    Serial.print(storgACi);  Serial.print(", ");
    Serial.print(storgACpwrActImpExp);  Serial.print(", ");
    Serial.print(storDCv);  Serial.print(", ");
    Serial.print(storDCi);  Serial.print(", ");
    Serial.print(storDCdir);  Serial.print(", ");
    Serial.print(storDCvLLC);  Serial.print(", ");
    Serial.print(storACvBypass);  Serial.print(", ");
    Serial.print(storACiBypass);  Serial.print(", ");

    // storageGroup5
    Serial.print(storDCsoc);  Serial.print(", ");
    Serial.print(storDCsoh);  Serial.print(", ");
    Serial.print(storBMSv);  Serial.print(", ");
    Serial.print(storBMSi);  Serial.print(", ");
    Serial.print(storBMSchgLim);  Serial.print(", ");
    Serial.print(storBMSdisChg);  Serial.print(", ");
    Serial.print(storBMSfail1);  Serial.print(", ");
    Serial.print(storBMSfail2);  Serial.print(", ");
    Serial.print(storAChlp);  Serial.print(", ");
    Serial.print(storACblp);  Serial.print(", ");
    Serial.print(storDCpwrBat);  Serial.print(", ");

    // storageGroup6
    Serial.print(storEnbatChgTtl);  Serial.print(", ");
    Serial.print(storEnbatChgTdy);  Serial.print(", ");
    Serial.print(storEnbatChgYdy);  Serial.print(", ");
    Serial.print(storEnbatDisTtl);  Serial.print(", ");
    Serial.print(storEnbatDisTdy);  Serial.print(", ");
    Serial.print(storEnbatDisYdy);  Serial.print(", ");
    Serial.print(storEngridImpTtl);  Serial.print(", ");
    Serial.print(storEngridImpTdy);  Serial.print(", ");
    Serial.print(storEngridImpYdy);  Serial.print(", ");
    Serial.print(storEngridExpTtl);  Serial.print(", ");
    Serial.print(storEngridExpTdy);  Serial.print(", ");
    Serial.print(storEngridExpYdy);  Serial.print(", ");

    // storageGroup7
    Serial.print(storgACpf);  Serial.print(", ");
    Serial.print(storgACf);  Serial.print(", ");
    Serial.print(storgACgridActImp);  Serial.print(", ");
    Serial.print(storgACgridActExp);  Serial.print(", ");



    //****************** UDP transmission *************** //
    //  lanUDP.beginPacket(destIp, remotePort);
    //  lanUDP.write((byte*)storagePV, sizeof(storagePV));
    //  lanUDP.endPacket();
    Serial.println(millis());

  }

  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  //******************* 1Hz TIMER LOOP  ******************* //

  if ((currentMillis - prevMillis1Hz) >= millisPer1Hz)
  {
    prevMillis1Hz = currentMillis;
    periods = periods + 1;
    Serial.print("1Hz| ");
    Serial.println(periods);

    Serial.print(", ");
    Serial.print(storSyDCvBus);
    Serial.print(", ");
    Serial.print(storACv);
    Serial.print(", ");
    Serial.print(storACi);
    Serial.print(", ");
    Serial.print(storACpwrAct);
    Serial.print(", ");
    Serial.print(storACpwrRea);
    Serial.print(", ");
    Serial.print(storACpwrApp);
    Serial.print(", ");
    Serial.print(storBMSv);
    Serial.print(", ");
    Serial.print(storBMSi);
    Serial.print(", ");


    /////////////////////////////////////////////////////////////////////
    //******************* THINGSPEAK TRANSMISSION  ******************* //

    if (periods >= updatePeriod)
    {
      periods = 0;  // reset the periods counter
      
      
    // Use the battery direction to make an inverter active power value with direction
    if (storDCdir == 1)
    {
      storACpwrActChgDis = storACpwrAct * -1;   // negative if discharging
    }
    else
    {
      storACpwrActChgDis = storACpwrAct;
    }

      
      pktTxTspk = pktTxTspk + 1;  // transmission loops counter

       // SolarStorage values transmission
            ThingSpeak.setField(1, pvACpwrAct); //time between gas pulses
            ThingSpeak.setField(2, storACpwrActChgDis); //
            ThingSpeak.setField(3, storDCdir); //
            ThingSpeak.setField(4, storDCsoc); //
            ThingSpeak.setField(5, storgACpwrActImpExp); //

           ThingSpeak.setStatus(solarBatteryStatus);

      // write to the ThingSpeak channel
            int solarStoragex = ThingSpeak.writeFields(solarBattery, solarBatteryStatusWriteAPIKey);
            if (solarStoragex == 200) {
              Serial.println("Solar Storage Channel update successful.");
            }
            else {
              Serial.println("Solar Storage Problem updating channel; " + String(solarStoragex));
            }
    }


  } // ====== END OF 1Hz LOOP ++++++
}   // ====== END OF MAIN LOOP ++++++



//================================================================================================================================

The output to the serial monitor for a loop looks like;

10:26:26.904 -> IP Address: 192.168.1.149																																																													
10:26:26.904 -> Signal strength (RSSI):-68 dBm																																																													
10:26:26.904 -> Modbus begin																																																													
10:26:26.904 -> 10619																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 0																																																													
10:26:27.093 -> solarGroup1 while: 430  | Ind: 1																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 2																																																													
10:26:27.093 -> solarGroup1 while: 511  | Ind: 3																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 4																																																													
10:26:27.093 -> solarGroup1 while: 142  | Ind: 5																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 6																																																													
10:26:27.093 -> solarGroup1 while: 67  | Ind: 7																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 8																																																													
10:26:27.093 -> solarGroup1 while: 75  | Ind: 9																																																													
10:26:27.093 -> solarGroup1 while: 16  | Ind: 10																																																													
10:26:27.093 -> solarGroup1 while: 32  | Ind: 11																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 12																																																													
10:26:27.093 -> solarGroup1 while: 142  | Ind: 13																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 14																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 15																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 16																																																													
10:26:27.093 -> solarGroup1 while: 1066  | Ind: 17																																																													
10:26:27.093 -> solarGroup1 while: 48  | Ind: 18																																																													
10:26:27.093 -> solarGroup1 while: 14  | Ind: 19																																																													
10:26:27.093 -> solarGroup1 while: 1  | Ind: 20																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 21																																																													
10:26:27.093 -> solarGroup1 while: 1  | Ind: 22																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 23																																																													
10:26:27.093 -> solarGroup1 while: 1  | Ind: 24																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 25																																																													
10:26:27.093 -> solarGroup1 while: 1866  | Ind: 26																																																													
10:26:27.093 -> solarGroup1 while: 3713  | Ind: 27																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 28																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 29																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 30																																																													
10:26:27.093 -> solarGroup1 while: 2487  | Ind: 31																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 32																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 33																																																													
10:26:27.093 -> solarGroup1 while: 17  | Ind: 34																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 35																																																													
10:26:27.093 -> solarGroup1 while: 0  | Ind: 36																																																													
10:26:27.093 -> solarGroup1 while: 384  | Ind: 37																																																													
10:26:27.093 -> solarGroup1 while: 4994  | Ind: 38																																																													
10:26:27.093 -> 10802																																																													
10:26:27.187 -> storageGroup1 while: 23  | Ind: 0																																																													
10:26:27.187 -> storageGroup1 while: 6  | Ind: 1																																																													
10:26:27.187 -> storageGroup1 while: 12  | Ind: 2																																																													
10:26:27.187 -> storageGroup1 while: 10  | Ind: 3																																																													
10:26:27.187 -> storageGroup1 while: 26  | Ind: 4																																																													
10:26:27.187 -> storageGroup1 while: 14  | Ind: 5																																																													
10:26:27.187 -> 10879																																																													
10:26:27.281 -> storageGroup2 while: 3885  | Ind: 0																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 1																																																													
10:26:27.281 -> storageGroup2 while: 2487  | Ind: 2																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 3																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 4																																																													
10:26:27.281 -> storageGroup2 while: 16  | Ind: 5																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 6																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 7																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 8																																																													
10:26:27.281 -> storageGroup2 while: 397  | Ind: 9																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 10																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 11																																																													
10:26:27.281 -> storageGroup2 while: 0  | Ind: 12																																																													
10:26:27.281 -> storageGroup2 while: 397  | Ind: 13																																																													
10:26:27.281 -> 11002																																																													
10:26:27.373 -> storageGroup3 while: 334  | Ind: 0																																																													
10:26:27.373 -> storageGroup3 while: 4994  | Ind: 1																																																													
10:26:27.373 -> 11068																																																													
10:26:27.467 -> storageGroup4 while: 41  | Ind: 0																																																													
10:26:27.467 -> storageGroup4 while: 8104  | Ind: 1																																																													
10:26:27.515 -> storageGroup4 while: 2498  | Ind: 2																																																													
10:26:27.515 -> storageGroup4 while: 131  | Ind: 3																																																													
10:26:27.515 -> storageGroup4 while: 65535  | Ind: 4																																																													
10:26:27.515 -> storageGroup4 while: 65534  | Ind: 5																																																													
10:26:27.515 -> storageGroup4 while: 35  | Ind: 6																																																													
10:26:27.515 -> storageGroup4 while: 535  | Ind: 7																																																													
10:26:27.515 -> storageGroup4 while: 71  | Ind: 8																																																													
10:26:27.515 -> storageGroup4 while: 0  | Ind: 9																																																													
10:26:27.515 -> storageGroup4 while: 3110  | Ind: 10																																																													
10:26:27.515 -> storageGroup4 while: 2487  | Ind: 11																																																													
10:26:27.515 -> storageGroup4 while: 17  | Ind: 12																																																													
10:26:27.515 -> 11200																																																													
10:26:27.609 -> storageGroup5 while: 93  | Ind: 0																																																													
10:26:27.609 -> storageGroup5 while: 100  | Ind: 1																																																													
10:26:27.609 -> storageGroup5 while: 5322  | Ind: 2																																																													
10:26:27.609 -> storageGroup5 while: 39  | Ind: 3																																																													
10:26:27.609 -> storageGroup5 while: 500  | Ind: 4																																																													
10:26:27.609 -> storageGroup5 while: 950  | Ind: 5																																																													
10:26:27.609 -> storageGroup5 while: 0  | Ind: 6																																																													
10:26:27.609 -> storageGroup5 while: 0  | Ind: 7																																																													
10:26:27.609 -> storageGroup5 while: 0  | Ind: 8																																																													
10:26:27.609 -> storageGroup5 while: 0  | Ind: 9																																																													
10:26:27.609 -> storageGroup5 while: 65535  | Ind: 10																																																													
10:26:27.609 -> storageGroup5 while: 65157  | Ind: 11																																																													
10:26:27.609 -> 11296																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 0																																																													
10:26:27.701 -> storageGroup6 while: 976  | Ind: 1																																																													
10:26:27.701 -> storageGroup6 while: 38  | Ind: 2																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 3																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 4																																																													
10:26:27.701 -> storageGroup6 while: 931  | Ind: 5																																																													
10:26:27.701 -> storageGroup6 while: 12  | Ind: 6																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 7																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 8																																																													
10:26:27.701 -> storageGroup6 while: 1914  | Ind: 9																																																													
10:26:27.701 -> storageGroup6 while: 2868  | Ind: 10																																																													
10:26:27.701 -> storageGroup6 while: 1416  | Ind: 11																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 12																																																													
10:26:27.701 -> storageGroup6 while: 780  | Ind: 13																																																													
10:26:27.701 -> storageGroup6 while: 0  | Ind: 14																																																													
10:26:27.701 -> storageGroup6 while: 84  | Ind: 15																																																													
10:26:27.701 -> 11407																																																													
10:26:27.795 -> storageGroup7 while: 65535  | Ind: 0																																																													
10:26:27.795 -> storageGroup7 while: 4994  | Ind: 1																																																													
10:26:27.795 -> storageGroup7 while: 2  | Ind: 2																																																													
10:26:27.795 -> storageGroup7 while: 60352  | Ind: 3																																																													
10:26:27.795 -> storageGroup7 while: 1  | Ind: 4																																																													
10:26:27.795 -> storageGroup7 while: 12548  | Ind: 5																																																													
10:26:27.795 -> 11480																																																													
10:26:27.795 -> 430, 430, 16, 1066, 48, 2487, 17, 384, 4994, 23, 6, 12, 10, 26, 14, 3885, 2487, 0, 397, 0, 397, 334, 4994, 2487, 2695080, 2498, 131, -2, 535, 71, 0, 3110, 2487, 17, 93, 100, 5322,  39, 500, 950, 0, 0, 0, 0, -379, 63964112, 38, 0, 931, 12, 0, 1914, 2868, 1416, 780, 0, 84, 65535, 4994, 2	78084, 11490

Timings look to be about 20ms to 200ms depending on the range of addresses being polled, with the whole group being about 900ms, although i see some groups i could split up as theres values i dont need, the serial prints will take up time too but sub 1 second is 10 times faster than before!

Many thanks for your help with this :slight_smile: Hope this is a useful to others doing the same or similar.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.