LED-Lichtband Sonnenaufgang unter dem Bett mit dem Raspberry PI

LED Sonnenaufgang unter dem Bett

  1. Materialliste
  2. Physikalischer Aufbau
  3. Einrichtung Raspberry PI
  4. Anlernen der Fernbedienung
  5. Umsetzen der Steureung als Python-Webservices
  6. Client in JQueryMobie und Deployment auf nginx

Matrialliste:

  • Raspberry PI 3
  • 16GB MicroSD Speicherkarte
  • Netzteil
  • Etwa 7 Meter LED lichtleiste (z.B. 2x LED-Lichtband mit IR Fernsteuerung
  • IR Shield von LinkSprite
  • 10 Meter Klett Klebeband
  • Merhfachsteckdose

Physikalischer Aufbau:

Zuerst müssen Matratzen und Lattenroste aus dem Bettgestell herausgehoben werden. Im unteren Bereich des Bettrahmens wird an der Innenseite eine Seite des Klett-Klebebands ringsherum angebracht, sodass die Beleuchtung im ausgeschalteten Zustand nicht sichtbar ist. an den Ecken werden die mitgelieferten Eck-Verbinder benutzt. Da die Stromversorgung der LED-Leisten auf eine Maximallänge von 5 Metern ausgelegt ist, müssen 2 Netzteile verwendet werden und je eine Leiste für die Hälfte des Bettrahmens genutzt werden. Die von einem Netzteil zur Verfügung gestellte Spannung ist nicht ausreichend um die gesamte Länge zu versorgen (es tritt ein Flackern und falsche Farbdarstellung auf). im Idealfall liegen beide Anschlüsse bzw. IR-Empfänger an einem Ende der beiden Leisten, sodass diese von der Diode des PI aus sichtbar sind (am besten eine der hintern Ecken des Bettes, damit man die Kabel gut verstecken kann). Der Raspberry selbst wird später ebenfalls mit Klettband am Bettrahmen befestigt.

Die LED Leisten werden auf die verbleibende Seite des Klettbandes angebracht und entlang des Rahmens befestigt (Durch das Klett können diese flexibel ausgetauscht werden, falls eine Leiste einmal kaputt geht).

An dieser Stelle ist der Bettrahmen fertig vorbereitet. Entlang des Rahmens sind die Lichtleisten montiert und die IR Empfänger sind beide in einer der hinteren Ecken des Bettes angebracht.

Die Stromversorgung wird durch eine Mehrfachsteckdose zur Verfügung gestellt.

Einrichtung des Raspberry PI:

Nun geht es an den Raspberry. Hier ist eigentlich nicht viel zu tun, außer das besagte IR-Shield auf die GPOI Pins aufzustecken und die SD-Karte für die Nutzung mit Raspbian vorzubereiten (entweder via NOOBS oder Installation per Hand). Nach der Installation und dem ersten Start von Raspbian sollten einige Basis-Einstellungen durchgeführt werden, sodass die weitere Arbeit erleichtert wird. Der Raspberry sollte im lokalen WLan kommunizieren, da ein zusätzliches Netzwerkkabel unter dem Bett eher umständlich ist und die Fernbedienung via website / mobile web App möglich sein soll.

Weiterhin empfiehlt es sich, den PI für einen Zugang über VNC und SSH einzurichten, sodass auf dem Gerät auch im, unter dem Bett montierten Zustand, "deployed" werden kann.

Nun zum spannenden Teil, dem Einrichten des IR-Sensors:

( Es empfiehlt sich, vorher das Betriebssystem des PI auf den neuesten Stand zu aktualisieren )

Zur Interaktion des Betriebssystems mit dem IR Sender / Empfänger wird LIRC verwendet. Die Installation erfolg über

apt-get install lirc -y

Im Anschluss muss der Sender / Empfänger im System bekannt gemacht werden (die verwendeten GPIO Pins registriert). Dies geschieht via Eintrag in der "/etc/modules“ mit den Zeilen

lirc_dev
lirc_rpi gpio_out_pin=18

In der Datei "/boot/config.txt„ wird die folgende Zeile eingefügt:

dtoverlay=lirc-rpi,gpio_in_pin=18,gpio_out_pin=17

LIRC benötigt darüberhinaus eine entsprechende Konfiguration in der Datei: "etc/lirc/hardware.conf“ Folgende Einstellungen werden durchgeführt:

LIRCD_ARGS="--uinput"
LOAD_MODULES=true

DRIVER="default"
DEVICE="/dev/lirc0"
MODULES="lirc_rpi"

Nun wird der Raspberry neu gestartet (shutdown -r now )

Erster Funktionstest des Empfängers:

Zum Testen der Funktion muss der LIRC Dienst kurzzeitig beendet werden. Dies geschieht via:

/etc/init.d/lirc stop

Nun kann mit dem Aufruf:

mode2 -d /dev/lirc0

Der Empfänger gestartet werden. Ein Tastendruck auf der Fernbedienung muss nun zu einer Reaktion in der Ausgabe führen, dann funktioniert euer Empfänger wie gewünscht.

Anlernen der Fernbedienung

Der nächste Schritt umfasst das anlernen der Fernbedienung. Hierbei werden den eingehenden Signalen Bezeichnungen zugeordnet um diese im Nachhinein aufrufen und versenden zu können. Um Signale bzw. Fernbedienungs-Konfigurationen anzulernen bekommen wir Unterstützung vom Tool: irrecord. Seitens LIRC gibt es eine Menge an zu vergebenen Bezeichnungen, die für das anlernen verwendet werden können wie z.B. KEY_OK. Die Liste kann über eine Option von irrecord ausgegeben werden.

Das lernen der Fernbedienung wird gestartet über:

irrecord -d /dev/lirc0 <Pfad zur Datei in der die config abgelegt wird>

Das Tool selbst führt durch den Prozess und am Ende ist die Konfigurationdatei gefüllt. In der entstandenen config Datei kann der Parameter "Name" noch angepasst werden, um die Fernbedienung entsprechend zu kennzeichnen.

Anschließend wird die default config im Verzeichnis /etc/lirc/ durch die neu erzeugte lircd.conf ersetzt.

Hinweis: Es können Mehrere Fernbedienungen in einer Config abgelegt werden.

Nach dem Start des LIRC Service via:

/etc/init.d/lirc start

kann mit dem Aufruf irw getestet werden, ob die Signale der Fernbedienung ankommen und die richtigen Zuordnungen besitzen. Die gedrückten Tasten müssen in der Ausgabe erscheinen.

Da das montierte IR-Shield auch bereits den Sender beherbergt, kann nun via irsend Kommando ein Befehl gesendet werden. Die Syntax lautet:

irsend SEND_ONCE <name der Fernbedienung aus der config> <Key des Kommandos aus der config>

Nun hat der Raspberry das Signal ersetzt, welches eigentlich beim Tastendruck von der Fernbedienung gesendet wird.

Somit wäre die Kommunikation zwischen PI und den Empfängern der Lichtleisten gewährleistet und es geht daran, die Fernbedienung als Web App nachzubauen und mit ein paar sonder-Features zu versehen.

Umsetzen der Steureung als Python Webservices

Code auf github

Für den "Server", der die Kommandos auf dem PI ausführt, habe ich mich für Python mit Flask entschieden, weil es eine einfache und portierbare Möglichkeit darstellt, webservice Endpoints zu erstellen und command line Kommandos abzusetzen. Der Python code enthält nicht viel mehr als je einen Endpoint für jede Taste, sowie eine Möglichkeit, eine Weckzeit zu übergeben. Das Python Skript benötigt dazu die Abhängigkeiten:

Flask
apscheduler
flask_cors (insofern client und server auf dem selben PI und response nicht ordentlich)

Die via pip3 installiert werden können. Das eigentlich Skript besteht bei mir nur aus den Endpoints, den Kommandos die ausgeführt werden, dem Handling für die Uhrzeit (gespeichert in einem File) und einem scheduler für zyklische Ausführung. Das Skript sieht (verkürzt) wie folgt aus:

From flask import Flask
import subprocess
from apscheduler.schedulers.background import BackgroundScheduler
from flask import request
import atexit
import time
from flask_cors import CORS, cross_origin

app = Flask(__name__)
CORS(app)

def execute(keyIdent):
    cmd = ["/usr/bin/irsend","SEND_ONCE","led",keyIdent]
    p = subprocess.Popen(cmd, stdout = subprocess.PIPE,
                            stderr=subprocess.PIPE,
                            stdin=subprocess.PIPE)
    out,err = p.communicate()
    return "200"

scheduler = BackgroundScheduler()

@app.route("/on")
def on():
    return execute("KEY_POWER")

@app.route("/off")
def off():
    return execute("KEY_POWER2")

@app.route("/brightnessup")
def brightnessup():
    return execute("KEY_BRIGHTNESSUP")

@app.route("/brightnessdown")
def brightnessdown():
    return execute("KEY_BRIGHTNESSDOWN")

.... alle Kommandos umsetzen ....

@app.route("/sunrise")
def sunrise():
    #start on
    on()
    #red color
    red()

#brightness down max
for num in range(0,20):
    time.sleep(.5)
    brightnessdown()

#brightnessup slow
for num in range(0,3):
    time.sleep(1.5)
    brightnessup()

#orange
time.sleep(1)
redtwo();

# brightnessup slow
for num in range(0,4):
    time.sleep(1.5)
    brightnessup()

#yellow
time.sleep(1)
redthree()

# brightnessup slow
for num in range(0,6):
   time.sleep(1.5)
   brightnessup()

# daylight
time.sleep(60)
return white()

# schedule new timer
def reschedule(hour,min):
    scheduler.remove_all_jobs()
    scheduler.add_job(sunrise, 'cron', day_of_week='mon-fri', hour=hour, minute=min, second=0)

# set sunrise time
@app.route("/sunrisetime", methods=['POST'])
def sunrisetime():
    request_data = request.get_json()
    file = open("remote.props","w")
    file.write(request_data['time'])
    file.close()
    readprops()
    return "200"

# read props and set new scheduler
def readprops():
    with open("remote.props", "r") as infile:
        for line in infile:
            items = line.split(":")
            reschedule(items[0],items[1])

# Shut down the scheduler when exiting the app
atexit.register(lambda: scheduler.shutdown())

if __name__ == "__main__":
#    request_data = request.get_json()
    file = open("remote.props", "w")
    file.write("6:00")
    file.close()
    scheduler.start()
    scheduler.add_job(sunrise, 'cron', day_of_week='mon-fri', hour=6, minute=0, second=0)
    app.run(host='0.0.0.0', port=8444)

Das Ganze lässt sich sicherlich noch knapper und eleganter formulieren (auch mit vernünftigen responses etc. ;) ), für Anschauungszwecke ist diese Fassung aber ausreichen.

