Projekt PiMP – logowanie danych
W poprzednim artykule udało nam się zmusić Pico do odczytu i wyświetlenia danych na wyświetlaczu OLED, w tej części będziemy starali się utworzyć odpowiedni plik o konkretnej strukturze do którego zapisywane będą odczyty z sensora, a on sam zapisywany będzie na pamięci Flash kontrolera.
Efektem tego była kolejna modyfikacja kodu programu :
from machine import Pin, I2C
import time
year, month, day, hour, mins, secs, weekday, yearday = time.localtime()
from time import sleep
import bme280
from ssd1306 import SSD1306_I2C
builtin_led = Pin(25, Pin.OUT)
i2c=I2C(0,sda=Pin(0), scl=Pin(1), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
datka = ("{}-{:02d}-{:02d}".format(year, month, day))
godzina = ("{}:{:02d}:{:02d}".format(hour, mins, secs))
znak = ('_')
#sekcja tworzenia pliku z logiem
#file=open("data.txt","w")
filename = (datka + znak + godzina)
file=open(filename + '.txt', 'w')
while True:
builtin_led.value(1)
oled.fill(0)
bme = bme280.BME280(i2c=i2c)
temperature = bme.values[0]
pressure = bme.values[1]
humidity = bme.values[2]
print("{}-{:02d}-{:02d}".format(year, month, day))
print("{}:{:02d}:{:02d}".format(hour, mins, secs))
print('Temperatura: ', temperature)
print('Wilgotnosc: ', humidity)
print('Cisnienie: ', pressure)
file.write(str(datka)+",")
file.write(str(godzina)+",")
file.write(str(temperature)+",")
file.write(str(pressure)+",")
file.write(str(humidity)+"\n")
oled.text(""+datka, 25, 0)
oled.text(""+godzina, 30, 10)
oled.text("Temp "+temperature, 15, 20)
oled.text("Cis "+pressure, 15, 30)
oled.text("Wilg "+humidity, 15,40)
oled.show() #wyswietlenie na wyswietlaczu
sleep(10) #przerwa 10 w petli
Wszystko byłoby okej z kodem powyżej gdyby nie to, że zegar jak już wcześniej pisałem nie pobiera poprawnych danych i nie potrafi zapisać odpowiedniego pliku z konkretną nazwą.
Na szczęście w moich rękach jest już Pico „W” z interfejsem sieciowym. Pierwsze testy połączenia zakończyły się sukcesem, uruchomienie programu zawierające zmienne czasu także zakończyło się sukcesem. Kod pozwalający na takie bajery przedstawia się mniej więcej tak :
from machine import Pin, I2C
import time
year, month, day, hour, mins, secs, weekday, yearday = time.localtime()
from time import sleep
import bme280
from ssd1306 import SSD1306_I2C
import network
builtin_led = Pin(25, Pin.OUT)
i2c=I2C(0,sda=Pin(0), scl=Pin(1), freq=400000)
oled = SSD1306_I2C(128, 64, i2c)
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
ssid = "nazwa_sieci"
pw = "hasło_sieci"
wlan.connect(ssid, pw)
def light_onboard_led():
led = machine.Pin('LED', machine.Pin.OUT)
led.on();
timeout = 10
while timeout > 0:
if wlan.status() >= 3:
light_onboard_led()
break
timeout -= 1
print('Waiting for connection...')
time.sleep(1)
wlan_status = wlan.status()
while True:
oled.fill(0)
bme = bme280.BME280(i2c=i2c)
temperature = bme.values[0]
pressure = bme.values[1]
humidity = bme.values[2]
data = ("{}-{:02d}-{:02d}".format(year, month, day))
godzina = ("{}:{:02d}:{:02d}".format(hour, mins, secs))
print("{}-{:02d}-{:02d}".format(year, month, day))
print("{}:{:02d}:{:02d}".format(hour, mins, secs))
print('Temperatura: ', temperature)
print('Wilgotnosc: ', humidity)
print('Cisnienie: ', pressure)
oled.text(""+data, 25, 0)
oled.text(""+godzina, 30, 10)
oled.text("Temp "+temperature, 15, 20)
oled.text("Cis "+pressure, 15, 30)
oled.text("Wilg "+humidity, 15,40)
oled.show() #wyswietlenie na wyswietlaczu
sleep(10) #przerwa 10 w petli
No i w pizdu !
Minęły chyba dwa dni od momentu kiedy napisałem wcześniejszą część tego artykułu i zabrałem się za pisanie tego co widzicie poniżej w kolejny nagłówku, no i przy przyjemnym kontemplowaniu innego problemu który pojawił się podczas pisania zauważyłem że co prawda rozwiązałem problem z datą i godziną na wyświetlaczu, w konsoli oraz tym co wpisuje się do pliku ALE cały czas mam datę pobraną w momencie uruchamiania programu. Data nie odświeżała się wcale a jest to w gruncie rzeczy dość istotny element całego systemu 🙂 Lamer jak widać w pełnej krasie. Więc przy okazji zmiany całej tej pętli zabrałem się za modyfikowanie kwestii pobierania daty i godziny. Celem modyfikacji było zabezpieczenie sytuacji w której urządzenie nie połączy się z Internetem aby było w tym momencie w stanie użyć jakiegoś zegara RTC. Owocem była absolutna zmiana działania programu. Poniżej przedstawiam wygląd kodu programu głównego oznaczonego wydawczo na wersję 0.4.4:
# PiMP ver 0.4.4
# by Slawek 'chinczyk' Meredyk
import network, urequests, utime, machine, time
from machine import RTC, I2C, Pin
from ssd1306 import SSD1306_I2C
import bme280
from time import sleep
# ustawienia
seryjny= "100" # numer rozpoznawania urzadzenia unikalny dla kazdej sztuki
znak="_"
znakczasu=str(time.ticks_ms())
ssid = "" # nazwa sieci ( w przyszlosci w innym pliku )
pw = "" # haslo ( w przyszlosci w innym pliku )
url = "http://worldtimeapi.org/api/timezone/Europe/Warsaw" # jak by co wszystkie strefy tu http://worldtimeapi.org/timezones
web_query_delay = 50000 # interwal zapytania strony o jsona
retry_delay = 4000 # interwal zapytania w razie bledu zapytania
filename = (seryjny + znak + znakczasu) #utworzenie nazwy pliku skladajacego sie z numeru seryjnego i czasu w formatowaniu milisekend
i2c=I2C(0,sda=Pin(0), scl=Pin(1), freq=400000)
# malowanie wyswietlacza wynikiem łączenia do wifi
print("Laczenie do wifi...")
oled = SSD1306_I2C(128, 64, i2c)
oled.fill(0)
oled.text("Laczenie", 0, 5)
oled.text(" do wifi...", 0, 15)
oled.show()
# wywolanie wewnetrznego RTC
rtc = RTC()
# Proba polaczenia wifi
wifi = network.WLAN(network.STA_IF) # station mode
wifi.active(True)
wifi.connect(ssid, pw)
# czekaj na polaczenie
while not wifi.isconnected():
pass
# wifi polaczone
print("IP:", wifi.ifconfig()[0], "\n")
oled.text("Polaczono. IP: ", 0, 35)
oled.text(" " + str(wifi.ifconfig()[0]), 0, 45)
oled.show()
sleep(2)
# ustaw timer
update_time = utime.ticks_ms() - web_query_delay
# Petla wlasciwa
while True:
# jesli zgubisz polaczenie zrob reboot ( nie wiem czy to dobre wyjdzie w testach )
if not wifi.isconnected():
machine.reset()
# query i dzejson z http
if utime.ticks_ms() - update_time >= web_query_delay:
#Pobieram dane HTTP
response = urequests.get(url)
if response.status_code == 200: # query success
# parasuje JSON
parsed = response.json()
datetime_str = str(parsed["datetime"])
year = int(datetime_str[0:4])
month = int(datetime_str[5:7])
day = int(datetime_str[8:10])
hour = int(datetime_str[11:13])
minute = int(datetime_str[14:16])
second = int(datetime_str[17:19])
subsecond = int(round(int(datetime_str[20:26]) / 10000))
# updejt wewnetrznego rtc
rtc.datetime((year, month, day, 0, hour, minute, second, subsecond))
update_time = utime.ticks_ms()
# generowanie poprawnego formatu daty
date_str = "{1:02d}/{2:02d}/{0:4d}".format(*rtc.datetime())
time_str = "{4:02d}:{5:02d}:{6:02d}".format(*rtc.datetime())
#odczyt z czujnikow
bme = bme280.BME280(i2c=i2c)
temperature = bme.values[0]
pressure = bme.values[1]
humidity = bme.values[2]
file=open(filename + '.txt', 'a')
print('Data',date_str)
print('Czas',time_str)
print('Temperatura: ', temperature)
print('Wilgotnosc: ', humidity)
print('Cisnienie: ', pressure)
# dopisywanie pliku
file.write(str(date_str)+",")
file.write(str(time_str)+",")
file.write(str(temperature)+",")
file.write(str(pressure)+",")
file.write(str(humidity)+"\n")
file.close()
else: # jesli query nie zadziaialo proboj jeszcze raz
update_time = utime.ticks_ms() - web_query_delay + retry_delay
# malowanie wyswietlacza
oled.fill(0)
oled.text(date_str,10, 0)
oled.text(time_str,10, 10)
oled.text("Temp "+temperature, 10, 20)
oled.text("Cis "+pressure, 10, 30)
oled.text("Wilg "+humidity, 10,40)
oled.show()
utime.sleep(1)
Tym razem przyjąłem już też zmiany rozwojowe takie jak dodanie zmiennej numeru seryjnego, który w przyszłości będzie identyfikatorem danej sztuki urządzenia w bazie danych i cloudzie do kolektowania i wyświetlania informacji z PiMP’s. Teraz czekamy tylko na GPS ponieważ pierwszy który przyjechał wydaje się nie działać poprawnie, więc aby się upewnić zakupiłem jeszcze jedną sztukę.
Budowa strony powitalnej w html i hotspot
Najprościej powiedzieć iż aby być dojść do hotspota trzeba hotspota 🙂
Zamysł konfiguracyjny jest taki że dostając do ręki gotowe urządzenie end user ma wykonać jak najmniej czynności prowadzących do poprawnego działania urządzenia. Inaczej mówiąc (trochę kolokwialnie) ma być idioto-odporne. Idąc tym tokiem rozumowania zacząłem budować pierwszy program który uruchomi się zaraz po starcie urządzenia ma on za zadanie z Pico zrobić hotspota o nazwie sieci PiMP + nr seryjny urządzenia i haśle podanym w dokumentacji.
Nowy użytkownik po połączeniu się do wyżej podanej sieci dostaje informacyjna stronę www na której musi wykonać akcję mającą na celu wypełnienie formularza w którym znajduje się nazwa sieci oraz hasło do telefonicznego hotspota. Połączenie to zasilać będzie urządzenie PiMP w internet, a to pozwoli na wymianę informacji między serwerem a urządzeniem.
Kolejnym etapem po zapisaniu danych w formularzu jest zapisanie danych za pomocą micropythona do osobnego pliku o nazwie dupa.py z którego będzie korzystał już program właściwy. Dalej będzie restart urządzenie i podłączenie się do właściwego hotspota telefonicznego.
I wszystko na tym etapie byłoby proste, łatwe i klarowne jak by nie fakt że formularz jest w HTML a zazwyczaj wykonanie posta czyli wysłanie danych z formularza gdzieś odbywa się w dzisiejszych czasach w PHP. Jak wiecie PHP działa po stronie serwera a nie jak HTML po stronie przeglądarki więc każdy Sherlock wykminił właśnie że PHP na Pico nie zadziała i tu dla mnie zaczęły się schody. Długo szukałem sposobu aby dane z formularza w HTML przekazać do Pythona i nim zbudować potrzebny plik.
Konkluzje po kolejnym etapie
Po rozmowach z kolegą odnośnie materiału obudowy oraz niej samej są nowe wnioski :
- kolor filamentu musi być najlepiej biały z powodu absorbowania światła słonecznego.
- obudowa musi być podzielona na dwie komory jedna będzie „hermetyczna” a druga musi być wentylowana aby zapewnić odpowiedni dopływ powietrza do badania.
- musi powstać plik z ustawieniami urządzenia, np numer seryjny , nick osoby używające, link do wysyłania danych. ( i tu już po części mamy takie ustawienia w głównej części programu, w przyszłości pomyślimy czy zrobić faktycznie dla nich odrębny plik czy budować sekcję w pliku głównym.