A Forum run by Enthusiasts of MidNite Solar

The Open Source software/hardware corner => Arduino => Topic started by: dgd on March 12, 2015, 01:00:24 AM

Title: Web server for Classic using Arduino DUE
Post by: dgd on March 12, 2015, 01:00:24 AM
To get the simple web server working with an Arduino DUE some library changes are required plus the hardware interface for rs232 needs reworking.
The Arduino DUE is a more resourced computer based on the ATmel 32bit SAM3X8e ARM Cortex M3 processor running at 84Mz.  Ram is 96k, and flash 512k. Compared to the UNO with 16Mhz 8 bit ATmel328 cpu, 32k flash and 2k ram, this Arduino has the potential to make a quite nice web server system for a Classic.
With all that available ram a decent sized data structure can be used to store hours and even days of Classic data. This could be used to provide a series of web pages and simple graph displays. It could be backed onto a Sandisk SD card intreface that is a standard fitting on the ethernet shield.

The DUE is a 3.3volt system so interfaces to the various digital connections have to be max 3.3v
The Wiznet 5100/5200 based ethernet shield is fully compatible with the DUE.
However, the rs232 shield is not as it is a 5volt TTL level card (the MAX232 onboard needs 5v for its voltage pump circuit to provide rs232 signal voltage levels). It also interfaces to the serial pins digital 0 and 1 which are used on the DUE to provide a USB port.
So one of the other serial pin sets, serial1, serial2 and serial3 needs to be used.
Connecting 5v to any of the DUE 3.3v pins will damage the cpu.

To get the Classics rs232 connected to a serial port on the DUE I used a teensy sized max232 board and wired it to the serial1 port (on digital pins 18 (tx1) and 19 (rx1). The problem is these digital pins operate at 3.3v and the output from the max232 card is 5v.
The tx1  pin is not a problem as the max232 can work with 3.3v signal levels so the tx can be directly wired to the max232. The issue is with the rx on the DUE nd the 5v output from the max232 needs to be level shifted so as not to kill the rx1 input.
The easiest way to do this was with a resistor divider network.
I used a 500 ohm and 1k ohm resistors for this.

Works perfectly and allows Classic rs232 data into the DUE
Image shows DUE with ethernet shield and at back the max232 pcb on a proto shield

dgd

Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 12, 2015, 01:51:22 AM
To get the simple web server compiled for the DUE needs some changes.

First the sketch needs the call to modbus_configure slightly changed -
from
  modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
to
  modbus_configure(&Serial1, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);

The DUE does not use the byteFormat parameter SERIAL_8N1 and the port address pointer is changed to $Serial1

Second, and a tad more complicated the libs for SimpleModbusMaster need some changes.
Go to the SimpleModbusMaster lib directory   -   on my Win8 laptop this is at:
> This PC > Documents> Arduino > libraries > SimpleModbusMasterv10

Two files need to be edited (I used Notepad editor)
SimpleModbusMaster.h
Search for

void modbus_configure(HardwareSerial* SerialPort,
                                 long baud,
                                 unsigned char byteFormat,
                                 unsigned int _timeout,
                                 unsigned int _polling,
                                 unsigned char _retry_count,
                                 unsigned char _TxEnablePin,
                                 Packet* _packets,
                                 unsigned int _total_no_of_packets);

and comment out the line
//                                                                                      unsigned char byteFormat
then save the file

Edit the file SimpleModbusMaster.cpp

find the lines

void modbus_configure(HardwareSerial* SerialPort,
                                 long baud,
                                 unsigned char byteFormat,
                                 unsigned int _timeout,
                                 unsigned int _polling,
                                 unsigned char _retry_count,
                                 unsigned char _TxEnablePin,
                                 Packet* _packets,
                                 unsigned int _total_no_of_packets)
{

Comment out the line
//                                                                                       unsigned char byteFormat,

then look for line further down this function

   (*ModbusPort).begin(baud, byteFormat);

comment it out

//     (*ModbusPort).begin(baud, byteFormat);

and insert below it

       (*ModbusPort).begin(baud);   


then save the cpp file.

The sketch for the simple web server should now compile and load onto the DUE

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 12, 2015, 11:03:50 PM
The next step with the DUE is to extend the sketch to provide more web page detail and reporting plus provide some auxiliary switching options.

This has been brought about by a local client who has a Classic Lite with WBjr and although he uses the local app when on site he asked for web access for him and his staff when off site. One tcp connection just proves too difficult to manage and anyway does not work on iPhone/iPad.

The DUE's digital pins are easy to connect to minuature relays such as the dual relay board in pic and controlling these using register data from Classic is straightforward.

The more interesting application is using the PWM output from the DUE to control an SSR for water heating. The Classic aux2 was used for this before the WBjr took away the aux2 so the waste not control should be possible with the DUE.
The Classic waste not appears to start the pwm signalling at a defined set point below the Absorb voltage and by the time the absorb voltage is reached the load is fully switched in circuit.
This does work but needs some careful design of the load so that there is sufficient power to drive it and keep the battery charging in Absorb stage.
PWMing a large load into circuit could cause charging to drop out off Absorb or even prevent the classic from reaching Absorb
(Unless I haveI misunderstood how this waste-not works?)

So with the DUE I planned the use of PWM slightly differently.
Only when the classic reaches absorb then will the PWM start and on each voltage register read (or charge state register read) the PWM will be adjusted/stepped to a higher duty cycle. If voltage/state drops below absorb voltage the duty cycle reduces.
I can't see any major problems with this approach but maybe there are issues I don't yet see.

The simple improved web page can show a list of various data from the Classic every 10 minutes.
A simple data structure of records, time stamped, is held in memory for 48 hours of data.
I will post the sketch for this soon.
Just investigating some simple bar graphs as a web page option.

Dgd

Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on March 12, 2015, 11:07:33 PM
Quoteusing the PWM output from the DUE to control an SSR for water heating.

Dgd, is this for DC heating elements? or AC?
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 12, 2015, 11:28:03 PM
Quote from: Westbranch on March 12, 2015, 11:07:33 PM
Quoteusing the PWM output from the DUE to control an SSR for water heating.

Dgd, is this for DC heating elements? or AC?

It should work for both since it depends on the relay - either an AC ssr or DC ssr

Dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on March 12, 2015, 11:54:07 PM
 :)  tks for the clarification, let us know if it works as planned...
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 19, 2015, 03:57:11 AM
Dual rs232 on proto shield for DUE, to connect two Classics to DUE web server. Maximum of 4 Classics could be connected. Modified simple web server sketch next post.
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on March 21, 2015, 08:07:59 PM
Nice write up dgd,

I see form the last photo you have a Freetronics ProtoShield, are you in Australia then, as these boards are made down here?

Currently I run a  Freetronics EtherMega (think Mega plus onboard Ethernet and SD) which has a switching supply rather than standard linear as used by most Arduino boards.
My setup is such that the Arduino controlls an AC SSR which I use as a diversion load controller to dump excess exnergy from my micro-hydro turbine.
I put in a PID loop comtroller routine that maintains my battery bank setpoint, at around 27.4 Volts.

Initially my program much like how you described, where I used the HTTP server library, Webduino, which really is a nicely structured library.
And it worked for some time and has been useful while I am over in Europe, allowing me to check in and see the system.

Then I started thinking more about it and also the probelms with the Wiznet-5100, in that it only allows 4 TCP connections, and for quite some time, there has been an ongoing problem with the Ethernet library, in that sometimes. connections were not released by the software. This problem has only been recently looked into and rectified, by some of the other folks on the Arduino forum, such as SurferTim.

With this, I moved away from having the Arduino acting as a HTTP server in itself, and instead started down the process of splitting the system into 2 parts.
Having Arduino as my interface to sensors and output devices and doing low level I/O things as the first part and then using somehting like a Cubieboard to play the part of HTTP server and all things higher level.

The DUE does bring a lot more resources to play with, but it is unfortunate that the designers of the board did not really do the best job.
For example, the SAM8E has onboard EMAC hardware for nice high speed Ethernet connectivity, but none of that is available on the standard DUE.

This is where the folks at Electhouse made a DUE with these pins brought out, called the Taijuino.
I was given some of these boards by Elechouse in the hope a native Ethernet library could be developed in conjunction with two other programmers.
Having such an Ethernet interface would not have the issues that the Wiznet gives, where the Wiznet is slow, it depends on SPI and has limited concurrent TCP connections sockets.
Unfortunately, to date, the DUE Ethernet library has not been completed.

I am currently moving my whole project from the Freetronics EtherMega to Taijuino DUE, with a whole lot more, in terms of sensors and features.
It will also be my solar charge controller, where I will control 6 indepenant high power MOSFETS in an interleaved PWM fashion.
I have a nice 3.2" LCD with touch panel from buydisplay that will give basic live data and status information.

With the DUE, we have an ADC that is capable of 12 bits rather than 10bits as found on AVR boards.

I guess I have a question;
If you have an Arduino with Ethernet interface, why not then talk to your Classic via TCP, rather than RS-232?
I don't yet know the Classic charge controllers well, not having one, but only reading the documents.
The reason I ask is, I am working with a guy in the states who does have two and I am helping him setup a full off-grid monitoring system.

Does this forum have code tags for your code to be displayed better?
I'll be interested to read more :)
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 22, 2015, 03:07:34 AM
Paul,

I'm in NZ where freetronics stuff is available from Jaycar. I would have liked a freetronics ether mega but the price at $150 is just crazy compared to a mega+ethernet shield for about $40.
As for Wiznet5100/5200 controllers I have not had any issues although I have read about some of the problems others have identified.

At one site I support we use a $5 uno and $12 rs232 shield to control a Crydom SSR for water heating using a PWM output and a couple of digital outputs for ventilation fan and night light.
The Uno connects to a Classic Lite 150 and pulls a few modbus register values for heating and lighting control. (I will post sketch and photos here soon).

My plan is not to go too far with developing the DUE for web access although the tiny web server installed and runs good. I liked the upload and serving of pages from the SD card.
However its all getting pretty complicated for the DUE as I also wanted to get three serial ports used to connect Classics.
I prefer simple solutions and after experimenting with rs485-rs232 converters on Classics
this approach was abandoned due to awful throughput rate.

Now, after buying a Cubie2, installing Linux and proper web server (and an SSD for proper data storage ) , I want to have each Classic connected via rs232 modbus to a mega2560
simply to manage raw data acquistion and then periodically transfer this data to the Cubie.
Ethernet or more likely I2C - since both Cubie and megas support this.

In meantime I have connected a 5v nano ($2 from Ebay) with one of the tiny max232 cards and have the nano using I2C to transfer Classic data to a DUE. Works perfectly and lets me make a simple web page to display data from two Classics individually and integrated into an overall set of data totals.

As for your question about why not use ethernet for Arduino to Classic comms ;)
Because basically the Classic's ethernet is not reliable and is limited to just one user TCP connection. There is a second but that is reserved for the MyMidnite server connection, is encrypted and MN do not provide either encryption details or the abililty to configure it for alternate use.
There are others in this forum who use ethernet to Classic to extract and process data.

Anyway, will be interested in your progress..

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 25, 2015, 04:11:11 AM
The simple web server code now updated to allow up to four Classics to be connected to the four serial ports of an Arduino Mega or Arduino DUE.
Although still using the SimpleModbusMaster library, which is designed for an rs485 bus where more than one slave device is connected to a master device OR only one rs232 slave device.
The sketch simply uses a time based round robin method to restart the (mis-named) modbus state engine with a different physical serial port.
This method works well enough to extract modbus data from several connected Classics but
the real solution would be to dump the lib or hack it down to just support Read-holding-registers and forget the state machine concept (the docs say it runs in background but thats
sure not true on a single non scheduled processor).

The DUE firmware supports  a simple program scheduler system so the possibilities are there to deal with multiple modbus readers each processing a Classic on its own rs232 port.

There are too many changes to the simple web server code to list so whole sketch posted in next message.
This is running on a DUE with the small rs232  serial port cards shown on Freetronics proto shield in previous posting. An additional small chopped down shield with one further serial port was also attached to the DUE to provide three rs232 ports for testing.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 25, 2015, 04:20:34 AM
This arduino sketch is for a DUE and needs multiple serial rs232 ports, one for each Classic connection.
The code is not very sophisticated and the lack of formatted print support means multiple simple print statements.
Compiled using IDE 1.6.1 and SimpleModbusMaster library v10 with mods as listed at start of this subject thread.

Cut and paste into IDE and check for split and truncated lines of text/code

dgd

---


/*
  Web Server

A simple web server that shows the values of several modbus registers from Midnite
Classic. Up to 4 Classics conected via separate rs232 connection to Arduino Mega or DUE
David Dix  20 March 2015  dgd@kc.net.nz
*/

#include <SimpleModbusMaster.h> 
#include <Ethernet.h>
#include <SPI.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

// setup modbus port information
#define baud 19200
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10
#define IDLE 1
#define CLTIME 15000

// used to toggle the receive/transmit pin - only for rs485
#define TxEnablePin 2

// up to 4 Classics connected the serial ports (Mega and DUE)
#define CLASSICS 2

enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  PACKET5,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];
packetPointer packet1 = &packets[PACKET1];  // pointers to access each packet
packetPointer packet2 = &packets[PACKET2];
packetPointer packet3 = &packets[PACKET3];
packetPointer packet4 = &packets[PACKET4];
packetPointer packet5 = &packets[PACKET5];

// Data read from the Classic will be stored in these arrays, using separate arrays to keep memory usage low
// if the array is initialized to the packet.
unsigned int readRegs[30];
unsigned int wbjrRegs[15];
unsigned int vRegs[2];
unsigned int idRegs[10];       // name 4209 to 4212, and time/date 4213 to 4216
unsigned int sernoRegs[2];

HardwareSerial* serialPort;


int current_classic = 0, prev_classic;

  float batt_volts[CLASSICS], in_volts[CLASSICS], batt_amps[CLASSICS], kwh[CLASSICS];
  float high_in_volts[CLASSICS], wbjr_amps[CLASSICS];
  int   cstate[CLASSICS], watts[CLASSICS], wbjr_soc[CLASSICS], vers[CLASSICS], model[CLASSICS];
  float fet_temp[CLASSICS], pcb_temp[CLASSICS], rem_temp[CLASSICS], bat_temp[CLASSICS];
  char  Classic_name[CLASSICS][10];
  int   eq_hrs[CLASSICS], eq_mins[CLASSICS], eq_secs[CLASSICS];
  int   fl_hrs[CLASSICS], fl_mins[CLASSICS], fl_secs[CLASSICS];
  int   abs_hrs[CLASSICS], abs_mins[CLASSICS], abs_secs[CLASSICS];
  int   serno[CLASSICS], rcheck[CLASSICS];
  unsigned long start_count, timenow;

void setup()
{
  int  i,j, k;
 
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
   
  modbus_construct(packet1, 10, READ_HOLDING_REGISTERS, 4114, 29, readRegs);
  modbus_construct(packet2, 10, READ_HOLDING_REGISTERS, 4360, 14, wbjrRegs);
  modbus_construct(packet3, 10, READ_HOLDING_REGISTERS, 4100, 1, vRegs);
  modbus_construct(packet4, 10, READ_HOLDING_REGISTERS, 4209, 9, idRegs);
  modbus_construct(packet5, 10, READ_HOLDING_REGISTERS, 0x7000, 2, sernoRegs);
 
  // initilise classic data arrays
  for (k=0; k < CLASSICS; k++ )   // initial all values
   { 
     batt_volts[k] = 0;
     in_volts[k] = 0;
     batt_amps[k] = 0;
     kwh[k] = 0;
     high_in_volts[k] = 0;
     cstate[k] = 0;
     watts[k] = 0;
     wbjr_soc[k] = 0;
     wbjr_amps[k] = 0;
     vers[k] = 0;
     model[k] = 0;
     for (j=0; j<10; j++)
       Classic_name[k][j] = 0x00;
     fet_temp[k] = pcb_temp[k] = rem_temp[k] = bat_temp[k] = 0;
     serno[k]=0;
     
   }

   start_count = millis();
   modbus_configure(&Serial1, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
}

void loop()
{
  char  c;
  int   i,j,k;


/*
   Use millis() time counting for changing between Classics being processed by the modbus state machine.
   Each change just pokes the harware serial parameter and re-executes modbus_configure/modbus_update.
   Not perfect but seems to work, may take an iteration or three to get the correct readings in each
   display column.
   Proper solution is to rewrite less sophisticated modbus lib and execute one modbus manager for each
   serial port - using the DUE scheduler would work well for this method     
     
*/
   prev_classic = current_classic;

   timenow = millis();
   if ((timenow - start_count) > (unsigned long)CLTIME)
   {
     start_count = timenow;
     current_classic++;
     if (current_classic >= CLASSICS) current_classic = 0;
     switch (current_classic)
     {
       case 0:
          serialPort = &Serial1;
          break;
       case 1:
          serialPort = &Serial2;
          break;
       case 2:
          serialPort = &Serial3;
          break;
       case 3:
          serialPort = &Serial;     // hardware serial 0
          break;
     }
     if (current_classic != prev_classic )
     {
       modbus_configure(serialPort, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
       modbus_update();
       delay(10);
     }
   }
 
   modbus_update();
   
   k = current_classic;
   
  // make Classic register values ready for web page
     model[k] = (unsigned int)lowByte(vRegs[0]);
     serno[k] = sernoRegs[1];
     rcheck[k] = sernoRegs[1];
     Classic_name[k][0] = lowByte (idRegs[0]);
     Classic_name[k][1] = highByte(idRegs[0]);
     Classic_name[k][2] = lowByte (idRegs[1]);
     Classic_name[k][3] = highByte(idRegs[1]);
     Classic_name[k][4] = lowByte (idRegs[2]);
     Classic_name[k][5] = highByte(idRegs[2]);
     Classic_name[k][6] = lowByte (idRegs[3]);
     Classic_name[k][7] = highByte(idRegs[3]);
     Classic_name[k][8] = 0x00;

     batt_volts[k] = readRegs[0];
     batt_volts[k] /=10;
     in_volts[k] = readRegs[1];
     in_volts[k] /=10;
     batt_amps[k] = readRegs[2];
     batt_amps[k] /=10;
     kwh[k]= readRegs[3];
     kwh[k] /=10;
     watts[k] = readRegs[4];
     cstate[k] = (unsigned int)readRegs[5] >> 8;  // high byte contains charge state code 
   
     high_in_volts[k] = readRegs[7];
     high_in_volts[k] /=10;
     bat_temp[k] = readRegs[17];    // need to test if BTS present
     bat_temp[k] /=10;
     fet_temp[k] = readRegs[18];
     fet_temp[k] /=10;
     pcb_temp[k] = readRegs[19];
     pcb_temp[k] /=10;
     
     fl_secs[k] = readRegs[23];      //4137 floatTimeodayseconds
     if (fl_secs[k] >= 3600) fl_hrs[k] = (int)fl_secs[k]/3600;
     fl_secs[k] -= fl_hrs[k]*3600;
     if (fl_secs[k] >= 60 ) fl_mins[k] = (int)fl_secs[k]/60;
     fl_secs[k] -= fl_mins[k]*60;
     
     eq_secs[k] = readRegs[28];      //4142 EqualizeTime
     if (eq_secs[k] >= 3600) eq_hrs[k] = (int)eq_secs[k]/3600;
     eq_secs[k] -= eq_hrs[k]*3600;
     if (eq_secs[k] >= 60 ) eq_mins[k] = (int)eq_secs[k]/60;
     eq_secs[k] -= eq_mins[k]*60;
     
     abs_secs[k] = readRegs[24];      //4138 AbsorbTime
     if (abs_secs[k] >= 3600) abs_hrs[k] = (int)abs_secs[k]/3600;
     abs_secs[k] -= abs_hrs[k]*3600;
     if (abs_secs[k] >= 60 ) abs_mins[k] = (int)abs_secs[k]/60;
     abs_secs[k] -= abs_mins[k]*60;
     
     rem_temp[k] = (unsigned int) lowByte (wbjrRegs[11]);
     rem_temp[k] -=50;
     j = (signed int)wbjrRegs[10];    // change from unsigned int in wbjrRegs to int in j then to float
     wbjr_amps[k] = j;
     wbjr_amps[k] /=10;
     wbjr_soc[k] = wbjrRegs[12];          // 4372

 
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client)
  {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank)
        {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");

          client.println("<html><body>");
         
          // simple page title
          client.print ("<h2>Midnite Classic </h2>");

          client.println ("<table border=\"1\" cellpadding=\"10\" style=\"width\:70%\"\>");
         
          client.println ("<tr>");         
          client.println ("<td>Name</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print (Classic_name[k]);
             client.print (" C");
             client.print (model[k]);
             client.print (" #");
             client.print (serno[k]);     // classic serial number
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");         
          client.print ("<td>Charge State</td>");
          for (k=0; k< CLASSICS; k++ )
          {
            switch (cstate[k])
            {
            case 0:
              client.print ("<td>Resting</td>");
              break;
            case 3:
              client.print ("<td>Absorb - time left ");
              client.print (abs_hrs[k]);
              client.print (":");
              client.print (abs_mins[k]);
              client.print (":");
              client.print (abs_secs[k]);
              client.print ("</td>");
              break;
            case 4:
              client.print ("<td>BulkMppt</td>");
              break;
            case 5:
              client.print ("<td>Float - total time ");
              client.print (fl_hrs[k]);
              client.print (":");
              client.print (fl_mins[k]);
              client.print (":");
              client.print (fl_secs[k]);
              client.print ("</td>");
              break;
            case 6:
              client.print ("<td>FloatMppt</td>");
              break;
            case 7:
              client.print ("<td>Equalize - time left ");
              client.print (eq_hrs[k]);
              client.print (":");
              client.print (eq_mins[k]);
              client.print (":");
              client.print (eq_secs[k]);
              client.print ("</td>");

              break; 
            case 10:
              client.print ("<td>HyperVoc</td>");
              break;
            case 18:
              client.print ("<td>EqMppt</td>");
              break;
            }
          }
          client.println("</tr>");
         
          client.println("<tr>");
          client.print("<td>Battery volts</td>");
          for (k=0; k< CLASSICS; k++ )
          {       
             client.print("<td>");
             client.print(batt_volts[k]);
             client.println("</td>");
          }
          client.println ("</tr>");
         

          client.println("<tr>");
          client.print ("<td>Output Amps</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(batt_amps[k]);
             client.println("</td>");
          }
          client.println ("</tr>");

          client.println("<tr>");
          client.print ("<td>Power Watts</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(watts[k]);
             client.println("</td>");
          }
          client.println("</tr>");
 
          client.println("<tr>");
          client.print ("<td>Energy Kwhr</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(kwh[k]);
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");
          client.print("<td>InputPV volts</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(in_volts[k]);
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");
         
          client.print("<td>High PV volts</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(high_in_volts[k]);
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");
          client.print("<td>PCB temp oC</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(pcb_temp[k]);
             client.println("</td>");
          }
          client.println("</tr>");

          client.print("<tr>"); 
          client.print("<td>FET temp oC</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print(fet_temp[k]);
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");
          client.print("<td>BAT temp oC</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             if (bat_temp[k] > 100 )
              client.print ("(25c)");
             else
              client.print(bat_temp[k]);
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");
          client.print("<td>Remote temp oC</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             if (rem_temp[k] == -50)  // no wbjr connected
               client.print ("-");
             else
               client.print(rem_temp[k]);
             client.println("</td>");
          }
          client.println("</tr>");
         
          client.println("<tr>");
          client.print ("<td>WBjr Amps</td>");
          for (k=0; k< CLASSICS; k++)
          {
             client.print ("<td>");
             if (rem_temp[k] == -50)  // no wbjr connected
               client.print ("-");
             else
             {
               if (wbjr_amps[k] > 6000) wbjr_amps[k] -= 6553;
               client.print(wbjr_amps[k]);
             }
             client.println ("</td>");
          }
          client.println("</tr>");
   
          client.println("<tr>");
          client.print ("<td>Battery SOC%</td>");
          for (k=0; k< CLASSICS; k++ )
          { 
             client.print ("<td>");
             if (rem_temp[k] == -50)
               client.print ("-");
             else
               client.print(wbjr_soc[k]);
             client.println ("</td>");
          }
          client.println("</tr>");
           
          client.println ("</table>");

          client.println("</body></html>");
          break;
        }
        if (c == '\n') {
          // you're starting a new line
          currentLineIsBlank = true; 
         
        }
        else if (c != '\r') {
          // you've gotten a character on the current line
          currentLineIsBlank = false;
        }
      }
    }
    // give the web browser time to receive the data
    delay(10);
    // close the connection:
    client.stop();
  }
}

Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on March 25, 2015, 09:18:14 AM
Dave,
Nice to read up on your progress here with the DUE.
Could you edit your last post to place the code within the code tags, it makes it easier to select it all then and is more readable as well.
I would like to take a closer look at it in Eclipse IDE.

With using client.print, can I suggest to try to get as much done in the one statement as you can.
With your code:client.print ("<h2>Midnite Classic </h2>");

          client.println ("<table border=\"1\" cellpadding=\"10\" style=\"width\:70%\"\>");
         
          client.println ("<tr>");         
          client.println ("<td>Name</td>");
          for (k=0; k< CLASSICS; k++ )
          {
             client.print ("<td>");
             client.print (Classic_name[k]);
             client.print (" C");
             client.print (model[k]);
             client.print (" #");
             client.print (serno[k]);     // classic serial number
             client.println("</td>");
          }
          client.println("</tr>");
         
Each client.print sends HTTP data in its own TCP packet, and this makes things slower, less efficient, granted it maybe more readable as source code though.

What you can do is to do something like the following, where all the print data is placed into a char buffer, then you simply send the buffer out directly after connection and headers.
An old example from when I used the Mega with HTTP to send data out.
void postAlarm() {
char postData[210];

sprintf(postData, "{\"data\":[\"alarm\",%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i]}",
vTurbine.minHour, vTurbine.maxHour, vTurbine.minDay, vTurbine.maxDay,                                                                          // 01 02 03 04
iTurbine.minHour, iTurbine.maxHour, iTurbine.minDay, iTurbine.maxDay, iTurbine.totHour, iTurbine.totDay,                            // 05 06 07 08 - 09 10
iSolar.minHour, iSolar.maxHour, iSolar.minDay, iSolar.maxDay, iSolar.totHour, iSolar.totDay,                                                 // 11 12 13 14 - 15 16
vBattery.minHour, vBattery.maxHour, vBattery.minDay, vBattery.maxDay, accum.batteryInDay, accum.batteryOutDay);          // 17 18 19 20 - 21 22

sendData(postData);
}
You can see that you can build up quite long blocks of formatted data using functions like sprintf(..).

Maybe look to calling routines to do this sort of work, then return back to the main loop() with any return error if needed.
And by placing these in a function, any local vars are released, thereby not holding them as global, which is good for large char buffers like above.

Your section:
client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");

          client.println("<html><body>");


Could be sent out in one shot as:
client.println("HTTP/1.1 200 OK\nContent-Type: text/html\nConnection: close\nRefresh: 5\n\n<!DOCTYPE HTML>\n<html><body>");

In case people wish to use EIA-485:
With the DUE, people need to know that the I/O is all 3.3v and any RS-485/RS-232 shield also needs to be 3.3v.
With the Mega, just normal standard 5v devices.

Looking forward to further updates :)
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 25, 2015, 02:17:53 PM
Paul,

Thanks for your responses and of course with a formatted print instruction such as sprintf those repetitive prints would not be necessary. Unfortunately the Arduino IDE included C compiler just simply does not support any type of printf instruction.
I know there are some good workarounds suggested in the arduino forums and that I should probably investigate alternates to arduino 1.6.1 IDE but I wanted to keep this code real simple.

Have now installed the tiny web server code so have proper web page server code available with SD card page storage/retrieval support. So next stage is the DUE better web server for up to 4 rs232 connected Classics with daily report listings and some simple stats, analysis etc.
I have also got a bit side tracked looking at the Outback inverter TCP modbus data input as a possibility to include in DUE web server  :) ...but the more I look at the DUE it then becomes clear I should be concentrating on the  Cubie2 as the web server platform  ;)

Dgd


Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on March 25, 2015, 05:55:18 PM
DGD,  I just did a search for
Quote"Arduino DUE"   "Cubieboard 2"
and there does not seem to be anything out there that is less than ~ 1 yr old...

any suggestions fro comparative articles?
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 25, 2015, 06:18:54 PM
Wb,
I could not find anything either, I suppose it's just a matter of comparing specs of both but the main difference, AFAIK, is that the DUE is really an embedded processor system that can be made a limited data processing/web server system, whereas the Cubie is not designed as an embedded system but is more general computer with normal stand alone computer interfaces plus some interesting lower level controller interfaces.
Others probably have different views  :D
Seems almost a waste to limit the Cubie to a black box type system but then it's so low cost  :)

Dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on March 25, 2015, 06:30:27 PM
speaking of low cost, have you seen the Texas Instruments Stellaris LMF3F120XL bare bones at ~$13.00 US.

Haven't read about it yet but lowest cost one I came across....
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on March 25, 2015, 07:36:28 PM
David wrote:
QuoteAFAIK, is that the DUE is really an embedded processor system /snip, whereas the Cubie is not designed as an embedded system but is more general computer /snip.
Others probably have different views  :D
Yes, and what is interesting, they are both using ARM cores.
The Arduino family of boards, in particular, the Arduino Mega and Arduino DUE, being more suited with extra resources, can be made to work quite well as simple web connected devices, say either as a HTTP server or client.

These boards start to suffer when the application gets more complex in terms of anything web based. For example, when you start to use the SD card to store HTML or javascript to be dished out to the client. The Ethernet interface uses SPI, as does the SD card, with there only being one SPI channel, there is then a fundamental inefficiency of performance. Also, as you know, the standard Arduino Ethernet can only accept four connections, which if your software becomes more complex can be used by one HTTP client requesting things like CSS, javascript, image or any other range of files called for. A second physical connection then becomes a problem, where it needs to wait, and may possibly time-out.

David wrote:
QuoteUnfortunately the Arduino IDE included C compiler just simply does not support any type of printf instruction.
Ok, I'm surprised by that. Most 'other IDE' setups still use the Arduino toolchain included in the Arduino IDE. I use Eclipse with the 'ArduinoEclipse' plugin developed by Jantje in Belgium. It's a pleasure to work with, with full syntax highlighting and hover over functions to see their requirements and much more. Since this plugin uses the Arduino IDE toolchain, which means the compiler I would think you should be able to use the printf functionality. For programs where I have used it, I have not included any other 'includes' or made any specific changes and the Eclipse setup uses no other resources to enable this. I'll be interested to see if you can.

Slightly off topic, I have setup my Eclipse on the Mac now so that I can both continue to develop Arduino based code as well as now the code for Quadlog, by setting up a GCC toolchian.
Now I can not only code Quadlog on the bigger screen vs my GNU/Linux laptop with piddly 15" screen, I can actually run the compiled application on OSX. I was surprised indeed.
I will start up a topic for it, where I can detail it more. :)

In terms of other boards, there is the new Quad core board form Cubie, but for me, that is too excessive for small systems like we are talking about.
There is also the Olimex range of boards as well as the new RPi which I think brings the RPi into the selection of boards that I would possibly now look to work with.
https://www.olimex.com/Products/OLinuXino/open-source-hardware (https://www.olimex.com/Products/OLinuXino/open-source-hardware)
I looked at these before purchasing the Cubieboard II, but sent an email to them requesting some info, but I never heard back form them, so I went elsewhere. I would try again with them.
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 25, 2015, 11:40:48 PM
Quote from: Westbranch on March 25, 2015, 06:30:27 PM
speaking of low cost, have you seen the Texas Instruments Stellaris LMF3F120XL bare bones at ~$13.00 US.

Haven't read about it yet but lowest cost one I came across....

Element14 sell this but the eval kit is about US$140 and the bare bones will need lots of extra interface/memory cards.
Just too expensive for me for another 80mhz arm? cpu system
Cubie2 is much better value since it is a complete eval computer system

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on March 25, 2015, 11:50:01 PM
Agree! 
QuoteJust too expensive
:o  :'(
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on March 26, 2015, 12:35:12 AM
Quote from: paul alting on March 25, 2015, 07:36:28 PM
... Yes, and what is interesting, they are both using ARM cores.
The Arduino family of boards, in particular, the Arduino Mega and Arduino DUE, being more suited with extra resources, can be made to work quite well as simple web connected devices, say either as a HTTP server or client.

The DUE certainly lacks the interfaces for high level data storage and the TCP limits could become a concern if the development moved towards a more featured system.
However, for the sort of simple blackbox system I had in mind the DUE looks almost ideally suited.

My goal was to create a black box that shows the running Classic's data and the battery SOC in one web page (at  least every 10 to 20 seconds) plus also parallel display same data for up to 4 Classics (this part looks to be already covered by the DUE with multiole serial ports and the sketch listed above) and perhaps integrate figures for an overall RE power generation picture.
An additional page or three to show a days running data with 5 to 30 minute data points
Other feature would be the downloading of a csv data file with 30 to 240 days of data for external processing/display etc. This would probably mean SD card storage of running data

The 4 tcp connection limitation, although not ideal, would be sufficient for me and one or two others to monitor a system and some_one_ else to use the MN Local App if they wanted to.

Also with the seeming plethora of resource, CPU, ram and flash plus countless IO pins the addition of several AUX type controls seems to be quite straighforward. I'm thinking at least two AUX outputs to relays, a couple of PWM outputs with at least one set up on the WasteNot basis for feeding a dump load such as water heating and those analogue inputs, using some for current measurement at various points in an RE system (they have 12bit accuracy, 4096 points, on a 3.3v input max).

Then I would leave all of the processing and analysis of data, graph management, proper web interfacing etc, to a more data processing orientated system such as the Cubie2.
Just some thoughts  :D

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on March 26, 2015, 03:54:07 AM
David wrote:
Quoteusing some for current measurement at various points in an RE system (they have 12bit accuracy, 4096 points, on a 3.3v input max).
Yes, another reason I am moving to the DUE from the Mega. I bough a few of the Alegro ACS758 50Amp uni-directional sensors from element14 which I will interface to the analog inputs. I'll operate the ACS758 at 5 volts for better performance, but reduce the output through resistor divider to suit the 3v3 input of the DUE.

QuoteThen I would leave all of the processing and analysis of data, graph management, proper web interfacing etc, to a more data processing orientated system such as the Cubie2
I'm hoping I can interest you in trying out Quadlog sometime then. It does these things and if you know how to program the Arduino DUE, I'd be sure you can easily understand the code for Quadlog as it is the same language, just different compiler. Not that you need to do anything with the code, but you would have it anyhow. I think you'll have more of a hoot setting it up, and it doesn't need any of that bloaty stuff like Apaché to run. It is small, simple and is designed with these specific things in mind.

David, you mentioned you had the thought of moving to an IDE that might prove to be more suited to your needs. If so, give me a shout, I would be happy to help you set it up if you need any helping hand.
I think if you are going to play more around with code, then I can only highly suggest trying something like Eclipse. I don't ever use the Arduino IDE, even for simple things, yet I started out my C programming with it for quite some time.

Anyhow, I find it exciting to hear your developments and to know there are others out there doing interesting things with renewable energy systems.
I know from looking at David's site he is in NZ, just across the big water from me, but Westbranch, where are you I wonder?
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on March 26, 2015, 11:38:21 AM
Paul, see additional details now in my sig line
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 02, 2015, 11:08:38 PM
The next stage is the not-so-simple web server.
The simple web server has used the loop() function in the sketch to read modbus registers from an rs232 connected Classic then using an in-code generated web page to display those register values. This process repeats using the main loop, the web page is completely redrawn as the web connection is closed then re-opened on each iteration of loop().
This has worked quite well for up to four rs232 connected Classics to one DUE board. Since most of the text identifying fields are static and only the data value fields change then the display has looked reasonably steady with only the occasional noticeable full screen refresh.
The flickering display issue due to full screen redraw would be more likely over a slow or congested connection/

So the next update is necessary to allow several additional features
The in-page selection of alternate web pages to display historical data/analysis reports etc.. and the automatic updating of only the data fields in the main display page.
For this the web pages will be stored on an SD card and loaded by the WEB server as needed. In-webpage buttons will select moving between web pages.
This will require web page keepalive, meaning that the sketch will remain inside the while-connected loop of the sketch. This leads to a problem in that without exiting to the main loop() that the code reading the modbus registers will not be executed until the connection closes.
The reading of modbus registers will have to occur inside the while-connected loop as part of the displayed data fields only update process.
Outside the connection loop the main loop will also continue to read modbus registers  when there is not an active tcp connection.

The first stored web page, index.html contains an AJAX function that will read modbus registers and update the page display fields.
Testing this code now also with the addition of analogue meter displays for various data.
This is using HTML5 canvas in javascript

Example sketch and web pages for SD card posted soon..


dgd

Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 03, 2015, 10:26:11 PM
An interesting part of this web server development has been (for me anyway) the possibility of getting some nice analogue meter displays included in the web pages. Especially the main page that displays updated  modbus registers.
The web page index.html stored on SD card and loaded by the DUE sketch to serve to a connecting web browser is listed below. The apparent gibberish at the start is the meter generating code, it is canv-gauge (by Mykhailo Stadnyk and can be downloaded from Mikhus at github)
I have included it 'as-is' in case anyone wants to download the html file and execute it with a web  browser. This will show the meter display but obviously no live updating as it cannot get an XML file from my DUE.
WIth firefox and W8 the meter window can be moved and enlarged/decreased, minimized etc - just like any normal window
An example that shows the WBjr amps register obtained by serial modbus from my Classic and using AJAX function in the html code to get this data from the Arduino DUE is also shown below.
In my live version of this the meter updates every second although the modbus register reading has difficulty keeping up with this.

I have also created two displays using multiple meters (similar to Local App) and a Battery monitor using WBjr data. More of these later when I get them looking better  :)

dgd


<!DOCTYPE html>
<html>
    <head>
        <title>Arduino Ajax Dial</title>
        <script>
var data_val = 0;
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};
if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)
if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('t W=v(f){W.2t.3T(A);A.B={Z:2u,19:1q,1h:1q,1J:U,1b:D,K:0,V:[\'0\',\'20\',\'40\',\'2A\',\'2B\',\'D\'],2r:10,2C:M,1E:U,2q:{2D:3,2E:2},2H:M,1c:{2j:10,2m:3y,2O:\'3v\'},J:{2P:\'#4h\',V:\'#31\',2r:\'#3k\',1J:\'#37\',1E:\'#37\',3e:\'#31\',1t:{2s:\'1e(3Y, 3d, 3d, 1)\',3c:\'1e(1Y, 5I, 5E, .9)\'}},1o:[{1n:20,1D:2A,1F:\'#3j\'},{1n:2A,1D:2B,1F:\'#36\'},{1n:2B,1D:D,1F:\'#5n\'}]};t g=0,1p=A,N=0,1S=0,1G=U;A.5d=v(a){N=f.1c?g:a;t b=(f.1b-f.K)/D;1S=a>f.1b?1S=f.1b+b:a<f.K?f.K-b:a;g=a;f.1c?3l():A.1g();C A};A.3m=v(a){N=g=a;A.1g();C A};A.4T=v(){g=N=1S=A.B.K;A.1g();C A};A.4R=v(){C g};A.13=v(){};v 2k(a,b){Q(t i 4P b){z(1H b[i]=="1W"&&!(4O.4y.2V.4p(b[i])===\'[1W 4n]\')&&i!=\'Z\'){z(1H a[i]!="1W"){a[i]={}}2k(a[i],b[i])}O{a[i]=b[i]}}};2k(A.B,f);A.B.K=1R(A.B.K);A.B.1b=1R(A.B.1b);f=A.B;N=g=f.K;z(!f.Z){4m 4j("4g 4d 4b 46 44 41 3Z 3W W 1W!");}t j=f.Z.5K?f.Z:2R.5v(f.Z),q=j.3u(\'2d\'),1i,1y,1A,14,17,u,1d;v 2M(){j.19=f.19;j.1h=f.1h;1i=j.4s(M);1d=1i.3u(\'2d\');1y=j.19;1A=j.1h;14=1y/2;17=1A/2;u=14<17?14:17;1i.2J=U;1d.3P(14,17);1d.G();q.3P(14,17);q.G()};2M();A.4Z=v(a){2k(A.B,a);2M();A.1g();C A};t k={4q:v(p){C p},4e:v(p){C E.1L(p,2)},4c:v(p){C E.1L(p,5)},3v:v(p){C 1-E.1O(E.5C(p))},5k:v(p){C 1-(v(p){Q(t a=0,b=1;1;a+=b,b/=2){z(p>=(7-4*a)/11){C-E.1L((11-6*a-11*p)/4,2)+E.1L(b,2)}}})(1-p)},4S:v(p){C 1-(v(p){t x=1.5;C E.1L(2,10*(p-1))*E.1T(20*E.1a*x/3*p)})(1-p)}};t l=2u;v 3S(d){t e=2v 3R;l=2x(v(){t a=2v 3R-e,1M=a/d.2m;z(1M>1){1M=1}t b=1H d.2g=="v"?d.2g:k[d.2g];t c=b(1M);d.3Q(c);z(1M==1){2b(l)}},d.2j||10)};v 3l(){l&&2b(l);t b=(1S-N),1n=N,29=f.1c;3S({2j:29.2j,2m:29.2m,2g:29.2O,3Q:v(a){N=1R(1n)+b*a;1p.1g()}})};q.5l="3O";A.1g=v(){z(!1i.2J){1d.3M(-14,-17,1y,1A);1d.G();t a=q;q=1d;3L();3K();3I();3H();3F();3D();3z();1i.2J=M;q=a;5G a}q.3M(-14,-17,1y,1A);q.G();q.4a(1i,-14,-17,1y,1A);z(!W.28){t b=2x(v(){z(!W.28){C}2b(b);2K();2L();z(!1G){1p.13&&1p.13();1G=M}},10)}O{2K();2L();z(!1G){1p.13&&1p.13();1G=M}}C A};v S(a){C a*E.1a/4J};v 1l(a,b,c){t d=q.4Y(0,0,0,c);d.1V(0,a);d.1V(1,b);C d};v 3L(){t a=u/D*5g,3x=u-a,2a=u/D*5q,5u=u-2a,1f=u/D*5z,5A=u-1f;3t=u/D*5F;q.G();z(f.2H){q.2o=3x;q.2n=\'1e(0, 0, 0, 0.5)\'}q.P();q.16(0,0,a,0,E.1a*2,M);q.L=1l(\'#42\',\'#43\',a);q.T();q.R();q.P();q.16(0,0,2a,0,E.1a*2,M);q.L=1l(\'#49\',\'#36\',2a);q.T();q.P();q.16(0,0,1f,0,E.1a*2,M);q.L=1l(\'#3j\',\'#3s\',1f);q.T();q.P();q.16(0,0,3t,0,E.1a*2,M);q.L=f.J.2P;q.T();q.G()};v 3H(){t r=u/D*2T;q.2e=2;q.2U=f.J.V;q.G();Q(t i=0;i<f.V.H;++i){t a=45+i*(1U/(f.V.H-1));q.1z(S(a));q.P();q.1K(0,r);q.F(0,r-u/D*15);q.1X();q.R();q.G()}z(f.2C){q.1z(S(2X));q.P();q.16(0,0,r,S(45),S(4N),U);q.1X();q.R();q.G()}};v 3I(){t r=u/D*2T;q.2e=1;q.2U=f.J.2r;q.G();t b=f.2r*(f.V.H-1);Q(t i=0;i<b;++i){t a=45+i*(1U/b);q.1z(S(a));q.P();q.1K(0,r);q.F(0,r-u/D*7.5);q.1X();q.R();q.G()}};v 3F(){t r=u/D*55;Q(t i=0;i<f.V.H;++i){t a=45+i*(1U/(f.V.H-1)),p=1w(r,S(a));q.1x=20*(u/1q)+"2i 2Y";q.L=f.J.3e;q.2e=0;q.2h="2f";q.27(f.V[i],p.x,p.y+3)}};v 3D(){z(!f.1J){C}q.G();q.1x=24*(u/1q)+"2i 2Y";q.L=f.J.1J;q.2h="2f";q.27(f.1J,0,-u/4.25);q.R()};v 3z(){z(!f.1E){C}q.G();q.1x=22*(u/1q)+"2i 2Y";q.L=f.J.1E;q.2h="2f";q.27(f.1E,0,u/3.25);q.R()};v 32(a){t b=f.2q.2E,34=f.2q.2D;a=1R(a);t n=(a<0);a=E.35(a);z(b>0){a=a.5t(b).2V().1j(\'.\');Q(t i=0,s=34-a[0].H;i<s;++i){a[0]=\'0\'+a[0]}a=(n?\'-\':\'\')+a[0]+\'.\'+a[1]}O{a=E.3O(a).2V();Q(t i=0,s=34-a.H;i<s;++i){a=\'0\'+a}a=(n?\'-\':\'\')+a}C a};v 1w(r,a){t x=0,y=r,1O=E.1O(a),1T=E.1T(a),X=x*1T-y*1O,Y=x*1O+y*1T;C{x:X,y:Y}};v 3K(){q.G();t a=u/D*2T;t b=a-u/D*15;Q(t i=0,s=f.1o.H;i<s;i++){t c=f.1o[i],39=(f.1b-f.K)/1U,1P=S(45+(c.1n-f.K)/39),1N=S(45+(c.1D-f.K)/39);q.P();q.1z(S(2X));q.16(0,0,a,1P,1N,U);q.R();q.G();t d=1w(b,1P),3a=1w(a,1P);q.1K(d.x,d.y);q.F(3a.x,3a.y);t e=1w(a,1N),3b=1w(b,1N);q.F(e.x,e.y);q.F(3b.x,3b.y);q.F(d.x,d.y);q.1C();q.L=c.1F;q.T();q.P();q.1z(S(2X));q.16(0,0,b,1P-0.2,1N+0.2,U);q.R();q.1C();q.L=f.J.2P;q.T();q.G()}};v 2L(){t a=u/D*12,1f=u/D*8,1u=u/D*3X,1r=u/D*20,2l=u/D*4,1B=u/D*2,38=v(){q.3f=2;q.3g=2;q.2o=10;q.2n=\'1e(5L, 3h, 3h, 0.45)\'};38();q.G();z(N<0){N=E.35(f.K-N)}O z(f.K>0){N-=f.K}O{N=E.35(f.K)+N}q.1z(S(45+N/((f.1b-f.K)/1U)));q.P();q.1K(-1B,-1r);q.F(-2l,0);q.F(-1,1u);q.F(1,1u);q.F(2l,0);q.F(1B,-1r);q.1C();q.L=1l(f.J.1t.2s,f.J.1t.3c,1u-1r);q.T();q.P();q.F(-0.5,1u);q.F(-1,1u);q.F(-2l,0);q.F(-1B,-1r);q.F(1B/2-2,-1r);q.1C();q.L=\'1e(1Y, 1Y, 1Y, 0.2)\';q.T();q.R();38();q.P();q.16(0,0,a,0,E.1a*2,M);q.L=1l(\'#3s\',\'#36\',a);q.T();q.R();q.P();q.16(0,0,1f,0,E.1a*2,M);q.L=1l("#47","#48",1f);q.T()};v 3i(x,y,w,h,r){q.P();q.1K(x+r,y);q.F(x+w-r,y);q.23(x+w,y,x+w,y+r);q.F(x+w,y+h-r);q.23(x+w,y+h,x+w-r,y+h);q.F(x+r,y+h);q.23(x,y+h,x,y+h-r);q.F(x,y+r);q.23(x,y,x+r,y);q.1C()};v 2K(){q.G();q.1x=40*(u/1q)+"2i 30";t a=32(g),2Z=q.4f(\'-\'+32(0)).19,y=u-u/D*33,x=0,2W=0.12*u;q.G();3i(-2Z/2-0.21*u,y-2W-0.4i*u,2Z+0.3n*u,2W+0.4k*u,0.21*u);t b=q.4l(x,y-0.12*u-0.21*u+(0.12*u+0.3o*u)/2,u/10,x,y-0.12*u-0.21*u+(0.12*u+0.3o*u)/2,u/5);b.1V(0,"#37");b.1V(1,"#3k");q.2U=b;q.2e=0.3n*u;q.1X();q.2o=0.3p*u;q.2n=\'1e(0, 0, 0, 1)\';q.L="#4o";q.T();q.R();q.3f=0.3q*u;q.3g=0.3q*u;q.2o=0.3p*u;q.2n=\'1e(0, 0, 0, 0.3)\';q.L="#31";q.2h="2f";q.27(a,-x,y);q.R()}};W.28=U;(v(){t d=2R,h=d.3r(\'4r\')[0],2S=4t.4u.4v().4w(\'4x\')!=-1,2Q=\'4z://4A-4B.4C/4D/4E/4F-7-4G.\'+(2S?\'4H\':\'4I\'),1I="@1x-4K {"+"1x-4L: \'30\';"+"4M: 2Q(\'"+2Q+"\');"+"}",1s,r=d.3w(\'1v\');r.2N=\'1I/4Q\';z(2S){h.2p(r);1s=r.2I;1s.3A=1I}O{4U{r.2p(d.4V(1I))}4W(e){r.3A=1I}h.2p(r);1s=r.2I?r.2I:(r.4X||d.3B[d.3B.H-1])}t b=2x(v(){z(!d.3C){C}2b(b);t a=d.3w(\'50\');a.1v.51=\'30\';a.1v.52=\'53\';a.1v.1h=a.1v.19=0;a.1v.54=\'56\';a.57=\'.\';d.3C.2p(a);58(v(){W.28=M;a.59.5a(a)},3y)},1)})();W.2t=[];W.2t.5b=v(a){z(1H(a)==\'5c\'){Q(t i=0,s=A.H;i<s;i++){z(A[i].B.Z.18(\'5e\')==a){C A[i]}}}O z(1H(a)==\'5f\'){C A[a]}O{C 2u}};v 3E(a){z(2G.3G){2G.3G(\'5h\',a,U)}O{2G.5i(\'5j\',a)}}3E(v(){v 2F(a){t b=a[0];Q(t i=1,s=a.H;i<s;i++){b+=a[i].1Z(0,1).5m()+a[i].1Z(1,a[i].H-1)}C b};v 3J(a){C a.5o(/^\\s+|\\s+$/g,\'\')};t c=2R.3r(\'5p\');Q(t i=0,s=c.H;i<s;i++){z(c[i].18(\'1k-2N\')==\'5r-5s\'){t d=c[i],B={},1m,w=2c(d.18(\'19\')),h=2c(d.18(\'1h\'));B.Z=d;z(w){B.19=w}z(h){B.1h=h}Q(t e=0,1s=d.3N.H;e<1s;e++){1m=d.3N.5w(e).5x;z(1m!=\'1k-2N\'&&1m.1Z(0,5)==\'1k-\'){t f=1m.1Z(5,1m.H-5).5y().1j(\'-\'),I=d.18(1m);z(!I){2z}5B(f[0]){2y\'J\':{z(f[1]){z(!B.J){B.J={}}z(f[1]==\'1t\'){t k=I.1j(/\\s+/);z(k[0]&&k[1]){B.J.1t={2s:k[0],3c:k[1]}}O{B.J.1t=I}}O{f.5D();B.J[2F(f)]=I}}26}2y\'1o\':{z(!B.1o){B.1o=[]}2w=I.1j(\',\');Q(t j=0,l=2w.H;j<l;j++){t m=3J(2w[j]).1j(/\\s+/),1Q={};z(m[0]&&m[0]!=\'\'){1Q.1n=m[0]}z(m[1]&&m[1]!=\'\'){1Q.1D=m[1]}z(m[2]&&m[2]!=\'\'){1Q.1F=m[2]}B.1o.3T(1Q)}26}2y\'1c\':{z(f[1]){z(!B.1c){B.1c={}}z(f[1]==\'2O\'&&/^\\s*v\\s*\\(/.5H(I)){I=3U(\'(\'+I+\')\')}B.1c[f[1]]=I}26}5J:{t n=2F(f);z(n==\'13\'){2z}z(n==\'V\'){I=I.1j(/\\s+/)}O z(n==\'2C\'||n==\'2H\'){I=I==\'M\'?M:U}O z(n==\'2q\'){t o=I.1j(\'.\');z(o.H==2){I={2D:2c(o[0]),2E:2c(o[1])}}O{2z}}B[n]=I;26}}}}t g=2v W(B);z(d.18(\'1k-3V\')){g.3m(1R(d.18(\'1k-3V\')))}z(d.18(\'1k-13\')){g.13=v(){3U(A.B.Z.18(\'1k-13\'))}}g.1g()}}});',62,358,'||||||||||||||||||||||||||ctx|||var|max|function||||if|this|config|return|100|Math|lineTo|save|length|attrValue|colors|minValue|fillStyle|true|fromValue|else|beginPath|for|restore|radians|fill|false|majorTicks|Gauge|||renderTo||||onready|CX||arc|CY|getAttribute|width|PI|maxValue|animation|cctx|rgba|r2|draw|height|cache|split|data|lgrad|prop|from|highlights|self|200|rOut|ss|needle|rIn|style|rpoint|font|CW|rotate|CH|pad2|closePath|to|units|color|imready|typeof|text|title|moveTo|pow|progress|ea|sin|sa|hlCfg|parseFloat|toValue|cos|270|addColorStop|object|stroke|255|substr||025||quadraticCurveTo|||break|fillText|initialized|cfg|r1|clearInterval|parseInt||lineWidth|center|delta|textAlign|px|delay|applyRecursive|pad1|duration|shadowColor|shadowBlur|appendChild|valueFormat|minorTicks|start|Collection|null|new|hls|setInterval|case|continue|60|80|strokeTicks|int|dec|toCamelCase|window|glow|styleSheet|i8d|drawValueBox|drawNeedle|baseInit|type|fn|plate|url|document|ie|81|strokeStyle|toString|th|90|Arial|tw|Led|444|padValue||cint|abs|ccc|888|shad|vd|pe|pe1|end|128|numbers|shadowOffsetX|shadowOffsetY|143|roundRect|eee|666|animate|setRawValue|05|045|012|004|getElementsByTagName|f0f0f0|r3|getContext|cycle|createElement|d0|250|drawUnits|cssText|styleSheets|body|drawTitle|domReady|drawNumbers|addEventListener|drawMajorTicks|drawMinorTicks|trim|drawHighlights|drawPlate|clearRect|attributes|round|translate|step|Date|_animate|push|eval|value|the|77|240|creating||when|ddd|aaa|specified||not|e8e8e8|f5f5f5|fafafa|drawImage|was|quint|element|quad|measureText|Canvas|fff|04|Error|07|createRadialGradient|throw|Array|babab2|call|linear|head|cloneNode|navigator|userAgent|toLocaleLowerCase|indexOf|msie|prototype|http|smart|ip|net|styles|fonts|digital|mono|eot|ttf|180|face|family|src|315|Object|in|css|getValue|elastic|clear|try|createTextNode|catch|sheet|createLinearGradient|updateConfig|div|fontFamily|position|absolute|overflow||hidden|innerHTML|setTimeout|parentNode|removeChild|get|string|setValue|id|number|93|DOMContentLoaded|attachEvent|onload|bounce|lineCap|toUpperCase|999|replace|canvas|91|canv|gauge|toFixed|d1|getElementById|item|nodeName|toLowerCase|88|d2|switch|acos|shift|122|85|delete|test|160|default|tagName|188'.split('|'),0,{}))
function GetArduinomodbus()
{
nocache = "&nocache=" + Math.random() * 1000000;
var request = new XMLHttpRequest();
request.onreadystatechange = function()
{
if (this.readyState == 4) {
if (this.status == 200) {
if (this.responseXML != null) {
document.getElementById("CLregs").innerHTML =
this.responseXML.getElementsByTagName('wbjr_amps')[0].childNodes[0].nodeValue;
data_val = this.responseXML.getElementsByTagName('wbjr_amps')[0].childNodes[0].nodeValue;
}
}
}
}
request.open("GET", "ajax_inputs" + nocache, true);
request.send(null);
setTimeout('GetArduinomodbus()', 200);

}
</script>
    </head>
    <body onload="GetArduinomodbus()">
    <h3>ClassicS C150 #879</h3>
    <p>    WBjr Amps<span id="CLreg">...</span></p>
   
    <canvas id="WBjrA_gauge" width="400" height="400"
               data-type="canv-gauge"
               data-title="Battery"
               data-major-ticks="-150 -100 -50 0 +50 +100 +150"
               data-minor-ticks="5"           
               data-units="Amps"
               data-value-format="3.1"
               data-max-value="+150"
               data-min-value="-150"
               data-highlights="-150 -100 #EEE"
               data-onready="setInterval( function ()
                            { Gauge.Collection.get('WBjrA_gauge').setValue(data_val);},1000);">
    </canvas>
    </body>
