Air:bit Programmering

Nå har vi alle verktøyene vi trenger for å starte programmering av Air:bit. For å komme i gang så kobler vi til Raspberry Pien via USB til PCen. Når vi kobler denne til PCen så vil den komme opp som en ny drive. Når vi åpner denne så kan vi bruke kontekstmenyen for å Åpne i Code. Nå har vi åpnet VS Code til Raspberry Pien, her kan vi lagre all koden vår, og den vil automatisk bli lastet opp til Raspberryen.

Hvis vi ikke har kontektsmenyen tilgjengelig så må vi åpne Visual Studio Code, og navigere vila File -> Open Folder (Fil -> Åpne Mappe), og gå til Raspberryen som sannsynlig har navnet CIRCUITPY (D:).

Kopiere bibliotekene

Her tar vi alt vi lastet ned fra Github repositoriet. Dette vil gi oss tilgang til airbit biblioteket, som vil gi oss all koden vi trenger for å starte.

Når vi har kopiert over all kode, og åpnet VS Code på raspberryen så vil vi se all kopiert kode listet opp på venstre side (inkludert alle mappene).

Her skal vi åpne filen som heter main.py. Dette er hovedfilen på raspberryen, hvor den alltid starter og avslutter å kjøre programmet vårt fra. Når vi begynner å programmere så vil Raspberryen starte på nytt hver gang vi lagrer main.py og starte å kjøre fra denne filen, så vi vil alltid oppdatere og teste programmet vårt.

La koden som ligger i filen være inntil videre.

Åpne seriell tilkobling

Hvis vi innstallerte riktige extensions til VS Code, så vil vi kunne koble til en såkalt seriell konsoll til Air:Bit’en, for å kunne se output fra denne.

Måten vi gjør dette på er å se helt nederst i høyre hjørne på VS Code (på den blå statusstripen). Der vil det stå <Choose a board> samt et lite symbol av en kontakt.

Her klikker vi på <Choose a board> og skriver inn pico w i søkelinjen som kommer opp.

Dermed så velger vi Raspberry Pi:Pico W fordi det er denne mikrokontrolleren vi bruker.

Så trykker vi på den lille kontakten, og da vil vi få opp en ny søkelinje, og her står det gjerne listet opp et eller flere valg. På disse valgene står det gjerne COM også et tall bak. Vi skal velge den hvor det står RapsberryPi etter tallet. Da vil vi få opp et terminalvindu helt nederst i VS Code, hvor vi kan se noe informasjon om hva som kjører på Raspberryen (Air:Bit’en). Hver gang vi lagrer main.py så vil Raspberryen restarte og kjøre koden på nytt, men vi kan også tvinge koden til å stoppe ved å trykke i terminalvinduet og trykke Ctrl+C, også kan vi restarte programmet med å trykke Ctrl+D i samme vindu.

Nå skal du kunne se de følgende meldingene hvis du har åpnet konsollen riktig:

Hello Airbit
Go to https://airbit.uit.no to get started with programming the Air:Bit

Når vi skal programmere Air:Bit’en, så gjør vi dette i main.py filen. Hver gang vi lagrer filen, enten ved å trykke Ctrl+S eller gå til Fil->Lagre (eller File->Save).

Alt som kommer ut fra Raspberryen vil komme opp i terminalvinduet slik:

Importere kode for sensorene

Vi kan nå åpne main.py igjen, og fjerne all koden som ligger i den. All kode vi skriver fra nå vil være i denne ene main.py filen.

Fra airbit så vil vi kunne importere kode som hjelper oss å lese ut data fra sensorene i Air:Bit’en. Dette gjør vi ved å bruke import i Python.

import airbit

På denne måten så får vi tilgang til følgende kode:

airbit.SHT31
airbit.UltimateGPS
airbit.DustSensor
airbit.DataLogger

Husk at når vi kun bruker import airbit så er vi nødt til å bruke airbit. før det vi ønsker tilgang til. En måte å slippe dette på er å bruke from sammen med import:

from airbit import SHT31
from airbit import UltimateGPS
from airbit import DustSensor
from airbit import DataLogger

Da kan vi bruke alle sensorene uten å måtte skrive airbit. foran.

I tillegg til airbit så vil vi trenge time biblioteket. Så øverste i filen vår så vil det se slik ut:

import time

