Control and view a network of Particle Mesh devices, seeing it all on a touchscreen, along with a calendar.
Things used in this project
Hardware components
Particle Argon
Particle Xenon
Raspberry Pi 3 Model B+
DHT11 Temperature & Humidity Sensor (4 pins)
ELEGOO Upgraded 37 in 1 Sensor Modules Kit V2.0
DFRobot 5in Touchscreen
Software apps and online services
Online Particle IDE
Raspberry Pi Raspbian
Google Calendar
Hand tools and fabrication machines
3D Printer (generic)
Story
In my previous project I demonstrated how I set up and tested the Particle Mesh network I created. For this project, I wanted to show how the network concept can be applied to a smart home. Since Elegoo sent me their New 37 in 1 Sensor Kit, I decided to use that as well to monitor the light levels of Christmas lights, along with setting a custom RGB LED color all via web interface.
The Concept
As with any great project, it all starts with a great plan. My plan was to create 3 identical sets of Xenon circuits by connecting a photoresistor and a common-cathode RGB LED to each Xenon board. Next I would create some kind of webpage that could read the light levels and also set the color of the LED for each board. This webpage would be displayed on a touchscreen connected to a Raspberry Pi, where a user could switch between the webpage and a Google calendar.
Raspberry Pi
I used a 5 inch Raspberry Pi touchscreen from DFRobot as my display.
For the OS, I loaded Raspbian onto an SD card and then booted up the Pi. Then I installed Firefox by using sudo apt-get install iceweasel and then restarting. I then opened Firefox and went to https://calendar.google.com. After signing in to my Google account, I set the browser to fullscreen mode, mainly to hide the navigation bar at the top.
Particle Mesh Network
My previous project https://www.hackster.io/gatoninja236/introduction-to-particle-mesh-7e393d went through the steps of setting up the network. After going through the setup process, I flashed the attached code to each of the Xenons. Four pins are used in the circuit, A0 for the photo-resistor, and A1, A2, and A4 for the LED.
Webpage
This was the hardest part, as I don't have any formal training, nor have I taken any classes, in web design. I simply began by sketching out how I wanted the webpage to function: 3 large buttons displaying information about each board, and when clicked, they allow the user to change the color of the LED.
CSS allows for the creation of modal displays, which function like a dialog box that sits on top of the window, and can disappear when closed. I used range sliders for each of the three colors (R, G, B), that when changed, display a preview in a box. Then the user can send that color to the chosen Xenon by clicking a button.
In order to get the light values to update regularly, I used the setInterval function that calls another function to get the values for each Xenon's cloud variable named "light_level". The color for the LED is sent as a string in the "RRRGGGBBB" format.
Video
Code
Webpage HTML
<html>
<head>
<title>Mesh Smart Home</title>
<link rel="stylesheet" href="https://www.w3schools.com/w3css/4/w3.css">
<script type="text/javascript" src="https://cdn.jsdelivr.net/particle-api-js/5/particle.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body class="w3-content" style="max-width:550px;">
<div id="main_container" class="w3-display-container w3-white" style="height:300px;">
<div id="xenon_list" class="w3-display-middle">
<button onclick="document.getElementById('color_change').style.display='block'; selected=devices.xenon1" class="w3-button xenon_select">
<div class="button_content">
<p>Xenon1</p>
<p id="light_lvl1">Light level: 0</p>
</div>
</button>
<button onclick="document.getElementById('color_change').style.display='block'; selected=devices.xenon2" class="w3-button xenon_select">
<div class="button_content">
<p>Xenon2</p>
<p id="light_lvl2">Light level: 0</p>
</div>
</button>
<button onclick="document.getElementById('color_change').style.display='block'; selected=devices.xenon3" class="w3-button xenon_select">
<div class="button_content">
<p>Xenon3</p>
<p id="light_lvl3">Light level: 0</p>
</div>
</button>
</div>
<div id="color_change" class="w3-modal" style="width:100%;">
<div class="w3-modal-content w3-animate-top">
<div class="w3-container" style="margin: auto; width: 500px;">
<span onclick="document.getElementById('color_change').style.display='none'" class="w3-button w3-display-topright">×</span>
<p>Change the color!</p>
<div class="slider_container">
<label for="red">Red</label><br>
<input type="range" min=0 max=255 value=0 class="slider" id="red" name="red"
onchange="updateColor(this.value,green.value,blue.value)"><br>
<label for="green">Green</label><br>
<input type="range" min=0 max=255 value=0 class="slider" id="green" name="green"
onchange="updateColor(red.value, this.value, blue.value)"><br>
<label for="blue">Blue</label><br>
<input type="range" min=0 max=255 value=0 class="slider" id="blue" name="blue"
onchange="updateColor(red.value, green.value, this.value)"><br>
</div>
<div id="final_color">
<button class="color_button w3-button" id="color_btn"></button>
<button onclick="updateXenon(red.value,green.value,blue.value)" class="w3-btn w3-round w3-medium w3-blue" id="submit_color">Change color</button>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Webpage Javascript
var particle = new Particle();
const token = "access token here";
var selected = "";
const devices = {xenon1:"device ids",xenon2:"",xenon3:"",argon:""};
function updateColor(r, g ,b){
var color_btn = document.getElementById("color_btn");
var red = Number(r);
var green = Number(g);
var blue = Number(b);
console.log(rgbToHex(red,green,blue));
$('#color_btn').css("background-color",rgbToHex(red,green,blue));
}
const rgbToHex = (r, g, b) => '#' + [r, g, b].map(x => {
const hex = x.toString(16)
return hex.length === 1 ? '0' + hex : hex
}).join('');
function updateXenon(r, g ,b){
console.log(selected,r,g,b);
rgbString = rgbToString(r,g,b);
console.log(rgbString);
var fnPr = particle.callFunction({
deviceId: selected, name: 'change_color', argument: rgbString,
auth: token
});
}
function zeroPad(num, places) {
var zero = places - num.toString().length + 1;
return Array(+(zero > 0 && zero)).join("0") + num;
}
function rgbToString(r,g,b){
return zeroPad(Number(r),3)+zeroPad(Number(g),3)+zeroPad(Number(b),3)
}
function updateLightLevels(){
const id_list = ["light_lvl1","light_lvl2","light_lvl3"];
const dev_list = [devices.xenon1,devices.xenon2,devices.xenon3];
id_list.forEach(function(item,index){
var element = document.getElementById(id_list[index]);
console.log(id_list[index]);
$.get("https://api.particle.io/v1/devices/"+dev_list[index]
+"/light_level?access_token="+token,function(data){
var percentage = Number(data.result) / 4096 * 100;
percentage = percentage.toFixed(1);
element.innerHTML = "Light level: "+percentage.toString()+"%";
});
});
}
setInterval(updateLightLevels,3000);
Webpage CSS
body{
width: 550px;
height: 500px;
}
#xenon_list{
width: 100%;
margin: auto;
}
.slider_container{
width: 100%;
display: inline-block;
}
.slider{
-webkit-appearance: none;
appearance: none;
width: 50%;
height: 25px;
margin: 10px;
background: rgb(255, 255, 255);
outline: none;
opacity: 0.7;
transition: opacity .2s;
-webkit-transition: 0.2s
}
.slider:hover{
opacity: 1;
}
label{
font-family: Arial, Helvetica, sans-serif;
font-size: 16px;
}
.slider::-webkit-slider-thumb{
-webkit-appearance: none;
appearance: none;
width:25px;
height:25px;
background: #fb0000;
cursor: pointer;
}
.slider::-moz-range-thumb{
width: 25px;
height: 25px;
cursor: pointer;
background: #fb0000;
}
#red::-moz-range-thumb{
background: #fb0000;
}
#green::-moz-range-thumb{
background: #00fb00;
}
#blue::-moz-range-thumb{
background: #0000fb;
}
#final_color{
display: inline-block;
margin-bottom: 50px;
margin-top: 50px;
}
#color_btn{
width: 100px;
height: 100px;
float: top;
background-color: black;
}
#submit_color{
float: bottom;
margin: 20px;
}
.xenon_select{
}
Particle Xenon Code
const int led_pins[3] = {A4,A2,A1};
const int ldr_pin = A0;
int lightLevel = 0;
int change_color(String data){
//format is RRRGGGBBB
int r = data.substring(0,3).toInt();
int g = data.substring(3,6).toInt();
int b = data.substring(6,9).toInt();
int color_array[3] = {r,g,b};
for(int i=0;i<3;i++){
analogWrite(led_pins[i],color_array[i]);
}
return 0;
}
void setup() {
Particle.variable("light_level", lightLevel);
Particle.function("change_color", change_color);
for(int i=0;i<3;i++){
pinMode(led_pins[i],OUTPUT);
}
}
void loop() {
lightLevel = analogRead(ldr_pin);
}