Evan Rust
Published © GPL3+

Christmas Light Monitor with Particle Mesh

Control and view a network of Particle Mesh devices, seeing it all on a touchscreen, along with a calendar.

IntermediateFull instructions provided4 hours1,153
Christmas Light Monitor with Particle Mesh

Things used in this project

Hardware components

Argon
Particle Argon
×1
Xenon
Particle Xenon
×3
Raspberry Pi 3 Model B+
Raspberry Pi 3 Model B+
×1
DHT11 Temperature & Humidity Sensor (4 pins)
DHT11 Temperature & Humidity Sensor (4 pins)
×3
ELEGOO Upgraded 37 in 1 Sensor Modules Kit V2.0
ELEGOO Upgraded 37 in 1 Sensor Modules Kit V2.0
×1
DFRobot 5in Touchscreen
×1

Software apps and online services

Online Particle IDE
Raspbian
Raspberry Pi Raspbian
Google Calendar

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

Wall Mount Brace

Left Bracket

Right Bracket

Schematics

Xenon Circuit

Code

Webpage HTML

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>
    <script src="scripts.js"></script>
    <link rel="stylesheet" href="styles.css">
    
</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">&times;</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

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

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

C/C++
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);
}

Credits

Evan Rust

Evan Rust

120 projects • 1053 followers
IoT, web, and embedded systems enthusiast. Contact me for product reviews or custom project requests.

Comments