Web server for Classic using Arduino DUE

Started by dgd, March 12, 2015, 01:00:24 AM

Previous topic - Next topic

dgd

#60
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>
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

#61
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>

Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

#62
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>
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

paul alting

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
6 x 200W PV into home-brew 6 stage MOSFET charge controller : Microhydro 220Vac 3 phase IMAG
8 x 400Ah LiFeYPO4 Winston : Latronics LS2412 inverter
QuadlogSCADA control and monitoring system : Tasmania, Australia : http://paulalting.com

dgd

#64
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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

paul alting

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
6 x 200W PV into home-brew 6 stage MOSFET charge controller : Microhydro 220Vac 3 phase IMAG
8 x 400Ah LiFeYPO4 Winston : Latronics LS2412 inverter
QuadlogSCADA control and monitoring system : Tasmania, Australia : http://paulalting.com

dgd

#66
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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

#67
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;
}

Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

paul alting

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
6 x 200W PV into home-brew 6 stage MOSFET charge controller : Microhydro 220Vac 3 phase IMAG
8 x 400Ah LiFeYPO4 Winston : Latronics LS2412 inverter
QuadlogSCADA control and monitoring system : Tasmania, Australia : http://paulalting.com

dgd

#70
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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

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>
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand

dgd

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
Classic 250, 150,  20 140w, 6 250w PVs, 2Kw turbine, MN ac Clipper, Epanel/MNdc, Trace SW3024E (1997), Century 1050Ah 24V FLA (1999). Arduino power monitoring and web server.  Off grid since 4/2000
West Auckland, New Zealand