# LED-strip met RPi

In deze toepassing vervang je de klassieke IR-module, die bij de meeste RGB LED-strips hoort, door een raspberry pi.
De RPi wordt via een tussenschakeling verbonden met de LED-strip. De klassieke afstandsbediening ga je vervangen door een app en door een website die je eveneens op de RPi host.
Zodra de RPi met het internet verbonden is, kan je de LED’s wereldwijd bedienen.

  • Maak in de map LesCordova een nieuw project aan:
    $ cordova create PIstrip be.uwnaam.pi_strip "Pi Strip"
    $ cd PIstrip
    $ cordova platform add browser android
    
    1
    2
    3
  • Installeer de plugin waarmee je een schudbeweging kan detecteren.
    $ cordova plugin add cordova-plugin-shake
    
    1

# PubNub

Voor de communicatie tussen de app en de RPi gebruik je PubNub. PubNub is een realtime Data Stream Network (DSN) en een realtime infrastructure-as-a-service (IaaS) voor web-, mobiele en IoT-toepassingen. Met een gratis account kan je per maand tot 100 devices connecteren en tot 1M berichten zenden/ontvangen.

  • Maak een (gratis) account aan op PubNub.
  • Maak een nieuwe app aan, bijvoorbeeld PI-strip.
  • Open de app PI-strip en klik op CREATE NEW KEYSET.
  • Geef de keyset een naam, bijvoorbeeld PIstrip.
    (De publish key en subscribe key heb je straks in je eigen app nodig.)
    Create new key
  • Open PIstrip. (Klik op FREE.)
  • Enable STORAGE & PLAYBACK en bewaar de wijzigingen.
    Storage and playback
  • Klik links onderaan het menu op DEBUG CONSOLE.
  • Vul PIstrip in onder Channel en klik op CREATE CLIENT.
    De naam van het kanaal (PIstrip) heb je straks ook in je eigen app nodig. Debug console

In de console kan je alle berichten volgen. Je kan hier ook een nieuw bericht verzenden. Voor deze toepassing zenden we een object met als key het woord color en als value de HEX-kleurwaarde zonder hekje.
Om de LED-strip groen te laten oplichten zend je: {"color":"00ff00"}.

# De hardware

Wil je deze opstelling thuis bouwen, volg dan hardware tutorial.
In de les hoef je enkel de app te maken. Deze kan je dan uittesten op de LED-strip die in het labo aanwezig is.

# Inhoud van de website

  • Wis de volledige inhoud van de www-folder en vervang deze door de bestanden uit dit zip-bestand.
  • Bestudeer de bestanden.

# index.html

<!DOCTYPE html>
<html lang="nl">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">
    <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css" />
    <title>Pi strip</title>
    <style>
        .row div {
            margin-top: 20px;
        }

        #colorWheel {
            width: 100%;
        }

        #lightOff {
            right: calc(100vw - 79px);
        }

        #colorBlock {
            display: flex;
            flex-wrap: wrap;
            justify-content: space-between;
        }

        #colorBlock span {
            display: block;
            width: 20%;
            text-align: center;
            padding: 0;
            margin: 5px;
            color: grey;
            border-bottom-width: 8px;
            border-bottom-style: solid;
        }
    </style>
</head>

