Skip to content

Instantly share code, notes, and snippets.

@steveio
Last active February 27, 2020 01:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steveio/a4684775057ae1a553b5e5cf45a7cd24 to your computer and use it in GitHub Desktop.
Save steveio/a4684775057ae1a553b5e5cf45a7cd24 to your computer and use it in GitHub Desktop.
Arduino Sensor Data Logger - DH11 Temp, Humidity, LDR Light

D3.js (v3) Line Charts Displaying rduino - Sensor Data: Temp, Humidity, LDR

  • Captured via Micro SD data logger on 25/02/2020
  • Arduino Mega2560 w/ DHT11, LDR ( https://learn.adafruit.com/photocells ) & DH1307 RTC
  • Indoor south facing window location in direct sunlight

D3.js Chart Features:

  • Line Chart loaded from CSV format data
  • Scatter plot data points with mouseover labels
  • HTML Legend
  • Dual y-Axis
  • Mean and Standard Deviation
  • Data manipulation - calculation of vOut to Lux
  • **
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Arduino sensor.meteo D3.js data vis chart</title>
<meta name="description" content="">
<meta name="author" content="">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
/* stroke: steelblue;*/
fill: none;
}
.chart-container {
float: left;
width: 100%;
margin-bottom: 20px;
}
.line {
fill: none;
stroke-width: 2;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.dot {
stroke: none;
}
.tooltip {
position: absolute;
font-size: 12px;
width: auto;
height: auto;
pointer-events: none;
background-color: white;
}
/* basic positioning */
.legend { list-style: none; }
.legend li { float: left; margin-right: 10px; }
.legend span { border: 1px solid #ccc; float: left; width: 12px; height: 12px; margin: 2px;}
/* your colors */
.legend .LDR { background-color: #FFFF00; }
.legend .LUX { background-color: #FF0000; }
.legend .TEMP { background-color: #FF7F0E; }
.legend .HUMIDITY { background-color: #1f77b4; }
</style>
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v4.js"></script>
<!--=<script src="https://d3js.org/d3.v5.min.js"></script>-->
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-lg-12">
<div class="chart-container">
<div id="my_dataviz_LDR"></div>
<ul class="legend">
<li><span class="LUX"></span> Lux</li>
<li><span class="LDR"></span> LDR (Vout)</li>
</ul>
</div>
<div class="chart-container">
<div id="my_dataviz_TEMP"></div>
<ul class="legend">
<li><span class="TEMP"></span> Temp (ºC)</li>
</ul>
</div>
<div class="chart-container">
<div id="my_dataviz_HUMIDITY"></div>
<ul class="legend">
<li><span class="HUMIDITY"></span> Humidity (%)</li>
</ul>
</div>
</div>
<div class="row">
<div class="col-lg-12">
</div>
</div>
</div>
<div class="row" style="float: left; width: 100%;">
<div class="col-lg-12">
<h3>Arduino - Sensor Data: Temp, Humidity, LDR</h3>
<p>Captured via Micro SD data logger on 25/02/2020</p>
<p>Generated by Arduino Mega2560 w/ DHT11, LDR ( https://learn.adafruit.com/photocells ) & DH1307 RTC in indoor window sill environment </p>
<p>Served from localhost (Ubuntu) using python -m SimpleHTTPServer 3001</p>
</div>
</div>
</div>
<script>
// set the dimensions and margins of the graph
var margin = {top: 10, right: 30, bottom: 50, left: 60},
width = 800 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
/*
var margin = {top: 50, right: 50, bottom: 50, left: 50}
, width = window.innerWidth - margin.left - margin.right // Use the window's width
, height = window.innerHeight - margin.top - margin.bottom; // Use the window's height
*/
// append the svg object to the body of the page
var svg_LDR = d3.select("#my_dataviz_LDR")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var svg_TEMP = d3.select("#my_dataviz_TEMP")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var svg_HUMIDITY = d3.select("#my_dataviz_HUMIDITY")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
//Read the data
//d3.json("http://127.0.0.1:3001/sensor.meteo.json", function(data) {
d3.csv("SENSOR.csv", function(data) {
var tempC = [];
var lux = [];
var humidity = [];
data.forEach(function(d){
d.ts = new Date(d.ts * 1000)
// Calculate LDR vOut as LUX
var res=10.0; // Resistance in the circuit of sensor 0 (KOhms)
var vOut = d.LDR * 0.0048828125; // calculate the voltage
//d.lux = 500 / (res * ((5-vOut) / vOut) ); // calculate the Lux
d.lux=(2500/vOut-500)/res;
tempC.push(d.tempC)
lux.push(d.lux)
humidity.push(d.humidity)
});
data.stats = {}
data.stats.tempC = calculateMeanSD(tempC);
data.stats.lux = calculateMeanSD(lux);
data.stats.humidity = calculateMeanSD(humidity);
console.log(data.stats);
// for each x tick append mean & sd (seems like data duplication)
data.forEach(function(d){
d.tempC_mean = data.stats.tempC.mean;
d.tempC_sd = data.stats.tempC.sd;
d.lux_mean = data.stats.lux.mean;
d.lux_sd = data.stats.lux.sd;
d.humidity_mean = data.stats.humidity.mean;
d.humidity_sd = data.stats.humidity.sd;
});
renderVis(data);
});
var calculateMeanSD = function(data) {
var n = data.length;
var m = data.reduce(function (a,b) { return Number(a) + Number(b) }) / n;
var sd = Math.sqrt(data.reduce(function (sq, n) {
return sq + Math.pow(n - m, 2);
}, 0) / (data.length - 1));
var o = {};
o.items = n;
o.mean = m;
o.sd = sd;
return o;
}
var renderVis = function(data) {
// Add X axis --> it is a date format
var x = d3.scaleTime()
.domain(d3.extent(data, function(d) { return d.ts; }))
.range([ 0, width ]);
svg_LDR.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%H:%M %p"))
.ticks(15))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)")
;
svg_TEMP.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%H:%M %p"))
.ticks(15))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)")
svg_HUMIDITY.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x))
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%H:%M %p"))
.ticks(15))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)")
// Add Y axis (vOut)
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.lux; })])
.range([ height, 0 ]);
// Add y2 axis (Lux)
var y1 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.LDR; })])
.range([ height, 0 ]);
svg_LDR.append("g")
.call(d3.axisLeft(y1));
svg_LDR.append("g")
.call(d3.axisRight(y))
.attr('transform', 'translate(' + (700) + ', 0)');
svg_LDR.append("text")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr("y", -margin.right+720)
.attr("x", -margin.top)
.text("LDR (vOut)")
svg_LDR.append("text")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left+20)
.attr("x", -margin.top)
.text("Light (Lux)")
// Add the line
svg_LDR.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#FFFF00")
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.LDR) })
.curve(d3.curveMonotoneX) // apply smoothing to the line
)
// Add the line
svg_LDR.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#FF0000")
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.lux) })
.curve(d3.curveMonotoneX) // apply smoothing to the line
)
// Plot Mean & Standard Deviation
svg_LDR.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#808080")
.style("stroke-dasharray", ("3, 3"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.lux_mean) })
)
svg_LDR.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#C0C0C0")
.style("stroke-dasharray", ("5, 5"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.lux_mean + d.lux_sd) })
)
var tooltip_LDR = d3.select("#my_dataviz_LDR").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// tooltip mouseover event handler
var tipMouseover_LDR = function(d) {
var color = "#FFFF00";
var html = "<b>" + d.LDR + "</b> LDR </b> (vOut)"
tooltip_LDR.html(html)
.style("left", (d3.event.pageX + 15) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.transition()
.duration(200) // ms
.style("opacity", .9) // started as 0!
;
};
// tooltip mouseout event handler
var tipMouseout_LDR = function(d) {
tooltip_LDR.transition()
.duration(300) // ms
.style("opacity", 0); // don't care about position!
};
// Add the scatterplot
svg_LDR.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.ts); })
.attr("cy", function(d) { return y(d.LDR) + 7; })
.style("fill", function(d) { return "#FFFF00" })
.on("mouseover", tipMouseover_LDR)
.on("mouseout", tipMouseout_LDR);
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.tempC; })])
.range([ height, 0 ]);
svg_TEMP.append("g")
.call(d3.axisLeft(y));
svg_TEMP.append("text")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left+20)
.attr("x", -margin.top)
.text("Temperature (ºC)")
// Add the line
svg_TEMP.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#FF7F0E")
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.tempC) })
)
// Plot Mean & Standard Deviation
svg_TEMP.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#808080")
.style("stroke-dasharray", ("3, 3"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.tempC_mean) })
)
svg_TEMP.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#C0C0C0")
.style("stroke-dasharray", ("5, 5"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.tempC_mean + d.tempC_sd) })
)
svg_TEMP.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#C0C0C0")
.style("stroke-dasharray", ("5, 5"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.tempC_mean - d.tempC_sd) })
)
var tooltip_TEMP = d3.select("#my_dataviz_TEMP").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// tooltip mouseover event handler
var tipMouseover_TEMP = function(d) {
var color = "#FF7F0E";
//var color = colorScale(d.LDR);
var html = "<b>" + d.tempC + "</b> TEMP (Celcius)"
tooltip_TEMP.html(html)
.style("left", (d3.event.pageX + 15) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.transition()
.duration(200) // ms
.style("opacity", .9) // started as 0!
;
};
// tooltip mouseout event handler
var tipMouseout_TEMP = function(d) {
tooltip_TEMP.transition()
.duration(300) // ms
.style("opacity", 0); // don't care about position!
};
// Add the scatterplot
svg_TEMP.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.ts); })
.attr("cy", function(d) { return y(d.tempC) + 7; })
.style("fill", function(d) { return "#FF7F0E" })
.on("mouseover", tipMouseover_TEMP)
.on("mouseout", tipMouseout_TEMP);
// Add Y axis
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return +d.humidity; })])
.range([ height, 0 ]);
svg_HUMIDITY.append("g")
.call(d3.axisLeft(y));
svg_HUMIDITY.append("text")
.attr("text-anchor", "end")
.attr("transform", "rotate(-90)")
.attr("y", -margin.left+20)
.attr("x", -margin.top)
.text("Humidity (%)")
// Add the line
svg_HUMIDITY.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#1f77b4")
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.humidity) })
)
// Plot Mean & Standard Deviation
svg_HUMIDITY.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#808080")
.style("stroke-dasharray", ("3, 3"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.humidity_mean) })
)
svg_HUMIDITY.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#C0C0C0")
.style("stroke-dasharray", ("5, 5"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.humidity_mean + d.humidity_sd) })
)
svg_HUMIDITY.append("path")
.datum(data)
.attr("class", "line")
.attr("fill", "none")
.attr("stroke", "#C0C0C0")
.style("stroke-dasharray", ("5, 5"))
.attr("d", d3.line()
.x(function(d) { return x(d.ts) })
.y(function(d) { return y(d.humidity_mean - d.humidity_sd) })
)
var tooltip = d3.select("#my_dataviz_HUMIDITY").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// tooltip mouseover event handler
var tipMouseover = function(d) {
var color = "#1f77b4";
var html = "<b>" + d.humidity + "</b> Humidity (%)"
tooltip.html(html)
.style("left", (d3.event.pageX + 15) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.transition()
.duration(200) // ms
.style("opacity", .9) // started as 0!
;
};
// tooltip mouseout event handler
var tipMouseout = function(d) {
tooltip.transition()
.duration(300) // ms
.style("opacity", 0); // don't care about position!
};
// Add the scatterplot
svg_HUMIDITY.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 3.5)
.attr("cx", function(d) { return x(d.ts); })
.attr("cy", function(d) { return y(d.humidity) + 7; })
.style("fill", function(d) { return "#1f77b4" })
.on("mouseover", tipMouseover)
.on("mouseout", tipMouseout);
};
</script>
</body>
</html>
ts tempC tempF humidity LDR
1582636955 19.00 66.20 57.00 151
1582636965 19.00 66.20 57.00 137
1582636975 19.00 66.20 57.00 162
1582636985 19.00 66.20 57.00 129
1582636995 19.00 66.20 57.00 129
1582637005 19.00 66.20 57.00 125
1582637015 19.00 66.20 57.00 138
1582637025 19.00 66.20 57.00 150
1582637035 19.11 66.39 57.00 170
1582637045 19.11 66.39 57.00 170
1582637055 19.11 66.39 57.00 141
1582637403 19.11 66.39 57.00 152
1582637413 19.21 66.56 58.00 150
1582637426 19.21 66.56 58.00 145
1582637456 19.21 66.56 57.00 19
1582637648 19.71 67.46 57.00 30
1582637658 22.40 72.32 51.00 29
1582637668 22.50 72.50 50.00 30
1582637678 22.50 72.50 50.00 29
1582637688 22.61 72.69 50.00 30
1582637698 22.61 72.69 50.00 31
1582637884 22.71 72.87 49.00 41
1582637922 22.71 72.87 50.00 41
1582639202 22.71 72.87 50.00 99
1582640102 21.40 70.52 57.00 261
1582641002 20.50 68.90 58.00 43
1582641902 21.11 69.98 58.00 103
1582642802 20.30 68.54 60.00 54
1582643702 20.90 69.62 59.00 31
1582644602 22.00 71.60 58.00 92
1582645502 20.90 69.62 63.00 57
1582646403 20.90 69.62 62.00 19
1582647303 28.71 83.67 42.00 64
1582648203 25.21 77.37 49.00 85
1582649103 23.40 74.12 56.00 58
1582650003 23.30 73.95 57.00 86
1582650903 22.50 72.50 60.00 263
1582651803 21.00 69.81 65.00 296
1582652704 20.21 68.37 67.00 595
1582653604 19.71 67.46 68.00 930
1582654504 19.50 67.10 68.00 1000
1582655404 19.40 66.92 69.00 1005
1582656304 19.30 66.74 69.00 1018
1582657204 19.21 66.56 69.00 1021
1582658104 19.11 66.39 69.00 1021
1582659004 19.00 66.20 70.00 1021
1582659904 19.00 66.20 70.00 1021
1582660804 19.00 66.20 70.00 1021
1582661705 18.80 65.84 70.00 1021
1582662605 18.80 65.84 70.00 1021
1582663505 18.80 65.84 70.00 1021
1582664405 18.71 65.67 70.00 1021
1582665305 18.71 65.67 70.00 1021
1582666205 18.71 65.67 70.00 1021
1582667105 18.61 65.48 70.00 1021
1582668005 18.50 65.31 71.00 1021
1582668905 18.50 65.31 71.00 1021
1582669806 18.40 65.12 71.00 1021
1582671608 18.11 64.59 72.00 1021
1582672508 18.00 64.40 72.00 1021
1582673408 17.80 64.04 72.00 1021
1582674308 17.71 63.87 72.00 1021
1582675208 17.50 63.50 73.00 1021
1582676108 17.40 63.32 73.00 1021
1582677008 17.21 62.96 73.00 1021
1582677908 17.11 62.78 73.00 1022
1582678809 17.00 62.60 73.00 1022
1582679709 16.80 62.24 73.00 1022
1582680609 16.71 62.07 73.00 1022
1582681509 16.71 62.07 73.00 1022
1582682409 16.50 61.70 74.00 1022
1582683309 16.40 61.52 74.00 1022
1582684209 16.30 61.34 74.00 1022
1582685109 16.21 61.16 74.00 1021
1582686009 16.11 60.98 74.00 1022
1582686909 16.00 60.80 75.00 1022
1582687809 16.00 60.80 75.00 1022
1582688709 15.81 60.45 74.00 1022
1582689610 15.81 60.45 74.00 1022
1582690510 15.70 60.26 74.00 1022
1582691410 15.70 60.26 74.00 1022
1582692310 15.61 60.09 74.00 1022
1582693210 15.50 59.91 74.00 1022
1582694110 15.50 59.91 74.00 1022
1582695010 15.40 59.73 74.00 1022
1582695910 15.40 59.73 74.00 1022
1582696810 15.31 59.55 74.00 1022
1582697710 15.20 59.37 74.00 1021
1582698610 15.11 59.19 74.00 1014
1582699510 15.11 59.19 74.00 886
1582700410 15.11 59.19 74.00 511
1582701311 15.00 59.00 74.00 291
1582702211 15.11 59.19 73.00 205
1582703111 15.11 59.19 73.00 165
1582704011 15.11 59.19 73.00 144
1582704911 15.20 59.37 73.00 130
1582705811 15.20 59.37 72.00 119
1582706711 15.31 59.55 72.00 91
1582707611 15.70 60.26 71.00 74
1582708511 16.11 60.98 70.00 69
1582709411 16.80 62.24 68.00 66
@steveio
Copy link
Author

steveio commented Feb 26, 2020

D3.js (v3) Line Charts Displaying rduino - Sensor Data: Temp, Humidity, LDR

  • Captured via Micro SD data logger on 25/02/2020
  • Arduino Mega2560 w/ DHT11, LDR ( https://learn.adafruit.com/photocells ) & DH1307 RTC
  • Indoor south facing window location in direct sunlight

D3.js Chart Features:

  • Line Chart loaded from CSV format data
  • Scatter plot data points with mouseover labels
  • HTML Legend
  • Dual y-Axis
  • Data manipulation - calculation of vOut to Lux
  • **

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment