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()