from airbit import SHT31
from airbit import UltimateGPS
from airbit import DustSensor
from airbit import DataLogger

Opprette alle sensorene

Først så må vi opprette variabler for alle sensorene. Disse kan vi navngi det vi ønsker, men det er anbefalt å velge navn som er forklarende for hva variabelen faktisk inneholder. Vi skal sette alle disse variablene lik de forskjellige sensorene.

sht = SHT31()
gps = UltimateGPS()
dust = DustSensor()

Nå har vi opprettet variabler og satt de lik objektene som håndterer å lese data fra sensorene. Legg merke til at vi ikke trengte å skrive f eks airbit.SHT31(), fordi vi brukte from airbit import SHT31 i stad.

Hvis vi lagrer programmet vårt så vil vi kunne se om sensorene kan opprettes. En feil som kan oppstå er at feilmeldingen sier

No pull up found on SDA or SCL; check your wiring

Dette betyr sannsynligvis at vi har en dårlig lodding på sensoren som er nevnt. Så her må vi gå over loddingen igjen.

Hvis vi ikke får opp noen feilmelding så betyr det at objektene for alle sensorene kunne opprettes, og vi kan begynne å jobbe med sensorene.

For å sjekke at vi får data fra sensorene så kan vi printe ut målingene slik:

print(f"Temp and hum: {sht.get_temp_hum()}")
print(f"Coordinates: {gps.get_coordinates()}")
print(f"Air Quality: {dust.get_airquality()}")
Temp and hum: (23.9479, 12.3705)
Coordinates: (None, None)
Air Quality: {'pm10 env': 15, 'pm100 env': 27, 'pm100 standard': 27, 'particles 03um': 2994, 'pm25 standard': 27, 'particles 10um': 194, 'pm10 standard': 15, 'pm25 env': 27, 'particles 05um': 788, 'particles 25um': 12, 'particles 100um': 0, 'particles 50um': 0}

Synkronisere GPS-klokken

For å kunne både få data/tid på alle målingene våre, samt hente ut GPS posisjon, så er vi avhengig av at den interne klokken stemmer. Dette synkroniseres via GPSen, da den kan stille klokken ut i fra GPS data.

Dette kan gjerne ta litt tid, og det er nødvendig for GPSen å ha klart utsyn til himmelen (evt gjennom et vindu, dog dette tar lengre tid).

For å programmere synkronisering av klokken så kan vi bruke metoden som heter sync_clock(). Dette er en metode som returnerer True hvis klokken ble synkronisert, men False hvis det ikke var mulig.

Dette kan vi da gjøre direkte i en while løkke som fortsetter å kalle sync_clock() så lenge vi ikke har synkronisert.

while not gps.sync_clock():
    print("[GPS] Clock Sync")
    time.sleep(0.5)

Her vil koden fortsette å kjøre samme kode så lenge at gps.sync_clock() returnerer False. Legg merke til at vi skrive while not som gjør at False blir til True og vi fortsetter å kjøre løkken så lenge det som står på linjen er True.

Når GPSen har synkronisert klokken, så kjører koden videre.

Opprette en datalogger

Vi har ennå ikke opprettet en variabel som skal representere SD kortet vi bruker til å lagre data. Det er fordi denne krever en dato-tids gruppe, og derfor måtte vi først synkronisere klokken.

Når denne nå er riktig, så kan vi opprette dataloggeren vår:

sdlog = DataLogger(time.localtime())

Her bruker vi da time.localtime() for å få tak i tiden akkurat nå. Dette fungerer fint fordi vi har synkronisert den interne klokken til Raspberryen mot GPSen. Hvis vi ikke gjør dette, så vil denne tiden være en standardverdi og ikke nåværende tidspunkt.

Hente ut data

For å hente ut data fra sensorene, så må vi kalle de forskjellige get metodene på objektene (lagret i variablene) vi har opprettet. Det gjør vi på følgende måte:

temphum = sht.get_temp_hum()
coordinates = gps.get_coordinates()
air_quality = dust.get_airquality()
t = time.localtime()

Nå har vi lagret dataen i variablene temphum, coordinates og air_quality. temphum og coordinates er såkalte tuples, det er en liste med flere verdier som ikke kan endres på.

Nå kan vi prøve printe ut dataen og se om den gir mening.