</html>




Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 03, 2015, 11:18:13 PM
If anyone does try running the web page in the previous posting and can edit the html file then its a neat experiment to alter some of those meter configuartion fields near the bottom of the file.
The meter range of +-150 amps is easiest to alter but major and minor ticks are good too  plus the +/- dont have to be the same ranges, also the ticks can be on a log type scale instead of linear. The meter size and possible highlight ranges can be shaded too.
In creating a battery voltage meter, in my case for a 24v battery, the meter range does not have to start at zero. I chose the range 22v to 32v so that 27v
was at centre top of the scale and I also added some red and green range shading, very pale colours.
Example shown below  :)
The second pic shows my first cut of a battery monitor analogue meters, this display needs further display boxes showing remote and battery temps, Total Ah capacity and remaining capacity and charge efficiency plus anything else i cn think of, these values are probably not worth metering

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 05, 2015, 02:28:39 AM
Testing has went quite well with this better web server for Classic.
The meter displays all move nicely and the AJAX updating of just the meter pointers and numbers in other fieds seems quite smoothe. My biggest concern is the horrible timing delays that modbus RTU imposes on serial connections.
It has me wondering how closely to the modbus specs the timing in the Classic actually is.
I'm leaning more towards using an arduino nano or uno connected to each classic to deal with modus and then, as my original plan was, use a wire i2c data bus to collect active data from each Classic.

Can't quite decide if making a local app lookalike for the meter dashboard is a good idea. Data that moves real slow like Energy and SOC seem rather wasteful of an analogue meter
Should be ready quite soon to post the web server code and the web pages.
I'm not sure if anyone will bother with this web server but it looks like it will run on an Arduino UNO so perhaps a well detailed explanation of setting up the hardware and where to load software to get it all together could be a good idea.
Anyone interested or any suggestions?

I was also looking for a jpg or gif of the stylised 1920s woman with slanted brimmed hat, I seem to remember it was on packing boxes for some Midnite products but I cannot find it on the MN web site

Dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on April 05, 2015, 08:54:04 AM
Hi David,
I'm reading with interest your developments and progress with each new post.
I like what you are doing, but the gauges are too plain and bland for my liking.

A few years back, I came across this gauge and and then after more research found the steelseries, which I really do like .
Then went and coded up some of my own bar gauges based on the steelseries code with statistical elements, which I was using on my site for a while.
I'm thinking of designing up some new gauges in the coming months that will be more modern and lightweight.

While gauges that look like real gauges look lovely, I am starting to ponder the effectiveness of them now.
The steelseries gauges are drawn using HTML5 canvas commands and there is a fair amount of code in the library.

You could use these gauges to jass up your web page.

I went looking for the image you mention, I think you will like this one.
(http://www.solar-electric.com/media/catalog/category/sasa.jpeg)
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 06, 2015, 01:01:55 AM
Paul,
I had looked at the GFX Steel meters and Swing libs and there is a good range of meter design possibilities. One reason I liked cnv-gauge was its simplicity and i rather liked the clean/crispness of the meters even though with various shading/glowing/colour attribute they can get more visually exciting.
However GFX Steel is in a different league  circular bargraphs. for example, look real neat.
WIth HTML5 canvas and css I did seriously consider making an exact visual replica of the MN Local App program, in time I may actually try this but I'm not sure it would serve any purpose as I could not resist making lots of changes - I still cringe at the thought of the App's voltmeter with main ticks at 14, 18.75, 23.5, 28.25 and 33 for a 24volt battery system and then the tick labels just about overwrite each other in the 'about 90degrees' range area of the meter.
But you touch on an interesting point, whether analogue meter displays are really of much practical use. No doubt there is a gee-whizz factor with them  and initially look very impressive but IMHO the novelty soon wears off.
My personal preference now is just a nice plain (ish) set of numbers with the more important ones in  bigger font and more eye catching colours. Really an RE system with charge controller all shakes down to, in order of importance, Battery Voltage, Battery Current, Charger watts, Charger current and Charger input voltage, Battery SOC. All the rest are just noise but there could be times if certain limits are exceeded then they flash up in the main display - such as the various temps.
As for historical reports, nice but would anyone serious study them if it wasn't for debugging the charger? I don't need to hnow from a graph that when the clouds are about then the battery gets less charge.

I will persevere with the simple gauges in my Arduino web server but when I get more serious about this and start web development on the Cubie2 then
skins for display formats is where I want to proceed to.

I will post code here and display examples of my better text/numbers only web page, css is just too much fun  :)

dgd

ps thanks for that image, i was wanting it as a faded background image for my main web page but I probably need to be careful about copyright
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on April 06, 2015, 08:45:30 PM
Quoteps thanks for that image, i was wanting it as a faded background image for my main web page but I probably need to be careful about copyright

Ask boB...

IMHO if it is displayed as above, with the MN brand in lower right, I would see it as FREE advertising...
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 07, 2015, 08:00:51 PM
Quote from: paul alting on March 26, 2015, 03:54:07 AM
David wrote:
Quoteusing some for current measurement at various points in an RE system (they have 12bit accuracy, 4096 points, on a 3.3v input max).
Yes, another reason I am moving to the DUE from the Mega. I bough a few of the Alegro ACS758 50Amp uni-directional sensors from element14 which I will interface to the analog inputs. I'll operate the ACS758 at 5 volts for better performance, but reduce the output through resistor divider to suit the 3v3 input of the DUE.
Paul,
How did you progress with these current sensors? I would be interested in using these for various solar panel arrays' current measurement and possibly
in my Clipper and DC load (water heater) for current measurement

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on April 09, 2015, 08:10:15 AM
Hi David,
The ACS758's are still sitting on the table unfortunately, while I am being kept busy helping my neighbour with his new LiFeYPO4 system and new PV array, as well as lagging with my own new install.
The new battery and control cabinet is almost ready for install and then mounting parts into, starting with LiFeYPO4 cells, then moving up to inverter and then the Arduino DUE and Cubieboard II.

My plan is still to use the technique I mentioned, as the ACS758 will have much better performance on a higher supply than 3v3 that the DUE uses.
Then by using an accurate resistor divider that maintains the necessary impedance suitable for the analog input of the DUE (< 10kΩ), it should be good.

I would get uni-directional sensors of these sort, whether you get the ACS712-xx or ACS758, depending on your measurement range, as you will get double the resolution right away.
With the more commonly used bi-directional sensors, the sensor output sits at Vcc ÷ 2, and swings direction based on current flow direction.
If you only have Vcc ÷ 2 to play with, you loose half of your available anlaog input range, unless you use an op-amp to off-set the signal from such a sensor.

I like that these sensors can be placed in any location of the circuit, not just on the low side as is done with current shunts, and they are fully isolated electrically.

Maybe you used these type already?
_____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 10, 2015, 11:40:26 PM
Paul,
I have ordered several of those devices so another fun job pending  :)
dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 10, 2015, 11:56:28 PM
Here is the bigger web page text version of the simple web server on DUE.
Again not very sophisticated, instead of plain table with data columns it using SVG boxes and text elements.
Still need to rewrite to stop using math floats and instead move real number modbus registers to a formatted string - lots of faffing about to just dispose of that unwanted second decimal place zero - ie 123.4 instead of 123.40
Also still can't figure out where or how to extract the modbus register for remaining battery AH count.
Re posted the sketch in next message - now back to playing with meters and highcharts. Found a real nice highchart that looks almost identical to the chart used in the MyMidnite web page, for Arduino  too  :)

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 11, 2015, 07:27:46 PM
..cleaned up the page before posting sketch.
Now the numbers display with only one digit after decimal point  ;D   had to move all usage of data type FLOAT and replace with some string manipulation, added benefit is program now runs significantly faster and compiles to smaller size.
dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on April 13, 2015, 08:44:06 AM
David,
Looks great, I saw it the day you posted it and have had it in one of my many open tabs intending to reply sooner.
You have made nice use of SVG.

You mention floats and strings, my curiosity is wondering and nagging me to suggest not using any strings or floats, at least no string use in the Arduino.
Without strings, I am currently assisting someone to decode the data stream from a TriMetric battery monitor, it's quite a challenge :)
Then, to pull the data across to the Cubieboard via Modbus holding registers, I then need to have the data as uint16_t, and scaled to get at least one or two decimal places.
(fun times I'm sure you know well enough)
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 14, 2015, 05:45:54 PM
Arduino DUE sketch that displays larger SVG text panels web page.
Top panel for battery info then one panel each for connected Classics
No floating point number processing now, replaced by manipulating char arrays, runs too fast now as modbus reading can't keep up

dgd



/*
  Web Server

A simple web server that shows the values of several modbus registers from Midnite
Classic. Up to 4 Classics conected via separate rs232 connection to Arduino Mega or DUE
Display using SVG text panels
David Dix  2 April 2015  dgd@kc.net.nz
*/

#include <SimpleModbusMaster.h> 
#include <Ethernet.h>
#include <SPI.h>

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network (just leave mac as it is now)
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);

// Initialize the Ethernet server library
// with the IP address and port
// (port 80 is default for HTTP):
EthernetServer server(80);

// setup modbus port information
#define baud 19200
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10
#define IDLE 1

// millis interval between switching modbus ports for different Classics
#define CLTIME 15000

// used to toggle the receive/transmit pin - only for rs485, not used here by lib still expects it - ugh!
#define TxEnablePin 2

// up to 4 Classics connected the serial ports (Mega and DUE)
// could just leave this at 4 but would run slower if cant find all four Classics
#define CLASSICS 2

// modbus lib structures
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  PACKET5,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];
packetPointer packet1 = &packets[PACKET1];  // pointers to access each packet
packetPointer packet2 = &packets[PACKET2];
packetPointer packet3 = &packets[PACKET3];
packetPointer packet4 = &packets[PACKET4];
packetPointer packet5 = &packets[PACKET5];

// Data read from the Classic will be stored in these arrays, using separate arrays to keep memory usage low
// each array is initialized to a packet.
// Note: serial port hardware buffer size means largest array of registers that can be obtained is 29
unsigned int readRegs[30];
unsigned int wbjrRegs[15];
unsigned int vRegs[2];
unsigned int idRegs[10];       // name 4209 to 4212, and time/date 4213 to 4216
unsigned int sernoRegs[2];

HardwareSerial* serialPort;


int current_classic = 0, prev_classic;

  char  invstr[CLASSICS][8], oastr[CLASSICS][8], kwhstr[CLASSICS][8];
  int   cstate[CLASSICS], watts[CLASSICS], wbjr_ahr, wbjr_soc, vers[CLASSICS], model[CLASSICS], remt;
  char  ftpstr[CLASSICS][8], ptpstr[CLASSICS][8];
  char  Classic_name[CLASSICS][10], bvstr[8], wbastr[8], btpstr[8], rtpstr[8];
  int   eq_hrs[CLASSICS], eq_mins[CLASSICS], eq_secs[CLASSICS];
  int   fl_hrs[CLASSICS], fl_mins[CLASSICS], fl_secs[CLASSICS];
  int   abs_hrs[CLASSICS], abs_mins[CLASSICS], abs_secs[CLASSICS];
  int   serno[CLASSICS];
  unsigned long start_count, timenow;

void setup()
{
  int  i,j, k;
 
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
   
  modbus_construct(packet1, 10, READ_HOLDING_REGISTERS, 4114, 29, readRegs);  // at max 29
  modbus_construct(packet2, 10, READ_HOLDING_REGISTERS, 4360, 14, wbjrRegs);
  modbus_construct(packet3, 10, READ_HOLDING_REGISTERS, 4100, 1, vRegs);
  modbus_construct(packet4, 10, READ_HOLDING_REGISTERS, 4209, 9, idRegs);
  modbus_construct(packet5, 10, READ_HOLDING_REGISTERS, 0x7000, 2, sernoRegs);
 
   // start modbus input from port Serial1 on DUE/Mega
   start_count = millis();
   modbus_configure(&Serial1, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
}

void loop()
{
  char  c;
  int   i,j,k, amps;

/*
   Use millis() time counting for changing between Classics being processed by the modbus state machine.
   Each change just pokes the harware serial parameter and re-executes modbus_configure/modbus_update.
   Not perfect but seems to work, may take an iteration or three to get the correct readings in each CLassic's
   display panel, modbus ugly timing is the problem.
   Proper solution is to rewrite less sophisticated modbus lib and execute one modbus manager for each
   serial port - using the DUE scheduler would work well for this method     
     
*/
   prev_classic = current_classic;

   // Move to next serial port for input if time counter used up ie swap to next Classic
   timenow = millis();
   if ((timenow - start_count) > (unsigned long)CLTIME)
   {
     start_count = timenow;
     current_classic++;
     if (current_classic >= CLASSICS) current_classic = 0;
     switch (current_classic)
     {
       case 0:                        // serial port order could be changed if desired
          serialPort = &Serial1;      // eg if there is only ever one Clssic on &Serial then make it first
          break;
       case 1:
          serialPort = &Serial2;
          break;
       case 2:
          serialPort = &Serial3;
          break;
       case 3:
          serialPort = &Serial;     // hardware serial 0
          break;
     }
     if (current_classic != prev_classic )  // if only one classic then don't reconfigure
     {
       modbus_configure(serialPort, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
       modbus_update();  // to start the reconfigure
       delay(10);
     }
   }
 
   modbus_update();   // the modbus lib state machine :-)
   
   k = current_classic;
   
  // make Classic register values ready for web page
     model[k] = (unsigned int)lowByte(vRegs[0]);
     serno[k] = sernoRegs[1];
     Classic_name[k][0] = lowByte (idRegs[0]);
     Classic_name[k][1] = highByte(idRegs[0]);
     Classic_name[k][2] = lowByte (idRegs[1]);
     Classic_name[k][3] = highByte(idRegs[1]);
     Classic_name[k][4] = lowByte (idRegs[2]);
     Classic_name[k][5] = highByte(idRegs[2]);
     Classic_name[k][6] = lowByte (idRegs[3]);
     Classic_name[k][7] = highByte(idRegs[3]);
     Classic_name[k][8] = 0x00;

     itoa (readRegs[0], bvstr, 10);
     i=strlen(bvstr);
     bvstr[i] = bvstr[i-1];
     bvstr[i-1] = '.';
     bvstr[i+1] = '\0';
     
     itoa (readRegs[1], invstr[k], 10);   // input voltage
     i = strlen (invstr[k]);
     invstr[k][i] = invstr[k][i-1];
     invstr[k][i-1] = '.';
     invstr[k][i+1] = '\0';
     
     itoa (readRegs[2], oastr[k], 10);    // output amps
     i = strlen (oastr[k]);
     if ( i==1 )                          // insert leading zero if value < 1
     {
       oastr[k][1]=oastr[k][0];
       oastr[k][0] = '0';
       i++;
     }
     oastr[k][i] = oastr[k][i-1];
     oastr[k][i-1] = '.';
     oastr[k][i+1] = '\0';

     itoa (readRegs[3], kwhstr[k], 10);   // kwh
     i = strlen (kwhstr[k]);
     if (i==1)              // insert leading zero if < 0
     {     kwhstr[k][1] = kwhstr[k][0];
           kwhstr[k][0] = '0';
           i++;
     }
     kwhstr[k][i] = kwhstr[k][i-1];
     kwhstr[k][i-1] = '.';
     kwhstr[k][i+1] = '\0';
         
     watts[k] = readRegs[4];
     cstate[k] = (unsigned int)readRegs[5] >> 8;  // high byte contains charge state code 
   
     if (readRegs[17] > 100)
     {
         btpstr[0] = '2';   // set to default 25c
         btpstr[1] = '5';
         btpstr[2] = '\0'; 
     }
     else
     {
         itoa (readRegs[17], btpstr, 10);    // need to test if BTS present
         i = strlen(btpstr);
         btpstr[i] = btpstr[i-1];
         btpstr[i-1] = '.';
         btpstr[i+1] = '\0';
     }
     
     itoa (readRegs[18], ftpstr[k], 10);
     i = strlen (ftpstr[k]);
     ftpstr[k][i] = ftpstr[k][i-1];
     ftpstr[k][i-1] = '.';
     ftpstr[k][i+1] = '\0'; 
     itoa (readRegs[19], ptpstr[k], 10);
     i = strlen (ptpstr[k]);
     ptpstr[k][i] = ptpstr[k][i-1];
     ptpstr[k][i-1] = '.';
     ptpstr[k][i+1] = '\0';

     fl_secs[k] = readRegs[23];      //4137 floatTimeofdayseconds
     if (fl_secs[k] >= 3600)
     { fl_hrs[k] = (int)fl_secs[k]/3600;
       fl_secs[k] -= (int) fl_hrs[k]*3600;
     }
     else fl_hrs[k] = 0;
     if (fl_secs[k] >= 60 )
     { fl_mins[k] = (int)fl_secs[k]/60;
       fl_secs[k] -= (int) fl_mins[k]*60;
     }
     else fl_mins[k]=0;
     
     
     eq_secs[k] = readRegs[28];      //4142 EqualizeTime
     if (eq_secs[k] >= 3600)
     {
       eq_hrs[k] = (int)eq_secs[k]/3600;
       eq_secs[k] -= (int) eq_hrs[k]*3600;
     }
     else eq_hrs[k] = 0;
     if (eq_secs[k] >= 60 )
     { eq_mins[k] = (int)eq_secs[k]/60;
       eq_secs[k] -= (int) eq_mins[k]*60;
     }
     else eq_mins[k]=0;
     
     abs_secs[k] = readRegs[24];      //4138 AbsorbTime
     if (abs_secs[k] >= 3600)
     {
       abs_hrs[k] = (int)abs_secs[k]/3600;
       abs_secs[k] -= (int) abs_hrs[k]*3600;
     }
     else abs_hrs[k] = 0;
     if (abs_secs[k] >= 60 )
     {
       abs_mins[k] = (int)abs_secs[k]/60;
       abs_secs[k] -= (int) abs_mins[k]*60;
     }
     else abs_mins[k] = 0;

// WBjr registers   
     remt = (int) lowByte (wbjrRegs[11]);
     remt -=50;
     if  (remt != -50 )  // there is WBjr connected
     {
        itoa (remt, rtpstr, 10);
        amps = (signed int)wbjrRegs[10];   // cast doesn't always work
  // wbjrRegs is unsigned int so need to make signed int before itoa conversion
        if (amps > 60000) amps -=65535;
        itoa (amps, wbastr, 10);
        i = strlen(wbastr);
  // need to insert leading zero if <1 and > -1,  eg .3 becomes 0.3, -.2 becomes -0.2
 
        if (i==1 || (i==2 && wbastr[0] == '-') )
        { 
            wbastr[i] = wbastr[i-1];
            wbastr[i-1] = '0';
            i++;
        }   
        wbastr[i] = wbastr[i-1];   // now divide by 10 by inserting decimal point
        wbastr[i-1] = '.';
        wbastr[i+1] = '\0';
        wbjr_soc = wbjrRegs[12];          // 4372
        wbjr_ahr = wbjrRegs[8];           // this battery Ah capacity`` [
     }
 
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client)
  {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected())
    {
      if (client.available())
      {
        char c = client.read();
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank)
        {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          client.println("Connection: close");  // the connection will be closed after completion of the response
          client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          client.println("<!DOCTYPE HTML>");     // HTML 5 reponse

          client.println("<html><body>");

         // a bigger display for battery/WBjr
         // top panel is for Battery stuff, Voltage, SOC, Amps in/out etc..
         // then one panel for each Classic with ID details, state, in voltage, Watts out and Amps out etc..
         
          client.println ("<svg width=\"1260\" height=\"185\">");
          client.println ("<rect x=\"145\" y=\"20\" rx=\"20\" ry=\"20\" width=\"935\" height=\"150\" style=\"fill:yellow;stroke:black;stroke-width:5;opacity:0.5\" />");
          client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"175\" y=\"56\">Battery SOC</text> ");
          client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"175\" y=\"123\">");
          client.print (wbjr_soc);
          client.println ("</text> ");
          client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"360\" y=\"56\">WBjr Amps</text>");
          client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"360\" y=\"123\">");
          client.print (wbastr);
          client.println ("</text> ");

          client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"535\" y=\"56\">Battery Volts</text> ");
          client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"535\" y=\"123\">");
          client.print (bvstr);
          client.println ("%</text> ");
          client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"710\" y=\"56\">Amp Hours</text> ");
          client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"710\" y=\"123\">");
          client.print (wbjr_ahr);
          client.println ("</text> ");

          client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"885\" y=\"56\">Temps</text> ");
          client.print ("<text font-size=\"25\" font-family=\"Verdana\" x=\"885\" y=\"96\">Battery ");
          client.print (btpstr);
          client.println ( "</text> ");
          client.print ("<text font-size=\"25\" font-family=\"Verdana\" x=\"885\" y=\"136\">Remote  ");
          client.print (rtpstr);
          client.println ("</text>");

        client.println ("Sorry, your browser does not support inline SVG.");
        client.println ("</svg>");

        for (k=0; k< CLASSICS; k++ )
        {

       
       // a bigger display for Classic data stuff
            client.println ("<svg width=\"1260\" height=\"245\">");
            client.println ("<rect x=\"145\" y=\"20\" rx=\"20\" ry=\"20\" width=\"935\" height=\"200\" style=\"fill:blue;stroke:black;stroke-width:5;opacity:0.1\" />");

            client.print ("<text font-size=\"20\" font-family=\"Verdana\" x=\"175\" y=\"56\">");
            client.print (Classic_name[k]);
            client.print ("  C");
            client.print (model[k]);
            client.print ("  #");
            client.print (serno[k]);
            client.println ("</text>");

            client.print ("<text font-size=\"25\" font-family=\"Verdana\" x=\"500\" y=\"56\">");
            switch (cstate[k])
            {
            case 0:
              client.print ("RESTING");
              break;
            case 3:
              client.print ("ABSORB - time left ");
              client.print (abs_hrs[k]);
              client.print (":");
              client.print (abs_mins[k]);
              client.print (":");
              client.print (abs_secs[k]);
              break;
            case 4:
              client.print ("BULKMPPT");
              break;
            case 5:
              client.print ("FLOAT - total time ");
              client.print (fl_hrs[k]);
              client.print (":");
              client.print (fl_mins[k]);
              client.print (":");
              client.print (fl_secs[k]);
              break;
            case 6:
              client.print ("FLOAT MPPT");
              break;
            case 7:
              client.print ("EQUALISE - time left ");
              client.print (eq_hrs[k]);
              client.print (":");
              client.print (eq_mins[k]);
              client.print (":");
              client.print (eq_secs[k]);
              break; 
            case 10:
              client.print ("HYPERVOC");
              break;
            case 18:
              client.print ("EQ MPPT");
              break;
            }
            client.println ("</text>");

            client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"175\" y=\"106\">Input Volts</text>");
            client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"175\" y=\"163\">");
            client.print (invstr[k]);
            client.println ("</text> ");
           
            client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"360\" y=\"106\">Kw Hours</text>");
            client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"360\" y=\"163\">");
            client.print (kwhstr[k]);
            client.println ("</text> ");
           
            client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"535\" y=\"106\">Output Watts</text> ");
            client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"535\" y=\"163\">");
            client.print (watts[k]);
            client.println ("</text> ");
           
            client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"710\" y=\"106\">Output Amps</text> ");
            client.print ("<text font-size=\"45\" font-family=\"Verdana\" x=\"710\" y=\"163\">");
            client.print (oastr[k]);
            client.println ("</text> ");

            client.println ("<text font-size=\"20\" font-family=\"Verdana\" x=\"885\" y=\"106\">Temps</text> ");
            client.print ("<text font-size=\"25\" font-family=\"Verdana\" x=\"885\" y=\"141\">PCB ");
            client.print (ptpstr[k]);
            client.println ( "</text> ");
            client.print ("<text font-size=\"25\" font-family=\"Verdana\" x=\"885\" y=\"176\">FET  ");
            client.print (ftpstr[k]);
            client.println ("</text> ");

            client.println ("Sorry, your browser does not support inline SVG.");
            client.println ("</svg>");

       
          }  // for loop CLASSICS

          client.println("</body></html>");
          break;
        }
        if (c == '\n')
        {
          // starting a new line
          currentLineIsBlank = true; 
         
        }
        else
          if (c != '\r')
          {
          // character on the current line
          currentLineIsBlank = false;
          }
       }
    }
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}

Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on April 14, 2015, 06:01:15 PM
I just scanned through the documentation, 'cause I am not conversant in this code language, and I like your self talk about where it can be improved..  Good job mate! ;D ;)
tks
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 14, 2015, 08:11:12 PM
A  programmer friend who looked at my code posted here said I should be @&%@ slapped for the comments I had inline in the  code.
She said no real self respecting programmer would include comments let alone any explanation of what the code is trying to achieve  :o
Thankfully I'm not one of those then  :-X

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on April 14, 2015, 08:23:20 PM
That's Professional speak for
''don't tell anyone you don't know something" :o  or
''always say something that they have to come to you for an explanation, in billable hours'' :'(  or horrors " that is not what we wanted the program to do'' ::)
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 15, 2015, 12:44:28 AM
..and this is the web screen design  I'm currently testing. I have not sorted out the background image yet. This test was to get XML updates of the text box only working. Hence good values there but no XML for the meter values.
Also trying to javascript to switch meters to 12volt and 48volt versions and when the Classic model is retreived change  the amps out and the volts meters to appropriate versions. 
Nearly there and will post the full sketch for this plus the html pages to store on SD card (and various background images)

AFter this I next  want to look at a web page for configuring the Classic  8)
Where is the KID info on reading its serial port for data?

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 21, 2015, 06:30:59 AM
Quote from: paul alting link=topic=2393.msg22951#msg22951date=1426982879
This is where the folks at Electhouse made a DUE with these pins brought out, called the Taijuino.
I was given some of these boards by Elechouse in the hope a native Ethernet library could be developed in conjunction with two other programmers.
Having such an Ethernet interface would not have the issues that the Wiznet gives, where the Wiznet is slow, it depends on SPI and has limited concurrent TCP connections sockets.
Unfortunately, to date, the DUE Ethernet library has not been completed.

Paul,
I managed to find one of these Taijiuino DUE boards complete with a programming interface card. The most interesting feature, for me, is the ethernet pins from the cpu being made available.
As you pointed out the Wiznet uses the spi interface, as does the SD card, and even with the latest IDE1.6.3 the drivers appear broken, each works well by itself but using both ethernet and SD card just does not work very well.
So question is - has there been any progress towards completing a DUE ethernet library for the Taijiuino?
The is so much program flash,512k, and ram 96k, that I can get my more advanced (analogue gauges) web interface for Classic into memory and not bother the SD storage. However the SD would be very useful for log file storage.
Or maybe I have reached the limits of the DUE and its time to get to the Cubie2  :)

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 22, 2015, 12:01:10 AM
I have almost completed the analogue meter web server for Classic and have also compiled the code on both the Arduino DUE and Arduino Mega2560. It appears that the Arduino IDE1.6.3 that I use for development now auto-sorts-out the modbus library calls so no hacking about of libs is necessary.
Still a few minor issues to tidy up but the web page works nicely, example from running web page below and both the Arduino sketch and web page to be saved on SD card are in next posting.
The Mega uses the standard rs232 shield that I used in the simple UNO web server and the lcd UNO in other message threads. This means no special soldering together of an RS232 shield as shown in an earlier posting in this thread. The special is still needed for use with the DUE because of its 3.3volt maximum interface pins The mega is 5v so is easy to use existing $12 shield.

This web server stores the web pages (html,javascript) on the SD card and the sketch on the Arduino reads the web pages and serves them to the connecting web browser.
This sketch uses AJAX to update the web page (gauge and info window) by serving XML data as requested by the browser running the web page.
This makes for a nice steady display page with no flickering as the page is not totally reloaded as data values change.
The only issue I need to work on is the abyssmal modbus throughput which means I can only refresh the gauges etc, about once every 2 seconds. I want 200ms as that is the default for the gauges.
Still no background images  :o
Image below has wrong Remaining Ah, WBjr amps greater then Output amps because there is another Classic also providing output amps

Also still impatiently waiting on the KID data extraction details, hopefully it will be less irksome than dealing with modbus RTU.
I also have this working with a Morningstar MPPT60.

