From 61c4296af66f7d1bbb03d6bd72c4d0012e4ca27a Mon Sep 17 00:00:00 2001 From: Pierrick C Date: Wed, 20 Sep 2017 18:06:00 +0200 Subject: [PATCH] Nouveau driver ePaper Display de Waveshare supportant les Image de la librairie PIL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Beaucoup plus facile à utiliser pour de l'affichage riche --- .gitignore | 1 + .../mqtt_esp8266_BME280.ino | 32 ++--- cameteo-interface/cameteo.conf | 6 + cameteo-interface/cameteo_conf.py | 8 +- cameteo-interface/epd2in13.py | 6 +- cameteo-interface/mqtt2epaper.py | 118 +++++++++++++----- 6 files changed, 121 insertions(+), 50 deletions(-) diff --git a/.gitignore b/.gitignore index 124f959..decbc67 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ raspberry/pictures/* boitier/old* .directory *__pycache__* +test* diff --git a/arduino/mqtt_esp8266_BME280/mqtt_esp8266_BME280.ino b/arduino/mqtt_esp8266_BME280/mqtt_esp8266_BME280.ino index 7fee4f7..2910f96 100644 --- a/arduino/mqtt_esp8266_BME280/mqtt_esp8266_BME280.ino +++ b/arduino/mqtt_esp8266_BME280/mqtt_esp8266_BME280.ino @@ -20,7 +20,7 @@ #define MQTT_USERNAME "arofarn" #define MQTT_KEY "WaKaW9XMGUZ3rRJD" #define MQTT_ID "huzzah0" -#define MQTT_PUB_INTERVAL 10000 +#define MQTT_PUB_INTERVAL 15000 // milliseconds /************************* Time and NTP Setup *********************************/ @@ -33,7 +33,7 @@ #define MODE_STATION true #define DEFAULT_ALTITUDE 140.0 -#define DEFAULT_MSLP 1013.25 //Atmospheric Pressure at Sea-Level +#define DEFAULT_MSLP 1013.25 //Mean Atmospheric Pressure at Mean Sea-Level // Create an ESP8266 WiFiClient class to connect to the MQTT server. WiFiClient wifi_client; @@ -152,21 +152,11 @@ void loop() { date_str = NTP.getTimeDateString(); date_str.toCharArray(date_val,20); - // Now we can publish stuff! - Serial.print(F("Sending date and time ")); - Serial.print(date_val); - Serial.print("..."); - if (! mqtt_client.publish("huzzah0/NTP/date", date_val, true, 2)) { - Serial.println(F("Failed")); - } else { - Serial.println(F("OK!")); - } - // Now we can publish stuff! Serial.print(F("Sending pressure value ")); Serial.print(pressure_val); Serial.print("..."); - val2json(pressure_val, date_str, "hPa", "PA").toCharArray(tosend, 120); + val2json(pressure_val, date_str, "hPa", "AP").toCharArray(tosend, 120); if (! mqtt_client.publish("huzzah0/AdaBME280_1/pressure", tosend, true, 2) ) { Serial.println(F("Failed")); } else { @@ -182,7 +172,7 @@ void loop() { Serial.print(F("Sending sea-level pressure value ")); Serial.print(atm_press_sea_level); Serial.print("..."); - val2json(atm_press_sea_level, date_str, "hPa", "SLPA").toCharArray(tosend, 120); + val2json(atm_press_sea_level, date_str, "hPa", "MSLP").toCharArray(tosend, 120); if (! mqtt_client.publish("huzzah0/AdaBME280_1/sea_level_pressure", tosend, true, 2) ) { Serial.println(F("Failed")); } else { @@ -202,7 +192,7 @@ void loop() { Serial.print(F("Sending humity value ")); Serial.print(humidity_val); Serial.print("..."); - val2json(humidity_val, date_str, "%", "HR").toCharArray(tosend, 120); + val2json(humidity_val, date_str, "%", "RH").toCharArray(tosend, 120); if (! mqtt_client.publish("huzzah0/AdaBME280_1/humidity", tosend, true, 2)) { Serial.println(F("Failed")); } else { @@ -213,12 +203,22 @@ void loop() { Serial.print(F("Sending temperature value ")); Serial.print(temperature_val); Serial.print("..."); - val2json(temperature_val, date_str, "degC", "TA").toCharArray(tosend, 120); + val2json(temperature_val, date_str, "degC", "AT").toCharArray(tosend, 120); if (! mqtt_client.publish("huzzah0/AdaBME280_1/temperature", tosend, true, 2)) { Serial.println(F("Failed")); } else { Serial.println(F("OK!")); } + + // __LAST__ thing to send : Date and Time + Serial.print(F("Sending date and time ")); + Serial.print(date_val); + Serial.print("..."); + if (! mqtt_client.publish("huzzah0/NTP/date", date_val, true, 2)) { + Serial.println(F("Failed")); + } else { + Serial.println(F("OK!")); + } } delay(10); diff --git a/cameteo-interface/cameteo.conf b/cameteo-interface/cameteo.conf index 9187da8..5da1079 100644 --- a/cameteo-interface/cameteo.conf +++ b/cameteo-interface/cameteo.conf @@ -32,5 +32,11 @@ photo_dir=/home/pi/Cameteo/photos/ photo_name=test_%%Y%%m%%d_%%H%%M%%S photo_format=jpeg +[EPD] +epd_font_file=/usr/share/fonts/truetype/freefont/FreeMono.ttf +epd_bold_font_file=/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf +epd_italic_font_file=/usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf +epd_rotate = 1 + [MISC] TimeZone=2 diff --git a/cameteo-interface/cameteo_conf.py b/cameteo-interface/cameteo_conf.py index cd404e2..bea4fb8 100644 --- a/cameteo-interface/cameteo_conf.py +++ b/cameteo-interface/cameteo_conf.py @@ -71,4 +71,10 @@ photo_extensions = {'jpeg': 'jpg', 'bmp': 'bmp', 'yuv': 'yuv', 'rgb': 'rgb', - 'rgba': 'rgba'} \ No newline at end of file + 'rgba': 'rgba'} + +#epaper display +epd_font_file = parser['EPD'].get('epd_font_file' , fallback='/usr/share/fonts/truetype/freefont/FreeMono.ttf') +epd_bold_font_file = parser['EPD'].get('epd_bold_font_file' , fallback='/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf') +epd_italic_font_file = parser['EPD'].get('epd_italic_font_file' , fallback='/usr/share/fonts/truetype/freefont/FreeMonoOblique.ttf') +epd_rotate = min(int(parser['EPD'].get('epd_rotate', fallback=0)), 1) \ No newline at end of file diff --git a/cameteo-interface/epd2in13.py b/cameteo-interface/epd2in13.py index b770946..c437b31 100644 --- a/cameteo-interface/epd2in13.py +++ b/cameteo-interface/epd2in13.py @@ -29,7 +29,7 @@ from PIL import Image import RPi.GPIO as GPIO # Display resolution -EPD_WIDTH = 122 +EPD_WIDTH = 128 EPD_HEIGHT = 250 # EPD2IN13 commands @@ -127,7 +127,7 @@ class EPD: def wait_until_idle(self): while(self.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy - self.delay_ms(100) + self.delay_ms(20) ## # @brief: module reset. # often used to awaken the module in deep sleep, @@ -214,7 +214,7 @@ class EPD: self.set_memory_pointer(0, 0) self.send_command(WRITE_RAM) # send the color data - for i in range(0, self.width / 8 * self.height): + for i in range(0, int(self.width / 8 * self.height)): self.send_data(color) ## diff --git a/cameteo-interface/mqtt2epaper.py b/cameteo-interface/mqtt2epaper.py index 10b962e..d6ba66b 100644 --- a/cameteo-interface/mqtt2epaper.py +++ b/cameteo-interface/mqtt2epaper.py @@ -11,13 +11,24 @@ Display data from MQTT broker on ePaper screen from cameteo_conf import * import paho.mqtt.client as mqtt -from spidev import SpiDev -import EPD_driver +import time import json -import netifaces +import netifaces, socket +import epd2in13 +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFont mqtt_client_id = "epaper_display" +def update_epd(): + #rotation de l'image de 90° + 180° si le paramètre epd_rotate est différent de 0 + # la translation permet de rattraper un petit décalage en cas de rotation de 90+180° + screen = image.rotate(90 + (epd_rotate * 180), expand=True, translate=(-6*epd_rotate, 0)) + #Mis en memoire de l'image à afficher et affichage de l'image + epd.set_frame_memory(screen, 0, 0) + epd.display_frame() + ############# # CALLBACKS # ############# @@ -51,61 +62,108 @@ def on_message(client, userdata, msg): val['value'] = float('nan') #Gestion du symbole des degrés parfois difficile pour certaines sources - #val['unit'] = val['unit'].replace('deg', '°') + val['unit'] = val['unit'].replace('deg', '°') #Affichage des données - coord_type = { 'TA' : 33, - 'HR' : 49, - 'SLPA' : 65 - } + coord_type = { 'AT' : (2, 50), + 'RH' : (127, 50), + 'MSLP' : (2, 66), + 'ALTI' : (127, 66), + } if val['type'] in coord_type: - disp.Dis_String(125, coord_type[val['type']], "{} {} ".format(val['value'], val['unit']), 16) - + #erase former text + draw.rectangle(coord_type[val['type']] + (coord_type[val['type']][0] + 122, coord_type[val['type']][1] + 14), fill=255) + #draw new values + draw.text(coord_type[val['type']], "{:8.1f} {} ".format(val['value'], val['unit']), font=font14, fill=0) +# #Update the display +# screen = image.rotate(90, expand=True) +# epd.set_frame_memory(screen, 0, 0) +# epd.display_frame() + #Callback managing date and time from MQTT broker def on_message_date(client, userdata, msg): payload = msg.payload.decode() print("Date : " + payload) + try: - d = datetime.strptime(payload, "%H:%M:%S %d/%m/%Y") - disp.Dis_String(0, 17, datetime.strftime(d + TimeZone.utcoffset(None), "%d/%m/%Y %H:%M:%S"), 16) + #Mise en forme de la date et heure reçu pour fuseau horaire configuré + dt_texte = datetime.strftime(datetime.strptime(payload, "%H:%M:%S %d/%m/%Y") + TimeZone.utcoffset(None), "%d/%m/%Y %H:%M:%S") except: - disp.Dis_String(0, 17, payload, 16) + dt_texte = payload + + #Affichage du résultat + draw.rectangle((0, 34, epd2in13.EPD_HEIGHT, 48), fill=255) + draw.text((0, 34), dt_texte, font=font14, fill=0) + update_epd() #Update display with info from camera (camera shooting new photo or name of the # last photo) def on_message_camera(client, userdata, msg): pl = msg.payload.decode() print("{} : {} ({})".format(msg.topic, pl, type(pl))) + + # Photo en cours if pl == "1" and msg.topic == camera_mqtt_topic + "/shooting" : - disp.Dis_String(0, 105, "Shooting photo... ", 16) + draw.rectangle((0, 105, epd2in13.EPD_HEIGHT, 120), fill=255) + draw.text((0, 105), "Shooting photo... ", font=font14, fill=0) + update_epd() + + #Dernière photo prise if msg.topic == camera_mqtt_topic + "/last_photo": - disp.Dis_String(0, 105, "Last: " + pl, 16) - + draw.rectangle((0, 105, epd2in13.EPD_HEIGHT, 120), fill=255) + draw.text((0, 105), "Last: " + pl, font=font14, fill=0) + update_epd() + #Callback de déconnexion au broker MQTT def on_disconnect(client, userdata, msg): if msg != 0: print("Déconnexion imprévu : %s" % msg) exit() - + ######## # Main # ######## -#init and Clear full screen -bus = 0 -device = 0 -disp = EPD_driver.EPD_driver(spi=SpiDev(bus, device)) +#Screen init. +epd = epd2in13.EPD() +epd.init(epd.lut_full_update) -print("Start display...") -disp.Dis_Clear_full() -disp.Dis_Clear_part() -print("OK") +#List of TrueType fonts +font12 = ImageFont.truetype(epd_font_file, 12) +font14 = ImageFont.truetype(epd_font_file, 14) +font16 = ImageFont.truetype(epd_font_file, 16) +font12_bold = ImageFont.truetype(epd_bold_font_file, 12) +font14_bold = ImageFont.truetype(epd_bold_font_file, 14) +font16_bold = ImageFont.truetype(epd_bold_font_file, 16) +#font12_obl = ImageFont.truetype(epd_italic_font_file, 12) +#font14_obl = ImageFont.truetype(epd_italic_font_file, 14) +#font16_obl = ImageFont.truetype(epd_italic_font_file, 16) -#disp.Dis_String(0, 0, "{} | {}".format(socket.gethostname(), netifaces.ifaddresses('wlan0')[2][0]['addr']), 12) -disp.Dis_String(0, 0, "IPv4 : {}".format(netifaces.ifaddresses('wlan0')[2][0]['addr']), 16) -disp.Dis_String(0, 33, "Temperature : ", 16) -disp.Dis_String(0, 49, "Humidity : ", 16) -disp.Dis_String(0, 65, "Sea Pression : ", 16) +#New image buffer +# Memo : 0=black, 255= white/blank +image = Image.new('1', (epd2in13.EPD_HEIGHT, epd2in13.EPD_WIDTH), 255) +draw = ImageDraw.Draw(image) + +#Draw basic text and frame : +draw.rectangle((0, 0, epd2in13.EPD_HEIGHT, 18), fill=0) +draw.text((60, 2), "Projet Camétéo", font=font16_bold, fill=255) +draw.text((0, 20), "{} : IP= {}".format(socket.gethostname(), netifaces.ifaddresses('wlan0')[2][0]['addr']), font = font12, fill=0 ) +draw.line((0,49,epd2in13.EPD_HEIGHT, 49), fill=0) +draw.line((0,65,epd2in13.EPD_HEIGHT, 65), fill=0) +draw.line((0,81,epd2in13.EPD_HEIGHT, 81), fill=0) +draw.line((125, 49, 125, 81), fill=0) + +#Display on ePaper screen +epd.clear_frame_memory(0xFF) +update_epd() + +# +#screen = image.rotate(90, expand=True) +#epd.set_frame_memory(screen, 0, 0) +#epd.display_frame() + +#Toggle to partial refresh +epd.init(epd.lut_partial_update) #Connect to MQTT broker and loop... mqtt_client = mqtt.Client(mqtt_client_id, clean_session=True)