Raspberry Pi - Kamerabild on Demand per Webserver abholen

Wenn man sich mit dem Thema Raspberry Pi und seine Kamera beschäftigt, dann stolpert man immer wieder über das Projekt motion. Wenn man aber nur bei Bedarf ein Foto haben will, dann ist das, wie mit Kanonen auf Spatzen schieeen. Vor allem wenn man sich einen Pi-Zero ansieht, dessen CPU konstant auf 100 % Volllast fährt.

Der einfache Ansatz ist recht simpel und lässt sich mit Bordmitteln bzw. Paketen aus den Raspian-Repos (fast) ohne Programmierkenntnisse umsetzen:

Schritt 1: Webserver mit Lua installieren.

apt-get install nginx-light libnginx-mod-http-lua

Ich hab mich nach jahrelanger Treue zu Apache diesmal für nginx entschieden, da dieser weniger Resourcen benötigt, von denen wir am Pi-Zero nicht allzuviel haben. Das Lua Modul erlaubt es, in der Site-Konfiguration des Webservers kleine Programme direkt zu hinterlegen und so den Output zu generieren. In unserem Fall wollen wir on request mit raspistill ein Foto machen und das direkt zur Verfügung stellen. Der Vorteil hierbei ist, dass das Bild nur im RAM liegt und wir keine kostbaren Schreibzyklen auf unserer Flash-Speicherkarte verbrauchen, um ein mitunter mehrere MB großes Bild für wenige Sekunden zwischenzuspeichern. (Vorausgesetzt natürlich wir haben nicht vor mehr als wenige Requests pro Minute maximal zu bedienen - sonst wäre ein Cache z.B. auf einer RAM-Disk natürlich angebracht, wo wir dann wieder Lua nicht bräuchten, aber das ist eine andere Geschichte.)

Schritt 2: Berechtigungen

Damit der Webserver auf die Kamera zugreifen darf, müssen wir ihm die nötigen Rechte geben. Wir legen eine UDEV Regel an, die der Gruppe video den Zugriff auf die Kamera gewährt und geben den Webserver in diese Gruppe:

(Quelle: https://raspberrypi.stackexchange.com/a/13457/31029)

echo 'SUBSYSTEM=="vchiq",GROUP="video",MODE="0660"' > /etc/udev/rules.d/10-vchiq-permissions.rules

usermod -a -G video www-data

Schritt 3: Raspistill ansprechen

Für Details dazu, siehe meinen Artikel über raspistill. Im folgenden Abschnitt geht's auch ohne diese Info weiter, wenn ihr (optional) imagemagick für das Textoverlay installiert:

apt-get install imagemagick

Schritt 4: Das Lua Script

Und hier kommt alles zusammen. Ihr legt im nginx Webserver eine neue Site an, z.B. /etc/nginx/sites-available/raspicam. Kopiert einfach die default site und fügt folgende Location ein:

location /camera {
default_type image/jpeg;
content_by_lua_block {
local w = 3280;
local h = 2464;

if (ngx.var.arg_w ~= nil) and (ngx.var.arg_h ~= nul) then
w = ngx.var.arg_w
h = ngx.var.arg_h
end

local text = h/20;
local programstring = "raspistill -w "..w.." -h "..h.." --timeout 1 -o - | convert - -font Helvetica -fill white -stroke black -pointsize "..text.." -annotate +0+"..text.." \""..os.date("%F %X").."\" - ":

local f = io.popen(programstring, "r")
local programoutput = f:read("*all")
f.close()
ngx.print(content)
}
}

Das Script erstellt jetzt sofort (ohne 5 Sekunden Timeout) ein raspistill mit maximaler Auflösung (außer über die URL-Paramter w und h werden entsprechende Werte geliefert), schickt dieses weiter zu convert, das uns die aktuelle Zeit links oben in die Ecke zaubert und das Bild nach Standard-Out schreibt.

Lua holt diesen Output per Filestream ab und lässt ihn von nginx direkt als Content ausgeben und an den Webbrowser schicken.

Weitere Lektüre:

Lua: module API: https://github.com/openresty/lua-nginx-module#readme
Lua: Filecontent to nginx: https://stackoverflow.com/a/24428699/841033
Lua: manual, popen: http://www.lua.org/manual/5.1/manual.html#6.9
Lua: time and date: https://www.lua.org/pil/22.1.html
Lua: tutorial on variables: http://openresty.org/download/agentzh-nginx-tutorials-en.html#01-nginxva...