Any questions, suggestions, free beer etc are very welcome  ::)

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 22, 2015, 12:11:30 AM
The web server sketch for Arduino Mega and DUE (won't fit in an UNO as it uses over 3k ram for variables, UNO=2k, Mega2560=8k, DUE=96k)
Best to use Arduino 1.6.3 IDE, I used this on Windows 8.1 ASUS laptop
SD card web page index.htm, node14.htm and node17.htm in later posts
This web server code expects to read from SD card (from micro SD card interface on Arduino ethernet shield), files named index.htm, node14.htm  node17.htm and sasa.jpg
The web server sketch is continued in next posting
This version of the sketch includes the in-memory log file processing

dgd


/*
  Web Server

A web server that shows the values of several modbus registers from Midnite
Classic. Uses the canv_gauge lib and svg to display data
Uses simplemodbusmaster library
David Dix  12 July 2015  dgd@kc.net.nz
*/
#include <SPI.h>
#include <SimpleModbusMaster.h> 
#include <Ethernet.h>
#include <SD.h>


// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network (just leave mac as it is now)
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
IPAddress ip(192, 168, 1, 177);

// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   50

// Initialize the Ethernet server library
// with the IP address and port
// (port 80 is default for HTTP):
EthernetServer server(80);
File webFile;               // the web page file on the SD card
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer

// setup modbus port information
#define baud 19200
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10
#define IDLE 1

// millis interval between switching modbus ports for different Classics
// #define CLTIME 15000

#define LOG_INTERVAL 594970    // millis time between saving 10 minute log entry 600,000 minus fine tuning

// maximum number of og records to store in memory
// keep small (48 to 96) for Mega and lots for DUE
// defaults to 10 minutely samples or a day for DUE, 12+ hours for Mega
// not yet writing these to SD file as its a bit slow
#define MAX_LOG_COUNT 48
// Daily log, keep deault 20 days in memory arrays
#define MAX_DLOG_COUNT 20

// used to toggle the receive/transmit pin - only for rs485, not used here by lib still expects it - ugh!
#define TxEnablePin 2

// up to 4 Classics connected the serial ports (Mega and DUE)
// could just leave this at 4 but would run slower if cant find all four Classics
#define CLASSICS 1

// modbus lib structures
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  PACKET5,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];
packetPointer packet1 = &packets[PACKET1];  // pointers to access each packet
packetPointer packet2 = &packets[PACKET2];
packetPointer packet3 = &packets[PACKET3];
packetPointer packet4 = &packets[PACKET4];
packetPointer packet5 = &packets[PACKET5];

// Data read from the Classic will be stored in these arrays, using separate arrays to keep memory usage low
// each array is initialized to a packet.
// Note: serial port hardware buffer size means largest array of registers that can be obtained is 29
unsigned int readRegs[30];
unsigned int wbjrRegs[21];
unsigned int vRegs[2];
unsigned int idRegs[10];       // name 4209 to 4212, and time/date 4213 to 4216
unsigned int sernoRegs[2];

// HardwareSerial* serialPort;


int current_classic = 0, prev_classic;

  float batt_volts, batt_amps, endamps, lowvolts, highvolts, in_volts[CLASSICS], out_amps[CLASSICS];
  char  invstr[CLASSICS][8], oastr[CLASSICS][8], kwhstr[CLASSICS][6], statestr[CLASSICS][30];
  char  lastkwhr[6];
  int   cstate[CLASSICS], watts[CLASSICS], wbjr_ahr, wbjr_ra, wbjr_soc, vers[CLASSICS], model[CLASSICS], remt;
  int   high_watts[CLASSICS], ctime_secs[CLASSICS], ctime_mins[CLASSICS], ctime_hrs[CLASSICS], last_min;
  int   ctime_day[CLASSICS], ctime_mth[CLASSICS];
  int   last_day, last_month;
  int   ctime_weekday[CLASSICS], last_weekday[CLASSICS],highwatts[CLASSICS];
  char  ftpstr[CLASSICS][8], ptpstr[CLASSICS][8], peakpstr[CLASSICS][6];
  char  Classic_name[CLASSICS][10], bvstr[8], wbastr[8], btpstr[8], rtpstr[8], ctimestr[CLASSICS][10];
  int   eq_hrs[CLASSICS], eq_mins[CLASSICS], eq_secs[CLASSICS];
  int   fl_hrs[CLASSICS], fl_mins[CLASSICS], fl_secs[CLASSICS];
  int   abs_hrs[CLASSICS], abs_mins[CLASSICS], abs_secs[CLASSICS];
  int   serno[CLASSICS];
  unsigned long start_count, timenow;
  int   log_index=0, last_index=0, next_mins=0;
 
  // 10 minute log file arrays (should eventually write to SD card)
  int   log_mins[MAX_LOG_COUNT], log_hrs[MAX_LOG_COUNT];   // for record time stamp, no date yet
  float log_battvolts[MAX_LOG_COUNT], log_battamps[MAX_LOG_COUNT];     
  int   log_watts[MAX_LOG_COUNT], log_state[MAX_LOG_COUNT];       
  float log_involts[MAX_LOG_COUNT], log_outamps[MAX_LOG_COUNT];
  int   log_ahremain[MAX_LOG_COUNT];
  char  log_kwh[MAX_LOG_COUNT][6];
  int   log_ahr[MAX_LOG_COUNT], log_soc[MAX_LOG_COUNT];   // Soc to 100%
 
  int   dlog_index=0, dlast_index=0;
  // Day log file in arrays max 20 entries
  int   dlog_mth[MAX_DLOG_COUNT], dlog_day[MAX_DLOG_COUNT];
  char  dlog_kwh[MAX_DLOG_COUNT][6];
  int   dlog_abhr[MAX_DLOG_COUNT], dlog_abmin[MAX_DLOG_COUNT], dlog_flhr[MAX_DLOG_COUNT];
  int   dlog_flmin[MAX_DLOG_COUNT];
  float dlog_lbv[MAX_DLOG_COUNT], dlog_hbv[MAX_DLOG_COUNT], dlog_ea[MAX_DLOG_COUNT];
  int   dlog_hwts[MAX_DLOG_COUNT];
  int   dlog_hsoc[MAX_DLOG_COUNT], dlog_lsoc[MAX_DLOG_COUNT];
  int   dlog_ham[MAX_DLOG_COUNT], dlog_lam[MAX_DLOG_COUNT];
  int   dlog_hahr[MAX_DLOG_COUNT], dlog_lahr[MAX_DLOG_COUNT];
 
  int   high_soc=0, low_soc=0;       
  int   high_ahr=0, low_ahr=0;     
  int   high_ra=0, low_ra=0;
  int   float_hrs, float_mins, absorb_hrs, absorb_mins;
   
void setup()
{
  int  i,j, k;
    // disable Ethernet chip
    pinMode(10, OUTPUT);
    digitalWrite(10, HIGH);

//    Serial.begin(9600);       // for debugging
   
    // initialize SD card
//    Serial.println("Initializing SD card...");
    if (!SD.begin(4)) {
//      Serial.println("ERROR - SD card initialization failed!");
        return;    // init failed
    }
//    Serial.println("SUCCESS - SD card initialized.");
    // check for index.htm file
    if (!SD.exists("index.htm")) {
//        Serial.println("ERROR - Can't find index.htm file!");
        return;  // can't find index file
    }
//    Serial.println("SUCCESS - Found index.htm file.");
   
     // start the Ethernet connection and the server:
    Ethernet.begin(mac, ip);
    server.begin();

   
  modbus_construct(packet1, 10, READ_HOLDING_REGISTERS, 4114, 29, readRegs);  // at max 29
  modbus_construct(packet2, 10, READ_HOLDING_REGISTERS, 4360, 20, wbjrRegs);
  modbus_construct(packet3, 10, READ_HOLDING_REGISTERS, 4100, 1, vRegs);
  modbus_construct(packet4, 10, READ_HOLDING_REGISTERS, 4209, 9, idRegs);
  modbus_construct(packet5, 10, READ_HOLDING_REGISTERS, 0x7000, 2, sernoRegs);
 
   // start modbus input from port Serial1 on DUE/ Serial on Mega
   start_count = millis();
   modbus_configure(&Serial, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
}


/*  This function reads the modbus registers from Classic using modbus_update/
    It then unpacks the register unsigned int values into appropriate global floats, ints and character
    arrays. In character arrays decimal points are inserted where needed and leading zero prepended on
    fraction numbers in preparation for displaying in html page.
*/
void read_registers()
{
     int  i,j,k, amps;
     char temp[7];
     
     modbus_update();   // the modbus lib state machine :-)
//   delay(10);
   
     k = current_classic;
   
  // make Classic register values ready for web page
     model[k] = (unsigned int)lowByte(vRegs[0]);
     serno[k] = sernoRegs[1];
     Classic_name[k][0] = lowByte (idRegs[0]);
     Classic_name[k][1] = highByte(idRegs[0]);
     Classic_name[k][2] = lowByte (idRegs[1]);
     Classic_name[k][3] = highByte(idRegs[1]);
     Classic_name[k][4] = lowByte (idRegs[2]);
     Classic_name[k][5] = highByte(idRegs[2]);
     Classic_name[k][6] = lowByte (idRegs[3]);
     Classic_name[k][7] = highByte(idRegs[3]);
     Classic_name[k][8] = 0x00;
     
   // time registers at address 4213,  secs and mins, 4214 hours and weekday
   // bit pattern 4213  ..mmmmmm..ssssss and 4214  .....ddd...hhhhh
   // casting to unsigned int necessary to prevent right shifting leftmost 1 results in negative number
     ctime_secs[k] = idRegs[4] << 10;
     ctime_secs[k] =  (unsigned int)ctime_secs[k] >> 10; 
     ctime_mins[k] = idRegs[4] << 2;
     ctime_mins[k] = (unsigned int) ctime_mins[k] >> 10;
     ctime_hrs[k] = idRegs[5] << 11;
     ctime_hrs[k] = (unsigned int) ctime_hrs[k] >> 11;
     ctime_weekday[k] = idRegs[5] << 5;
     ctime_weekday[k] = (unsigned int) ctime_weekday[k] >> 13;
     ctime_day[k] = idRegs[6] << 11;
     ctime_day[k] = (unsigned int)ctime_day[k] >> 11;
     ctime_mth[k] = idRegs[6] << 4;
     ctime_mth[k] = (unsigned int) ctime_mth[k] >> 12;
 
  // Battery volts in float and string   
     batt_volts = readRegs[0];
     batt_volts /= 10;
     
     // record high/low battery voltages for day log
     if (batt_volts > highvolts ) highvolts = batt_volts;
     if (lowvolts == 0 || batt_volts < lowvolts ) lowvolts = batt_volts;
     
     itoa (readRegs[0], bvstr, 10);
     i=strlen(bvstr);
     bvstr[i] = bvstr[i-1];
     bvstr[i-1] = '.';
     bvstr[i+1] = '\0';

  // Input volts in float and string
     in_volts[k] = readRegs[1];
     in_volts[k] /= 10;   
     itoa (readRegs[1], invstr[k], 10);   // input voltage
     i = strlen (invstr[k]);
     invstr[k][i] = invstr[k][i-1];
     invstr[k][i-1] = '.';
     invstr[k][i+1] = '\0';

  // Output amps in float and string (note NOT wbjr amps but amps from Classic)   
     out_amps[k] = readRegs[2];
     out_amps[k] /=10;
     itoa (readRegs[2], oastr[k], 10);    // output amps
     i = strlen (oastr[k]);
     if ( i==1 )                          // insert leading zero if value < 1
     {
       oastr[k][1]=oastr[k][0];
       oastr[k][0] = '0';
       i++;
     }
     oastr[k][i] = oastr[k][i-1];
     oastr[k][i-1] = '.';
     oastr[k][i+1] = '\0';

  // Kwh as string
     itoa (readRegs[3], kwhstr[k], 10);   // kwh
     i = strlen (kwhstr[k]);
     if (i==1)              // insert leading zero if < 0
     {     kwhstr[k][1] = kwhstr[k][0];
           kwhstr[k][0] = '0';
           i++;
     }
     kwhstr[k][i] = kwhstr[k][i-1];
     kwhstr[k][i-1] = '.';
     kwhstr[k][i+1] = '\0';
 
  // Create string output line with day's peak Watts reading and timestamp it.
     watts[k] = readRegs[4];
     if (highwatts[k] < watts[k] )
     {
        highwatts[k] = watts[k];
        itoa (highwatts[k], peakpstr[k], 10);    // peak wattage from Classic today
       
        itoa(ctime_hrs[k], temp, 10);            // save time of peak power
        strcpy ( ctimestr[k], temp );            // build string ctimestr by writing ctime hours then
                                                 // ctime minutes then ctime seconds separted by a :
        itoa (ctime_mins[k], temp, 10);          // if mins or secs re single digit then insert leading zero
        i = strlen(temp);                        // to get  hh:mm:ss or h:mm:ss (not fussed about leading
        if (i==1)                                // zero on hours part
        {
            temp[2] = temp[0];
            temp[0] = ':';
            temp[1] = '0';
         }
         else
         {
            temp[2] = temp[1];
            temp[1] = temp[0];
            temp[0] = ':';
          }
          temp[3] = '\0';
          strcat (ctimestr[k], temp);   
         
         itoa (ctime_secs[k], temp, 10);
         i = strlen(temp);
         if (i==1)
         {
            temp[2] = temp[0];
            temp[0] = ':';
            temp[1] = '0';
         }
         else
         {
            temp[2] = temp[1];
            temp[1] = temp[0];
            temp[0] = ':';
          }
          temp[3] = '\0';
          strcat (ctimestr[k], temp);     // now have string ctimestr for displaying

     }

  // Charge state   
     cstate[k] = (unsigned int)readRegs[5] >> 8;  // high byte contains charge state code
     
  // Battery temperature string in degrees C (need to consider degrees F option here)   
     if (readRegs[17] > 100) // if its a crazy over 100 deg C measurement then likely no BTS present
     {
         btpstr[0] = '2';   // set to default 25c
         btpstr[1] = '5';
         btpstr[2] = '\0'; 
     }
     else
     {
         itoa (readRegs[17], btpstr, 10);    // need to properly test if BTS present
         i = strlen(btpstr);
         btpstr[i] = btpstr[i-1];
         btpstr[i-1] = '.';
         btpstr[i+1] = '\0';
     }
 
  // FET temperature string   
     itoa (readRegs[18], ftpstr[k], 10);
     i = strlen (ftpstr[k]);
     ftpstr[k][i] = ftpstr[k][i-1];
     ftpstr[k][i-1] = '.';
     ftpstr[k][i+1] = '\0';
     
  // PCB temperature string
     itoa (readRegs[19], ptpstr[k], 10);
     i = strlen (ptpstr[k]);
     ptpstr[k][i] = ptpstr[k][i-1];
     ptpstr[k][i-1] = '.';
     ptpstr[k][i+1] = '\0';

  // string for total float time in hh:mm:ss or h:mm:ss  inserting leading zero in mm and ss if needed
     fl_secs[k] = readRegs[23];      //4137 floatTimeofdayseconds
     if (fl_secs[k] >= 3600)
     { fl_hrs[k] = (int)fl_secs[k]/3600;
       fl_secs[k] -= (int) fl_hrs[k]*3600;
     }
     else fl_hrs[k] = 0;
     if (fl_secs[k] >= 60 )
     { fl_mins[k] = (int)fl_secs[k]/60;
       fl_secs[k] -= (int) fl_mins[k]*60;
     }
     else fl_mins[k]=0;
 
   // string for total Equalise time hh:mm:ss or h:mm:ss  inserting leading zero in mm and ss if needed       
     eq_secs[k] = readRegs[28];      //4142 EqualizeTime
     if (eq_secs[k] >= 3600)
     {
       eq_hrs[k] = (int)eq_secs[k]/3600;
       eq_secs[k] -= (int) eq_hrs[k]*3600;
     }
     else eq_hrs[k] = 0;
     if (eq_secs[k] >= 60 )
     { eq_mins[k] = (int)eq_secs[k]/60;
       eq_secs[k] -= (int) eq_mins[k]*60;
     }
     else eq_mins[k]=0;
     
   // string for Absorb time left hh:mm:ss or h:mm:ss inserting leading zero in mm and ss if needed
     abs_secs[k] = readRegs[24];      //4138 AbsorbTime
     if (abs_secs[k] >= 3600)
     {
       abs_hrs[k] = (int)abs_secs[k]/3600;
       abs_secs[k] -= (int) abs_hrs[k]*3600;
     }
     else abs_hrs[k] = 0;
     if (abs_secs[k] >= 60 )
     {
       abs_mins[k] = (int)abs_secs[k]/60;
       abs_secs[k] -= (int) abs_mins[k]*60;
     }
     else abs_mins[k] = 0;
     
   // set up state string 
     switch (cstate[k])
     {
            case 0:
              strcpy( statestr[k], "RESTING");
              break;
            case 3:
              strcpy ( statestr[k], "ABSORB - time left ");
              itoa(abs_hrs[k], temp, 10);
              strcat ( statestr[k], temp );
                     
              itoa (abs_mins[k], temp, 10);
              i = strlen(temp);
              if (i==1)
              {
                temp[2] = temp[0];
                temp[0] = ':';
                temp[1] = '0';
              }
              else
              {
                temp[2] = temp[1];
                temp[1] = temp[0];
                temp[0] = ':';
              }
              temp[3] = '\0';
              strcat (statestr[k], temp);     
           
              itoa(abs_secs[k], temp, 10);
              i = strlen(temp);
              if (i==1)
              {
                temp[2] = temp[0];
                temp[0] = ':';
                temp[1] = '0';
              }
              else
              {
                temp[2] = temp[1];
                temp[1] = temp[0];
                temp[0] = ':';
              }
              temp[3] = '\0';

              strcat (statestr[k], temp);
              break;
            case 4:
              strcpy (statestr[k], "BULKMPPT");
              break;
            case 5:
              strcpy ( statestr[k], "FLOAT - Total time ");
              itoa(fl_hrs[k], temp, 10);
              strcat ( statestr[k], temp );
              itoa (fl_mins[k], temp, 10);
              i = strlen(temp);
              if (i==1)
              {
                temp[2] = temp[0];
                temp[0] = ':';
                temp[1] = '0';
              }
              else
              {
                temp[2] = temp[1];
                temp[1] = temp[0];
                temp[0] = ':';
              }
              temp[3] = '\0';
              strcat (statestr[k], temp);     
           
              itoa(fl_secs[k], temp, 10);
              i = strlen(temp);
              if (i==1)
              {
                temp[2] = temp[0];
                temp[0] = ':';
                temp[1] = '0';
              }
              else
              {
                temp[2] = temp[1];
                temp[1] = temp[0];
                temp[0] = ':';
              }
              temp[3] = '\0';
              strcat (statestr[k], temp);
              break;
 
            case 6:
              strcpy ( statestr[k], "FLOAT MPPT");
              break;
            case 7:
              strcpy ( statestr[k], "EQUALIZE - Time left ");
              itoa(eq_hrs[k], temp, 10);
              strcat ( statestr[k], temp );
                     
              itoa (eq_mins[k], temp, 10);
              i = strlen(temp);
              if (i==1)
              {
                temp[2] = temp[0];
                temp[0] = ':';
                temp[1] = '0';
              }
              else
              {
                temp[2] = temp[1];
                temp[1] = temp[0];
                temp[0] = ':';
              }
              temp[3] = '\0';
             
              strcat (statestr[k], temp);     
           
              itoa(eq_secs[k], temp, 10);
              i = strlen(temp);
              if (i==1)
              {
                temp[2] = temp[0];
                temp[0] = ':';
                temp[1] = '0';
              }
              else
              {
                temp[2] = temp[1];
                temp[1] = temp[0];
                temp[0] = ':';
              }
              temp[3] = '\0';

              strcat (statestr[k], temp);
              break;
             case 10:
              strcpy ( statestr[k],"HYPERVOC");
              break;
            case 18:
              strcpy ( statestr[k],"EQ MPPT");
              break;
     }

 
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 22, 2015, 12:13:19 AM
Arduino sketch continued Part 2 (was too big for one posting), Part 3 is in later posting
All 3 parts need o be extracted and joined to make the complete Arduino sketch


   // WBjr registers

  // wbjr remote temperature
     remt = (int) lowByte (wbjrRegs[11]);
     remt -=50;
     if  (remt != -50 )  // there is WBjr connected
     {
        itoa (remt, rtpstr, 10);
       
    // wbjr battery amps minus value = discharging, positive = charging, save as float and string
        amps = (signed int)wbjrRegs[10];   // cast doesn't seem to work
        // wbjrRegs is unsigned int so need to make signed int before itoa conversion
        if (amps > 60000) amps -=65535;
        batt_amps = (float) amps/10;      // float  battery amps
        if (cstate[k] == 3 ) endamps = batt_amps;  // ABSORB so set end amps
        itoa (amps, wbastr, 10);          // string battery amps
        i = strlen(wbastr);
          // need to insert leading zero if <1 and > -1,  eg .3 becomes 0.3, -.2 becomes -0.2
        if (i==1 || (i==2 && wbastr[0] == '-') )
        { 
            wbastr[i] = wbastr[i-1];
            wbastr[i-1] = '0';
            i++;
        }   
        wbastr[i] = wbastr[i-1];   // now divide by 10 by inserting decimal point
        wbastr[i-1] = '.';     
        wbastr[i+1] = '\0';
       
      // wbjr battery state of charge %
        wbjr_soc = wbjrRegs[12];          // 4372
       
        // record high/low SOC % for day log
       if (wbjr_soc > high_soc ) high_soc = wbjr_soc;
       if (low_soc == 0 || wbjr_soc < low_soc ) low_soc = wbjr_soc;
       
      // wbjr net Amp hours charge
        wbjr_ahr = wbjrRegs[8];           // Net Ah
        // record high/low net amp  hours for day log
       if (wbjr_ahr > high_ahr ) high_ahr = wbjr_ahr;
       if (low_ahr == 0 || wbjr_ahr < low_ahr ) low_ahr = wbjr_ahr;
       
      // wbjr Amp hours left in battery
        wbjr_ra = wbjrRegs[16];           // Remining Ah
        // record high/low remaining amp hours for day log
        if (wbjr_ra > high_ra ) high_ra = wbjr_ra;
        if (low_ra == 0 || wbjr_ra < low_ra ) low_ra = wbjr_ra;

     }
   
     //  save a new 10 minute log file record in memory arrays 
     //  log records are stored in a set of memory arrays, enough for MAX_LOG_COUNT entries with time
     //  of 10 minutes between each  The arrays are cyclic and a log_index is
     //  kept of where the last log record is stored. They are eventually display starting at latest and
     //  going backwards down dispay screen 
     timenow = millis();
     // millis may have cycled back to zero after 50 days so reset start_count as well
     if (timenow < start_count) start_count = 0;  // so there may be one 10min+ time gap
     if ((timenow - start_count) > (unsigned long)LOG_INTERVAL)
     {
         start_count = timenow;
         if (log_index >= MAX_LOG_COUNT ) log_index=0;  // back to start of log array
         log_mins[log_index] = ctime_mins[k];         
         log_hrs[log_index] = ctime_hrs[k];
         
         // now store all current data items
         log_battvolts[log_index] = batt_volts;
         log_battamps[log_index] = batt_amps;     
         log_watts[log_index] = watts[k];
         log_state[log_index] = cstate[k];       
         log_involts[log_index] = in_volts[k];
         log_outamps[log_index] = out_amps[k];
         log_soc[log_index] = wbjr_soc;
         log_ahremain[log_index] = wbjr_ra;
         strcpy (log_kwh[log_index], kwhstr[k]);
         strcpy (lastkwhr, kwhstr[k]);  // for the day log report
         log_ahr[log_index] = wbjr_ahr;
         last_index = log_index;
         log_index++;
       // end of writing new log record
       // now save last KWHr string, day and month, for Day log record
       // (because Classic clears Kwh to zero at midnite and day/month moves forward
         if (last_day == 0 )last_day = ctime_day[k];
         if (last_month == 0) last_month = ctime_mth[k];
         if (ctime_hrs[k] > 0 ) // so as not to clear them when Classic reset times at midnie
         {                      // saved float/absorb times will reset here at 1am
             float_hrs = fl_hrs[k];        // save float time for day log           
             float_mins = fl_mins[k];
             absorb_hrs = 1 - abs_hrs[k];       // for 2 hour max time absorb           
             absorb_mins = 60 - abs_mins[k];    // need to revisit this for EA terminated absorb
             if (absorb_mins == 60)             // and different than 2hr abs time settings
             {
               absorb_mins=0;
               absorb_hrs++;
             }

         }

     // daylog time testing done within 10 minute log time logic to reduce unnecessary
     // testing in main loop     
     // save a day log entry for yesterday just after midnight and reset highwatts
     // value/timestamp.
         if ( ctime_hrs[k] == 0 && ctime_mins[k] < 10 )   // rolled over to next day
         {       
            dlog_day[dlog_index] = last_day;     // the day of the month 1-31   
            dlog_mth[dlog_index] = last_month;     // the month of the year 1-12   
         // now store day data items
            dlog_ea[dlog_index] = endamps;            // the EA vale of last ABSORB state
            strcpy (dlog_kwh[dlog_index], lastkwhr);   // day's kwh reading
            dlog_hwts[dlog_index] = highwatts[k];
            dlog_hbv[dlog_index] = highvolts;
            dlog_lbv[dlog_index] = lowvolts;
            dlog_hsoc[dlog_index] = high_soc;
            dlog_lsoc[dlog_index] = low_soc;       
            dlog_ham[dlog_index] = high_ra;
            dlog_lam[dlog_index] = low_ra;     
            dlog_hahr[dlog_index] = high_ahr;
            dlog_lahr[dlog_index] = low_ahr;
            dlog_flhr[dlog_index] = float_hrs;        // save float/absorb times for day log           
            dlog_flmin[dlog_index] = float_mins;
            dlog_abhr[dlog_index] = absorb_hrs;                 
            dlog_abmin[dlog_index] = absorb_mins;

         // so clear highwatts value and its datestamp
            last_weekday[k] = ctime_weekday[k];
            highwatts[k] = 0;
            ctimestr[k][0] = '\0';                  // clear by nulling the string 
         // move array pointer dlog_index forward, roll back to zero if too large and reset variables
            dlast_index = dlog_index;
            dlog_index++;                             // for next dlog entry in array
            if (dlog_index >= MAX_DLOG_COUNT ) dlog_index=0;  // loop back to start of day log array
            endamps = 0;
            lowvolts = 0;
            highvolts = 0;
            low_soc = high_soc = 0;       
            high_ahr = low_ahr = 0;     
            high_ra = low_ra = 0;
            last_day = last_month = 0;
         } // end of day logging
     }
     //end of 10min and day logging
     
} // thats all the modbus registers moved and log file entries completed


void loop()
{
  char  c;
  int   i,j,k, amps;

/*
   Use millis() time counting for changing between Classics being processed by the modbus state machine.
   Each change just pokes the harware serial parameter and re-executes modbus_configure/modbus_update.
   Not perfect but seems to work, may take an iteration or three to get the correct readings in each CLassic's
   display panel, modbus ugly timing is the problem.
   Proper solution is to rewrite less sophisticated modbus lib and execute one modbus manager for each
   serial port - using the DUE scheduler would work well for this method     
     
*/
   prev_classic = current_classic;

/* for Analogue gauge displays only one Classic is processed on Serial port 0 and CLASSICS=1 defined at top
   Code for processing up to 4 Classics/KIDS left here for future design (and its more bother to remove it)
*/
/*
   // Move to next serial port for input if time counter used up ie swap to next Classic
   timenow = millis();
   if ((timenow - start_count) > (unsigned long)CLTIME)
   {
     start_count = timenow;
     current_classic++;
     if (current_classic >= CLASSICS) current_classic = 0;
     switch (current_classic)
     {
       case 0:                        // serial port order could be changed if desired
          serialPort = &Serial;      // eg if there is only ever one Classic on &Serial then make it first
          break;
       case 1:
          serialPort = &Serial2;
          break;
       case 2:
          serialPort = &Serial3;
          break;
       case 3:
          serialPort = &Serial1;     // hardware serial 0
          break;
     }
     if (current_classic != prev_classic )  // if only one classic then don't reconfigure
     {
       modbus_configure(serialPort, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
       modbus_update();  // to start the reconfigure
//       delay(10);
     }
   }
*/
  read_registers();  //does modbus_update, converts and stores register values
 
  // listen for incoming clients
      EthernetClient client = server.available();  // try to get client

    if (client)
    {  // got client?
        boolean currentLineIsBlank = true;
        while (client.connected())
        {
            read_registers();
            if (client.available())
            {   // client data available to read
                char c = client.read(); // read 1 byte (character) from client
                // buffer first part of HTTP request in HTTP_req array (string)
                // leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
                if (req_index < (REQ_BUF_SZ - 1)) {
                    HTTP_req[req_index] = c;          // save HTTP request character
                    req_index++;
                }
                // last line of client request is blank and ends with \n
                // respond to client only after last line received
                if (c == '\n' && currentLineIsBlank)
                {
                    // send a standard http response header
                    client.println(F("HTTP/1.1 200 OK"));
                    // remainder of header follows below, depending on if
                    // web page or XML page is requested
                    // Ajax request - send appropriate XML file
                    if (StrContains(HTTP_req, "ajax_inputs") || StrContains(HTTP_req, "log"))
                    {
                        // send rest of HTTP header
                        client.println(F("Content-Type: text/xml"));
                        client.println(F("Connection: keep-alive"));
                        client.println(F("<?xml version = \"1.0\" ?>"));
                        client.println();
                        if (StrContains(HTTP_req, "ajax_inputs"))
                           XML_response(client);  // 1sec data XML for Canv gauges
                        else
                        {
                          if (StrContains(HTTP_req, "CLlog")) // send 10min log XML file
                             XML_CLlog(client);
                          else  // send day log XML file
                             XML_Daylog(client);
                        }
                    }
                    else
                    {  // web page or image request
                           if (StrContains(HTTP_req, "GET / ")
                                 || StrContains(HTTP_req, "GET /index.htm")
                                 || StrContains(HTTP_req, "node14.htm")
                                 || StrContains(HTTP_req, "node17.htm"))
                           {
                             // send rest of HTTP header
                             client.println(F("Content-Type: text/html"));
                             client.println(F("Connection: keep-alive"));
                             client.println();
                            // send web page
                             if (StrContains(HTTP_req, "node14.htm"))
                                webFile = SD.open("node14.htm");
                             else
                             {  if (StrContains(HTTP_req, "node17.htm"))
                                   webFile = SD.open("node17.htm");
                                else
                                   webFile = SD.open("index.htm");        // open web page file
                             }
                             if (webFile)
                             {
                               while(webFile.available())
                               {
                                client.write(webFile.read()); // send web page to client
                               }
                              webFile.close();
                             }
                           }
                           else
                           {
                             if (StrContains(HTTP_req, "GET /sasa.jpg"))
                             {
                               client.println(F("Content-Type: image/jpeg"));
                               client.println();
                               webFile = SD.open("sasa.jpg");
                               if (webFile)
                               {
                                 byte clientBuf[64];
                                 int clientCount = 0;

                                 while(webFile.available())
                                 {
                                   clientBuf[clientCount] = webFile.read();
                                   clientCount++;
                                   if(clientCount > 63)
                                   {
                                     // Serial.println("Packet");
                                     client.write(clientBuf,64);
                                     clientCount = 0;
                                   }
                                 }  // webfile available
                               //final <64 byte cleanup packet
                                 if(clientCount > 0) client.write(clientBuf,clientCount);           
                               // close the file:
                                 webFile.close();
                               } // if webfile
                             } // if sasa.jpg
                           } //else
                       
                    }
                    // display received HTTP request on serial port (debugging only)
                    // reset buffer index and all buffer elements to 0
                    req_index = 0;
                    StrClear(HTTP_req, REQ_BUF_SZ);
                    break;
                }
                // every line of text received from the client ends with \r\n
                if (c == '\n')
                {
                    // last character on line of received text
                    // starting new line with next character read
                    currentLineIsBlank = true;
                }
                else if (c != '\r') {
                    // a text character was received from client
                    currentLineIsBlank = false;
                }
            } // end if (client.available())
        } // end while (client.connected())
        delay(1);      // give the web browser time to receive the data
        client.stop(); // close the connection
    } // end if (client)
}

Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 22, 2015, 04:03:03 PM
The next development of this gauges web page is to have the gauge setup automated where possible.
The input voltage range set to match the Classic model, the battery voltage range to match 12,24 or 48 battery bank voltage and dynamic adjusting of the WBjr amps range and the watts input range.
The index.htm web page listed above just defines the gauges in html code but a better option is in JavaScript api so that a complete gauge can be redrawn as needed.

Now also have some nice background graphics and for the KID version have used a nice semi transparent image of a black KID with a dimly glowing blue lcd and LEDs. These don't do anything as the gauges simply overlay the image although the Kid by Mario scripted text shows through nicely.
Too much fun messing with images, need to get to the Cubie2  8)

Dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on April 26, 2015, 07:58:16 AM
Gauge display looks a little nicer now. Modified arduino sketch now deals with request from browser to load a jpg file so I copied sasa.jpg to the SD, adjusted the html css for background image control, changed the svg panel colours and transparency and arrived at the display below.
Also messed about with transparency on the meters, changed face colours, text and tick colours but in end went back to non transparent white faces with hints of colour except the battery voltage with stronger colours. Of course any jpg image could be used for the background.

Now to wrap up the Arduino black box server I just want to get the DUE working with up to 4 Classics and/or Kids and figure out what a possible gauges and svg panels web page should look like.
The gauges now run with 1 to 1.5 second updating after reviewing code to remove some delays from the modbus lib

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on May 02, 2015, 11:49:09 PM
After some experimenting with the SimpleModbusMaster library its more obvious that it is not really very suitable for dealing with multiple rs232 connections - its really designed to deal with an rs485 bus with attached devices identified by unique device ID numbers.
My earlier method of just rerunning the configure with a different serial port specified then waiting a few seconds before moving to the next port, etc in a round robin manner, is just not going to work  for a realtime gauge type display.
So the solution has been to not use SimpleModbusMaster but just take out the bits needed and use a. configure that does not specify the serial port. Instead have the port as part of the packet string so that one call to update_modbus reads packets as needed from all rs232 attached Classics.
This appears to work quite well with under 1 second retreival time of data from 3 Classics. This, in turn, gets a gauge refresh time of about 1.3 seconds which looks pretty close to real time.

The Canv gauge also looks possible to modify. I looked at a double gauge with left and right sections each  with its own needle and value ticks, looks nice but gets weird with 4 Classics on a quarter of a gauge each. I sort of abandoned the multineedle gauge as the best looking display I could make for 4 Classics, was two double gauges for input volts or output amps. Just didn't look right either. Far too many gauges on one screen.

The best looking gauge screen (for me) was just Battery volts, Watts, WBjr amps in 3 qauges beside each other then a separate svg panel for the battery info and then one for each Classic. Radio buttons allows the Watts gauge to display either total watts being output or the watts from any of the Classics. Other data: input volts, output amps, kwh, temps, charging state were text and numbers in the svg panel.
Images below soon  :)

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on May 08, 2015, 07:05:00 PM
Now have several different web page display options on SD card, same Arduino code with Ajax updating  of display data fields just different loaded web page.
Justgage gauges for a different look  :)

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on May 13, 2015, 07:28:23 AM
..looking at some methods to display multiple gauges for the 2 to 4 controllers  rs232 interfaced to the DUE.
this one looked interesting with overlaid gauges and mousing over the top of a gauge brought it to the surface.
All rendered in HTML5 canvas so only the web page changes, the same XML is provided from the DUE server code.
These Rchart gauges can be configured for LHS or RHS half display so its easy, but impractical, to make a porsche 911 dash lookalike  :P    several images below of different gauges on surface

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on May 17, 2015, 08:11:00 AM
An updated version of the index.htm file stored on SD card that is the web code served by the Arduino DUE or Mega with the five canv gauges and svg panel for one Classic with Wbjr fitted.
This code now works with most browsers - Firefox, IE, Safari, Chrome etc.. I have tested it on several PCs, Apple Ipad, Iphone and various pads and phones using Android versions.

There should also be a background image file named sasa.jpg on the SD card. This can be any jpg image.

dgd


<!DOCTYPE html>
<html>
    <head>
        <title>Classic Canv gauges</title>
        <script>
var batV_val = 22;
var batA_val  = 0;
                var Cwatts_val = 0;
var Cvin_val = 0;
var Camps_val = 0;

<!--- Start of gauge code  --->
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};
while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('t W=v(f){W.2t.3T(A);A.B={Z:2u,19:1q,1h:1q,1J:U,1b:D,K:0,V:[\'0\',\'20\',\'40\',\'2A\',\'2B\',\'D\'],2r:10,2C:M,1E:U,2q:{2D:3,2E:2},2H:M,1c:{2j:10,2m:3y,2O:\'3v\'},J:{2P:\'#4h\',V:\'#31\',2r:\'#3k\',1J:\'#37\',1E:\'#37\',3e:\'#31\',1t:{2s:\'1e(3Y, 3d, 3d, 1)\',3c:\'1e(1Y, 5I, 5E, .9)\'}},1o:[{1n:20,1D:2A,1F:\'#3j\'},{1n:2A,1D:2B,1F:\'#36\'},{1n:2B,1D:D,1F:\'#5n\'}]};t g=0,1p=A,N=0,1S=0,1G=U;A.5d=v(a){N=f.1c?g:a;t b=(f.1b-f.K)/D;1S=a>f.1b?1S=f.1b+b:a<f.K?f.K-b:a;g=a;f.1c?3l():A.1g();C A};A.3m=v(a){N=g=a;A.1g();C A};A.4T=v(){g=N=1S=A.B.K;A.1g();C A};A.4R=v(){C g};A.13=v(){};v 2k(a,b){Q(t i 4P b){z(1H b[i]=="1W"&&!(4O.4y.2V.4p(b[i])===\'[1W 4n]\')&&i!=\'Z\'){z(1H a[i]!="1W"){a[i]={}}2k(a[i],b[i])}O{a[i]=b[i]}}};2k(A.B,f);A.B.K=1R(A.B.K);A.B.1b=1R(A.B.1b);f=A.B;N=g=f.K;z(!f.Z){4m 4j("4g 4d 4b 46 44 41 3Z 3W W 1W!");}t j=f.Z.5K?f.Z:2R.5v(f.Z),q=j.3u(\'2d\'),1i,1y,1A,14,17,u,1d;v 2M(){j.19=f.19;j.1h=f.1h;1i=j.4s(M);1d=1i.3u(\'2d\');1y=j.19;1A=j.1h;14=1y/2;17=1A/2;u=14<17?14:17;1i.2J=U;1d.3P(14,17);1d.G();q.3P(14,17);q.G()};2M();A.4Z=v(a){2k(A.B,a);2M();A.1g();C A};t k={4q:v(p){C p},4e:v(p){C E.1L(p,2)},4c:v(p){C E.1L(p,5)},3v:v(p){C 1-E.1O(E.5C(p))},5k:v(p){C 1-(v(p){Q(t a=0,b=1;1;a+=b,b/=2){z(p>=(7-4*a)/11){C-E.1L((11-6*a-11*p)/4,2)+E.1L(b,2)}}})(1-p)},4S:v(p){C 1-(v(p){t x=1.5;C E.1L(2,10*(p-1))*E.1T(20*E.1a*x/3*p)})(1-p)}};t l=2u;v 3S(d){t e=2v 3R;l=2x(v(){t a=2v 3R-e,1M=a/d.2m;z(1M>1){1M=1}t b=1H d.2g=="v"?d.2g:k[d.2g];t c=b(1M);d.3Q(c);z(1M==1){2b(l)}},d.2j||10)};v 3l(){l&&2b(l);t b=(1S-N),1n=N,29=f.1c;3S({2j:29.2j,2m:29.2m,2g:29.2O,3Q:v(a){N=1R(1n)+b*a;1p.1g()}})};q.5l="3O";A.1g=v(){z(!1i.2J){1d.3M(-14,-17,1y,1A);1d.G();t a=q;q=1d;3L();3K();3I();3H();3F();3D();3z();1i.2J=M;q=a;5G a}q.3M(-14,-17,1y,1A);q.G();q.4a(1i,-14,-17,1y,1A);z(!W.28){t b=2x(v(){z(!W.28){C}2b(b);2K();2L();z(!1G){1p.13&&1p.13();1G=M}},10)}O{2K();2L();z(!1G){1p.13&&1p.13();1G=M}}C A};v S(a){C a*E.1a/4J};v 1l(a,b,c){t d=q.4Y(0,0,0,c);d.1V(0,a);d.1V(1,b);C d};v 3L(){t a=u/D*5g,3x=u-a,2a=u/D*5q,5u=u-2a,1f=u/D*5z,5A=u-1f;3t=u/D*5F;q.G();z(f.2H){q.2o=3x;q.2n=\'1e(0, 0, 0, 0.5)\'}q.P();q.16(0,0,a,0,E.1a*2,M);q.L=1l(\'#42\',\'#43\',a);q.T();q.R();q.P();q.16(0,0,2a,0,E.1a*2,M);q.L=1l(\'#49\',\'#36\',2a);q.T();q.P();q.16(0,0,1f,0,E.1a*2,M);q.L=1l(\'#3j\',\'#3s\',1f);q.T();q.P();q.16(0,0,3t,0,E.1a*2,M);q.L=f.J.2P;q.T();q.G()};v 3H(){t r=u/D*2T;q.2e=2;q.2U=f.J.V;q.G();Q(t i=0;i<f.V.H;++i){t a=45+i*(1U/(f.V.H-1));q.1z(S(a));q.P();q.1K(0,r);q.F(0,r-u/D*15);q.1X();q.R();q.G()}z(f.2C){q.1z(S(2X));q.P();q.16(0,0,r,S(45),S(4N),U);q.1X();q.R();q.G()}};v 3I(){t r=u/D*2T;q.2e=1;q.2U=f.J.2r;q.G();t b=f.2r*(f.V.H-1);Q(t i=0;i<b;++i){t a=45+i*(1U/b);q.1z(S(a));q.P();q.1K(0,r);q.F(0,r-u/D*7.5);q.1X();q.R();q.G()}};v 3F(){t r=u/D*55;Q(t i=0;i<f.V.H;++i){t a=45+i*(1U/(f.V.H-1)),p=1w(r,S(a));q.1x=20*(u/1q)+"2i 2Y";q.L=f.J.3e;q.2e=0;q.2h="2f";q.27(f.V[i],p.x,p.y+3)}};v 3D(){z(!f.1J){C}q.G();q.1x=24*(u/1q)+"2i 2Y";q.L=f.J.1J;q.2h="2f";q.27(f.1J,0,-u/4.25);q.R()};v 3z(){z(!f.1E){C}q.G();q.1x=22*(u/1q)+"2i 2Y";q.L=f.J.1E;q.2h="2f";q.27(f.1E,0,u/3.25);q.R()};v 32(a){t b=f.2q.2E,34=f.2q.2D;a=1R(a);t n=(a<0);a=E.35(a);z(b>0){a=a.5t(b).2V().1j(\'.\');Q(t i=0,s=34-a[0].H;i<s;++i){a[0]=\'0\'+a[0]}a=(n?\'-\':\'\')+a[0]+\'.\'+a[1]}O{a=E.3O(a).2V();Q(t i=0,s=34-a.H;i<s;++i){a=\'0\'+a}a=(n?\'-\':\'\')+a}C a};v 1w(r,a){t x=0,y=r,1O=E.1O(a),1T=E.1T(a),X=x*1T-y*1O,Y=x*1O+y*1T;C{x:X,y:Y}};v 3K(){q.G();t a=u/D*2T;t b=a-u/D*15;Q(t i=0,s=f.1o.H;i<s;i++){t c=f.1o[i],39=(f.1b-f.K)/1U,1P=S(45+(c.1n-f.K)/39),1N=S(45+(c.1D-f.K)/39);q.P();q.1z(S(2X));q.16(0,0,a,1P,1N,U);q.R();q.G();t d=1w(b,1P),3a=1w(a,1P);q.1K(d.x,d.y);q.F(3a.x,3a.y);t e=1w(a,1N),3b=1w(b,1N);q.F(e.x,e.y);q.F(3b.x,3b.y);q.F(d.x,d.y);q.1C();q.L=c.1F;q.T();q.P();q.1z(S(2X));q.16(0,0,b,1P-0.2,1N+0.2,U);q.R();q.1C();q.L=f.J.2P;q.T();q.G()}};v 2L(){t a=u/D*12,1f=u/D*8,1u=u/D*3X,1r=u/D*20,2l=u/D*4,1B=u/D*2,38=v(){q.3f=2;q.3g=2;q.2o=10;q.2n=\'1e(5L, 3h, 3h, 0.45)\'};38();q.G();z(N<0){N=E.35(f.K-N)}O z(f.K>0){N-=f.K}O{N=E.35(f.K)+N}q.1z(S(45+N/((f.1b-f.K)/1U)));q.P();q.1K(-1B,-1r);q.F(-2l,0);q.F(-1,1u);q.F(1,1u);q.F(2l,0);q.F(1B,-1r);q.1C();q.L=1l(f.J.1t.2s,f.J.1t.3c,1u-1r);q.T();q.P();q.F(-0.5,1u);q.F(-1,1u);q.F(-2l,0);q.F(-1B,-1r);q.F(1B/2-2,-1r);q.1C();q.L=\'1e(1Y, 1Y, 1Y, 0.2)\';q.T();q.R();38();q.P();q.16(0,0,a,0,E.1a*2,M);q.L=1l(\'#3s\',\'#36\',a);q.T();q.R();q.P();q.16(0,0,1f,0,E.1a*2,M);q.L=1l("#47","#48",1f);q.T()};v 3i(x,y,w,h,r){q.P();q.1K(x+r,y);q.F(x+w-r,y);q.23(x+w,y,x+w,y+r);q.F(x+w,y+h-r);q.23(x+w,y+h,x+w-r,y+h);q.F(x+r,y+h);q.23(x,y+h,x,y+h-r);q.F(x,y+r);q.23(x,y,x+r,y);q.1C()};v 2K(){q.G();q.1x=40*(u/1q)+"2i 30";t a=32(g),2Z=q.4f(\'-\'+32(0)).19,y=u-u/D*33,x=0,2W=0.12*u;q.G();3i(-2Z/2-0.21*u,y-2W-0.4i*u,2Z+0.3n*u,2W+0.4k*u,0.21*u);t b=q.4l(x,y-0.12*u-0.21*u+(0.12*u+0.3o*u)/2,u/10,x,y-0.12*u-0.21*u+(0.12*u+0.3o*u)/2,u/5);b.1V(0,"#37");b.1V(1,"#3k");q.2U=b;q.2e=0.3n*u;q.1X();q.2o=0.3p*u;q.2n=\'1e(0, 0, 0, 1)\';q.L="#4o";q.T();q.R();q.3f=0.3q*u;q.3g=0.3q*u;q.2o=0.3p*u;q.2n=\'1e(0, 0, 0, 0.3)\';q.L="#31";q.2h="2f";q.27(a,-x,y);q.R()}};W.28=U;(v(){t d=2R,h=d.3r(\'4r\')[0],2S=4t.4u.4v().4w(\'4x\')!=-1,2Q=\'4z://4A-4B.4C/4D/4E/4F-7-4G.\'+(2S?\'4H\':\'4I\'),1I="@1x-4K {"+"1x-4L: \'30\';"+"4M: 2Q(\'"+2Q+"\');"+"}",1s,r=d.3w(\'1v\');r.2N=\'1I/4Q\';z(2S){h.2p(r);1s=r.2I;1s.3A=1I}O{4U{r.2p(d.4V(1I))}4W(e){r.3A=1I}h.2p(r);1s=r.2I?r.2I:(r.4X||d.3B[d.3B.H-1])}t b=2x(v(){z(!d.3C){C}2b(b);t a=d.3w(\'50\');a.1v.51=\'30\';a.1v.52=\'53\';a.1v.1h=a.1v.19=0;a.1v.54=\'56\';a.57=\'.\';d.3C.2p(a);58(v(){W.28=M;a.59.5a(a)},3y)},1)})();W.2t=[];W.2t.5b=v(a){z(1H(a)==\'5c\'){Q(t i=0,s=A.H;i<s;i++){z(A[i].B.Z.18(\'5e\')==a){C A[i]}}}O z(1H(a)==\'5f\'){C A[a]}O{C 2u}};v 3E(a){z(2G.3G){2G.3G(\'5h\',a,U)}O{2G.5i(\'5j\',a)}}3E(v(){v 2F(a){t b=a[0];Q(t i=1,s=a.H;i<s;i++){b+=a[i].1Z(0,1).5m()+a[i].1Z(1,a[i].H-1)}C b};v 3J(a){C a.5o(/^\\s+|\\s+$/g,\'\')};t c=2R.3r(\'5p\');Q(t i=0,s=c.H;i<s;i++){z(c[i].18(\'1k-2N\')==\'5r-5s\'){t d=c[i],B={},1m,w=2c(d.18(\'19\')),h=2c(d.18(\'1h\'));B.Z=d;z(w){B.19=w}z(h){B.1h=h}Q(t e=0,1s=d.3N.H;e<1s;e++){1m=d.3N.5w(e).5x;z(1m!=\'1k-2N\'&&1m.1Z(0,5)==\'1k-\'){t f=1m.1Z(5,1m.H-5).5y().1j(\'-\'),I=d.18(1m);z(!I){2z}5B(f[0]){2y\'J\':{z(f[1]){z(!B.J){B.J={}}z(f[1]==\'1t\'){t k=I.1j(/\\s+/);z(k[0]&&k[1]){B.J.1t={2s:k[0],3c:k[1]}}O{B.J.1t=I}}O{f.5D();B.J[2F(f)]=I}}26}2y\'1o\':{z(!B.1o){B.1o=[]}2w=I.1j(\',\');Q(t j=0,l=2w.H;j<l;j++){t m=3J(2w[j]).1j(/\\s+/),1Q={};z(m[0]&&m[0]!=\'\'){1Q.1n=m[0]}z(m[1]&&m[1]!=\'\'){1Q.1D=m[1]}z(m[2]&&m[2]!=\'\'){1Q.1F=m[2]}B.1o.3T(1Q)}26}2y\'1c\':{z(f[1]){z(!B.1c){B.1c={}}z(f[1]==\'2O\'&&/^\\s*v\\s*\\(/.5H(I)){I=3U(\'(\'+I+\')\')}B.1c[f[1]]=I}26}5J:{t n=2F(f);z(n==\'13\'){2z}z(n==\'V\'){I=I.1j(/\\s+/)}O z(n==\'2C\'||n==\'2H\'){I=I==\'M\'?M:U}O z(n==\'2q\'){t o=I.1j(\'.\');z(o.H==2){I={2D:2c(o[0]),2E:2c(o[1])}}O{2z}}B[n]=I;26}}}}t g=2v W(B);z(d.18(\'1k-3V\')){g.3m(1R(d.18(\'1k-3V\')))}z(d.18(\'1k-13\')){g.13=v(){3U(A.B.Z.18(\'1k-13\'))}}g.1g()}}});',62,358,'||||||||||||||||||||||||||ctx|||var|max|function||||if|this|config|return|100|Math|lineTo|save|length|attrValue|colors|minValue|fillStyle|true|fromValue|else|beginPath|for|restore|radians|fill|false|majorTicks|Gauge|||renderTo||||onready|CX||arc|CY|getAttribute|width|PI|maxValue|animation|cctx|rgba|r2|draw|height|cache|split|data|lgrad|prop|from|highlights|self|200|rOut|ss|needle|rIn|style|rpoint|font|CW|rotate|CH|pad2|closePath|to|units|color|imready|typeof|text|title|moveTo|pow|progress|ea|sin|sa|hlCfg|parseFloat|toValue|cos|270|addColorStop|object|stroke|255|substr||025||quadraticCurveTo|||break|fillText|initialized|cfg|r1|clearInterval|parseInt||lineWidth|center|delta|textAlign|px|delay|applyRecursive|pad1|duration|shadowColor|shadowBlur|appendChild|valueFormat|minorTicks|start|Collection|null|new|hls|setInterval|case|continue|60|80|strokeTicks|int|dec|toCamelCase|window|glow|styleSheet|i8d|drawValueBox|drawNeedle|baseInit|type|fn|plate|url|document|ie|81|strokeStyle|toString|th|90|Arial|tw|Led|444|padValue||cint|abs|ccc|888|shad|vd|pe|pe1|end|128|numbers|shadowOffsetX|shadowOffsetY|143|roundRect|eee|666|animate|setRawValue|05|045|012|004|getElementsByTagName|f0f0f0|r3|getContext|cycle|createElement|d0|250|drawUnits|cssText|styleSheets|body|drawTitle|domReady|drawNumbers|addEventListener|drawMajorTicks|drawMinorTicks|trim|drawHighlights|drawPlate|clearRect|attributes|round|translate|step|Date|_animate|push|eval|value|the|77|240|creating||when|ddd|aaa|specified||not|e8e8e8|f5f5f5|fafafa|drawImage|was|quint|element|quad|measureText|Canvas|fff|04|Error|07|createRadialGradient|throw|Array|babab2|call|linear|head|cloneNode|navigator|userAgent|toLocaleLowerCase|indexOf|msie|prototype|http|smart|ip|net|styles|fonts|digital|mono|eot|ttf|180|face|family|src|315|Object|in|css|getValue|elastic|clear|try|createTextNode|catch|sheet|createLinearGradient|updateConfig|div|fontFamily|position|absolute|overflow||hidden|innerHTML|setTimeout|parentNode|removeChild|get|string|setValue|id|number|93|DOMContentLoaded|attachEvent|onload|bounce|lineCap|toUpperCase|999|replace|canvas|91|canv|gauge|toFixed|d1|getElementById|item|nodeName|toLowerCase|88|d2|switch|acos|shift|122|85|delete|test|160|default|tagName|188'.split('|'),0,{}))
<!--- End of gauge code --->

function GetArduinomodbus()
{
    nocache = "&nocache=" + Math.random() * 1000000;
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
if (this.readyState == 4)
        {
        if (this.status == 200)
{
if (this.responseXML != null)
{
    batA_val = this.responseXML.getElementsByTagName('bamps')[0].childNodes[0].nodeValue;
    batV_val = this.responseXML.getElementsByTagName('bvolts')[0].childNodes[0].nodeValue;
    Cvin_val = this.responseXML.getElementsByTagName('cvin')[0].childNodes[0].nodeValue;
    Cwatts_val = this.responseXML.getElementsByTagName('cwatts')[0].childNodes[0].nodeValue;
    Camps_val = this.responseXML.getElementsByTagName('camps')[0].childNodes[0].nodeValue;

    document.getElementById("CL_NAME").textContent =
      this.responseXML.getElementsByTagName('clname')[0].childNodes[0].nodeValue;
    document.getElementById("CL_MODEL").textContent =
      this.responseXML.getElementsByTagName('clmodel')[0].childNodes[0].nodeValue;
    document.getElementById("CL_SERNO").textContent =
      this.responseXML.getElementsByTagName('clserno')[0].childNodes[0].nodeValue;

    document.getElementById("CL_STATE").textContent =
      this.responseXML.getElementsByTagName('clstate')[0].childNodes[0].nodeValue;
    document.getElementById("CL_KWH").textContent =
      this.responseXML.getElementsByTagName('clkwh')[0].childNodes[0].nodeValue;
    document.getElementById("CL_AH").textContent =
      this.responseXML.getElementsByTagName('bah')[0].childNodes[0].nodeValue;
    document.getElementById("CL_AHR").textContent =
      this.responseXML.getElementsByTagName('bahr')[0].childNodes[0].nodeValue;

    document.getElementById("CL_SOC").textContent =
      this.responseXML.getElementsByTagName('bsoc')[0].childNodes[0].nodeValue;

    document.getElementById("Fet_Temp").textContent =
      this.responseXML.getElementsByTagName('fettemp')[0].childNodes[0].nodeValue;
    document.getElementById("Pcb_Temp").textContent =
      this.responseXML.getElementsByTagName('pcbtemp')[0].childNodes[0].nodeValue;
    document.getElementById("Bat_Temp").textContent =
      this.responseXML.getElementsByTagName('battemp')[0].childNodes[0].nodeValue;
    document.getElementById("Rem_Temp").textContent =
      this.responseXML.getElementsByTagName('remtemp')[0].childNodes[0].nodeValue;

    document.getElementById("Peak_Pwr").textContent =
      this.responseXML.getElementsByTagName('peakpwr')[0].childNodes[0].nodeValue;
    document.getElementById("Peak_Time").textContent =
      this.responseXML.getElementsByTagName('peaktime')[0].childNodes[0].nodeValue;

}
}
}
    }
    request.open("GET", "ajax_inputs" + nocache, true);
    request.send(null);
    setTimeout('GetArduinomodbus()', 500);
}
</script>
        <style>
          html {
            background: url(sasa.jpg) no-repeat center center fixed;
            background-size: cover;
          }

          body {
            color: white;
          }
        </style>

    </head>
    <body onload="GetArduinomodbus()">
    <table>
    <tr>
    <td width="100"></td>
    <td>
    </canvas>
    <canvas id="CV_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="Input"
               data-major-ticks="0 30 60 90 120 150"
               data-minor-ticks="6"           
               data-units="Volts"
               data-value-format="3.1"
               data-max-value="150"
               data-min-value="0"
               data-highlights="0 150 #ffe"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('CV_gauge').setValue(Cvin_val);
},500);">
    </canvas>
    </td>
    <td>
    </canvas>
    <canvas id="CW_gauge" width="450" height="450"
               data-type="canv-gauge"
               data-title="Power"
               data-major-ticks="0 250 500 750 1000 1250 1500 1750 2000"
               data-minor-ticks="5"           
               data-units="Watts"
               data-value-format="4.0"
               data-max-value="2000"
               data-min-value="0"
               data-color-plate="#0FF"
               data-highlights="0 2000 #FFe"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('CW_gauge').setValue(Cwatts_val);
},500);">
    </canvas>
    </td>

    <td>
    </canvas>
    <canvas id="Ca_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="Output"
               data-major-ticks="0 10 20 30 40 50 60 70 80 90 100"
               data-minor-ticks="4"           
               data-units="Amps"
               data-value-format="2.1"
               data-max-value="100"
               data-min-value="0"
               data-highlights="0 100 #ebffd6 "
               data-onready="setInterval( function ()
{ Gauge.Collection.get('Ca_gauge').setValue(Camps_val);
},500);">
    </canvas>
    </td>
    </tr>