print(f"Time: {t}")
print(f"Temp and hum: {temphum}")
print(f"Coordinates: {coordinates}")
print(f"Air Quality: {air_quality}")

Lagre dataen på SD-kortet

Når vi har hentet ut alle dataene fra sensorene, inkludert tiden, så skal vi lagre disse på SD kortet.

Dette tar dataloggeren seg av, men vi må opprette en dictionary hvor vi setter inn alle datapunktene først, får vi gir dette videre til dataloggeren. Husk at her må alle keys i dictionarien være: "datetime", "coordinates", "temphum", "dust".

data = {
    "datetime": t,
    "coordinates": coordinates,
    "temphum": temphum,
    "dust": air_quality
}

Her spiller det ikke noen rolle hvilken rekkefølge vi legger inn ting i, så lenge keys (altså det som står til venstre for :) er like med de over. Husk at hvis du har brukt andre variabelnavn (tid istedet for t for eksempel), så må du bruke disse her. Eksempelvis:

data = {
    "datetime": tid,
    "coordinates": koordinater,
    "temphum": temperatur_fuktighet,
    "dust": luftkvalitet
}

Vi kan bruke de variabelnavnene vi ønsker, men disse må stemme internt med det vi har brukt tidligere for å få med oss dataen.

Vi kan nå kalle metoden for å skrive dataen til SD-kortet.

sdlog.log(data)

Hvis all dataen ser ut som den burde, så vil ikke programmet si noe. Hvis noe data ikke er riktig formatert, så vil programmet si ifra.

Hente inn data kontinuerlig

Vi ønsker jo gjerne å hente inn mer enn en måling. Da må vi sørge for å kontinuerlig hente inn data fra sensorene og skrive disse til SD-kortet.

Her er koden vi har skrevet så langt.

import time

from airbit import SHT31
from airbit import UltimateGPS
from airbit import DustSensor
from airbit import DataLogger

sht = SHT31()
gps = UltimateGPS()
dust = DustSensor()

while not gps.sync_clock():
    print("[GPS] Clock Sync")
    time.sleep(0.5)

sdlog = DataLogger(time.localtime())

temphum = sht.get_temp_hum()
coordinates = gps.get_coordinates()
air_quality = dust.get_airquality()
t = time.localtime()

print(f"Time: {t}")
print(f"Temp and hum: {temphum}")
print(f"Coordinates: {coordinates}")
print(f"Air Quality: {air_quality}")

data = {
    "datetime": t,
    "coordinates": coordinates,
    "temphum": temphum,
    "dust": air_quality
}

sdlog.log(data)

Vi ønsker jo nå at dette skal skje flere ganger, eller hele tiden til vi slår av strømmen på Air:biten. En måte å gjøre dette på er å bruke en while-løkke med True. Da vil løkken kjøre for alltid, inntil vi slår av strømmen (eller bruker Ctrl+C i konsollen).

All koden vi ønsker å kjøre om igjen må da innenfor denne løkken.

import time

from airbit import SHT31
from airbit import UltimateGPS
from airbit import DustSensor
from airbit import DataLogger

sht = SHT31()
gps = UltimateGPS()
dust = DustSensor()

while not gps.sync_clock():
    print("[GPS] Clock Sync")
    time.sleep(0.5)

sdlog = DataLogger(time.localtime())

while True:

    temphum = sht.get_temp_hum()
    coordinates = gps.get_coordinates()
    air_quality = dust.get_airquality()
    t = time.localtime()

    print(f"Time: {t}")
    print(f"Temp and hum: {temphum}")
    print(f"Coordinates: {coordinates}")
    print(f"Air Quality: {air_quality}")

    data = {
        "datetime": t,
        "coordinates": coordinates,
        "temphum": temphum,
        "dust": air_quality
    }

    sdlog.log(data)
    time.sleep(2.5)

Legg merke til at det kun er koden som henter data fra sensorene, og ikke hvor vi oppretter objektene for sensorene. Dette er ting vi bare gjør en gang, så det trengs ikke å gjentas.

Helt til slutt i programmet, så kan det være lurt å bruke time.sleep(2.5) for å pause programmet i 2.5 sekunder. Det er fordi sensorene klarer ikke å oppdatere særlig mye fortere, så da vil vi mange ganger ikke få noe data/ny data. while-løkken vil alltid kjøre så fort som mulig hvis vi ikke legger ved dette.