chart.tc¶
Google Line Chart — 1000 data points stored in array
// Google Line Chart — 1000 data points stored in array
// Demonstrates: heap arrays, WebPage, WebCall, webSend, webFlush, random()
// WebPage draws the chart once, EverySecond shifts new data in
// WebCall shows a sensor-style status row (refreshed periodically)
#define N 1000
int data[N]; // ring buffer of sensor values (heap-allocated, >255)
int pos = 0; // write position (ring buffer head)
int counter = 0;
// Simple integer sine approximation (x in 0..399 period, returns -100..100)
int isin(int x) {
int p = x % 400;
if (p < 0) p = p + 400;
if (p < 100) return p;
if (p < 200) return 200 - p;
if (p < 300) return -(p - 200);
return -(400 - p);
}
void EverySecond() {
// Add a new data point each second (sine + noise)
int s = isin(counter);
int noise = random(-15, 15);
data[pos] = s + noise;
pos++;
if (pos >= N) pos = 0;
counter++;
}
void WebPage() {
// Called once when page is drawn (FUNC_WEB_ADD_MAIN_BUTTON)
// Perfect for charts, custom HTML, scripts that should load once
char buf[128];
int i;
// ── Chart container and Google Charts loader ──
webSend("<div id=\"tc_chart\" style=\"width:100%;height:400px;\"></div>");
webSend("<script src=\"https://www.gstatic.com/charts/loader.js\"></script>");
webSend("<script>");
webSend("google.charts.load('current',{packages:['corechart']});");
webSend("google.charts.setOnLoadCallback(function(){");
webSend("var d=new google.visualization.DataTable();");
webSend("d.addColumn('number','X');");
webSend("d.addColumn('number','Value');");
webSend("d.addColumn('number','Avg');");
webSend("d.addRows([");
webFlush();
// ── Output all 1000 data points from ring buffer ──
int avg = 0;
i = 0;
while (i < N) {
// Read from ring buffer: oldest first
int idx = pos + i;
if (idx >= N) idx = idx - N;
int val = data[idx];
// Running average (smoothing filter)
avg = (avg * 9 + val * 10) / 10;
int smooth = avg / 10;
// Format: [x, value, avg],
sprintf(buf, "[%d,%d,%d]", i, val, smooth);
if (i < N - 1) {
strcat(buf, ",");
}
webSend(buf);
// Flush every 100 rows to keep buffer manageable
if (i % 100 == 99) {
webFlush();
}
i++;
}
// ── Chart options and render ──
webSend("]);");
webSend("var o={title:'TinyC Live Data (1000 pts)',");
webSend("curveType:'none',legend:{position:'bottom'},");
webSend("hAxis:{title:'Sample'},vAxis:{title:'Value'},");
webSend("chartArea:{width:'80%',height:'70%'},");
webSend("series:{0:{lineWidth:1,color:'#89b4fa'},");
webSend("1:{lineWidth:2,color:'#f38ba8'}}};");
webSend("var c=new google.visualization.LineChart(");
webSend("document.getElementById('tc_chart'));");
webSend("c.draw(d,o);});");
webSend("</script>");
webFlush();
}
void WebCall() {
// Called periodically for sensor display (FUNC_WEB_SENSOR)
// Shows a status row that auto-refreshes
char buf[64];
sprintf(buf, "{s}TinyC Chart{m}%d samples{e}", counter);
webSend(buf);
}
void JsonCall() {
char buf[64];
sprintf(buf, ",\"TinyC\":{\"Samples\":%d}", counter);
responseAppend(buf);
}
int main() {
// Fill array with initial data
int i = 0;
while (i < N) {
data[i] = isin(i) + random(-15, 15);
i++;
}
pos = 0;
counter = 0;
printStr("Chart demo active\n");
printStr("Open Tasmota web page to see the chart\n");
return 0;
}