Nach dem kopieren auf den PI und der Ausführung mit dem Kommando

python3 <meinskript.py>

läuft der Server. Nun können die Kommandos bereits im Browser durch Aufruf der entsprechenden URLs ausgelöst werden. Da wir aber wollen, dass der Server bei jedem Start des PI automatisch gestartet wird, ergänzen wir in der Datei: /etc/rc.local

nohup python3 <pfad zum python file> &

Client in JQueryMobie und Deployment auf ngix

Code auf github

Der web client modelliert die Fernbedienung und ist im lokalen WLAN zu erreichen (Webserver kommt ebenfalls auf den PI). Hinter dem client verbirgt sich eine html Seite die mit der Unterstützung von JQuery Mobile und etwas Javascript die Funktionen des Servers anspricht. Der (verkürzte) Code der Seite lautet:

<!DOCTYPE html>
<html>
<head>
    <title>LEDRemote</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="themes/remote.min.css" />
  <link rel="stylesheet" href="themes/jquery.mobile.icons.min.css" />
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile.structure-1.4.5.min.css" />
    <link href="http://cdn.jtsage.com/jtsage-datebox/4.1.1/jtsage-datebox-4.1.1.jqm.min.css" rel="stylesheet" type="text/css">
    <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
    <script src="functions.js"></script>
    <script src="http://cdn.jtsage.com/jtsage-datebox/4.1.1/jtsage-datebox-4.1.1.jqm.min.js" type="text/javascript"></script>
