Tweet-a-watt - hur man gör en kvittrande kraftmätare... (15 / 19 steg)
Steg 15: Design - lyssna
I detta avsnitt kommer vi att arbeta på mottagare programvara, som kommer att prata med en mottagare XBee och räkna ut vad sensordata medel. Jag kommer att skriva koden i python som är en ganska-lätt att använda skriptspråk. Det kan köras på alla operativsystem och har massor av tutorials på nätet. Dessutom använder Google AppEngine det så det är en bra tid att lära sig!
Här hela avsnittet förutsätts att du bara har 1 sändare och 1 mottagare, mest för att göra graphing lättare för att hantera. I nästa avsnitt ska vi knyta fler sensorer när vi kommer till den datalogging delen!
RAW analog ingång
Vi ska börja med bara att få raw-data från XBee och kolla det. Packet formatet för XBees är publicerad men istället för att böka i det, jag använder bara händig XBee biblioteket skriven för python. Med det, kan jag fokusera på data i stället för räknar byte och beräkna kontrollsummor.
Att använda biblioteket, först använda modulen pyserial för att öppna upp en seriell port (dvs COM4 under windows, / dev/ttyUSB0 under mac/linux/etc) du kan titta på XBee projektets sida för information om hur ta reda på vilken COM-port du letar. Vi ansluter på standard standard vilket baudvärde för XBees, som är 9600 och ser för paket
xbee importera xbee
importera följetong
SERIALPORT = "COM4" # com/serieporten XBee är ansluten till
BAUD = 9600 # baudhastighet vi tala med xbee
# Öppna FTDI serieporten att få uppgifter som överförts till xbee
ser = följetong. Följetong (SERIALPORT, BAUDVÄRDE)
ser.Open()
samtidigt sant:
# ta ett paket från xbee eller timeout
Packet = xbee.find_packet(ser)
om paket:
xb = xbee(packet)
skriva ut xb
Kör den här koden, får du följande utdata:
< xbee {app_id: 0x83, address_16: 1, rssi: 85, address_broadcast: falskt, pan_broadcast: falskt, total_samples: 19, digital: [[-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1 -1, -1, -1, -1 , -1, -1, -1, -1], [-1, -1 , -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1 , -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1 , -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1 , -1, -1 , -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1 , -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1 , -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1, -1], [-1, -1, -1, -1, -1, -1, -1, -1 , -1], [-1 , -1, -1, -1, -1, -1, -1, -1, -1]], analog: [[190, -1, -1, -1, 489, -1], [109, -1, -1, -1, 484, -1], [150, -1, -1, -1, 492, -1], [262, -1, -1, -1, 492 , -1], [423, -1, -1, -1, 492, -1], [589, -1, -1, -1, 492, -1], [740, -1, -1, -1, 492, -1], [843, -1, -1, -1, 492, -1], [870, -1, -1, -1 , 496, -1], [805, -1, -1, -1, 491, -1], [680, -1, -1, -1, 492, -1], [518, -1, -1, -1, 492, -1], [349, -1, -1, -1, 491, -1], [199, -1, -1, -1, 491, -1], [116, -1, -1, -1, 468, -1], [108, -1, -1, -1, 492, -1], [198, -1, -1, -1, 492, -1], [335, -1, -1, -1, 492, -1], [523, -1, -1, -1, 492 -1]]} >
som vi kommer att formatera om för att göra lite mer lättläst
< xbee {
app_id: 0x83,
address_16: 1,
RSSI: 85,
address_broadcast: falska,
pan_broadcast: falska,
total_samples: 19,
Digital: [[-1 -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1, -1, -1, -1, -1, -1, -1, -1, -1],
[-1 -1, -1, -1, -1, -1, -1, -1, -1]],
Analog: [[190 -1, -1, -1, 489, -1],
[109, -1, -1, -1, 484, -1],
[150, -1, -1, -1, 492, -1],
[262, -1, -1, -1, 492, -1],
[423 -1, -1, -1, 492, -1],
[589, -1, -1, -1, 492, -1],
[740 -1, -1, -1, 492, -1],
[843, -1, -1, -1, 492, -1],
[870 -1, -1, -1, 496, -1],
[805 -1, -1, -1, 491, -1],
[680, -1, -1, -1, 492, -1],
[518 -1, -1, -1, 492, -1],
[349, -1, -1, -1, 491, -1],
[199, -1, -1, -1, 491, -1],
[116, -1, -1, -1, 468, -1],
[108, -1, -1, -1, 492, -1],
[198, -1, -1, -1, 492, -1],
[335 -1, -1, -1, 492, -1],
[523, -1, -1, -1, 492, -1]]
} >
OK nu dess tydligt vad som händer här. Först och främst, vi får vissa uppgifter som sändaren ID (address_16) och signalstyrka (RSSI). Paketet berättar också om hur många prov är tillgänglig (19). Nu, de digitala proverna är alla -1 eftersom vi inte begära någon ska skickas. Biblioteket fortfarande fyller dem i tho så det är därför icke-data finns. Det andra segmentet är 19 uppsättningar av analoga data, allt från 0 till 1023. Som ni ser, det första provet (#0) och femte prov (#4) innehåller verkliga data, resten är -1. Det motsvarar till maskinvaruavsnittet där vi setup AD0 och AD4 vara vår spänning och aktuella sensorer.
Vi ska justera vår kod så att vi kan extrahera data endast och ignorera resten av paketet.
Denna kod skapas två matriser, voltagedata och ampdata där vi kommer att hålla data. Vi kastar ut det första provet eftersom vanligtvis ADCs är lite ostadig på första provet och då är bra att gå efter det. Det kan inte vara nödvändigt tho
#! / usr/bin/env python
importera följetong
xbee importera xbee
SERIALPORT = "COM4" # com/serieporten XBee är ansluten till
BAUD = 9600 # baudhastighet vi tala med xbee
CURRENTSENSE = 4 # som XBee ADC har strömmen dra data
VOLTSENSE = 0 # vilka XBee ADC har elnätet spänning data
# Öppna FTDI serieporten att få uppgifter som överförts till xbee
ser = följetong. Följetong (SERIALPORT, BAUDVÄRDE)
ser.Open()
samtidigt sant:
# ta ett paket från xbee eller timeout
Packet = xbee.find_packet(ser)
om paket:
xb = xbee(packet)
#print xb
# Vi ska bara lagra n-1 prover eftersom den första som vanligtvis är förstörd
voltagedata = [-1] * (len(xb.analog_samples) - 1)
ampdata = [-1] * (len (xb.analog_samples) -1)
# ta 1 thru Nilsson i ADC avläsningen, refererar till ADC konstanter
# och lagra dem i trevlig liten matriser
för i i range(len(voltagedata)):
voltagedata [i] = xb.analog_samples[i+1][VOLTSENSE]
ampdata [i] = xb.analog_samples[i+1][CURRENTSENSE]
skriva ut voltagedata
skriva ut ampdata
Nu är våra data lättare att se:
Spänning: [672, 801, 864, 860, 755, 607, 419, 242, 143, 108, 143, 253, 433, 623, 760, 848, 871, 811]
Aktuell: [492, 492, 510, 491, 492, 491, 491, 491, 492, 480, 492, 492, 492, 492, 492, 492, 497, 492]
Observera att spänningen gungor från ca 100 till 900, sinusformigt.
Normalisera data
Nästa kommer att upp vi "normalisera" data. Spänningen ska gå från-170 till +170 som är den faktiska spänningen på raden, i stället för 100 till 900 vilket är precis vad ADC läser. För att göra att vi kommer att få det genomsnittliga värdet av den största och minsta behandlingen och subtrahera det från alla prover. Efter det ska vi normalisera nuvarande mätningarna också, för att få siffrorna lika aktuella dragningen i ampere.
#! / usr/bin/env python
importera följetong
xbee importera xbee
SERIALPORT = "COM4" # com/serieporten XBee är ansluten till
BAUD = 9600 # baudhastighet vi tala med xbee
CURRENTSENSE = 4 # som XBee ADC har strömmen dra data
VOLTSENSE = 0 # vilka XBee ADC har elnätet spänning data
# Öppna FTDI serieporten att få uppgifter som överförts till xbee
ser = följetong. Följetong (SERIALPORT, BAUDVÄRDE)
ser.Open()
samtidigt sant:
# ta ett paket från xbee eller timeout
Packet = xbee.find_packet(ser)
om paket:
xb = xbee(packet)
#print xb
# Vi ska bara lagra n-1 prover eftersom den första som vanligtvis är förstörd
voltagedata = [-1] * (len(xb.analog_samples) - 1)
ampdata = [-1] * (len (xb.analog_samples) -1)
# ta 1 thru Nilsson i ADC avläsningen, refererar till ADC konstanter
# och lagra dem i trevlig liten matriser
för i i range(len(voltagedata)):
voltagedata [i] = xb.analog_samples[i+1][VOLTSENSE]
ampdata [i] = xb.analog_samples[i+1][CURRENTSENSE]
# få max och min spänning och normalisera kurvan till '0'
# att göra grafen 'AC tillsammans' / undertecknad
min_v = 1024 # XBee ADC är 10 bitar, så max värde är 1023
max_v = 0
för i i range(len(voltagedata)):
om (min_v > voltagedata[i]):
min_v = voltagedata [i]
om (max_v < voltagedata[i]):
max_v = voltagedata [i]
# räkna ut den "genomsnittliga" av max och min värden
avgv = (max_v + min_v) / 2
# också beräkna topp till topp mätningarna
VPP = max_v-min_v
för i i range(len(voltagedata)):
#remove 'dc bias", som vi kallar genomsnittligt Läs
voltagedata [i]-= avgv
# Vi vet att nätspänningen är 120Vrms = +-170Vpp
voltagedata [i] = (voltagedata [i] * MAINSVPP) / vpp
# normalisera nuvarande läsningar till ampere
för i i range(len(ampdata)):
# VREF är hårdkodade 'DC bias' värde, dess
# ca 492 men skulle vara trevligt om vi kunde på något sätt
# få dessa data då och då kanske med xbeeAPI
ampdata [i]-= VREF
# CURRENTNORM är vår normaliserande konstant
# som konverterar ADC läsande till ampere
ampdata [i] = CURRENTNORM
skriva ut "spänning i volt:", voltagedata
skriva ut "aktuella i ampere:", ampdata
Vi kör detta nu för att få dessa data, som ser ganska bra, det är sinusformad spänning vi förväntar oss och nuvarande är mestadels på 0 och sedan toppar upp och ner då och då. Obs att nuvarande är ibland negativa men det är OK eftersom vi multiplicera det med spänning och om båda är negativa det fortfarande kommer ut som en positiv kraft dra
Spänning i volt: [-125,-164,-170, nummer -128,-64, 11, 93, 148, 170, 161, 114, 46, -39,-115, -157,-170, -150, -99]
Aktuella i ampere: [0.064516129032258063,-1.096774193548387, 0,0, 0,0, 0,0, 0,0, 0,0, 0,0, 1.096774193548387,]
0,0 0,0 0,0,-0.064516129032258063, 0,0, 0,0,-0.70967741935483875, 0,0, 0,0]
Grundläggande data graphing
Slutligen, jag ska lägga till en hel drös mer kod som använder numpy graphing moduler för att göra ett fint diagram för våra data. Observera att du måste du installera wxpython och numpy, samt matplotlib!
Vid denna punkt, koden blir waaay för stor att klistra in här så grab "wattcher.py elnätet Graf" från nedladdningssidan!
Köra den och du bör se en graf fönster dyker upp med en trevlig sinusformad spänning diagram och olika strömstyrka data. Denna första graf är till exempel av en bärbar dator inkopplad. Du ser att det är en växling leverans, och endast drar ström under toppen av spänning kurvan.
Nu kan försöka koppla in en 40W glödlampa. Du kommer att märka att till skillnad från växlingen leverans, nuvarande följer spänningen nästan perfekt. Thats eftersom en glödlampa är bara ett motstånd!
Slutligen låter prova klibba mätaren på en dimbar switch. Du ser att spänningen är "hackas" upp, inte längre sinusformad. Och även om nuvarande följer spänningen, dess fortfarande på ganska bra.
Graphing wattal!
OK snyggt, dess alla kul att titta på vågformer men vad vi-vill - är verkligen de watt används. Kom ihåg, P = VI annars känd som watt = spänning * nuvarande. Vi kan beräkna sammanlagda watt används genom att multiplicera de spänningar och strömmar vid varje prov punkt, sedan läggs ihop över en cykel & genomsnitt för att få den effekt som används per cykel. När vi har watt, det är lätt att bara multiplicera det med 'tid' att få wattimmar!
Ladda ner och kör wattcher.py - watt grapher skript från download-sidan
Nu du kanna se den senaste timmen till ett värde av watt historia (3600 sekunder dividerat med 2 sekunder per prov = 1800 prover) i bilden ovan kan du se som jag dämpa en 40-watts glödlampa. Det är mycket "spridda" titta eftersom vi inte har gjort någon low-pass filtrerar data. Om vi hade en bättre analog samplingsfrekvens, detta kanske inte är så stor en del men med endast 17 prover att arbeta med precision är lite svårt
Gjort!
OK bra! Vi har lyckats läsa data, tolka ut analog sensor nyttolasten och bearbeta det på ett sätt som ger oss meningsfull grafer. Naturligtvis, detta är bra för momentana kunskap men det - skulle - vara trevligt om vi kunde ha längre sikt lagring, och också hålla reda på flera sensorer. I nästa steg kommer vi göra genom att dra nytta av några "cloud computing" gratistjänster!