Code-Outlet von Andreas Bahr, Zürich
Diese Kamera postet Fotos via FTP auf einen Webserver. Der Webserver kann irgendwo im Internet stehen... oder auch direkt im eigenen LAN/WLAN. Das wäre dann quasi ein CCC - eine Closed Circuit Camera. So wie damals, vor dem Internetz...
Moderne "Internet of Things"-Produkte stellen typischerweise nach einigen Jahren Ihren Betrieb ein, weil die genutzten Cloud-Ressourcen ausgeschaltet werden. Ebenfalls problematisch sind nachträgliche Änderungen der Funktionsweise sowie "Zwangs-Updates" die Geld kosten. Aber das schlimmste Problem sind vermutlich die sich ständig ändernden AGB's, deren Regeln oft sehr unfair sind, oder gegen lokale Gesetze verstossen.
Das Toolkit für die Einbindung auf dem Webserver ist für die Verwendung mit Fotos ausgelegt. Bei der Entwicklung wurde darauf geachtet, sehr schnelle Bildwechsel zu ermöglichen, ohne Flimmern, Flackern oder Aussetzer. Das gilt auch für grosse Bilder-Sammlungen. Allerdings ist es sinnvoll, nicht mehr als 1000 Bilder gleichzeitig zu laden, der Aufruf der Seite kann sonst mehrere Sekunden dauern.
Für die Kamera empfehle ich Original Maker-Komponenten von Raspberry Pi und Zubehör-Lieferanten. Je nach Anforderung, kann auch eine andere Kamera, oder ein alternativer Computer, verwendet werden. Datenübertragung via LAN / WLAN.
Standart-Konfiguration:
Situation 2022: Wegen der eingeschränkten Verfügbarkeit der Produkte von Raspberry Pi, bieten die Konkurrenten aus Asien eine gute Alternative. Die aus Hong Kong kommenden Kameras von ArduCam haben interessante Features wie Autofokus und mehr Megapixels...
Auf dem Kamera-Computer läuft motionEyeOS, eine Open-Source Software. Diese Software kostet keine Lizenzgebühr. Prinzipiell kann jede Software verwendet werden, die für Linux geschrieben wurde und auf ARM-Prozessoren kompiliert werden kann.
Einige deutsche Webseiten mit Information zur Software:
Raspberry Pi Geek
Stefan's Weblog
Electreeks.de
Eine Besonderheit der verwendeten Hardware ist die besonders einfache Programmierbarkeit in Python. Das ermöglicht vielfältige Anwendungen für Kameras, die durch Timer oder Sensoren gesteuert werden. Bilder können lokal gespeichert, über Netzwerke übertragen, oder auf Bildschirmen angezeigt werden.
Das folgende Listing zeigt ein Script, das alle 30 Minuten ein Bild via FTP auf einen Server überträgt. Es prüft dabei zuerst, ob das Bild eine gewisse Helligkeit besitzt, um Nacht-Bilder auszuschliessen.
# foto-feeder FTP v0.1.2
# with luminance-check to avoid black pics
# Raspberry Pi and the Original V2 & HQ cameras
# 2021 Andreas Bahr
# license: GPL3
from picamera import PiCamera
import numpy as np
from time import sleep, localtime, strftime
import ftplib
# configuration
pixfolder = "/home/pi/DCIM/foto-feeder/"
minLuminance = 8 # 0...99, 0 = no luminance-threshold, max.: 255
ftpDomain = ""
ftpUser = ""
ftpPass = ""
def triggerCondition1():
nowtime = localtime()
# compare minutes from struct_time
if(nowtime[4] == 0 or nowtime[4] == 30):
return True
else:
return False
# https://picamera.readthedocs.io
# chapter 4.3 ... at the end
def captureLuminance():
global camera
y_data = np.empty((240, 320), dtype=np.uint8)
try:
camera.capture(y_data, 'yuv')
except IOError:
pass
return int(np.mean(y_data))
def simpleLogger(text):
global logfilename
timestamp = strftime("%Y%m%d-%H%M%S: ", localtime())
log = open(logfilename, "a")
log.write(timestamp + text + "\n")
log.close()
def singleFTPupload(filename, localFolder, ftpFolder, ftpDomain, ftpUser, ftpPass):
# connect to ftp-host
print("connect FTP host: " + ftpDomain + "...")
with ftplib.FTP(ftpDomain, ftpUser, ftpPass) as ftp:
#print(ftp.getwelcome())
#ftp.dir()
# change/make folder
if(ftpFolder == "" or ftpFolder == "." or ftpFolder == "/"):
pathReady = True
else:
pathReady = False
try:
ftp.cwd(ftpFolder)
print("entered folder: " + ftpFolder)
pathReady = True
except ftplib.all_errors as e:
print("FTP error while entering folder")
if(not pathReady):
try:
ftp.mkd(ftpFolder)
print("created folder: " + ftpFolder)
except ftplib.all_errors as e:
print("FTP error: ", e)
sleep(2)
try:
ftp.cwd(ftpFolder)
pathReady = True
except ftplib.all_errors as e:
print("FTP error while entering folder, again")
# binary upload
if(pathReady):
try:
with open(localFolder + filename, "rb") as fp:
res = ftp.storbinary("STOR " + filename, fp)
print(res)
if not res.startswith("226"):
simpleLogger("ERROR: FTP upload failed")
else:
simpleLogger("FTP upload successful")
except ftplib.all_errors as e:
print("FTP error: ", e)
else:
print("ERROR: FTP upload not possible!")
simpleLogger("ERROR: FTP upload not possible!")
ftp.quit()
print("FTP disconnected")
# logfile
logfilename = strftime("ffeedr-log-%Y%m%d.txt", localtime())
print("foto-feeder FTP 2021 Andreas Bahr")
print("picture-folder: " + pixfolder)
simpleLogger("--------------------")
simpleLogger("foto-feeder FTP start")
simpleLogger("picture-folder: " + pixfolder)
# camera setup
simpleLogger("camera setup")
camera = PiCamera()
#camera.resolution = (4056, 3040) # HQ max. resolution, needs 256MB gpu-ram
#camera.resolution = (3280, 2464) # V2 max. resolution, needs 256MB gpu-ram
#camera.resolution = (2028, 1520) # matches HQ-sensor-mode 2
#camera.resolution = (1640, 1232)
#camera.resolution = (1920, 1080)
#camera.resolution = (1280, 960)
camera.resolution = (1024, 768)
#camera.resolution = (640, 480)
#camera.rotation = 180 # 0/90/180/270
print("capture device:", camera.revision)
print("----------------------------------------------------------")
simpleLogger("--------------------")
# main loop
simpleLogger("start preview")
camera.start_preview(alpha=128,resolution=(640,480))
sleep(3)
while True:
if(triggerCondition1()):
lumival = captureLuminance()
print("luminance:", lumival)
simpleLogger("luminance: " + str(lumival))
if(minLuminance == 0 or lumival >= minLuminance):
pixname = strftime("%H-%M-%S.jpg", localtime())
camera.capture(pixfolder + pixname)
print("new picture:", pixname)
simpleLogger("captured " + pixname)
dailyFolder = strftime("%Y-%m-%d", localtime())
singleFTPupload(pixname, pixfolder, dailyFolder, ftpDomain, ftpUser, ftpPass)
else:
print("dark-mode: no picture captured")
simpleLogger("dark-mode: no picture captured")
print("----------------------------------------------------------")
sleep(60)
camera.stop_preview()
camera.close()