</head>
<body>
<div data-role="page">
    <div data-role="header">
        <h1>Bed - LED Remote V 1.0</h1>
    </div><!-- /header -->
  <div class="ui-grid-c">
      <div class="ui-block-a"><a id="1x1" href="index.html" data-role="button" data-icon="arrow-u">UP</a></div>
    <div class="ui-block-b"><a id="1x2" href="index.html" data-role="button" data-icon="arrow-d">DOWN</a></div>
    <div class="ui-block-c"><a id="1x3" href="index.html" data-role="button" data-theme="p" data-icon="delete">OFF</a></div>
    <div class="ui-block-d"><a id="1x4" href="index.html" data-role="button" data-theme="b" data-icon="check">ON</a></div>

    <div class="ui-block-a"><a id="2x1" href="index.html" data-role="button" data-theme="b" >.</a></div>
    <div class="ui-block-b"><a id="2x2" href="index.html" data-role="button" data-theme="h" >.</a></div>
    <div class="ui-block-c"><a id="2x3" href="index.html" data-role="button" data-theme="n" >.</a></div>
    <div class="ui-block-d"><a id="2x4" href="index.html" data-role="button" >.</a></div>

  .... alle weiteren buttons ....

   <br>
  <div class="ui-grid-a">
      <div class="ui-block-a"><input id="timebox" type="text" data-role="datebox" data-options='{"mode":"timebox"}' value="6:00"> </input></div>
      <div class="ui-block-b"><a id="setSunriseTime" href="index.html" data-role="button" data-theme="b" >Weckzeit stellen</a></div>
  </div>
