#!/usr/bin/python3 # -*- coding: UTF8 -*- """datacam datacam is design to take a picture with the PiCamera and collect environnemental data from the PiSense HAT, (almost) at the same time. """ ################# # Configuration # ################# #General version = "v0.4" #time_lapse = 20 # Time between photos et data captures verbose = True # Pictures ajust_time = 1 #Time waiting for the camera to ajust before taking the photo (in seconds) x_res = 2592 #X resolution (max = 2592) y_res = 1944 #Y resolution (max = 1944) photo_dir = "pictures" #Photo directory photo_file = "Test_%Y-%m-%d_%H%M%S" # Picture files name (strftime() compatible) photo_rotation = "180" # Image rotation in degree #camera_LED = True # Set to False to disable red camera LED during capture, need root privileges sense_LED_flash = True # Data #Data to collect : # - 'temperature_h' : temperature from humidity sensor # - 'temperature_p' : temperature from pressure sensor # - 'temperature_cpu' : temperature from CPU # - 'pressure' : pressure # - 'humidity' : relative humidity data_collection = ['temperature_p', 'pressure', 'temperature_h', 'humidity', 'temperature_cpu', 'blabla', ] data_display = ['pressure', 'temperature_h', 'humidity', 'blabla', ] data_dir = 'data' data_log = ['date', 'value', 'quality'] # Fonts fonts_dir = '/usr/share/fonts/truetype/freefont/' font_bold = 'FreeMonoBold.ttf' font_basic = 'FreeMono.ttf' font_italic = 'FreeMonoOblique.ttf' font_bold_italic = 'FreeMonoBoldOblique.ttf' font_default = font_basic ################# # Imports # ################# from os import path, popen from time import sleep, strftime import csv # Picture management with the Pi Cam from picamera import PiCamera #Pi Sense HAT module (sensors) from sense_hat import SenseHat # Traitement d'images import PIL from PIL import ImageFont from PIL import Image from PIL import ImageDraw # For picture metadata (EXIF, IPTC...) import piexif #Divers from pprint import pprint ######################### # Déclarations globales # ######################### sense = SenseHat() data = {} data_dir = path.join('/home/pi/datacam', version, data_dir) photo_dir = path.join('/home/pi/datacam', version, photo_dir) ############# # Fonctions # ############# #Get one data from one sensors and return the value in a dict object with some metadata # like time/date, description, unit... class Raw_Data: """Data class. """ dtype = '' date = '' value = '' quality = -1 # -1 : default value/data type unknown, 0 : OK, 1 : non-available metadata = {'desc' : 'Unknow', 'unit' : '', } def __init__(self, data_type): "Initialize new data object of the defined type." self.dtype = data_type if self.dtype == 'temperature_p': self.metadata = {'desc' : 'Air temperature (pressure sensors)', 'unit' : '°C', 'short' : 'Temperature', 'category' : 'Environnement/Temperature', } elif self.dtype == 'temperature_h': self.metadata = {'desc' : 'Air temperature (pressure sensors)', 'unit' : '°C', 'short' : 'Temperature', 'category' : 'Environnement/Temperature', } elif self.dtype == 'pressure': self.metadata = {'desc' : 'Atmospheric Pressure', 'unit' : 'mbar', 'short' : 'Pression', 'category' : 'Environnement/Pressure', } elif self.dtype == 'humidity': self.metadata = {'desc' : 'Relative air humidity', 'unit' : '%', 'short' : 'Humidity', 'category' : 'Environnement/Humidity', } elif self.dtype == 'temperature_cpu': self.metadata = {'desc' : 'CPU temperature', 'unit' : '°C', 'short' : 'Temperature', 'category' : 'System/Temperature', } else: print(data_type + " is unknown.") def get_raw(self): "Get raw data from sensors and update information about this data." self.date = strftime('%Y-%m-%d_%H:%M:%S') if self.dtype == 'temperature_p': try: self.value = float(sense.get_temperature_from_pressure()) self.quality = 0 except: self.value = None self.quality = 1 elif self.dtype == 'temperature_h': try: self.value = float(sense.get_temperature_from_humidity()) self.quality = 0 except: self.value = None self.quality = 1 elif self.dtype == 'pressure': try: self.value = float(sense.get_pressure()) self.quality = 0 except: self.value = None self.quality = 1 elif self.dtype == 'humidity': try: self.value = float(sense.get_humidity()) self.quality = 0 except: self.value = None self.quality = 1 elif self.dtype == 'temperature_cpu': try: temp = popen('/opt/vc/bin/vcgencmd measure_temp').readline() self.value = float(temp.replace('temp=','').replace("'C\n","")) self.quality = 0 except: self.value = None self.quality = 1 else: print(data_type + " is unknown.") self.metadata.update({'desc' : 'Unknown'}) self.value = None self.quality = -1 def write_csv(self, file_path): "Write data in CSV file. Creates CSV file and metadata text file if they do not exist." if path.isfile(file_path): with open(file_path, 'a') as csvfile: datawriter = csv.DictWriter(csvfile, fieldnames=data_log, delimiter=';') datawriter.writerow({n : v for n, v in vars(data[data_type]).items() if n in data_log }) else: #Creation of metadata file if data[data_type]['desc'] != 'Unknown': with open(path.join(data_dir, data_type + '_desc.txt'), 'w') as metadata_file: for desc, value in data[data_type].items(): if desc not in data_log: metadata_file.write(desc + " : " + str(value) + '\n') #Creation of data file with open(csvfile_path, 'w') as csvfile: datawriter = csv.DictWriter(csvfile, fieldnames=data_log, delimiter=';') datawriter.writeheader() datawriter.writerow({n : v for n, v in vars(data[data_type]).items() if n in data_log}) def __repr__(self): "For debug" if self.metadata['desc'] == 'Unknown': return ''.format(d=self.metadata['desc'], t=self.dtype, dt=self.date, ) elif type(self.value) == float: return ''.format(d=self.metadata['desc'], v=self.value, u=self.metadata['unit'], q=self.quality, dt=self.date, ) elif self.value == None: return ''.format(d=self.metadata['desc'], v=self.value, q=self.quality, dt=self.date, ) else: return ''.format(d=self.metadata['desc'], v=self.value, u=self.metadata['unit'], q=self.quality, dt=self.date, ) return def __str__(self): "For print (to user)" if self.metadata['desc'] == 'Unknown': return '{data_type:} = {desc:}'.format(desc=self.metadata['desc'], data_type=self.dtype, ) elif type(self.value) == float: return '{d:} = {v:.1f}{u}'.format(d=self.metadata['desc'], v=self.value, u=self.metadata['unit'], ) elif self.value == None: return '{d:} = {v}'.format(d=self.metadata['desc'], v='NA', ) else: return '{d:} = {v} {u}'.format(d=self.metadata['desc'], v=self.value, u=self.metadata['unit'], ) def __call__(self): return self.value # Take a picture def get_pict(): with PiCamera() as camera: camera.resolution = (x_res, y_res) camera.rotation = photo_rotation #camera.led = camera_LED # !!! need root privileges !!! if sense_LED_flash: sense.show_message(text_string="", back_colour=[255, 255, 255], ) camera.start_preview() sleep(ajust_time) sense.set_rotation(r=180, redraw=True) #sense.show_message(text_string=":-)", #scroll_speed=0.1 , #text_colour=[255, 0, 0], #back_colour=[0, 0, 255]) pict_path = path.join(photo_dir, strftime(photo_file) + '.jpg') camera.capture(pict_path) sense.clear() #camera.led = True # !!! need root privileges !!! return pict_path ######## # MAIN # ######## # Collect asked data from sensors for data_type in data_collection: data[data_type] = Raw_Data(data_type) data[data_type].get_raw() if verbose : print(data[data_type].__repr__()) print(data[data_type]()) #Save data in CSV file csvfile_path = path.join(data_dir, data_type + '.csv') data[data_type].write_csv(csvfile_path) #Take a picture #picture_path = get_pict() ## Write data on picture in new picture file #pict = Image.open(picture_path) #draw = ImageDraw.Draw(pict) ## Text line height (=font size) and line space calculation #line_height = int(y_res / 40) #line_space = int(line_height / 10) #text = '' #l = line_space ## Add a line with data for each data to display #for d in data_display: #text = str(data[d]) #.__str__() ## Select color and font to match data quality #if data[d].quality == 0: #text_color = (0, 255, 0) # Quality OK : text in green #font = font_default #elif data[d].quality < 0: #text_color = (100, 100, 100) # Unknown in grey #font = font_italic #elif data[d].quality == 1: #text_color = (255, 255, 0) # Sensor unavailable : yellow #font = font_italic #else: #text_color = (255, 0, 0) # Other = error in red #font = font_bold #img_font = ImageFont.truetype(path.join(fonts_dir, font), line_height) #draw.text((5,l), text, text_color, img_font) #draw = ImageDraw.Draw(pict) #l = l + line_height + line_space ##Get EXIF metadata and delete embedded thumbnail #pict_exif = piexif.load(pict.info['exif']) #del(pict_exif['thumbnail']) #pict.save(picture_path, 'jpeg', exif=piexif.dump(pict_exif) ) #if verbose : #print('Picture = ' + picture_path)