</table>

<table>
<tr>
    <td width="100"></td>
    <td>
    </td>
    <td>
    <canvas id="BV_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="Battery"
               data-major-ticks="22 23 24 25 26 27 28 29 30 31"
               data-minor-ticks="5"           
               data-units="Volts"
               data-value-format="2.1"
               data-max-value="31"
               data-min-value="22"
               data-highlights="22 23 #F00, 23 24 #ff1, 24 29 #0F1, 29 30 #ff1, 30 31 #F02"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('BV_gauge').setValue(batV_val);
},500);">
    </canvas>
    </td>
    <td>
  <svg width="450" height="350">
  <defs>
    <radialGradient id="gradsq" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:0" />
      <stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
    </radialGradient>
    <filter id="filter" x="0" y="0">
      <feGaussianBlur stdDeviation="5" />
      <feOffset dx="5" dy="5" />
    </filter>
  </defs>

  <rect x="10" y="20" rx="20" ry="20" width="430" height="330" style="fill:blue;stroke:black;stroke-width:5;opacity:0.8" />
  <text font-size="20" font-family="Verdana" x="25" y="56" fill="white">
          <tspan id="CL_NAME">........</tspan> C<tspan id="CL_MODEL">...</tspan> #<tspan id="CL_SERNO"></tspan>
          </text>
 
  <text id="CL_STATE" font-size="25" font-family="Verdana" x="25" y="96" fill="white"></text>

  <text font-size="20" font-family="Verdana" x="25" y="131" fill="white">Energy kwh <tspan id="CL_KWH">...</tspan>    Battery SOC <tspan id="CL_SOC">...</tspan>%</text>
   <text font-size="20" font-family="Verdana" x="25" y="166" fill="white">Battery Ah Net <tspan id="CL_AH">.....</tspan> remaining      <tspan id="CL_AHR">....</tspan></text>
   
  <text font-size="20" font-family="Verdana" x="25" y="201" fill="white">Temps oC</text>
  <text font-size="18" font-family="Verdana" x="150" y="201" fill="white">Bat <tspan id="Bat_Temp">....</tspan></text>
  <text font-size="18" font-family="Verdana" x="275" y="201" fill="white">Fet <tspan id="Fet_Temp">....</tspan></text>
  <text font-size="18" font-family="Verdana" x="150" y="236" fill="white">Pcb <tspan id="Pcb_Temp">....</tspan></text>
  <text font-size="18" font-family="Verdana" x="275" y="236" fill="white">Rem <tspan id="Rem_Temp">....</tspan></text>

  <text font-size="20" font-family="Verdana" x="25" y="271" fill="white">Peak Power <tspan id="Peak_Pwr">....</tspan> watts at <tspan      id="Peak_Time">........</tspan>
         </text>

  <a xlink:href="node14.htm">
  <rect x="25" y="300" rx="5" ry="5" width="80" height="30" style="fill:yellow;stroke:black;stroke-width:2;opacity:1" />
  <text class="text" font-size="20" x="35" y="322"  fill="blue" text-anchor="start">10mlog</text>
  </a>
  <a xlink:href="node17.htm">
  <rect x="125" y="300" rx="5" ry="5" width="80" height="30" style="fill:yellow;stroke:black;stroke-width:2;opacity:1" />
  <text class="text" font-size="20" x="135" y="322"  fill="blue" text-anchor="start">Daylog</text>
  </a>
  <a xlink:href="hchart.htm">
  <rect x="225" y="300" rx="5" ry="5" width="80" height="30" style="fill:yellow;stroke:black;stroke-width:2;opacity:1" />
  <text class="text" font-size="20" x="235" y="322"  fill="blue" text-anchor="start">HChart</text>
  </a>

  Sorry, your browser does not support inline SVG.
    </svg>
    </td>
    <td>

    <canvas id="BA_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="WBjr"
               data-major-ticks="-80 -60 -40 -20 0 20 40 60 80"
               data-minor-ticks="4"           
               data-units="Amps"
               data-value-format="2.1"
               data-max-value="80"
               data-min-value="-80"
               data-colors-plate="#fff"
               data-highlights="-80 0 #ffcccc, 0 80 #ebffd6"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('BA_gauge').setValue(batA_val);
},500);">
    </canvas>
    </td>
</tr>
</table>
</html>
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on May 29, 2015, 05:32:39 PM
Just a coupe of pics and details of the hardware used for the dashboard gauge display.
For connecting to a single Classic with Wbjr installed.

This uses the Arduino Mega 2560 with ethernet/SD card shield and a single port RS232 shield.
http://www.ebay.com/itm/New-ATmega2560-16AU-Improved-version-CH340G-MEGA2560-R3-Board-For-Arduino-OT8G-/221770058429?pt=LH_DefaultDomain_0&hash=item33a2868abd (http://www.ebay.com/itm/New-ATmega2560-16AU-Improved-version-CH340G-MEGA2560-R3-Board-For-Arduino-OT8G-/221770058429?pt=LH_DefaultDomain_0&hash=item33a2868abd)
http://www.ebay.com/sch/i.html?_from=R40&_trksid=p2047675.m570.l1313.TR0.TRC0.H0.Xethernet+shield+Arduino.TRS0&_nkw=ethernet+shield+Arduino&_sacat=0 (http://www.ebay.com/sch/i.html?_from=R40&_trksid=p2047675.m570.l1313.TR0.TRC0.H0.Xethernet+shield+Arduino.TRS0&_nkw=ethernet+shield+Arduino&_sacat=0)
http://www.ebay.com/itm/RS232-Shield-V1-for-Arduino-/321752984988?pt=LH_DefaultDomain_0&hash=item4ae9f8ed9c (http://www.ebay.com/itm/RS232-Shield-V1-for-Arduino-/321752984988?pt=LH_DefaultDomain_0&hash=item4ae9f8ed9c)

These, along with a small 2 relay card, are mounted in an ex-computer power supply case, an aluminum slotted cover design. Makes a nice 'black box' computer  :D
The 2560 on bottom then ethernet/SD shield plugged into it then serial card plugged into ehernet shield.
The rx/tx leds on the serial card are visible through the slots and are continually blinking as serial data is input from the Classic and I like seeing blinky lights  :)
Still need to neaten up the wiring and get it inside a bit of flex conduit.
The two small changeover relays, not connected yet, are to provide two additional AUX ports and a tiny  circuit board, also not installed yet, will bring two PWM outputs to a small terminal block.

The version of this to connect up to four Classics is based on the Arduino DUE and uses a different sketch, multiport serial card and different html code on SD card.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 03, 2015, 12:17:13 AM
Have just updated the index.htm file for Classic canv gauge dashboard that is listed in previous posting.

This now includes two buttons that display further web pages.
The first is a 20 line text list of 10 minute intervals of Classic data, similar to that node14(?) list that was part of the mynidnite testing a couple of years ago
The second is  Highchart graph(s) with selectable graph types and data to graph.
Will post these additional web pages after some more testing. Highcharts is a very easy charting system to include and supports many chart types and configurations.

The Arduino code is also updated to save a 1 minute interval csv file on SD storage, up to several months to a years data in total. Just need to let this build up for a few days/weeks to get enough data to test graph dispays
The web pages display this csv file data.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 10, 2015, 04:58:19 AM
Listed below is the file node14.htm that is stored on SD card in ARduino ethernet card. This is the file loaded to web browser when the rightmost yellow button CLdata is pressed in the window of the gauges display.
It loads a web page showing the text list log of 10 minute data records from Classic

The html and javascript code is not too arduous, it loads data from file CLlog.xml that is created by the Arduino server, extracting runtime data from memory arrays and formatting into XML file format.
Layout uses simple html tables with some css colour and layout definitions.
Data is displayed latest first down list to oldest in lhs table then continues in rhs table.
Will eventually use svg design to make visually nicer.
Web page example as produced by this file several messages below

dgd


<!DOCTYPE html>
<html>
<head>
<style>
table, th, td {
    border: 1px solid black;
    border-collapse:collapse;
    font-family:"Verdana";
}
th, td {
    padding: 5px;
}
caption {
    caption-side: top;
    text-align:left;
    background-color: rgba(128, 255, 255, 0.5);
    font-faamily:"Verdana";
}
body { background-color: #F5F5DC;}  /* beige */
th { background-color: rgba(0, 0, 255, 0.3);}  /* blue with opacity */
td { background-color: rgba(0, 0, 255, 0.1);}  /* blue with opacity */
</style>
</head>

<body>

<title> CLlog.xml test </title>
<script>
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","CLlog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("CLdata");

  document.write("<table><caption>  Ten minute Log records for ");
  document.write(x[0].getElementsByTagName("clname")[0].childNodes[0].nodeValue);
  document.write("  C");
  document.write(x[0].getElementsByTagName("clmodel")[0].childNodes[0].nodeValue);
  document.write("  #");
  document.write(x[0].getElementsByTagName("clserno")[0].childNodes[0].nodeValue);
  document.write("</b></caption><tr><td>");

document.write

("<table><tr><th>Time</th><th>State</th><th>InpV</th><th>KwH</th><th>Watts</th><th>BattV</th><th>OutA</th>");
document.write("<th>BattA</th><th>SoC</th><th>AhRem</th><th>AhNet</th></tr>");

for (i=1;i<x.length;i++)
  {
     if (i == 25)
     {
           document.write("</td></tr></table></td><td valign=\"top\">");
           document.write     

("<table><tr><th>Time</th><th>State</th><th>InpV</th><th>KwH</th><th>Watts</th><th>BattV</th><th>OutA</th>");
           document.write("<th>BattA</th><th>SoC</th><th>AhRem</th><th>AhNet</th></tr>");
     }
  document.write("<tr><td>");
  document.write(x[i].getElementsByTagName("HRS")[0].childNodes[0].nodeValue);
  document.write(":");
  if ( x[i].getElementsByTagName("MINS")[0].childNodes[0].nodeValue.length == 1)
     document.write("0");
  document.write(x[i].getElementsByTagName("MINS")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  switch (x[i].getElementsByTagName("STATE")[0].childNodes[0].nodeValue)
  {
  case '0':
    document.write("Resting");
    break;
  case '3':
    document.write("Absorb");
    break;
  case '4':
    document.write("BulkMppt");
    break;
  case '5':
    document.write("Float");
    break;
  case '6':
    document.write("Float Mppt");
    break;
  case '7':
    document.write("Equalize");
    break;
  case "10":
    document.write("HyperVOC");
    break;
  case "18":
    document.write("Eq Mppt");
    break;
  default:
    document.write("Unknown");
  }
  document.write("</td><td>");
  var val=(x[i].getElementsByTagName("INV")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("INV")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("KWH")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("WATTS")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  var val=(x[i].getElementsByTagName("BATTV")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("BATTV")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");
  var val=(x[i].getElementsByTagName("OUTA")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("OUTA")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");
  var val=(x[i].getElementsByTagName("BATTA")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("BATTA")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("SOC")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("AHREM")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("AHR")[0].childNodes[0].nodeValue);

  document.write("</td></tr>");

  }
document.write("</table></td></tr></table>");
</script>

</body>
</html>


Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on June 10, 2015, 08:24:59 AM
Gosh, I think i have missed a few posts here from you David,
Just caught up and read what I have missed and am impressed with your progress David.
The background image is striking and the way you have used it looks good.
Normally I don't use background images, but this one, I like.

Now, way back you asked me a question, which I haven't yet answered.
QuoteI managed to find one of these Taijiuino DUE boards complete with a programming interface card.
/snip
As you pointed out the Wiznet uses the spi interface, as does the SD card, and even with the latest IDE1.6.3 the drivers appear broken, each works well by itself but using both ethernet and SD card just does not work very well.
So question is - has there been any progress towards completing a DUE ethernet library for the Taijiuino?
The is so much program flash,512k, and ram 96k, that I can get my more advanced (analogue gauges) web interface for Classic into memory and not bother the SD storage. However the SD would be very useful for log file storage.
Unfortunately it has mostly come to a standstill, through circumstance.
Surprisingly, not a lot of people showed interest in having the onboard EMAC put into play with a suitable PHY chip and having the Ethernet library developed for the DUE family.
Coding such a library for this EMAC sub section is a fair sized task and not without some difficulties.
It's a shame really, as being able to use the onboard EMAC on the DUE would bring the DUE into the realms of what a larger computer such as Cubieboard can do in terms of Ethernet connectivity.
It would be fast, very fast and be totally separate from the SD sub section. The library together with this hardware would support a greater number of TCP connections that what the standard Wiznet shield can support.

Yes, with all that extra onboard flash and RAM on the DUE, many exciting things would be possible.

I did come to the realisation not long after our little group was looking at writing the Ethernet library, that for more intensive Ethernet based projects, making use of small GNU/Linux boards was more likely the better way to go. Also, we were going to invest a lot of effort into writing this library for who, and for how long was the DUE going to be around, considering new boards were being launched every other day with more features. And lastly, the DUE, as good as it seems to be, has many, not quite design flaws, but feature flaws, and would possibly see the DUE the odd one out and not so loved as other Arduino boards.

Still, I do love my Taijuino , alongside with my Ether-Mega and Ether-Ten :)

QuoteOr maybe I have reached the limits of the DUE and its time to get to the Cubie2  :)
Yes, for me, both have relevancy, Arduino style boards for that direct low level I/O work and critical timing, much like how we use and put trust into a PLC in an industrial situation. And the Cubieboard II or what ever flavour excite your taste buds, used for actual data processing and connecting with higher level devices and to us humans. :)
_____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: zoneblue on June 11, 2015, 02:46:12 AM
Quote
that for more intensive Ethernet based projects, making use of small GNU/Linux boards was more likely the better way to go.

Have you guys thought about contributing to theblackboxproject instead of redoing everything ive done from scratch? If the svn or anything else is a turnoff, let me i know. Blackbox code is written in a modular way, such that you can easily add support for new devices, multiple devices. Writing a web app in c does seem like an awful chore, when php was designed exactly for that. And php uses a lot of c syntax so easy learning curve.
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 11, 2015, 05:26:42 AM
Quote from: zoneblue on June 11, 2015, 02:46:12 AM
Have you guys thought about contributing to theblackboxproject instead of redoing everything ive done from scratch? If the svn or anything else is a turnoff, let me i know. Blackbox code is written in a modular way, such that you can easily add support for new devices, multiple devices. Writing a web app in c does seem like an awful chore, when php was designed exactly for that. And php uses a lot of c syntax so easy learning curve.

Good point. I want to move my efforts to the cubie2 with Linux, Apache and maybe PHP sometime soon.
The main reason I went off in the Arduino direction was to eventually design and build hardware and software for an embedded processor addon for the Classic Lite that would essentially be a plug in faceplate like the MNGP or MNLP.
Because of the very limited resources such processors have its not feasible to have an OS like linux, featured web server programs such as Apache or have available high density/high speed storage interfaces.
Also the software needed to be tight and small in size and served web pages had to hand off as much processing as possible to the web browser. ALthough I have used the SD card to store and serve web pages from, the pages use javascript for the browser to execute and hence do much of the work, also the pages are so small they coud easily fit in program memory of an embedded cpu.
As you are aware, PHP is a server side processing system with all sorts of good interfacing to databases, files etc and does most of the complicated stuff on the server and presents straight forward html to the web browser - ok thats a bit simplified  :)
The Cubie2 is, IMHO, more of a general data processing system like my asus laptop but with some good lower level interfaces
Little annoyances like not easily getting an OS that boots and loads from hdd are disappointing. ANyway, will get there soon

Sorry for the long reply but there is also another reason for doing all the Arduino stuff, its just damm good fun and C++ is the only real development language.
Also I do appreciate the efforts you have made with the blackbox project, there is some good stuff there and I do look forward to getting involved with it.
dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 11, 2015, 05:42:09 AM
Quote from: paul alting on June 10, 2015, 08:24:59 AM
I did come to the realisation not long after our little group was looking at writing the Ethernet library, that for more intensive Ethernet based projects, making use of small GNU/Linux boards was more likely the better way to go. Also, we were going to invest a lot of effort into writing this library for who, and for how long was the DUE going to be around, considering new boards were being launched every other day with more features. And lastly, the DUE, as good as it seems to be, has many, not quite design flaws, but feature flaws, and would possibly see the DUE the odd one out and not so loved as other Arduino boards.

I can see the logic in this. I just noticed in the last couple of weeks a new Chinese single board Arduino Mega sized PcDuino
system. Arm A20 cpu, flash, gpu, Lots of interfaces, ethernet, SATA, SD, wireless etc and it has an Arduino interface for various shields.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: zoneblue on June 11, 2015, 04:06:03 PM
Youve no doubt heard about the upcoming "9 dollar computer". Its Arm Cortex, and very tiny.  Has wifi but no ethernet, but maybe thats not a major, given the power overheads of ethernet, and the ubiquity of wifi these days.

https://www.kickstarter.com/projects/1598272670/chip-the-worlds-first-9-computer/description

They won 2 mill on their kickstarter, impressive.
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 15, 2015, 08:17:11 AM
replaced node14.html  in previous posting with new version that displays up to 48 ten minute log records in two 24 record side by side tables in one screen. This covers 8 hours of log records and gives a more complete picture of a days power production. Some cosmetic changes too to make it look neater.
Example screen display below..
There is enough screen width left to probably include temperatures in the tables.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 18, 2015, 04:53:14 AM
The second, left button in the Canv gauge display, loads the file bocfet.htm stored on SD card. This file provides several graph report options that use log file data stored in memory data structures in the Arduino Mega or DUE.
In the initial version the data is 10 minute snapshot records of Classic modbus register values, time and date stamped.
The Mega with 8k ram can store near 100 records covering a 16 hour period, however this can extend to the power input periods of nearer two days if zero input power periods are not stored. This only applies to solar input where predictable period of darkness is known. With wind a different approach testing for adjacent periods of zero power and creating a time started/stopped special record is used.
With the DUE there is almost unlimited memory for log records covering a week to 10 days.
Each day after the Classic resets  KwHr to zero the 16 hours, or less, of power input measurements are compressed then stored to a log file bocolog.dat  This file can then be transferred using AJAX as input to a HighStock graph (using another SD file outlog.htm)

html files in following posts with display examples.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 23, 2015, 07:55:10 PM
A Daily log file is kept for 20 days data in Arduino memory, each record stores:
KwHr produced, Total Float time and Absorb time. Absorb EA on transition to Float, min and max BattV , Highest Watts input, plus if WBjr installed then max and min SoC%,  AmpHours remaining and AmpHour Net
   
Text report of actual data uses web page node17.htm, accessed via Daylog  button in Canv gauge screen (index.htm)
Most of the data headings in the text display can be clicked and a HighChart bargraph will display in a new window

dgd

node17.htm

<!DOCTYPE html>
<html>
<head>
<style>
table, th, td {
    border: 1px solid black;
    border-collapse:collapse;
    font-family:"Verdana";
}
th, td {
    padding: 5px;
}
caption {
    caption-side: top;
    text-align:left;
    background-color: rgba(128, 255, 255, 0.5);
    font-faamily:"Verdana";
}

body { background-color: #fffcce;}  /*  */
th { background-color: rgba(0, 128, 255, 0.4);}  /* with opacity */
td { background-color: rgba(0, 120, 255, 0.2);}  /* with opacity */
</style>
</head>

<body>

<title> Day Log Report </title>
<script>
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");
  document.write("<table><caption>  Daily Log records for ");
  document.write(x[0].getElementsByTagName("clname")[0].childNodes[0].nodeValue);
  document.write("  C");
  document.write(x[0].getElementsByTagName("clmodel")[0].childNodes[0].nodeValue);
  document.write("  #");
  document.write(x[0].getElementsByTagName("clserno")[0].childNodes[0].nodeValue);
  document.write("</caption>");

document.write("<tr><th>Date</th><th onclick=\"window.open ('hc110.htm')\">KwH</th><th>Absorb</th><th>Float</th><th onclick=\"window.open ('hc111.htm')\">Absorb</th>");
document.write("<th onclick=\"window.open ('hc112.htm')\" colspan=\"2\">Batt Volts</th><th onclick=\"window.open ('hc109.htm')\">Watts</th><th onclick=\"window.open ('hc113.htm')\" colspan=\"2\">SOC%</th>");
document.write
("<th onclick=\"window.open ('hc114.htm')\" colspan=\"2\">Ahr Rem</th><th colspan=\"2\">Ahr Net</th></tr>");
document.write
("<tr><th>2015</th><th>Total</th><th>hr:mn</th><th>hr:mn</th><th>EndA</th><th>High</th><th>Low</th><th>Max</th>");
document.write ("<th>High</th><th>Low</th><th>High</th><th>Low</th><th>High</th><th>Low</th></tr>");

for (i=1;i<x.length;i++)
{
  document.write("<tr><td>");
  switch (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue)
  {
    case '1':
       document.write("Jan");
       break;
    case '2':
       document.write("Feb");
       break;
    case '3':
       document.write("Mar");
       break;
    case '4':
       document.write("Apr");
       break;
    case '5':
       document.write("May");
       break;
    case '6':
       document.write("Jun");
       break;
    case '7':
       document.write("Jul");
       break;
    case '8':
       document.write("Aug");
       break;
    case '9':
       document.write("Sep");
       break;
    case "10":
       document.write("Oct");
       break;
    case "11":
       document.write("Nov");
       break;
    case "12":
       document.write("Dec");
       break;
  }
  document.write(" ");
  document.write(x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("KWH")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("ABHR")[0].childNodes[0].nodeValue);
  document.write(":");
  document.write(x[i].getElementsByTagName("ABMIN")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("FLHR")[0].childNodes[0].nodeValue);
  document.write(":");
  document.write(x[i].getElementsByTagName("FLMIN")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  var val=(x[i].getElementsByTagName("EA")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("EA")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");

  var val=(x[i].getElementsByTagName("HBV")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("HBV")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");
  var val=(x[i].getElementsByTagName("LBV")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("LBV")[0].childNodes[0].nodeValue.length -1));
  document.write(val);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("HWTS")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("HSOC")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("LSOC")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("HAM")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("LAM")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("HAHR")[0].childNodes[0].nodeValue);
  document.write("</td><td>");
  document.write(x[i].getElementsByTagName("LAHR")[0].childNodes[0].nodeValue); 
  document.write("</td></tr>");

}
document.write("</table>");
</script>
</body>
</html>
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 24, 2015, 07:32:28 AM
From Node17.htm web page, when text lines are displayed then a highchart graph can display in bargraph form the column data with dates
Column headings are clickable and load a Highchart with column data per day (hc109.htm to hc114.htm listed below)

dgd

hc109.htm

<!DOCTYPE html>
<html>
<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/highcharts-3d.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>

<script>

var year = 2015;
var hw = [];
var gdate = [];

$(function () {

    $('#container').highcharts({
        chart: {
            backgroundColor: {
                linearGradient: [0, 0, 500, 500],
                stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(240, 240, 255)']
                    ]
            },

            type: 'column',
            margin: 75,
            options3d: {
                enabled: true,
                alpha: 10,
                beta: 25,
                depth: 70,
                ContextButtonTitle: 'KwHr'
            }
        },
        title: {
            text: 'Peak Watts each day'
        },
        subtitle: {
            text: clname +' C'+ clmodel +' #' + clserno + ' peak Watts'
        },

        plotOptions: {
            column: {
                depth: 25
            }
        },
        xAxis: {
            type: 'datetime',
            dateTimeLabelFormats: {
                day: '%e %b'
            },
            title: null
        },
        yAxis: {
            title: {
                text: 'Power Watts',
            },
            floor: 0,
            ceiling: 2500,
            breaks: [0,  500, 1000, 1500, 2000, 2500]

        },
        series: [{
            name: 'Watts',
            data: hw,
            pointStart: Date.UTC(year, mn-1, day),
            pointInterval: 24 * 3600 * 1000 // one day
        }]
    });
});

</script>
</head>

<body>
<title>High Power Watts</title>

<script>
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");
var clname = (x[0].getElementsByTagName("clname")[0].childNodes[0].nodeValue);
var clmodel = (x[0].getElementsByTagName("clmodel")[0].childNodes[0].nodeValue);
var clserno = (x[0].getElementsByTagName("clserno")[0].childNodes[0].nodeValue);

// scan through XML data records, starting at 1st, for month and day, high and low battery voltages, 0th record
// has Classic ID info so ignore it (might put year in there soon!)
// put data into arrays gdate[] and kwhr[] and pass to Hgraph
// Note XML data comes in reverse order (latest first to oldest last) as needed by Hgraph
// hence array stored in reverse to XML reading order

for (var i=1;i<x.length;i++)  // data starts at 1 as 0'th record is ID info
{
     var mn =  (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue);
     var day = (x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
     gdate[(x.length-i)-1] = [year, day, mn]; 
     var hwt = (x[i].getElementsByTagName("HWTS")[0].childNodes[0].nodeValue);         
     hw[(x.length-i)-1] = [parseInt(hwt)];

}  //for loop
</script>

<! now draw the HighChart >
<div id="container" style="height: 600px; width: 900px; border: 1px solid black;
                           background-color:lightgray">
</div>
</body>



hc110.htm

<!DOCTYPE html>
<html>
<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/highcharts-3d.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>

<script>

var year = 2015;
var kwhr = [];
var gdate = [];

$(function () {
    $('#container').highcharts({
        chart: {
            backgroundColor: {
                linearGradient: [0, 0, 500, 500],
                stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(240, 240, 255)']
                ]
            },

            type: 'column',
            margin: 75,
            options3d: {
                enabled: true,
                alpha: 4,
                beta: 25,
                depth: 70,
                ContextButtonTitle: 'KwHr'
            }
        },
        title: {
            text: 'Total Energy per day '
        },
        subtitle: {
            text: clname +' C'+ clmodel +' #' + clserno + ' KwHr energy output'
        },
        plotOptions: {
            column: {
                depth: 25
            }
        },
        xAxis: {
            type: 'datetime',
            dateTimeLabelFormats: {
                day: '%e %b'
            },
            title: null
        },
        yAxis: {
            title: {
                text: 'Kwatt Hours',
            },
            floor: 0,
            ceiling: 7,
            breaks: [0, 1, 2, 3, 4, 5, 6, 7]

        },
        series: [{
            name: 'KwHrs',
            data: kwhr,
            pointStart: Date.UTC(year, mn-1, day),
            pointInterval: 24 * 3600 * 1000 // one day
        }]
    });
});

</script>
</head>

<body>
<title>Daily Energy</title>

<script>
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");
var clname = (x[0].getElementsByTagName("clname")[0].childNodes[0].nodeValue);
var clmodel = (x[0].getElementsByTagName("clmodel")[0].childNodes[0].nodeValue);
var clserno = (x[0].getElementsByTagName("clserno")[0].childNodes[0].nodeValue);

// scan through XML data records, starting at 1st, for month and day, high and low battery voltages, 0th record
// has Classic ID info so ignore it (might put year in there soon!)
// put data into arrays gdate[] and kwhr[] and pass to Hgraph
// Note XML data comes in reverse order (latest first to oldest last) as needed by Hgraph
// hence array stored in reverse to XML reading order

for (var i=1;i<x.length;i++)  // data starts at 1 as 0'th record is ID info
{
     mn =  (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue);
     day = (x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
     gdate[(x.length-i)-1] = [year, day, mn]; 
     var ener = (x[i].getElementsByTagName("KWH")[0].childNodes[0].nodeValue);         
     kwhr[(x.length-i)-1] = parseFloat(ener);
}  //for loop
</script>

<! now draw the HighChart >
<div id="container" style="height: 600px; width: 900px; border: 1px solid black;
                           background-color:lightgray">
</div>
</body>
</html>



hc111.htm

<!DOCTYPE html>
<html>
<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/highcharts-3d.js"></script>
<script src="http://code.highcharts.com/modules/exporting.js"></script>

<script>

var year = 2015;
var eamps = [];
var gdate = [];

$(function () {

    $('#container').highcharts({
        chart: {
            backgroundColor: {
                linearGradient: [0, 0, 500, 500],
                stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(240, 240, 255)']
                    ]
            },

            type: 'column',
            margin: 75,
            options3d: {
                enabled: true,
                alpha: 10,
                beta: 25,
                depth: 70,
                ContextButtonTitle: 'KwHr'
            }
        },
        title: {
            text: 'Absorb Charge Ending Amps'
        },
        subtitle: {
            text: clname +' C'+ clmodel +' #' + clserno + ' end amps'
        },

        plotOptions: {
            column: {
                depth: 25
            }
        },
        xAxis: {
            type: 'datetime',
            dateTimeLabelFormats: {
                day: '%e %b'
            },
            title: null
        },
        yAxis: {
            title: {
                text: 'Charge Current Amps',
            },
            floor: 0,
            ceiling: 50,
            breaks: [0,  10, 20, 30, 40, 50]

        },
        series: [{
            name: 'Amps',
            data: eamps,
            pointStart: Date.UTC(year, mn-1, day),
            pointInterval: 24 * 3600 * 1000 // one day
        }]
    });
});

</script>
</head>

<body>
<title>High Power Watts</title>

<script>
if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");

// read Classic ID xml fields for chart sub-title
var clname = (x[0].getElementsByTagName("clname")[0].childNodes[0].nodeValue);
var clmodel = (x[0].getElementsByTagName("clmodel")[0].childNodes[0].nodeValue);
var clserno = (x[0].getElementsByTagName("clserno")[0].childNodes[0].nodeValue);

// scan through XML data records, starting at 1st, for month and day, high and low battery voltages, 0th record
// has Classic ID info so ignore it (might put year in there soon!)
// put data into arrays gdate[] and eamps[] and pass to Hgraph
// Note XML data comes in reverse order (latest first to oldest last) as needed by Hgraph
// hence array stored in reverse to XML reading order

for (var i=1;i<x.length;i++)  // data starts at 1 as 0'th record is ID info
{
     var mn =  (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue);
     var day = (x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
     gdate[(x.length-i)-1] = [year, day, mn]; 
     var eam = (x[i].getElementsByTagName("EA")[0].childNodes[0].nodeValue);         
     eamps[(x.length-i)-1] = [parseFloat(eam)];

}  //for loop
</script>

<! now draw the HighChart >
<div id="container" style="height: 500px; width: 750px; border: 1px solid black;
                           background-color:lightgray">
</div>
</body>
</html>

Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 26, 2015, 11:37:44 PM
Two of the max/min highcharts hc112.htm for battery high/low volts and hc113.htm for high/low SoC%
These are Javscript that gets Daylog.xml from Arduino server, extracts some date info and the max and min values, orders them into array and calls Highchrt graph funstion to draw the graph.
The graph auto ranges the values scales depending on data values (so votage good for 12,24,48V etc battery banks.
dgd

hc112.htm

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/highcharts-more.js"></script>
<! only necessary if chart export options are included in chart >
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<script>

var year = 2015;   // eventually need to extract year from 0th xml record
var bvolt = [];
var gdate = [];

$(function () {

    $('#container').highcharts({

        chart: {
            backgroundColor: {
                linearGradient: [0, 0, 500, 500],
                stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(240, 240, 255)']
                    ]
            },
            type: 'columnrange',
            inverted: false,
          },

        title: {
            text: 'Battery Voltage'
        },

        subtitle: {
            text: 'Maximum and Minimum per day'
        },

        xAxis: {
            type: 'datetime',
            dateTimeLabelFormats: {
                day: '%e %b'
            },
            title: null

          },

        yAxis: {
            title: {
                text: 'Volts'
            }
        },

        plotOptions: {
            columnrange: {
                dataLabels: {
                    enabled: true,
                    formatter: function () {
                        return this.y;
                    }
                }
            }
        },

        legend: {
            enabled: false
        },

        series: [{
            name: 'Volts',
            pointStart: Date.UTC(year, mn-1, day),  // mn-1 because month indexes from 0
            pointInterval: 24 * 3600 * 1000, // one day
            data: bvolt
       }]
    });
});

</script>
</head>

<body>
<title>Daily Volts Range</title>

<script>


if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");

// scan through XML data records, starting at 1st, for month and day, high and low battery voltages, 0th record
// has Classic ID info so ignore it (might put year in there soon!)
// put data into arrays gdate[] and bvolt[] and pass to Hgraph
// Note XML data comes in reverse order (latest first to oldest last) as needed by Hgraph
// hence array stored in reverse to XML reading order

for (var i=1;i<x.length;i++)  // data starts at 1 as 0'th record is ID info
{
  mn = (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue)
  var day=(x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
  gdate[(x.length-i)-1] = [year, day, mn];
  var hv=(x[i].getElementsByTagName("HBV")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("HBV")[0].childNodes[0].nodeValue.length -1)); 
  var lv=(x[i].getElementsByTagName("LBV")[0].childNodes[0].nodeValue.slice(0,
          x[i].getElementsByTagName("LBV")[0].childNodes[0].nodeValue.length -1));         
  bvolt[(x.length-i)-1] = [parseFloat(lv), parseFloat(hv)];

}  //for loop

</script>

<! now draw the HighChart >
<div id="container" style="height: 600px; width: 900px; border: 1px solid black;
                           background-color:lightgray">

</body>
</html>


hc113.htm

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/highcharts-more.js"></script>
<! only necessary if chart export options are included in chart >
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<script>

var year = 2015;   // eventually need to extract year from 0th xml record
var soc = [];
var gdate = [];

$(function () {

    $('#container').highcharts({

        chart: {
            backgroundColor: {
                linearGradient: [0, 0, 500, 500],
                stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(240, 240, 255)']
                    ]
            },
            type: 'columnrange',
            inverted: false,
          },

        title: {
            text: 'State of Charge %'
        },

        subtitle: {
            text: 'Maximum and Minimum per day'
        },

        xAxis: {
            type: 'datetime',
            dateTimeLabelFormats: {
                day: '%e %b'
            },
            title: null

          },

        yAxis: {
            title: {
                text: '%'
            }
        },

        plotOptions: {
            columnrange: {
                dataLabels: {
                    enabled: true,
                    formatter: function () {
                        return this.y;
                    }
                }
            }
        },

        legend: {
            enabled: false
        },

        series: [{
            name: 'SOC',
            pointStart: Date.UTC(year, mn-1, day),  // mn-1 because month indexes from 0
            pointInterval: 24 * 3600 * 1000, // one day
            data: soc
       }]
    });
});

</script>
</head>

<body>
<title>Daily SOC Range</title>

<script>


if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");

// scan through XML data records, starting at 1st, for month and day, high and low battery voltages, 0th record
// has Classic ID info so ignore it (might put year in there soon!)
// put data into arrays gdate[] and soc[] and pass to Hgraph
// Note XML data comes in reverse order (latest first to oldest last) as needed by Hgraph
// hence array stored in reverse to XML reading order

for (var i=1;i<x.length;i++)  // data starts at 1 as 0'th record is ID info
{
  mn = (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue)
  var day=(x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
  gdate[(x.length-i)-1] = [year, day, mn];
  var hs=(x[i].getElementsByTagName("HSOC")[0].childNodes[0].nodeValue);
  var ls=(x[i].getElementsByTagName("LSOC")[0].childNodes[0].nodeValue);         
  soc[(x.length-i)-1] = [parseInt(ls), parseInt(hs)];

}  //for loop
</script>

<! now draw the HighChart >
<div id="container" style="height: 600px; width: 900px; border: 1px solid black;
                           background-color:lightgray">
</body>
</html>
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on June 27, 2015, 09:03:38 AM
I like :)
I will be getting back into developing my web app in the next few weeks and intend to go further with HighCharts.
You really are quite prolific with your developments David, great to see :)
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 27, 2015, 10:53:14 PM
Paul,
There are just so many graph option with Highcharts and HighStock charts. I especially like some of the weirder graphs like the spider web, polar and polygon charts and that 3d bubble chart I will just have to find a use for with Classic data  :)
And the nice thing is that they are relatively simple to drive and interface to. I can see fun for the next few weeks  :P