<body class="lime lighten-4">
    <!-- Fixed navbar -->
    <div class="navbar-fixed">
        <nav class="lime darken-3">
            <div class="nav-wrapper container">
                <a href="#!" class="brand-logo center">PI strip</a>
            </div>
        </nav>
    </div>
    <!-- Fixed Action Button -->
    <div class="fixed-action-btn" id="vibrate">
        <a class="btn-floating btn-large waves-effect waves-light lime darken-3">
            <i class="material-icons">vibration</i>
        </a>
    </div>
    <!-- Fixed Action Button (zie nieuwe positionering in CSS! -->
    <div class="fixed-action-btn" id="lightOff">
        <a class="btn-floating btn-large waves-effect waves-light black">
            <i class="material-icons">highlight_off</i>
        </a>
    </div>
    <!-- Grid -->
    <div class="container">
        <div class="row">
            <div class="col s12" id="colorBlock"></div>
            <div class="col s12 input-field">
                <button id="colorWheel" onchange="update(this.jscolor)" class="s12 waves-effect waves-light btn jscolor {value:'000000'}"></button>
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jscolor/2.0.4/jscolor.min.js"></script>
    <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.29.5.min.js"></script>
    <script src="cordova.js"></script>
    <script src="js/pubnub.js"></script>
    <script src="js/shake.js"></script>
    <script defer src="js/app.js"></script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  • Bovenaan de pagina staat een fixed navbar.
  • De fixed action button div#vibrate zal straks een willekeurige kleur genereren.
  • De tweede fixed action button div#lightOff dooft de LED-strip.
    (Deze button wordt via CSS onderaan links in het scherm geplaatst.)
  • In div#colorBlock komen straks enkele voorgedefinieerde kleuren te staan. Deze links worden vanuit de JavaScript-code gegenereerd.
  • De JavaScript color picker is via de class .jscolor verbonden met button#colorWheel.
    Zodra je een kleur selecteert en buiten het kleurpalet klikt, komt het onChange-event in actie.
    De achtergrondkleur van de button wijzigt en de tekst wordt vervangen door de gekozen HEX-waarde.

LET OP

Het jscolor-script kan je niet via jQuery coderen. Alles gebeurt volledig in "pure" JavaScript!

# js/app.js

Deze pagina is volledig klaar. Hier hoef je niets meer aan te wijzigen.










 







 








 




 




 




 




$(function () {
    document.addEventListener("deviceready", onDeviceReady, false);
});

function onDeviceReady() {
    console.log('Device is ready');
    Shake.init();
    Pubnub.init();

    const colors = [
        'FF0000 255,0,0,1', '00FF00 0,255,0,1', '0000FF 0,0,255,1', 'FFFFFF 255,255,255,1',
        'BB0000 255,0,0,.7', '00BB00 0,255,0,.7', '0000BB 0,0,255,.7', 'BBBBBB 255,255,255,.7',
        '7A0000 255,0,0,.5', '007700 0,255,0,.5', '000077 0,0,255,.5', '7A7A7A 255,255,255,.5',
        '2A0000 255,0,0,.3', '002400 0,255,0,.3', '000024 0,0,255,.3', '2B2B2B 255,255,255,.3',
        '050000 255,0,0,.1', '000500 0,255,0,.1', '000005 0,0,255,.1', '050505 255,255,255,.1'
    ];

    colors.forEach(function (value, key) {
        const c = value.split(' ');
        $('#colorBlock').append(`
            <span class="waves-effect btn lime lighten-3" data-rgb="${c[0]}" style="border-bottom-color: rgba(${c[1]})">
                <i class="material-icons">lightbulb_outline</i></span>
            </span>
        `);
    });

    $('#vibrate').click(function () {
        const rgb = Shake.shuffle();
        Pubnub.publish(rgb);
    });

    $('#colorWheel').blur(function () {
        const rgb = $(this).text();
        Pubnub.publish(rgb);
    });

    $('#colorBlock').on('click', 'span', function () {
        const rgb = $(this).data('rgb');
        Pubnub.publish(rgb);
    });

    $('#lightOff').click(function () {
        Pubnub.publish('000000');
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  • Lijn 10:
    De array colors bevat een reeks voorgedefinieerde kleuren. Elke string binnen de array bestaat uit twee delen. Voor de spatie staat de HEX-kleur die je publiceert op PubNub en na de spatie een rgba-kleur die de rand van de buttons accentueert.
  • Lijn 18:
    Doorloop met een forEach-lus alle elementen uit de array en splits de string op een spatie. De variabele c is terug een array en deze bevat twee elementen.
    Voor de eerste string uit de vierde rij wordt dit: c[0] = 2A0000 en c[1] = 255,0,0,.3. Genereer vervolgens voor elke kleur een span-tag en verwerk hierin de twee kleuren.
    Bijvoorbeeld:
    <span class="..." data-rgb="2A0000" style="border-bottom-color: rgba(255,0,0,.3)">...</span>
    De span-tags worden één voor één aan div#colorBlock toegevoegd.
    Merk op dat je aan de span-tag enkel de border-bottom-color meegeeft.
    De twee overige eigenschappen (border-bottom-width en border-bottom-style) zijn voor alle buttons hetzelfde. Deze vind je terug in de CSS-code op de indexpagina.
  • Lijn 27:
    Telkens je op #vibrate klikt, wordt een willekeurige kleur gegenereerd en vervolgens via PubNub gepubliceerd.
  • Lijn 32:
    Zodra je een kleur in de color picker selecteert, wordt de HEX-waarde opgevraagd en gepubliceerd.
  • Lijn 37:
    Klik je op één van de voorgedefinieerde kleuren, lees dan de waarde van data-rgb uit en publiceer deze.
  • Lijn 42:
    Je kan de LED-stript doven door de waarde voor zwart (000000) te publiceren.

# js/shake.js

const Shake = function () {

    const digit1 = [0,1,2,3,4,5,6];
    const digit2 = [0,1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'];

    const init = function() {
        shake.startWatch(function() {
            const rgb = shuffle();
            Pubnub.publish(rgb);
        },40);
    };

    const shuffle = function () {
        return 'FF0000';
    };

    return {
        init: init,
        shuffle: shuffle
    }
}();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Met de plugin cordova-plugin-shake kan je een schudbeweging detecteren. De gevoeligheid staat ingesteld op 40. Elke gedetecteerde beweging roept de functie shuffle() op en publiceert deze waarde op PubNub.

Voorlopig genereert shuffle() enkel een rode kleur.

  • Vervang de code van shuffle() door:


     
     
     
     
     
     
     


    const shuffle = function () {
        const rgb = '';
        for (i = 0; i < 3; i++) {
            rgb += digit1[Math.floor(Math.random() * digit1.length)];
            rgb += digit2[Math.floor(Math.random() * digit2.length)];
        }
        console.log('Random color: ', rgb);
        return rgb;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

Deze functie genereert een random HEX-waarde voor rgb.

  • De laagste HEX-waarde is 000000, de hoogte HEX-waarde is FFFFFF.
  • Om al te felle kleuren te vermijden, ga je niet het volledig spectrum gebruiken, maar ga je de maximale waarde beperken tot 6F6F6F.
  • Helemaal bovenaan de pagina vind je twee arrays (digit1 en digit2).
    Binnen de for-lus ga je driemaal een willekeurige waarde uit beide arrays plukken en deze aan de string rgb plakken.
  • Hiervoor gebruik je twee wiskundige functies van JavaScript.
    • Math.random() genereert een getal tussen 0 en 0.999....
    • Vermenigvuldig dit getal nu met de lengte van de array.
      Voor digit1 geeft dit een getal tussen 0 en 6.999....
      Voor digit2 geeft dit een getal tussen 0 en 15,999....
    • Vervolgens ga je dit getal met Math.floor() naar beneden afronden.
      Je houdt nu nog enkel een geheel getal (0, , ... of 15) over.
    • Stel dat je op getal 11 uitkomt, dan is digit2[11] gelijk aan B.

# js/pubnub.js



 
 









 








 



 











 








 
















const Pubnub = function () {
    
    const publishKey = 'myPublishKey';
    const subscribeKey = 'mySubscribeKey';
    const channel = 'PIstrip';

    const init = function () {
        pubnub = new PubNub({
            subscribeKey: subscribeKey,
            publishKey: publishKey,
            ssl: true
        });

        pubnub.addListener({
            message: function (m) {
                console.log('addListener: message m', m);
                if (m.message.color) {
                    _changeColorWheel(m.message.color);
                }
            }
        });

        pubnub.subscribe({
            channels: [channel]
        });

        pubnub.history({
                channel: channel,
                count: 1
            },
            function (status, response) {
                console.log('history response', response);
                const color = response.messages[0].entry.color;
                _changeColorWheel(color);
            }
        );
    };

    const publish = function (rgb) {
        pubnub.publish({
            channel: channel,
            message: {
                color: rgb
            }
        });
    };

    const _changeColorWheel = function (color) {
        color = color.trim();
        try {
            $('#colorWheel').val(color);
            document.getElementById('colorWheel').jscolor.fromString(color);
        }
        catch (err) {
            console.log('ColorWheel error', err);
        }
    };

    return {
        init: init,
        publish: publish,
    };
}();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63

De init-functie bevat de volledige configuratie van PubNub.

  • PubNub initialisatie:
    Vervang op lijn 3 en 4 myPublishKey en mySubscribeKey door de API-keys die bij het project horen.
  • Lijn 14: Listeners toevoegen:
    Voor onze toepassing hebben we alleen de message nodig. Het verzonden object staat in m.message.
    Haal vervolgens de kleur op (m.message.color) en geef de waarde door naar de functie _changeColorWheel(rgb).
  • Lijn 23:
    Luister naar wijzigingen op het kanaal 'PIstrip'.
  • Lijn 27: Geschiedenis opvragen:
    Enkel bij het starten van de app wordt deze functie (lijn 31) eenmalig uitgevoerd. Uit de response halen we de laatst gekozen kleur op en sturen deze naar _changeColorWheel(rgb). Dit is vooral handig als meerdere personen dezelfde app gebruiken. Zo blijven alle apps volledig synchroon met elkaar.

Verder bevat het script nog twee functies.

  • Lijn 39: Kleur publiceren:
    De functie let publish = function (rgb) wordt bij elke kleurwijziging opgeroepen. De functie publiceert een zeer eenvoudig object op het kanaal PIstrip.
    Stel dat je bijvoorbeeld blauw selecteert, dan wordt het gepubliceerd object {"color":"0000FF"}.
    Tip: je kan alle wijzingen opvolgen in de PubNub console.
  • Lijn 48:
    De functie let _changeColorWheel = function (color) wijzigt de laatst gekozen kleur en de tekst in button#colorWheel.
    Merk op dat we via try-catch werken. Het kleurenpalet van jscolor bevat niet alle 16M kleurcombinaties die je met RGB kan vormen.
    Stel dat je een random kleur genereert die niet in het palet voorkomt, dan krijg je op lijn 52 een fout. Catch zal de fout dan opvangen. Werk je niet met catch, dan "hangt" de toepassing en zal je de app volledig moeten herstarten.

# Installeer de app

  • Zet alle overbodige logs (console.log(...)) in commentaar.
  • Maak een icoon voor de app en plaats dit in de map resources.
    $ cordova-res android --type icon
    
    1
  • Installeer de meteo app op je smartphone en controleer de werking.
    $ cordova run android
    
    1
Last Updated: 10/14/2021, 9:41:11 AM