diff --git a/config.txt b/config.txt new file mode 100644 index 0000000..7b13d74 --- /dev/null +++ b/config.txt @@ -0,0 +1,24 @@ +[config] +ftp_url = domain.com +ftp_picture_directory = /camera04/latest +ftp_log_directory = /logs +ftp_user = uploader +ftp_pw = de8f57DontTryIt151bd1 +enable_modem = false +picture_directory = /mnt/RAMDisk/ +log_directory = /media/usb0/logs/ +picture_size = 2300000 +camera_number = 04 +internet = true +modem_apn = web.vodafone.de +upload_only = 1 +interval = 300 +I/O_Board = true +mute = false +wakeup_Time: 8,8,7,7,6,5.5,6,6.5,7.5,7.5,8,8 +bed_Time: 17,17.5,18,20,20.5,21.5,21.5,20.5,19.5,17.5,16.5,16.5 +TV_Output: true +Width: 3280 +Height: 2464 +Reboot: false +Flash: true diff --git a/photobox.py b/photobox.py new file mode 100644 index 0000000..e26c3eb --- /dev/null +++ b/photobox.py @@ -0,0 +1,516 @@ +# (c)2019 Hendrik Schutter + +# Fotobox V01 + +from picamera import PiCamera, Color +from time import sleep +from ftplib import FTP +from fractions import Fraction +from datetime import datetime +import ftplib +import time +import os +import http.client as httplib +from collections import namedtuple +import logging +import configparser +import subprocess +from spidev import SpiDev +import RPi.GPIO as GPIO +import re +import fcntl +import itertools + + +configParser = configparser.RawConfigParser() +configFilePath = r'/home/pi/config.txt' +configParser.read(configFilePath) + + +# ioBoard +piezo = 25 +bootSwitch = 17 +switch12V = 23 +flashPin = 18 + +version = '01' +print('Copyright CopterSicht - info@coptersicht.de 2015-' + str(datetime.now().year)) +print('starting camera version: ' + version) +print('reading config...') +ftp_url = configParser.get('config', 'ftp_url') +ftp_picture_directory = configParser.get('config', 'ftp_picture_directory') +ftp_log_directory = configParser.get('config', 'ftp_log_directory') +ftp_user = configParser.get('config', 'ftp_user') +ftp_pw = configParser.get('config', 'ftp_pw') +picture_directory = configParser.get('config', 'picture_directory') +log_directory = configParser.get('config', 'log_directory') +camera_number = configParser.get('config', 'camera_number') +enable_modem = configParser.get('config', 'enable_modem') +picture_size = int(configParser.get('config', 'picture_size')) +internet = configParser.get('config', 'internet') +modem_apn = configParser.get('config', 'modem_apn') +upload_only = int(configParser.get('config', 'upload_only')) +interval = int(configParser.get('config', 'interval')) +ioBoard = configParser.get('config', 'I/O_Board') +mute = configParser.get('config', 'mute') +wakeup_Time = str(configParser.get('config', 'wakeup_Time')).split(',') +bed_Time = str(configParser.get('config', 'bed_Time')).split(',') +tvOut = configParser.get('config', 'TV_Output') +width = configParser.get('config', 'Width') +height = configParser.get('config', 'Height') +rebootCMD = configParser.get('config', 'Reboot') +flash = configParser.get('config', 'Flash') + +print('done!') + + +rebootFlag = 'false' +picture_name = ' ' +picture_path = ' ' + +print('initializing logger...') +log_name = camera_number + '_' + str(datetime.now().strftime("%A %d. %B %Y")) + '.log' +log_name = log_name.replace(" ", "-") +logging.getLogger('').handlers = [] +try: + logging.basicConfig(filename=log_directory + log_name, format='%(asctime)s [%(levelname)s] %(message)s', + level=logging.DEBUG, datefmt='%d/%m/%Y %H:%M:%S') +except Exception as er: + print('[Logger] Could not create .log' + str(er)) + print('[USB] Could not find USB storage!') + while 1: + checkConnectionToFTPServer() + if internet == 'true' and internetTmp == 'false': + if internetTmp == 'false': + rebootFlag = 'true' + print('[Timelapse Core] Reboot Flag set') + if rebootFlag == 'true': + rebootSystem() + print('[Timlapse Core] Waiting for maintenance!') + sleep(3600) +logging.info('starting camera version: ' + version) +print('done') +print('reading picture number...') +try: + f = open("/home/pi/picture_Number.txt", 'r') + picture_number = int(f.read()) + f.close() +except Exception as ea: + print('No picture_Number.txt found. Creating new .. ') + f = open("/home/pi/picture_Number.txt", 'w') + f.write(str(0)) + f.close() + f = open("/home/pi/picture_Number.txt", 'r') + picture_number = int(f.read()) + f.close() + logging.info('Maybe a fatal error occourd! New picture_Number was created. Risk of redundancy!') + print('Maybe a fatal error occourd! New picture_Number was created. Risk of redundancy!') +print('done, picture number is:', str(picture_number)) + +camera = PiCamera() +upload_every = upload_only +internetTmp = internet + +if ioBoard == "true": + print('Running with the nice IO board!') + spi = SpiDev() + spi.open(0, 0) + spi.max_speed_hz = 5000 + GPIO.setmode(GPIO.BCM) + GPIO.setwarnings(False) + GPIO.setup(piezo, GPIO.OUT) + GPIO.setup(flashPin, GPIO.OUT) + GPIO.setup(bootSwitch, GPIO.IN) + GPIO.setup(switch12V, GPIO.IN) + GPIO.output(piezo, GPIO.LOW) + GPIO.output(flashPin, GPIO.LOW) + + if GPIO.input(bootSwitch) == 0: + #camera = PiCamera() + camera.resolution = (3280, 2464) # aufloesung der bilder maximal 3280,2464s + camera.awb_mode = 'auto' + camera.start_preview() + # camera.start_preview(fullscreen=False, window = (100, 20, 960, 720)) + print('---starting sharpness tool---') + print('--------normal output--------') + sleep(1000) + camera.stop_preview() + camera.close() + if mute == "false": + GPIO.output(piezo, GPIO.HIGH) + sleep(1) + GPIO.output(piezo, GPIO.LOW) +else: + print('Running without the nice IO board!') + +if rebootCMD == 'true': + print('Automatic reboot is allowed while wake up!') + logging.info('Automatic reboot is allowed while wakeup!') + +if tvOut == 'false': + stdoutdata = subprocess.getoutput('sudo /usr/bin/tvservice -o ') + print('[TV Out] Status: ' + stdoutdata) + logging.info('[TV Out] Status: ' + stdoutdata) + +if internetTmp == 'true': + print('Running in Online mode, internet connection is required') + if enable_modem == 'true': reconnectToInternet() + try: + ftp = FTP(ftp_url, ftp_user, ftp_pw, timeout=20) + ftp.cwd(ftp_picture_directory) + print('New ftp connection successful! current dicertory is: ' + ftp.pwd()) + logging.info('New ftp connection successful! current dicertory is: ' + ftp.pwd()) + except Exception as ea: + print('no internet or ftp connection on startup trying to reconnect later!') + logging.error('no internet or ftp connection on startup trying to reconnect later!') + internetTmp = 'false' +else: + print('Running in Offline mode, no internet connection is required') + + +print('[Camera] Resolution: ' + width + 'x' + height) +logging.info('[Camera] Resolution: ' + width + 'x' + height) + +subprocess.getoutput('sudo sh -c "TERM=linux setterm -foreground black -clear all >/dev/tty0"') + +# get cpu temperature +def getCpuTemperature(): + tempFile = open("/sys/class/thermal/thermal_zone0/temp") + cpu_temp = tempFile.read() + tempFile.close() + return float(cpu_temp) / 1000 + + +# uploading picture to ftp and make chmod 754 when done sleep until sleeping time is over +def uploadToFTP(): + global picture_name + global picture_path + print('trying to upload file ...') + logging.info('trying to upload file ...') + try: + ftp.storbinary('STOR ' + picture_name, open(picture_path, 'rb'), 1024) + ftp.sendcmd('SITE CHMOD 754 ' + picture_name) + os.remove(picture_path) + print('upload successful!') + logging.info('[Upload] successful!') + except Exception as e: + print('upload failed!') + print(e) + logging.error('[Upload] failed!') + logging.error(str(e)) + try: + print('[FTP] trying to reconnect ...') + logging.info('[FTP] trying to reconnect ...') + ftp.connect(ftp_url) + ftp.login(ftp_user, ftp_pw) + ftp.cwd(ftp_picture_directory) + print('[FTP] re-connecting successful!') + logging.info('[FTP] re-connecting successful!') + ftp.storbinary('STOR ' + picture_name, open(picture_path, 'rb'), 1024) + ftp.sendcmd('SITE CHMOD 754 ' + picture_name) + os.remove(picture_path) + print('[FTP] upload successful!') + logging.info('[FTP]upload successful!') + except Exception as e9: + print('[FTP] re-connecting failed!') + print(e9) + logging.error('[FTP] re-connecting failed!') + logging.error(str(e9)) + if reconnectToInternet() == 'true': + print('Reconnect successful') + logging.info('[Internet] Reconnect successful!') + else: + print('Reconnect failed') + logging.error('[Internet] Reconnect failed!') + return + + +def checkDiskSpace(): + st = os.statvfs(picture_directory) + free = st.f_bavail * st.f_frsize + total = st.f_blocks * st.f_frsize + + freePercentaged = int(((st.f_bavail * st.f_frsize) / (st.f_blocks * st.f_frsize)) * 100) + logging.info('[Disk] Free space: ' + str(freePercentaged) + ' %') + print('Free disk space ', str(freePercentaged), ' %') + if freePercentaged < 20 and freePercentaged > 10: logging.warning('[Disk] Storage Drive nearly full!') + if freePercentaged < 10: logging.critical('[Disk] Storage Drive full, Backup Space in use!') + return + + +def checkCpu(): + try: + if getCpuTemperature() <= -5.0: + logging.critical('[CPU] Coold: ' + str(getCpuTemperature()) + ' C') + elif getCpuTemperature() <= 7.0: + logging.warning('[CPU] Coold: ' + str(getCpuTemperature()) + ' C') + elif getCpuTemperature() >= 45.0: + logging.warning('[CPU] Hot: ' + str(getCpuTemperature()) + ' C') + elif getCpuTemperature() >= 50.0: + logging.critical('[CPU] Hot: ' + str(getCpuTemperature()) + ' C') + else: + logging.info('[CPU] Temperature: ' + str(getCpuTemperature()) + ' C') + return + except Exception as e2: + print('[CPU] ' + str(e2)) + logging.warning('[CPU] ' + str(e2)) + +def readMPC(channel): + adc = spi.xfer2([1, (8 + channel) << 4, 0]) + data = ((adc[1] & 3) << 8) + adc[2] + return data + +def checkSystemVoltage(): + systemVoltage = (((readMPC(0) / 1023.0 * 3.3) * 6.62) * 0.95) + print("System Input Voltage: %.2f" % systemVoltage, "Volt") + logging.info('[Voltage] ' + str("%.2f" % systemVoltage) + ' Volt') + if systemVoltage < 11.2 and systemVoltage > 9: + logging.warning('[Voltage] Low System Input Voltage! Maybe bad power supply or no sun.') + if tvOut == 'auto': + stdoutdata = subprocess.getoutput('sudo /usr/bin/tvservice -o ') + print('[TV Out] Status: ' + stdoutdata) + logging.info('[TV Out] Status: ' + stdoutdata) + if systemVoltage < 9: logging.critical('[Voltage] Very low System Input Voltage! Bad power supply or no sun.') + return + + +def reconnectToInternet(): + try: + conn = httplib.HTTPConnection("www.coptersicht.de", timeout=5) + conn.request("HEAD", "/") + conn.close() + print('Internet connection established') + print('Server is maybe not online!') + logging.info('Internet connection established') + logging.error('Server maybe is not online!') + global interntTmp + interntTmp = 'true' + #checkConnectionToFTPServer() + return 'true' + except Exception as e2: + conn.close() + print('no Internet connection') + print('trying to reconnect to internet') + logging.error('no Internet connection!') + logging.info('trying to reconnect to internet') + if enable_modem == 'true': + print('[MODEM] no UMTS connection') + logging.error('[MODEM] no UMTS connection') + try: + stdoutdata = subprocess.getoutput('sudo /usr/bin/modem3g/sakis3g status') + print('[MODEM] Network Status: ' + stdoutdata) + logging.info('[MODEM] Network Status: ' + stdoutdata) + stdoutdata = subprocess.getoutput( + "sudo /usr/bin/modem3g/sakis3g disconnect --console APN='" + modem_apn + "' USBINTERFACE='0' USBMODEM='12d1:1436'") + print(stdoutdata) + logging.info(stdoutdata) + logging.info('[MODEM] disconnected') + sleep(20) + resetModem() + stdoutdata = subprocess.getoutput( + "sudo /usr/bin/modem3g/sakis3g connect --console APN='" + modem_apn + "' USBINTERFACE='0' USBMODEM='12d1:1436'") + print(stdoutdata) + print('[MODEM] reconnection successful attempt') + logging.info('[MODEM] reconnection successful attempt') + logging.info(stdoutdata) + sleep(10) + try: + conn = httplib.HTTPConnection("www.google.de", timeout=5) + conn.request("HEAD", "/") + conn.close() + print('Internet connection established') + logging.info('Internet connection established') + return 'true' + except Exception as e5: + conn.close() + print('[MODEM] UMTS connection failed') + logging.error('[MODEM] UMTS connection failed') + print(str(e5)) + logging.error(str(e5)) + return 'false' + except Exception as e4: + print('[MODEM] UMTS connection failed') + logging.error('[MODEM] UMTS connection failed') + print(str(e4)) + logging.error(str(e4)) + return 'false' + else: + print('LAN or WLAN not connected!') + logging.error('LAN or WLAN not connected!') + return 'false' + +def checkConnectionToFTPServer(): + try: + # ping ftp url instead something else + conn = httplib.HTTPConnection("www.coptersicht.de", timeout=5) + conn.request("HEAD", "/") + conn.close() + try: + global ftp + ftp = FTP(ftp_url, ftp_user, ftp_pw, timeout=20) + ftp.cwd(ftp_picture_directory) + print('[FTP] New ftp connection successful! current dicertory is: ' + ftp.pwd()) + logging.info('[FTP] New ftp connection successful! current dicertory is: ' + ftp.pwd()) + global internetTmp + internetTmp = 'true' + global upload_every + upload_every = upload_only + print('[FTP] Server connection established') + logging.info('[FTP] Server connection established') + except Exception as ea: + internetTmp = 'false' + print('[FTP] no internet or ftp connection trying to reconnect later!') + logging.error('[FTP] no internet or ftp connection trying to reconnect later!') + except Exception as e5: + conn.close() + print('[FTP] Server connection not established') + logging.info('[FTP] Server connection not established') + reconnectToInternet() + + return + +def resetModem(): + try: + lsusb_cmd = 'lsusb | grep {}'.format('Huawei') + lsusb_out = subprocess.check_output(lsusb_cmd, shell=True) + parts = re.search(r'Bus (?P\d+) Device (?P\d+): ID [:\d\w]+ (?P.*)$', str(lsusb_out)) + bus = parts.group('bus') + dev = parts.group('dev') + desc = parts.group('desc').strip() + print('[Modem] Found device {} on bus {} for "{}"'.format(bus, dev, desc)) + f = open('/dev/bus/usb/{}/{}'.format(bus, dev), 'w', os.O_WRONLY) + fcntl.ioctl(f, 21780, 0) + sleep(15) + print('[Modem] Reset successfully attempt') + except Exception as ea: + print('[Modem] Reset Error: ' + str(ea)) + +def rebootSystem(): + rebootFlag = 'false' + os.system('sudo shutdown -r now') + print('[Timelapse Core] Reboot') + logging.info('[Timelapse Core] Reboot') + if mute == "false": + GPIO.output(piezo, GPIO.HIGH) + sleep(0.5) + GPIO.output(piezo, GPIO.LOW) + sleep(0.5) + GPIO.output(piezo, GPIO.HIGH) + sleep(1) + GPIO.output(piezo, GPIO.LOW) + sleep(1) + GPIO.output(piezo, GPIO.HIGH) + sleep(2) + GPIO.output(piezo, GPIO.LOW) + sleep(2) + sleep(70) + +def takePic(): + global picture_number + global picture_name + global picture_path + time_next_unix = time.time() + interval # time for next picture + time_name = time.asctime(time.localtime(time.time())) # get time for picture_name + time_name = time_name.replace(" ", "_") + time_name = time_name.replace(":", "-") + picture_name = camera_number + '_' + str(picture_number).zfill(6) + '_' + time_name + '.jpg' + picture_path = picture_directory + picture_name + print("Time:", time_name) + #camera = PiCamera() + camera.resolution = (int(width), int(height)) # aufloesung der bilder maximal 3280,2464s + camera.exposure_mode = 'auto' + camera.awb_mode = 'auto' + camera.crop = (0, 0, 0, 0) + try: + camera.exif_tags['IFD0.Make'] = 'CopterSicht' + camera.exif_tags['IFD0.Model'] = 'PhotoBox Camera' + camera.exif_tags['IFD0.ShutterSpeedValue '] = '' + camera.exif_tags['IFD0.ISOSpeedRatings '] = '' + camera.exif_tags['IFD0.WhiteBalance '] = '' + camera.exif_tags['IFD0.Copyright'] = 'Copyright (c) ' + str(datetime.now().year) + ' CopterSicht' + camera.exif_tags['IFD0.ImageDescription'] = 'user triggered photo' + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + if flash == "true":GPIO.output(flashPin, GPIO.HIGH) + print('Capturing image') + logging.info('[Camera] Capturing image') + camera.annotate_text = " " + camera.capture(picture_path) # capture image and save to picture_path (picture_directory + picture_name) + print('[Camera] image captured') + if flash == "true":GPIO.output(flashPin, GPIO.LOW) + if mute == "false": GPIO.output(piezo, GPIO.LOW) + picture_number += 1 # increase picture_number + print('picture_number:', str(picture_number)) + f = open("/home/pi/picture_Number.txt", 'w') + f.write(str(picture_number)) # save picture_number to a text file + f.close() + except Exception as e2: + logging.error(str(e2)) + print(str(e2)) + print('Picture size:', os.path.getsize(picture_path), "Byte") + logging.info('[Camera] Picture size: ' + str(os.path.getsize(picture_path)) + ' Byte') + + +def sleepToCapture(): + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + camera.annotate_text = " 3 " + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.LOW) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.LOW) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + camera.annotate_text = " 2 " + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.LOW) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.LOW) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + camera.annotate_text = " 1 " + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.LOW) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.HIGH) + sleep(.25) + if mute == "false":GPIO.output(piezo, GPIO.LOW) + sleep(.25) + +print('ready') +sleep(1) + +camera.resolution = (3280, 2464) +camera.awb_mode = 'auto' +camera.start_preview(fullscreen=True) +camera.annotate_text_size = 160 +camera.annotate_foreground = Color('white') + + +while 1: + camera.annotate_text = ' Bereit ' + + if GPIO.input(switch12V) == 0: + print('Btn pressed') + sleepToCapture() + takePic() + if (internetTmp == 'true') and (upload_every == 1): + uploadToFTP() + if ioBoard == "true": checkSystemVoltage() + if internet == 'true' and internetTmp == 'false': + checkConnectionToFTPServer() + checkCpu() + else: + print(' ') + + + + + + + + + +