Another max/min chart from Daily log, this one if for high/low amp hours remaining

hc114.htm

<!DOCTYPE html>
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script src="http://code.highcharts.com/highcharts.js"></script>
<script src="http://code.highcharts.com/highcharts-more.js"></script>
<! only necessary if chart export options are included in chart >
<script src="http://code.highcharts.com/modules/exporting.js"></script>
<script>

var year = 2015;
var ahrem = [];
var gdate = [];

$(function () {

    $('#container').highcharts({

        chart: {
            backgroundColor: {
                linearGradient: [0, 0, 500, 500],
                stops: [
                    [0, 'rgb(255, 255, 255)'],
                    [1, 'rgb(240, 240, 255)']
                    ]
            },
            type: 'columnrange',
            inverted: false,
          },

        title: {
            text: 'Amp Hours Remaining'
        },

        subtitle: {
            text: 'Maximum and Minimum per day'
        },

        xAxis: {
            type: 'datetime',
            dateTimeLabelFormats: {
                day: '%e %b'
            },
            title: null

          },

        yAxis: {
            title: {
                text: 'Amp Hrs'
            }
        },

        plotOptions: {
            columnrange: {
                dataLabels: {
                    enabled: true,
                    formatter: function () {
                        return this.y;
                    }
                }
            }
        },

        legend: {
            enabled: false
        },

        series: [{
            name: 'AhRem',
            pointStart: Date.UTC(year, mn-1, day),  // mn-1 because month indexes from 0
            pointInterval: 24 * 3600 * 1000, // one day
            data: ahrem
       }]
    });
});

</script>
</head>

<body>
<title>Daily SOC Range</title>

<script>


if (window.XMLHttpRequest)
  {// code for IE7+, Firefox, Chrome, Opera, Safari
  xmlhttp=new XMLHttpRequest();
  }
else
  {// code for IE6, IE5
  xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }

xmlhttp.open("GET","Daylog.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("Daydata");

// scan through XML data records, starting at 1st, for month and day, high and low battery voltages, 0th record
// has Classic ID info so ignore it (might put year in there soon!)
// put data into arrays gdate[] and soc[] and pass to Hgraph
// Note XML data comes in reverse order (latest first to oldest last) as needed by Hgraph
// hence array stored in reverse to XML reading order

for (var i=1;i<x.length;i++)  // data starts at 1 as 0'th record is ID info
{
  mn = (x[i].getElementsByTagName("MTH")[0].childNodes[0].nodeValue)
  var day=(x[i].getElementsByTagName("DAY")[0].childNodes[0].nodeValue);
  gdate[(x.length-i)-1] = [year, day, mn];
  var hs=(x[i].getElementsByTagName("HAM")[0].childNodes[0].nodeValue);
  var ls=(x[i].getElementsByTagName("LAM")[0].childNodes[0].nodeValue);         
  ahrem[(x.length-i)-1] = [parseInt(ls), parseInt(hs)];

}  //for loop

</script>

<! now draw the HighChart >
<div id="container" style="height: 600px; width: 900px; border: 1px solid black;
                           background-color:lightgray">

</body>
</html>


dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on June 27, 2015, 10:58:18 PM
Yes, agreed.
Looking forward to seeing more of your ideas.
Presently, as in right now, I have my head into your code for Arduino reading Classic and LCD and giving it a full re-work with PWM diversion for Will Eert.
The PWM will be controlled by a PID block I have proved and will set it up so that it can handle two loads in a lead / lag control.
I hope you don't mind me using your code as the basis. :)

Lots to do :)
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on June 28, 2015, 01:25:50 AM
I did a fair amount of development for PWM diversion for hot water heating using an Arduino.
I will post what I did in the other thread about PWM diversion, didn't finish it as one of my customer sites wanted daily logs for his PV/Classic system from web server.
Please make use of anything posted here.
dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on July 16, 2015, 07:06:01 AM
Time now to update the Arduino sketch, again for Mega2560 with 8k ram. This update has the web server section now
providing XML files for the gauge display, the 10minute log, from 48 by 10minute data array, and the day log from 20 by 1day data array.
The index.htm web page on SD card now has three buttons in bottom of svg window for 10mlog Daylog and Hchart.
These link to node14.htm, node17.htm and hchart.htm.

The Highchart still needs some tidying up, its actually a one second refreshing sliding to left Highstock type chart that
is sharing the xml file data with gauge display. This really needs to be changed so that the Arduino server is sending a json format file as the conversion from XML to json that is currently being managed by a data module in the web page is not too reliable.
I used this method to try and shift as much processing to the web page so that the browser cpu was doing the work instead of the ARduino server with its limited cpu and memory resources.

So next Arduino sketch update after this one will produce the json file for the highstock chart. This is getting close to the end of the web server on Arduino Mega for Classic as Mega resources are being squeezed.
Will be moving back to DUE to enable the multi-controller functions in the web server then transferring it all, as much as possible to the Cubie2. Hopefully at that time all of the Classic ethernet firmware issues wil be resolved.

Sketch and web pages in previous postings have been updated.. note the sketch is too big for the previous 2 posts so has been divided into 3 parts, the 3rd part is listed here. All 3 parts need to be joined to make the complete skech.
dgd



// send the XML file containing modbus values for canv gauge updates (about once per second)
void XML_response(EthernetClient cl)
{   
    cl.print("<inputs>");   
    // output stored modbus registers
    cl.print("<bamps>");    cl.print(batt_amps);    cl.print("</bamps>");
    cl.print("<bvolts>");   cl.print(batt_volts);   cl.print("</bvolts>");   
    cl.print("<cvin>");     cl.print(in_volts[0]);  cl.print("</cvin>");
    cl.print("<cwatts>");   cl.print(watts[0]);     cl.print("</cwatts>");
    cl.print("<camps>");    cl.print(out_amps[0]);  cl.print("</camps>");
   
    cl.print("<clname>");   cl.print (Classic_name[0]);   cl.print("</clname>");
    cl.print("<clmodel>");  cl.print (model[0]);    cl.print("</clmodel>");
    cl.print("<clserno>");  cl.print (serno[0]);    cl.print("</clserno>");
   
    cl.print("<clstate>");  cl.print (statestr[0]); cl.print("</clstate>");
    cl.print("<clkwh>");    cl.print (kwhstr[0]);   cl.print("</clkwh>");
    cl.print("<bah>");      cl.print (wbjr_ahr);    cl.print("</bah>");
    cl.print("<bahr>");     cl.print (wbjr_ra);     cl.print("</bahr>");
    cl.print("<bsoc>");     cl.print (wbjr_soc);    cl.print("</bsoc>");
   
    cl.print("<fettemp>");  cl.print (ftpstr[0]); cl.print("</fettemp>");
    cl.print("<pcbtemp>");  cl.print (ptpstr[0]); cl.print("</pcbtemp>");
    cl.print("<battemp>");  cl.print (btpstr);    cl.print("</battemp>");
    cl.print("<remtemp>");  cl.print (rtpstr);    cl.print("</remtemp>");
   
     
    cl.print("<peakpwr>");  cl.print (peakpstr[0]);  cl.print("</peakpwr>");
    cl.print("<peaktime>"); cl.print (ctimestr[0]);  cl.print("</peaktime>");
 
    cl.print("</inputs>");
}

// send the XML file containing 10 minute log records
void XML_CLlog(EthernetClient cl)
{
    int i,j; 

    cl.print("<CLlog>");   
    // output stored log file values
    cl.print("<CLdata>");   
    cl.print("<clname>");   cl.print (Classic_name[0]);   cl.print("</clname>");
    cl.print("<clmodel>");  cl.print (model[0]);    cl.print("</clmodel>");
    cl.print("<clserno>");  cl.print (serno[0]);    cl.print("</clserno>");
    cl.print("</CLdata>");
   
    for (i=last_index,j=0; j < MAX_LOG_COUNT; i--,j++)  // for Mega2560 keep this to 48 maximum
    {
        if (i < 0 ) i = MAX_LOG_COUNT-1;
        if (log_ahr[i] == 0) break;
        cl.print("<CLdata>");
        cl.print("<HRS>");    cl.print(log_hrs[i]);       cl.print("</HRS>");
        cl.print("<MINS>");   cl.print(log_mins[i]);      cl.print("</MINS>");
        cl.print("<STATE>");  cl.print (log_state[i]);    cl.print("</STATE>");
        cl.print("<INV>");    cl.print(log_involts[i]);   cl.print("</INV>");
        cl.print("<KWH>");    cl.print(log_kwh[i]);       cl.print("</KWH>");
        cl.print("<WATTS>");  cl.print(log_watts[i]);     cl.print("</WATTS>");
        cl.print("<OUTA>");   cl.print(log_outamps[i]);   cl.print("</OUTA>");       
        cl.print("<BATTV>");  cl.print(log_battvolts[i]); cl.print("</BATTV>");
        cl.print("<BATTA>");  cl.print(log_battamps[i]);  cl.print("</BATTA>");   
        cl.print("<SOC>");    cl.print(log_soc[i]);       cl.print("</SOC>");
        cl.print("<AHREM>");  cl.print(log_ahremain[i]);  cl.print("</AHREM>");         
        cl.print("<AHR>");    cl.print(log_ahr[i]);       cl.print("</AHR>");               
        cl.print("</CLdata>");
    }
    cl.print("</CLlog>");
    delay(1);  // give browser time to receive data
}

// send the XML file containing Day log records
void XML_Daylog(EthernetClient cl)
{
     int i,j; 
    cl.print("<Daylog>");   
    // output stored log file values
    cl.print("<Daydata>");   
    cl.print("<clname>");   cl.print (Classic_name[0]);   cl.print("</clname>");
    cl.print("<clmodel>");  cl.print (model[0]);    cl.print("</clmodel>");
    cl.print("<clserno>");  cl.print (serno[0]);    cl.print("</clserno>");
    cl.print("</Daydata>");
       
    for (i=dlast_index,j=0; j < MAX_DLOG_COUNT; i--,j++)  // for Mega2560 keep this to 20 maximum
    {
        if (i < 0 ) i = MAX_DLOG_COUNT-1;
        if (dlog_mth[i] == 0) break;  // dont bother with empty log entries1
        cl.print("<Daydata>");
        cl.print("<MTH>");    cl.print(dlog_mth[i]);    cl.print("</MTH>");
        cl.print("<DAY>");    cl.print(dlog_day[i]);    cl.print("</DAY>");
        cl.print("<KWH>");    cl.print(dlog_kwh[i]);    cl.print("</KWH>");
        cl.print("<ABHR>");   cl.print(dlog_abhr[i]);   cl.print("</ABHR>");
        cl.print("<ABMIN>");  cl.print(dlog_abmin[i]);  cl.print("</ABMIN>");
        cl.print("<FLHR>");   cl.print(dlog_flhr[i]);   cl.print("</FLHR>");
        cl.print("<FLMIN>");  cl.print(dlog_flmin[i]);  cl.print("</FLMIN>");
        cl.print("<EA>");     cl.print(dlog_ea[i]);     cl.print("</EA>");       
        cl.print("<HBV>");    cl.print(dlog_hbv[i]);    cl.print("</HBV>");   
        cl.print("<LBV>");    cl.print(dlog_lbv[i]);    cl.print("</LBV>");
        cl.print("<HWTS>");   cl.print(dlog_hwts[i]);   cl.print("</HWTS>");         
        cl.print("<HSOC>");   cl.print(dlog_hsoc[i]);   cl.print("</HSOC>");
        cl.print("<LSOC>");   cl.print(dlog_lsoc[i]);   cl.print("</LSOC>");               
        cl.print("<HAM>");    cl.print(dlog_ham[i]);    cl.print("</HAM>");
        cl.print("<LAM>");    cl.print(dlog_lam[i]);    cl.print("</LAM>");               
        cl.print("<HAHR>");   cl.print(dlog_hahr[i]);   cl.print("</HAHR>");
        cl.print("<LAHR>");   cl.print(dlog_lahr[i]);   cl.print("</LAHR>");                     
        cl.print("</Daydata>");
    }
    cl.print("</Daylog>");
    delay(1);   // give browser time to receive data
}

// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
{
    for (int i = 0; i < length; i++) {
        str[i] = 0;
    }
}

// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
{
    char found = 0;
    char index = 0;
    char len;

    len = strlen(str);
   
    if (strlen(sfind) > len) {
        return 0;
    }
    while (index < len) {
        if (str[index] == sfind[found]) {
            found++;
            if (strlen(sfind) == found) {
                return 1;
            }
        }
        else {
            found = 0;
        }
        index++;
    }
    return 0;
}

Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on July 18, 2015, 07:26:07 PM
Just reviewed the Arduino web server code and as far as the code/sketch for a single Classic is concerned the sketch is now complete. I can't see any further developments needed apart from maybe tidying up the code to remove redundant variables and other bits. There may be a few bugs in there too but none has surfaced yet.. So nothing new from now except minor things.

Basically the sketch interfaces to a Classic using serial modbus every second or so, processes and re formats data and makes it ready in XML file format for a connecting web browser. The Arduino is in effect a simplistic web server with SD card storage.

Three different XML files can be provided, the one second data for the gauges web page and the HighStock chart display, the ten minute data for text log display and some Highcharts pages, and the daily data for text log display and associated Highcharts displays.

The Arduino web server does minimal data preparation for web pages, really almost none, with the web pages using the web browser to do all the page formatting and presentation via JavaScript code within each page. All the Arduino does is provide the web pages from its SD storage.

The sketch is designed to run on an Arduino Mega2560 R3 and with minute and day logs of 48and 20 entry arrays it's using about 5.8k of ram leaving 2.2k for runtime variables. Some static text strings are stored in program flash memory. EEPROM is not used as it's awfully slow but this may change when, if ever, I get to implement rtc hardware.

So sketch is complete for now. Still working on web pages for various graph displays.

The multi classic server on DUE I will post sketch when finished which is not too far away.
Will post web server for KID in KID area.

Dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: paul alting on July 18, 2015, 07:35:10 PM
Good to see and follow the progress you have made with this project David.
I think you're a mad coder like me, and you have skills in many different areas.
I will like to go over the code for Arduino and also the javascript code at some point to see what I can glean from it.
____
Paul
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on July 20, 2015, 05:27:20 PM
hi Paul,
yes,certainly, absolutely definitely , somewhat mad approaching barking mad at times. People near to me cant understand enthusiasm over a teensy primitive looking computer and a weird looking solar thingy box. They are ok with batteries and solar panels and free electricity but understanding leaves me gobsmacked when I get a question from a cuz asking how many cars that row of big batteries would start.
Anyway, Arduino server project nears completion, just updated/added web pages in previous postings, damm those highcharts look good  :P  Had quite a few emails and some great suggestions inc some I move project to Arduino forums, my reply is that my primary interest is in solar power generation and Classics etc rather than Arduino (its only a means to an end for me, rather than the focus of the project). What I will be doing soon is bundling the lot, sketches, web pages, documentation etc onto Github and then in a few weeks the multi-Classic/Kid version too.
Original goal was to get this all done in 6 months so still have a few weeks left.
Then new fun starts with the Cubie2 and the Classic SL - when I can get one  or three :)

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on July 26, 2015, 06:44:17 PM
Now have reworked how log files are dealt with by the Arduino Mega
The 10 minute log file still gets stored in ram and is 48 entries giving 8 hours. Every time Arduino is restarted this is lost but will rebuild completey in 8 hours.
The Day log file with 20 records was also in ram and was lost on restart. This was bothersome as 20 days to rebuild was just too long a delay. The Day log is now moved to use EEPROM, the Mega has 4K available so 20 by 48byte records is under 1K used.
With only 1 record written per day this will not impact the EEPROM life and with the record written just after midnight each day the slow write speed is not a concern. EEPROM reading is slightly slower than reading ram so Day log web pages will take a second or so longer to load.
This change is in updated sketch.
dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on September 25, 2015, 06:15:50 AM
Had a couple of months busy but nothing else black box seems to be moving forward so back to the DUE.  Done lots of little updates and reworked all the multi Classic web server and mixed controller versions Classics and MS mppt. Kid is there too but it's serial port stuff is a mess to deal with, badly need some plain written docs and usage examples (eh Mario?)
So if no objections I will reawaken this thread and post more DUE sketch updates.
Lots of web pages too, graphs and more complex JS but some web stuff is IMHO just stunning.
Fingers sore from unwinding toroids and straightening 2mm copper wire, finished ebrt  :) so restocked with single malt and now it's all web server and 6kw inverter for rest of this year.
Still want to get a Classic SL and MX60 to get server done. Kid clipper Mia.
Anyone interested?

Dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on November 15, 2015, 06:56:28 PM
Had a few emails about the gauges display page (index.html), ie the startup web page, not auto detecting properly for different system battery voltages.

Its probably best to set the values for the gauges in the index.htm file as there is no way of knowing the range of the Power (Watts) gauge as this depends on the pv array size, also the WBjr amps range is again system dependant. So since these need to be setup anyway it easy to adjust the other ranges for personal preference.

The index.htm file included below is for a 48volt system, so simply edit the range numbers in the code for what is needed, its fairly self documented and easy to follow.
To check changes make sense just excute the index.htm file by clicking on it and your web browser will display it (although no XML data is retrieved from your Classic)

dgd


<!DOCTYPE html>
<html>
    <head>
        <title>Classic Canv gauges</title>
        <script>
var batV_val = 44;
var batA_val  = 0;
                var Cwatts_val = 0;
var Cvin_val = 0;
var Camps_val = 0;