</div>
<div data-role="footer">
</div>
</div>
</body>
</html>

Das zugehörige Javascript spricht die Server endpoints an:

function doAjaxCall(ident,url){
    $.ajax({
        url: url,
        error: function(){
            alert("LED Bettbeleuchtung nicht erreichbar")
        },
        success: function(){
            $(ident).removeClass("ui-btn-active");
        },
        timeout: 5000 // sets timeout to 3 seconds
    });
};

$(document).ready(function() {
    // ROW one
    //off
    $( "#1x3" ).click(function() {
      doAjaxCall("#1x3","http://ledremote:8444/off");
    });

    //on
    $( "#1x4" ).click(function() {
        doAjaxCall("#1x4","http://ledremote:8444/on");
    });

    //brightnessup
    $( "#1x1" ).click(function() {
        doAjaxCall("#1x1","http://ledremote:8444/brightnessup");
    });

    //brightnessdown
    $( "#1x2" ).click(function() {
        doAjaxCall("#1x2","http://ledremote:8444/brightnessdown");
    });

    ..... alle weiteren .......

    $("#setSunriseTime").click(function() {

        var value = $("#timebox").val();

        $.ajax({
            type: "POST",
            contentType: "application/json; charset=utf-8",
            url: "http://ledremote:8444/sunrisetime",
            data: JSON.stringify({time: value}),
            error: function(err){
                alert(err)
            },
            success: function(){
                $("#setSunriseTime").removeClass("ui-btn-active");
            },
            dataType: "json",
            timeout: 1000
        });

    });
});

Zum Schluss installieren wir auf dem PI einen kleinen nginx webserver und lassen diesen unseren client zur Verfügung stellen:

sudo apt-get install nginx

Unsere Files legen wir in das Verzeichnis cd /var/www/html/ und entfernen dort die Demo Datei, die per default ausgeliefert wird. Das Kommando

sudo /etc/init.d/nginx start

startet den Service. Nun sollte der client unter der IP oder dm DNS Namen des PI im Browser zu erreichen sein.

Wer möchte kann das blinken der grünen LED am fertigen PI unter dem Bett noch ausschalten wenn es stört:

sudo sh -c 'echo 0 > /sys/class/leds/led0/brightness'