<!--- Start of gauge code  --->
eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};
while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('t W=v(f){W.2t.3T(A);A.B={Z:2u,19:1q,1h:1q,1J:U,1b:D,K:0,V:[\'0\',\'20\',\'40\',\'2A\',\'2B\',\'D\'],2r:10,2C:M,1E:U,2q:{2D:3,2E:2},2H:M,1c:{2j:10,2m:3y,2O:\'3v\'},J:{2P:\'#4h\',V:\'#31\',2r:\'#3k\',1J:\'#37\',1E:\'#37\',3e:\'#31\',1t:{2s:\'1e(3Y, 3d, 3d, 1)\',3c:\'1e(1Y, 5I, 5E, .9)\'}},1o:[{1n:20,1D:2A,1F:\'#3j\'},{1n:2A,1D:2B,1F:\'#36\'},{1n:2B,1D:D,1F:\'#5n\'}]};t g=0,1p=A,N=0,1S=0,1G=U;A.5d=v(a){N=f.1c?g:a;t b=(f.1b-f.K)/D;1S=a>f.1b?1S=f.1b+b:a<f.K?f.K-b:a;g=a;f.1c?3l():A.1g();C A};A.3m=v(a){N=g=a;A.1g();C A};A.4T=v(){g=N=1S=A.B.K;A.1g();C A};A.4R=v(){C g};A.13=v(){};v 2k(a,b){Q(t i 4P b){z(1H b[i]=="1W"&&!(4O.4y.2V.4p(b[i])===\'[1W 4n]\')&&i!=\'Z\'){z(1H a[i]!="1W"){a[i]={}}2k(a[i],b[i])}O{a[i]=b[i]}}};2k(A.B,f);A.B.K=1R(A.B.K);A.B.1b=1R(A.B.1b);f=A.B;N=g=f.K;z(!f.Z){4m 4j("4g 4d 4b 46 44 41 3Z 3W W 1W!");}t j=f.Z.5K?f.Z:2R.5v(f.Z),q=j.3u(\'2d\'),1i,1y,1A,14,17,u,1d;v 2M(){j.19=f.19;j.1h=f.1h;1i=j.4s(M);1d=1i.3u(\'2d\');1y=j.19;1A=j.1h;14=1y/2;17=1A/2;u=14<17?14:17;1i.2J=U;1d.3P(14,17);1d.G();q.3P(14,17);q.G()};2M();A.4Z=v(a){2k(A.B,a);2M();A.1g();C A};t k={4q:v(p){C p},4e:v(p){C E.1L(p,2)},4c:v(p){C E.1L(p,5)},3v:v(p){C 1-E.1O(E.5C(p))},5k:v(p){C 1-(v(p){Q(t a=0,b=1;1;a+=b,b/=2){z(p>=(7-4*a)/11){C-E.1L((11-6*a-11*p)/4,2)+E.1L(b,2)}}})(1-p)},4S:v(p){C 1-(v(p){t x=1.5;C E.1L(2,10*(p-1))*E.1T(20*E.1a*x/3*p)})(1-p)}};t l=2u;v 3S(d){t e=2v 3R;l=2x(v(){t a=2v 3R-e,1M=a/d.2m;z(1M>1){1M=1}t b=1H d.2g=="v"?d.2g:k[d.2g];t c=b(1M);d.3Q(c);z(1M==1){2b(l)}},d.2j||10)};v 3l(){l&&2b(l);t b=(1S-N),1n=N,29=f.1c;3S({2j:29.2j,2m:29.2m,2g:29.2O,3Q:v(a){N=1R(1n)+b*a;1p.1g()}})};q.5l="3O";A.1g=v(){z(!1i.2J){1d.3M(-14,-17,1y,1A);1d.G();t a=q;q=1d;3L();3K();3I();3H();3F();3D();3z();1i.2J=M;q=a;5G a}q.3M(-14,-17,1y,1A);q.G();q.4a(1i,-14,-17,1y,1A);z(!W.28){t b=2x(v(){z(!W.28){C}2b(b);2K();2L();z(!1G){1p.13&&1p.13();1G=M}},10)}O{2K();2L();z(!1G){1p.13&&1p.13();1G=M}}C A};v S(a){C a*E.1a/4J};v 1l(a,b,c){t d=q.4Y(0,0,0,c);d.1V(0,a);d.1V(1,b);C d};v 3L(){t a=u/D*5g,3x=u-a,2a=u/D*5q,5u=u-2a,1f=u/D*5z,5A=u-1f;3t=u/D*5F;q.G();z(f.2H){q.2o=3x;q.2n=\'1e(0, 0, 0, 0.5)\'}q.P();q.16(0,0,a,0,E.1a*2,M);q.L=1l(\'#42\',\'#43\',a);q.T();q.R();q.P();q.16(0,0,2a,0,E.1a*2,M);q.L=1l(\'#49\',\'#36\',2a);q.T();q.P();q.16(0,0,1f,0,E.1a*2,M);q.L=1l(\'#3j\',\'#3s\',1f);q.T();q.P();q.16(0,0,3t,0,E.1a*2,M);q.L=f.J.2P;q.T();q.G()};v 3H(){t r=u/D*2T;q.2e=2;q.2U=f.J.V;q.G();Q(t i=0;i<f.V.H;++i){t a=45+i*(1U/(f.V.H-1));q.1z(S(a));q.P();q.1K(0,r);q.F(0,r-u/D*15);q.1X();q.R();q.G()}z(f.2C){q.1z(S(2X));q.P();q.16(0,0,r,S(45),S(4N),U);q.1X();q.R();q.G()}};v 3I(){t r=u/D*2T;q.2e=1;q.2U=f.J.2r;q.G();t b=f.2r*(f.V.H-1);Q(t i=0;i<b;++i){t a=45+i*(1U/b);q.1z(S(a));q.P();q.1K(0,r);q.F(0,r-u/D*7.5);q.1X();q.R();q.G()}};v 3F(){t r=u/D*55;Q(t i=0;i<f.V.H;++i){t a=45+i*(1U/(f.V.H-1)),p=1w(r,S(a));q.1x=20*(u/1q)+"2i 2Y";q.L=f.J.3e;q.2e=0;q.2h="2f";q.27(f.V[i],p.x,p.y+3)}};v 3D(){z(!f.1J){C}q.G();q.1x=24*(u/1q)+"2i 2Y";q.L=f.J.1J;q.2h="2f";q.27(f.1J,0,-u/4.25);q.R()};v 3z(){z(!f.1E){C}q.G();q.1x=22*(u/1q)+"2i 2Y";q.L=f.J.1E;q.2h="2f";q.27(f.1E,0,u/3.25);q.R()};v 32(a){t b=f.2q.2E,34=f.2q.2D;a=1R(a);t n=(a<0);a=E.35(a);z(b>0){a=a.5t(b).2V().1j(\'.\');Q(t i=0,s=34-a[0].H;i<s;++i){a[0]=\'0\'+a[0]}a=(n?\'-\':\'\')+a[0]+\'.\'+a[1]}O{a=E.3O(a).2V();Q(t i=0,s=34-a.H;i<s;++i){a=\'0\'+a}a=(n?\'-\':\'\')+a}C a};v 1w(r,a){t x=0,y=r,1O=E.1O(a),1T=E.1T(a),X=x*1T-y*1O,Y=x*1O+y*1T;C{x:X,y:Y}};v 3K(){q.G();t a=u/D*2T;t b=a-u/D*15;Q(t i=0,s=f.1o.H;i<s;i++){t c=f.1o[i],39=(f.1b-f.K)/1U,1P=S(45+(c.1n-f.K)/39),1N=S(45+(c.1D-f.K)/39);q.P();q.1z(S(2X));q.16(0,0,a,1P,1N,U);q.R();q.G();t d=1w(b,1P),3a=1w(a,1P);q.1K(d.x,d.y);q.F(3a.x,3a.y);t e=1w(a,1N),3b=1w(b,1N);q.F(e.x,e.y);q.F(3b.x,3b.y);q.F(d.x,d.y);q.1C();q.L=c.1F;q.T();q.P();q.1z(S(2X));q.16(0,0,b,1P-0.2,1N+0.2,U);q.R();q.1C();q.L=f.J.2P;q.T();q.G()}};v 2L(){t a=u/D*12,1f=u/D*8,1u=u/D*3X,1r=u/D*20,2l=u/D*4,1B=u/D*2,38=v(){q.3f=2;q.3g=2;q.2o=10;q.2n=\'1e(5L, 3h, 3h, 0.45)\'};38();q.G();z(N<0){N=E.35(f.K-N)}O z(f.K>0){N-=f.K}O{N=E.35(f.K)+N}q.1z(S(45+N/((f.1b-f.K)/1U)));q.P();q.1K(-1B,-1r);q.F(-2l,0);q.F(-1,1u);q.F(1,1u);q.F(2l,0);q.F(1B,-1r);q.1C();q.L=1l(f.J.1t.2s,f.J.1t.3c,1u-1r);q.T();q.P();q.F(-0.5,1u);q.F(-1,1u);q.F(-2l,0);q.F(-1B,-1r);q.F(1B/2-2,-1r);q.1C();q.L=\'1e(1Y, 1Y, 1Y, 0.2)\';q.T();q.R();38();q.P();q.16(0,0,a,0,E.1a*2,M);q.L=1l(\'#3s\',\'#36\',a);q.T();q.R();q.P();q.16(0,0,1f,0,E.1a*2,M);q.L=1l("#47","#48",1f);q.T()};v 3i(x,y,w,h,r){q.P();q.1K(x+r,y);q.F(x+w-r,y);q.23(x+w,y,x+w,y+r);q.F(x+w,y+h-r);q.23(x+w,y+h,x+w-r,y+h);q.F(x+r,y+h);q.23(x,y+h,x,y+h-r);q.F(x,y+r);q.23(x,y,x+r,y);q.1C()};v 2K(){q.G();q.1x=40*(u/1q)+"2i 30";t a=32(g),2Z=q.4f(\'-\'+32(0)).19,y=u-u/D*33,x=0,2W=0.12*u;q.G();3i(-2Z/2-0.21*u,y-2W-0.4i*u,2Z+0.3n*u,2W+0.4k*u,0.21*u);t b=q.4l(x,y-0.12*u-0.21*u+(0.12*u+0.3o*u)/2,u/10,x,y-0.12*u-0.21*u+(0.12*u+0.3o*u)/2,u/5);b.1V(0,"#37");b.1V(1,"#3k");q.2U=b;q.2e=0.3n*u;q.1X();q.2o=0.3p*u;q.2n=\'1e(0, 0, 0, 1)\';q.L="#4o";q.T();q.R();q.3f=0.3q*u;q.3g=0.3q*u;q.2o=0.3p*u;q.2n=\'1e(0, 0, 0, 0.3)\';q.L="#31";q.2h="2f";q.27(a,-x,y);q.R()}};W.28=U;(v(){t d=2R,h=d.3r(\'4r\')[0],2S=4t.4u.4v().4w(\'4x\')!=-1,2Q=\'4z://4A-4B.4C/4D/4E/4F-7-4G.\'+(2S?\'4H\':\'4I\'),1I="@1x-4K {"+"1x-4L: \'30\';"+"4M: 2Q(\'"+2Q+"\');"+"}",1s,r=d.3w(\'1v\');r.2N=\'1I/4Q\';z(2S){h.2p(r);1s=r.2I;1s.3A=1I}O{4U{r.2p(d.4V(1I))}4W(e){r.3A=1I}h.2p(r);1s=r.2I?r.2I:(r.4X||d.3B[d.3B.H-1])}t b=2x(v(){z(!d.3C){C}2b(b);t a=d.3w(\'50\');a.1v.51=\'30\';a.1v.52=\'53\';a.1v.1h=a.1v.19=0;a.1v.54=\'56\';a.57=\'.\';d.3C.2p(a);58(v(){W.28=M;a.59.5a(a)},3y)},1)})();W.2t=[];W.2t.5b=v(a){z(1H(a)==\'5c\'){Q(t i=0,s=A.H;i<s;i++){z(A[i].B.Z.18(\'5e\')==a){C A[i]}}}O z(1H(a)==\'5f\'){C A[a]}O{C 2u}};v 3E(a){z(2G.3G){2G.3G(\'5h\',a,U)}O{2G.5i(\'5j\',a)}}3E(v(){v 2F(a){t b=a[0];Q(t i=1,s=a.H;i<s;i++){b+=a[i].1Z(0,1).5m()+a[i].1Z(1,a[i].H-1)}C b};v 3J(a){C a.5o(/^\\s+|\\s+$/g,\'\')};t c=2R.3r(\'5p\');Q(t i=0,s=c.H;i<s;i++){z(c[i].18(\'1k-2N\')==\'5r-5s\'){t d=c[i],B={},1m,w=2c(d.18(\'19\')),h=2c(d.18(\'1h\'));B.Z=d;z(w){B.19=w}z(h){B.1h=h}Q(t e=0,1s=d.3N.H;e<1s;e++){1m=d.3N.5w(e).5x;z(1m!=\'1k-2N\'&&1m.1Z(0,5)==\'1k-\'){t f=1m.1Z(5,1m.H-5).5y().1j(\'-\'),I=d.18(1m);z(!I){2z}5B(f[0]){2y\'J\':{z(f[1]){z(!B.J){B.J={}}z(f[1]==\'1t\'){t k=I.1j(/\\s+/);z(k[0]&&k[1]){B.J.1t={2s:k[0],3c:k[1]}}O{B.J.1t=I}}O{f.5D();B.J[2F(f)]=I}}26}2y\'1o\':{z(!B.1o){B.1o=[]}2w=I.1j(\',\');Q(t j=0,l=2w.H;j<l;j++){t m=3J(2w[j]).1j(/\\s+/),1Q={};z(m[0]&&m[0]!=\'\'){1Q.1n=m[0]}z(m[1]&&m[1]!=\'\'){1Q.1D=m[1]}z(m[2]&&m[2]!=\'\'){1Q.1F=m[2]}B.1o.3T(1Q)}26}2y\'1c\':{z(f[1]){z(!B.1c){B.1c={}}z(f[1]==\'2O\'&&/^\\s*v\\s*\\(/.5H(I)){I=3U(\'(\'+I+\')\')}B.1c[f[1]]=I}26}5J:{t n=2F(f);z(n==\'13\'){2z}z(n==\'V\'){I=I.1j(/\\s+/)}O z(n==\'2C\'||n==\'2H\'){I=I==\'M\'?M:U}O z(n==\'2q\'){t o=I.1j(\'.\');z(o.H==2){I={2D:2c(o[0]),2E:2c(o[1])}}O{2z}}B[n]=I;26}}}}t g=2v W(B);z(d.18(\'1k-3V\')){g.3m(1R(d.18(\'1k-3V\')))}z(d.18(\'1k-13\')){g.13=v(){3U(A.B.Z.18(\'1k-13\'))}}g.1g()}}});',62,358,'||||||||||||||||||||||||||ctx|||var|max|function||||if|this|config|return|100|Math|lineTo|save|length|attrValue|colors|minValue|fillStyle|true|fromValue|else|beginPath|for|restore|radians|fill|false|majorTicks|Gauge|||renderTo||||onready|CX||arc|CY|getAttribute|width|PI|maxValue|animation|cctx|rgba|r2|draw|height|cache|split|data|lgrad|prop|from|highlights|self|200|rOut|ss|needle|rIn|style|rpoint|font|CW|rotate|CH|pad2|closePath|to|units|color|imready|typeof|text|title|moveTo|pow|progress|ea|sin|sa|hlCfg|parseFloat|toValue|cos|270|addColorStop|object|stroke|255|substr||025||quadraticCurveTo|||break|fillText|initialized|cfg|r1|clearInterval|parseInt||lineWidth|center|delta|textAlign|px|delay|applyRecursive|pad1|duration|shadowColor|shadowBlur|appendChild|valueFormat|minorTicks|start|Collection|null|new|hls|setInterval|case|continue|60|80|strokeTicks|int|dec|toCamelCase|window|glow|styleSheet|i8d|drawValueBox|drawNeedle|baseInit|type|fn|plate|url|document|ie|81|strokeStyle|toString|th|90|Arial|tw|Led|444|padValue||cint|abs|ccc|888|shad|vd|pe|pe1|end|128|numbers|shadowOffsetX|shadowOffsetY|143|roundRect|eee|666|animate|setRawValue|05|045|012|004|getElementsByTagName|f0f0f0|r3|getContext|cycle|createElement|d0|250|drawUnits|cssText|styleSheets|body|drawTitle|domReady|drawNumbers|addEventListener|drawMajorTicks|drawMinorTicks|trim|drawHighlights|drawPlate|clearRect|attributes|round|translate|step|Date|_animate|push|eval|value|the|77|240|creating||when|ddd|aaa|specified||not|e8e8e8|f5f5f5|fafafa|drawImage|was|quint|element|quad|measureText|Canvas|fff|04|Error|07|createRadialGradient|throw|Array|babab2|call|linear|head|cloneNode|navigator|userAgent|toLocaleLowerCase|indexOf|msie|prototype|http|smart|ip|net|styles|fonts|digital|mono|eot|ttf|180|face|family|src|315|Object|in|css|getValue|elastic|clear|try|createTextNode|catch|sheet|createLinearGradient|updateConfig|div|fontFamily|position|absolute|overflow||hidden|innerHTML|setTimeout|parentNode|removeChild|get|string|setValue|id|number|93|DOMContentLoaded|attachEvent|onload|bounce|lineCap|toUpperCase|999|replace|canvas|91|canv|gauge|toFixed|d1|getElementById|item|nodeName|toLowerCase|88|d2|switch|acos|shift|122|85|delete|test|160|default|tagName|188'.split('|'),0,{}))
<!--- End of gauge code --->

function GetArduinomodbus()
{
    nocache = "&nocache=" + Math.random() * 1000000;
    var request = new XMLHttpRequest();
    request.onreadystatechange = function()
    {
if (this.readyState == 4)
        {
        if (this.status == 200)
{
if (this.responseXML != null)
{
    batA_val = this.responseXML.getElementsByTagName('bamps')[0].childNodes[0].nodeValue;
    batV_val = this.responseXML.getElementsByTagName('bvolts')[0].childNodes[0].nodeValue;
    Cvin_val = this.responseXML.getElementsByTagName('cvin')[0].childNodes[0].nodeValue;
    Cwatts_val = this.responseXML.getElementsByTagName('cwatts')[0].childNodes[0].nodeValue;
    Camps_val = this.responseXML.getElementsByTagName('camps')[0].childNodes[0].nodeValue;

    document.getElementById("CL_NAME").textContent =
      this.responseXML.getElementsByTagName('clname')[0].childNodes[0].nodeValue;
    document.getElementById("CL_MODEL").textContent =
      this.responseXML.getElementsByTagName('clmodel')[0].childNodes[0].nodeValue;
    document.getElementById("CL_SERNO").textContent =
      this.responseXML.getElementsByTagName('clserno')[0].childNodes[0].nodeValue;

    document.getElementById("CL_STATE").textContent =
      this.responseXML.getElementsByTagName('clstate')[0].childNodes[0].nodeValue;
    document.getElementById("CL_KWH").textContent =
      this.responseXML.getElementsByTagName('clkwh')[0].childNodes[0].nodeValue;
    document.getElementById("CL_AH").textContent =
      this.responseXML.getElementsByTagName('bah')[0].childNodes[0].nodeValue;
    document.getElementById("CL_AHR").textContent =
      this.responseXML.getElementsByTagName('bahr')[0].childNodes[0].nodeValue;

    document.getElementById("CL_SOC").textContent =
      this.responseXML.getElementsByTagName('bsoc')[0].childNodes[0].nodeValue;

    document.getElementById("Fet_Temp").textContent =
      this.responseXML.getElementsByTagName('fettemp')[0].childNodes[0].nodeValue;
    document.getElementById("Pcb_Temp").textContent =
      this.responseXML.getElementsByTagName('pcbtemp')[0].childNodes[0].nodeValue;
    document.getElementById("Bat_Temp").textContent =
      this.responseXML.getElementsByTagName('battemp')[0].childNodes[0].nodeValue;
    document.getElementById("Rem_Temp").textContent =
      this.responseXML.getElementsByTagName('remtemp')[0].childNodes[0].nodeValue;

    document.getElementById("Peak_Pwr").textContent =
      this.responseXML.getElementsByTagName('peakpwr')[0].childNodes[0].nodeValue;
    document.getElementById("Peak_Time").textContent =
      this.responseXML.getElementsByTagName('peaktime')[0].childNodes[0].nodeValue;

}
}
}
    }
    request.open("GET", "ajax_inputs" + nocache, true);
    request.send(null);
    setTimeout('GetArduinomodbus()', 500);
}
</script>
        <style>
          html {
            background: url(sasa.jpg) no-repeat center center fixed;
            background-size: cover;
          }

          body {
            color: white;
          }
        </style>

    </head>
    <body onload="GetArduinomodbus()">
    <table>
    <tr>
    <td width="100"></td>
    <td>
    </canvas>
    <canvas id="CV_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="Input"
               data-major-ticks="0 30 60 90 120 150"
               data-minor-ticks="6"           
               data-units="Volts"
               data-value-format="3.1"
               data-max-value="150"
               data-min-value="0"
               data-highlights="0 150 #ffe"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('CV_gauge').setValue(Cvin_val);
},500);">
    </canvas>
    </td>
    <td>
    </canvas>
    <canvas id="CW_gauge" width="450" height="450"
               data-type="canv-gauge"
               data-title="Power"
               data-major-ticks="0 250 500 750 1000 1250 1500 1750 2000"
               data-minor-ticks="5"           
               data-units="Watts"
               data-value-format="4.0"
               data-max-value="2000"
               data-min-value="0"
               data-color-plate="#0FF"
               data-highlights="0 2000 #FFe"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('CW_gauge').setValue(Cwatts_val);
},500);">
    </canvas>
    </td>

    <td>
    </canvas>
    <canvas id="Ca_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="Output"
               data-major-ticks="0 10 20 30 40 50 60 70 80 90 100"
               data-minor-ticks="4"           
               data-units="Amps"
               data-value-format="2.1"
               data-max-value="100"
               data-min-value="0"
               data-highlights="0 100 #ebffd6 "
               data-onready="setInterval( function ()
{ Gauge.Collection.get('Ca_gauge').setValue(Camps_val);
},500);">
    </canvas>
    </td>
    </tr>
</table>

<table>
<tr>
    <td width="100"></td>
    <td>
    </td>
    <td>
    <canvas id="BV_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="Battery"
               data-major-ticks="44 46 48 50 52 54 56 58 60 62"
               data-minor-ticks="5"           
               data-units="Volts"
               data-value-format="2.1"
               data-max-value="62"
               data-min-value="44"
               data-highlights="44 46 #F00, 46 48 #ff1, 48 58 #0F1, 58 60 #ff1, 60 62 #F02"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('BV_gauge').setValue(batV_val);
},500);">
    </canvas>
    </td>
    <td>
  <svg width="450" height="350">
  <defs>
    <radialGradient id="gradsq" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
      <stop offset="0%" style="stop-color:rgb(255,255,255);stop-opacity:0" />
      <stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
    </radialGradient>
    <filter id="filter" x="0" y="0">
      <feGaussianBlur stdDeviation="5" />
      <feOffset dx="5" dy="5" />
    </filter>
  </defs>

  <rect x="10" y="20" rx="20" ry="20" width="430" height="330" style="fill:blue;stroke:black;stroke-width:5;opacity:0.8" />
  <text font-size="20" font-family="Verdana" x="25" y="56" fill="white">
          <tspan id="CL_NAME">........</tspan> C<tspan id="CL_MODEL">...</tspan> #<tspan id="CL_SERNO"></tspan>
          </text>
 
  <text id="CL_STATE" font-size="25" font-family="Verdana" x="25" y="96" fill="white"></text>

  <text font-size="20" font-family="Verdana" x="25" y="131" fill="white">Energy kwh <tspan id="CL_KWH">...</tspan>    Battery SOC <tspan id="CL_SOC">...</tspan>%</text>
   <text font-size="20" font-family="Verdana" x="25" y="166" fill="white">Battery Ah Net <tspan id="CL_AH">.....</tspan> remaining      <tspan id="CL_AHR">....</tspan></text>
   
  <text font-size="20" font-family="Verdana" x="25" y="201" fill="white">Temps oC</text>
  <text font-size="18" font-family="Verdana" x="150" y="201" fill="white">Bat <tspan id="Bat_Temp">....</tspan></text>
  <text font-size="18" font-family="Verdana" x="275" y="201" fill="white">Fet <tspan id="Fet_Temp">....</tspan></text>
  <text font-size="18" font-family="Verdana" x="150" y="236" fill="white">Pcb <tspan id="Pcb_Temp">....</tspan></text>
  <text font-size="18" font-family="Verdana" x="275" y="236" fill="white">Rem <tspan id="Rem_Temp">....</tspan></text>

  <text font-size="20" font-family="Verdana" x="25" y="271" fill="white">Peak Power <tspan id="Peak_Pwr">....</tspan> watts at <tspan      id="Peak_Time">........</tspan>
         </text>

  <a xlink:href="node14.htm">
  <rect x="25" y="300" rx="5" ry="5" width="80" height="30" style="fill:yellow;stroke:black;stroke-width:2;opacity:1" />
  <text class="text" font-size="20" x="35" y="322"  fill="blue" text-anchor="start">10mlog</text>
  </a>
  <a xlink:href="node17.htm">
  <rect x="125" y="300" rx="5" ry="5" width="80" height="30" style="fill:yellow;stroke:black;stroke-width:2;opacity:1" />
  <text class="text" font-size="20" x="135" y="322"  fill="blue" text-anchor="start">Daylog</text>
  </a>

  Sorry, your browser does not support inline SVG.
    </svg>
    </td>
    <td>

    <canvas id="BA_gauge" width="350" height="350"
               data-type="canv-gauge"
               data-title="WBjr"
               data-major-ticks="-40 -30 -20 -10 0 10 20 30 40"
               data-minor-ticks="4"           
               data-units="Amps"
               data-value-format="2.1"
               data-max-value="40"
               data-min-value="-40"
               data-colors-plate="#fff"
               data-highlights="-40 0 #ffcccc, 0 40 #ebffd6"
               data-onready="setInterval( function ()
{ Gauge.Collection.get('BA_gauge').setValue(batA_val);
},500);">
    </canvas>
    </td>
</tr>
</table>
</html>
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on November 15, 2015, 07:08:25 PM
The index.htm page listed in previous post can be directly run by a PC's local browser and although it shows the gauge layouts it is not retreiving data from a Classic.
This is not too difficult to do and would need a simple wrapper to call an ethernet/modbus data extractor to pull register values from an ethernet connected Classic and use AJAX to interface the data to the gauges display htm file.

I sort of left this with several other people who had enquired about it but if there is enough interest I should get time soon to get some JS code together to create a gauges App for any system running a web browser that supports html5

{I had not pursued this approach while the Classic had ethernet reliability issues but now that it appears these may be getting resolved it may be worth revisiting  :) }

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on December 12, 2015, 07:11:31 PM
I have had quite a few emails and there seems to be some confusion about how this Arduino based web server for MN Classic is designed to operate.

The web pages listed in previous postings are simply just HTML5 pages BUT they are designed to be served by the Arduino to a connecting web browser. The Arduino is talking to the Classic and retrieving the running data (Volts, Amps, Watts etc..) then communicating this data every second (or so) to the user's web browser, this is done by a type of HTML file (called an XML file) to just refresh the data readout parts of the display (the gauge dials and
data fields in the svg blue box). This means the whole page does not have to be refreshed every time running data updates (which would flicker the display and slow everything down)

For those that have downloaded the web pages, especially index.htm, and just clicked on the file, then because the file is an HTML5 file, your computer is executing it using your web browser.
This will draw the gauges display on your screen.
The reason there is no data values from your Classic being shown in the display is because your browser cannot find the one-second-refreshed-XML file named ajax_inputs which the index.htm is trying to load.

To create this file a program will be needed to read the required data from the Classic, either over ethernet or serial port. This needs to use modbus protocols to achieve this, then format the output into an XML file named ajax_inputs
Unfortunately this is not simple to do, but is very possible.
(Sorry I have run out of time to do this very soon).
Anyone interested can get a good start from RossW's ethernet/modbus
software newmodbus to extract data from the Classic.
AFAIK this is for Linux (unix) systems only, therefore should be straightforward to interface
to the gauges HTML5 page.

I hope this explains that the gauges page is not broken or has a bug, it is just not designed to run stand alone on a PC etc..

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: ClassicCrazy on January 08, 2016, 01:25:42 AM
dgd
I just saw this info on getting data to / from Outback inverters without using the Mate . Is there anything else or anyone who has it figured out ?
https://hackaday.io/project/7624-outback-mate-reverse-engineering

Larry
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on January 10, 2016, 06:16:39 PM
One of the otherpower forums had this link and mid last year I had emailed to me the packet structure of data between the MX60 and mate from someone who analysed the data stream.
Also in very early docs supplied with new MX60s there was an appendix that detailed the structure of data from the serial port, this was removed from the docs when mates appeared.
When I used a mate to look at MX60 data, from its rs232 port, I was disappointed to see the MX60 over reporting its power output. I tried 3 MX60 and they all did the same. A five year old FM80 was no better.
I came to the conclusion the MX60 may be a good mppt controller but  exaggerating power production by 10 to 15% was just fraudulent and invalidated any efforts to create an accurate power production web reporting system.
Figures that inaccurate were useless and IMHO makes OB controllers useless.
If this inaccuracy has always existed with OB controllers as opposed to being an age related degrading them I'm surprised they got away with it for so long.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Westbranch on January 10, 2016, 07:42:16 PM
Quote from: dgd on January 10, 2016, 06:16:39 PM
If this inaccuracy has always existed with OB controllers as opposed to being an age related degrading them I'm surprised they got away with it for so long.

Without detailed knowledge and the wherewithal to get it,  how  would one ever know???
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on January 10, 2016, 11:20:23 PM
Quote from: Westbranch on January 10, 2016, 07:42:16 PM
Without detailed knowledge and the wherewithal to get it,  how  would one ever know???

You would never know unless for some reason you connected an ammeter on MX60 output and measured the real output current. Then the real power watts could be calculated and compared to what the MX displayed on its LCD screen.
This was unlikely to happen as people will believe the MX60 reported output is true.

It would be nice to think the later FM controllers were more accurate on reporting power output, I won't be buying one to find out  :)
The MN SLs are what I will be buying

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Halfcrazy on January 17, 2016, 01:57:02 PM
Yeah back when Robin and boB did the Outback controllers accuracy did not seem real important. The Outback would "Usually" read on the high side and that was actually not designed in but. The issue with all manufacturers is not knowing where the future is going. But we really need to strive for accuracy as data is one of the most important things now. 5 years hardly anyone hooked a pc to the solar system but today I bet it is a very high percentage.
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on February 21, 2016, 04:58:53 AM
Its a while since I updated progress with DUE web server.
Here is the HTML5 page I am using for multiple Classic systems web monitoring page. I only obtained enough Classics recently to fully test this web page with four Classics including a 150SL (its serial modbus only no ethernet)
I was also able to test with a KID but the KID firmware needs additional data items made available via serial port for the web page to be useful.

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: Resthome on February 21, 2016, 03:13:18 PM
Nice job dgd.
Title: Re: Web server for Classic using Arduino DUE
Post by: ClassicCrazy on February 21, 2016, 04:35:01 PM
Looks good !
Title: Re: Web server for Classic using Arduino DUE
Post by: dgd on February 21, 2016, 05:51:50 PM
Thanks John,
I'm also working with a friend who develops MS windows applications who is debugging my W10 program that collects Classic modbus data over ethernet and then sends it to the html5 page for display. I hope this will get finished soon  :)

Larry, I see you have an MS MPPT60 and a Classic. If you ever decide to try an Arduino web server then I have all the code to interface the MPPT60 so the HTML5 page above could have a line with the MPPT60 and show in gauges their combined output etc.. :D

dgd
Title: Re: Web server for Classic using Arduino DUE
Post by: ClassicCrazy on February 23, 2016, 10:41:01 AM
dgd
Thanks for the offer on the Morningstar software.
I just took it off line though - installing updated system with Midnite controller and lithium batteries.
But I will try out your code on the Classics one of these days .
Larry

Title: Re: Web server for Classic using Arduino DUE
Post by: Gabriel Tanasescu on October 10, 2017, 11:34:43 AM
I use the attached web server.
Also I want to use for Nidnite by Arduino .
This web  sever ia also Arduino dedicated .
Title: Re: Web server for Classic using Arduino DUE
Post by: binkino on October 10, 2017, 12:42:08 PM
Quote from: dgd on February 21, 2016, 04:58:53 AM
Its a while since I updated progress with DUE web server.
Here is the HTML5 page I am using for multiple Classic systems web monitoring page. I only obtained enough Classics recently to fully test this web page with four Classics including a 150SL (its serial modbus only no ethernet)
I was also able to test with a KID but the KID firmware needs additional data items made available via serial port for the web page to be useful.

dgd

hey, also looks great :)