Läsa den BMP180 trycksensorn med en Attiny85 och lägga till en DHT11 också
Jag älskar Attiny 85-serien och gillar att utforska alla de saker du kan göra med den. Eftersom den inte har alltför många stift, är med I2C hårdvara på det en bra idé. I2C på attiny kan vara lite krångliga som det går inte att kompilera tråd biblioteket, men TinyWireM biblioteket fungerar bra.
BMP180 trycksensor är en relativt billiga och populära sensor läsa atmosfäriskt tryck. Den kan dessutom läsa temperatur. Om du vill använda denna sensor på en arduino, Adafruit har ett bibliotek (för BMP085 och BMP180) det wil läsa den för du. Men det nya biblioteket måste också deras allmänna "Sensor bibliotek" och de är minne guzzlers. Kanske OK på en Arduino, men inte på en attiny. De har en för Tiny85 också. Sparkfun har också ett bibliotek för Arduino.
Så, om du vill läsa BMP180 sensorn på en attiny, skulle du behöva göra en del arbete själv. Datablad är lyckligtvis mycket mycket tydligt. Sidan 15 berättar exakt vad de ska göra. Sekvensen är följande: 1-Läs chip specifika kalibrering data 2-Läs den okorrigerade temparature värde 3-Läs det okorrigerade tryck värdet 4-beräkna sann temperatur 5-beräkna sann trycket
Den också visar vad som är i en loop och vad inte: läsa kalibreringsdata behöver bara göras en gång och därför går i rutinen 'Setup'. Resten är i en loop och därför går i rutinen "loop".
Så, programmering är en bris om du följer flödet kartlägger på sidan 15... Vi behöver bara att "översätta" som till språk I2C-protokollet förstår. Vi därför starta programmet med att definiera vissa allmänna parametrar: för Attiny det är den TinyWireM bibliotek som implementerar en I2C-protokollet på attiny, så vi måste ladda biblioteket. Vi behöver I2C adress BMP180 (som är 0x77), och vi måste deklarera en hel massa variabler. De flesta av de variabler som används innehåller chip specifika kalibreringsdata som vi kommer att läsa från den chip EEPROM, vi behöver vissa variabler för olika beräkningar och vi kommer att behöva vissa variabler innehåller utdata (temperatur och tryck) för att hålla det enkelt, jag har valt namn för variablerna som anges i databladet.
Bara ett ord för förklaring av enheten adressen 0x77. Sidan 20 av databladet nämner två enhetsadresserna: 0xEE för Läs och 0xEF för skrivning.
En I2C enhetens adress kan anges som en 7-bitars adress som är 7 bitar som kan särskilja en enhet från en annan. Eller som en byte som inkluderar R/W lite i LSB position.
Bosch databladet anger inte 7-bitars adressen som är 0x77. I stället anger det (sidan 20) 8 bit skriva adressen som är 0xEE och 8 lite Läs adress, som är 0xEF. Båda är 0x77 R/W
0x77 = 111 0111
0xEE = 111 01110
0xEF = 111 01111
i TinywireM biblioteket två värden definieras #define USI_SEND 0 / / indikerar att TWI #define USI_RCVE 1 / / indikerar häleri från TWIthese kombineras med 7 bitars adress anger en läsa eller skriva åtgärder
Så, de första raderna i ett program kommer att se ut så här:
Anslutningen för Attiny & BMP180 är SDA stift 5, SCL stift 7 för I2C
#include < TinyWireM.h >
#define BMP180_ADDRESS 0x77 / / I2C adress BMP180
definiera kalibreringsdata för temperatur:
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;
långa b5;
definiera variabler för beräkning av tryck och temperatur
lång x1, x2;
definiera variabler för trycket beräkning
lång x3, b3, b6, p;
osignerade långa b4, b7;
definiera variabler för temperatur och oljetryck läsning
kort temperatur;
långa tryck;
CONST unsigned char OSS = 0; Översampling inställning
/ * blz 12 datablad
OSS = 0 ultra låg energiinställning, 1 prov, 4.5 ms 3uA
OSS = 1 Standard energiinställning, 2 prover, 7.5 ms 5uA
OSS = 2 hög upplösning, 4 prov, 13,5 ms 7uA
OSS = 3 Ultra hög upplösning, 2 prover, 25.5 ms 12uA
*/
Sedan måste vi definiera rutinen "Setup". Ärligt talat, är det enda vi har att göra där Läs kalibreringsdata. För att hålla det enkelt, kallar jag bara ett förfarande "bmp180ReadInt(address)", som vi sedan kan genomföra senare. Vår inställning därför kommer att se ut så här:
void setup() {
Först läsa kalibreringsdata från EEPROM
AC1 = bmp180ReadInt(0xAA);
AC2 = bmp180ReadInt(0xAC);
AC3 = bmp180ReadInt(0xAE);
AC4 = bmp180ReadInt(0xB0);
AC5 = bmp180ReadInt(0xB2);
ac6 = bmp180ReadInt(0xB4);
B1 = bmp180ReadInt(0xB6);
B2 = bmp180ReadInt(0xB8);
MB = bmp180ReadInt(0xBA);
MC = bmp180ReadInt(0xBC);
MD = bmp180ReadInt(0xBE);
}
Såklart jag kunde ha bara kallat 1 förfarande och kalla det "bmp180ReadCalibration" men att förfarandet sedan skulle göra samma som jag nu definierat redan i setup
"Loop" förfarandet är lika enkel. Det är i princip läsa okorrigerad temperaturen korrekt att okorrigerad temperatur läser okorrigerad tryck korrekt det okorrigerade tryck men som ingen är intresserad av den okorrigerade datan, vi göra förfarandet: Correct(Read Uncorrected temperature) Correct(Read Uncorrected pressure) så här:
void loop() {
först läsa Okompenserad temperatur
temperatur = bmp180ReadUT();
och sedan beräkna kalibrerad temperatur
temperatur = bmp180CorrectTemperature(bmp180ReadUT());
Läs sedan, Okompenserad tryck
Tryck = bmp180ReadUP();
och sedan beräkna kalibrerad
Tryck = bmp180CorrectPressure(bmp180ReadUP());
}
Så det var allt. Nu har vi bara definiera procedurer som vi kallar. Vi börjar med "bmp180ReadInt(address)" detta förfarande kommer att använda TinyWireM biblioteket för att läsa ett heltal från en viss adress. Att få data från en enhet som I2C, Huvudregeln är att först skriva till enheten tala om det vad man ska göra och sedan läsa på en viss adress för resultatet. Som vi kommer att läsa från EEPROM finns det inget kommando för vi måste skicka, annat än för att meddela den I2C hamn där vi vill vara (på adressen I2C chip) och skicka den adress vi vill läsa och hur många byte vi vill läsa. Vi kombinerar dessa två butes i ett heltal och återvända som. Vår föregå alltså ut så här:
int bmp180ReadInt (unsigned char adress)
{
unsigned char msb, lsb;
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(address);
TinyWireM.endTransmission();
TinyWireM.requestFrom (BMP180_ADDRESS, 2);
While(TinyWireM.available() < 2);
MSB = TinyWireM.receive();
lsb = TinyWireM.receive();
tillbaka (int) msb << 8 | lsb;
}
Nästa procedur vi behöver är att läsa den okompenserade temperaturen. För att få att vi måste först skicka värdet för 0x2E att registrera 0xF4 och vänta minst 4,5 MSEK. Det vill säga tiden chipet måste ta 1 läsning. När vi väntade läser vi den okompenserade temperaturen från registren 0xF6 och 0xf7. Senast läste som vi gör med det tidigare definierade "bmp180ReadInt"-förfarande som läser 2 byte och kombinerar dem till ett heltal. Förfarandet som således kommer att se ut så här:
unsigned int bmp180ReadUT()
{
unsigned int ut;
Skriva 0x2E i registret 0xF4 och vänta minst 4.5mS
Detta kräver en temperatur läsning
med resultat i 0xF6 och 0xF7
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(0xF4);
TinyWireM.send(0x2E);
TinyWireM.endTransmission();
Vänta minst 4.5ms
Delay(5);
Sedan läsa två byte från registren 0xF6 (MSB) och 0xF7 (LSB)
och kombinera som osignerat heltal
ut = bmp180ReadInt(0xF6);
återvända ut;
}
Därefter måste vi beräkna den korrigerade temperaturen från okorrigerad temperaturen. Databladet definierar som som följer: UT = Okompenserad temperatur X1 =(UT-AC6) * AC5/2 ^ 15 X 2 = (MC * 2 ^ 11 /(X1+MD) B5 = X 1 + X 2 T =(B5+8)/2 ^ 4 i programvara som ser ut så här
dubbel bmp180CorrectTemperature (unsigned int ut)
{
x1 = (((långa) ut - (long)ac6)*(long)ac5) >> 15.
x2 = ((långa) mc << 11) / (x 1 + md);
B5 = x1 + x2;
Return (((b5 + 8) >> 4));
}
Temperaturen är bra, nu måste vi läsa Okompenserad trycket. För att vi behöver skriva värdet 0x34 i registret 0xF4, men vi måste också fastställa värdet vor översampling skattesatsen. Andelen översampling bestämmer antalet prover chip behöver göra innan de ger ett resultat. Sidan 4 av databladet berättar vi har 4 val: OSS = 0 ultra låg energiinställning, 1 prov, 4.5 ms 3uA OSS = 1 Standard energiinställning, 2 prov, 7.5 ms 5uA OSS = 2 hög upplösning, 4 prov, 13,5 ms 7uA OSS = 3 Ultra hög upplösning 12 prover, 25.5 ms 12uA för detta program har jag valt OSS vara 0 OSS innehåller bitar 6 och 7 i registrera 0xF4. Bit 0-4 bestämma kontroll av mätning. om vi skriver värdet 0x34 i binära: 00110100. BITS 0-4 är inte så viktigt för nu, men bit 5 kommer också att sättas och således starta konverteringen. Det kommer att bo högt under konverteringen och återställa till låg efter konverteringen. För att ställa in bitar 6 och eller 7 vi vänster SKIFT 6 värdet av OSS. Anta att vi hade velat ange OSS som 3. i binär som är 0b11 om vi lämnade SKIFT 6 som man 11000000 (= 192d eller 0xC0), som kommer att ställa bitar 6 och 7. 0x34 + 0xC0 = 0xF4 = 0b11110100 som som vi kan se är det samma som 0x34 plus lite 6 och 7. Vi använder '0' för OSS värde, sätts inte båda stycke 6 och 7. efter vi starta konverteringen måste vi vänta mellan 4,5 och 25,5 uppdateringsförsök (beroende på OSS). Som vi har OSS = 0 vi väntar 5msec. Därefter läser vi 3 bytes som temperaturen är en "lång" (4 byte) inte ett heltal, måste vi emellertid endast 3 byte. När det gäller förseningen vore det trevligt om vi kommer att definiera det som ett beroende av operativsystem så du inte behöver manuellt ändra det när du ändrar OSS. Adafruit bibliotek solevs detta med vissa om uttalanden:
om (översampling == BMP085_ULTRALOWPOWER) delay(5);
annars om (översampling == BMP085_STANDARD) delay(8);
annars om (översampling == BMP085_HIGHRES) delay(14);
annat delay(26);
Men hoppades jag att hitta en formel som kommer att avgöra den. Eftersom det inte är en strikt linjär funktion, det närmaste man får är formeln: 5+(OSS*5). OSS = 0 -> 5 OSS = 1 -> 10 OSS = 2 -> 15 OSS = 3 -> 25 Ja, jag antar att det skulle vara tillräckligt nära förfarandet är följande
/-------------------------------------------
Läsa värdet Okompenserad tryck
osignerade långa bmp180ReadUP()
{
unsigned char msb, lsb, xlsb;
osignerade långa upp = 0;
Skriva 0x34 + (OSS << 6) till registrera 0xF4
Begära ett tryck läsning med översampling inställning
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(0xF4);
TinyWireM.send (0x34 + (OSS << 6));
TinyWireM.endTransmission();
Vänta för konvertering, fördröjningstid beroende på operativsystem
fördröjning (5 + (5 * OSS));
Läs registrera 0xF6 (MSB), 0xF7 (LSB) och 0xF8 (XLSB)
TinyWireM.beginTransmission(BMP180_ADDRESS);
TinyWireM.send(0xF6);
TinyWireM.endTransmission();
TinyWireM.requestFrom (BMP180_ADDRESS, 3);
Vänta på data blir tillgängliga
While(TinyWireM.available() < 3)
;
MSB = TinyWireM.receive();
lsb = TinyWireM.receive();
xlsb = TinyWireM.receive();
upp = (((unsigned long) msb << 16) | ((unsigned Long) lsb << 8) | (unsigned Long) xlsb) >> (8-OSS);
tillbaka upp;
}
Nu är gjort, måste vi korrigera Okompenserad trycket. Resultatet blir i Pascal
dubbel bmp180CorrectPressure (osignerade långa upp)
{
B6 = b5 - 4000;
Beräkna B3
x1 = (b2 * (b6 * b6) >> 12) >> 11.
x2 = (ac2 * b6) >> 11.
x3 = x1 + x2;
B3 = (((långa) ac1) * 4 + x3) << OSS) + 2) >> 2;
Beräkna B4
x1 = (ac3 * b6) >> 13.
x2 = (b1 * ((b6 * b6) >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
B4 = (ac4 * (osignerade långa) (x3 + 32768)) >> 15;
B7 = ((unsigned long) (upp - b3) * (50000 >> OSS));
om (b7 < 0x80000000)
p = (b7 << 1) / b4;
annat
p = (b7/b4) << 1;
x1 = (p >> 8) * (p >> 8);
x1 = (x 1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
p + = (x1 + x2 + 3791) >> 4;
returnera p;
}
Med ovanstående program man kan avgöra för sig själv vad man ska göra med hittade data: antingen skicka det till en display, eller kanske skicka det via en RF-länk till en basstation. Som sagt, är produktionen av oljetryck läsning i Pascal (Pa). hPa är en bekväm enhet. Några andra enheter kan det beräknas i är: 1 hPa = 100 Pa = 1 mbar = 0,001 bar 1 hPa = 0.75006168 Torr 1 hPa = 0.01450377 psi (pounds per kvadrattum) 1 hPa = 0.02953337 inHg (tum kvicksilver) 1 hpa = 0.00098692 atm (standard atmosfär)
Ett sista råd ändå: när du använder BMP180, kom ihåg det behöver 3,3 Volt. 5 volt kommer att döda den. Använder det på I2C från en 5 Volt microcontroller formella orsaka ett problem dock. Olika paus utombordare faktiskt har en 3.3 spänningsregulator på den.
Varning 1: Det finns ganska lång "fiskkrokar" i ovanstående program och tyvärr instructables (och andra webbplatser) har en tendens att ibland se dem som HTML-kod. Jag har kontrollerat grundligt om koden är OK och jag tycker det är. Det är dock bäst att kolla koden som jag kommer att länka till i mitt nästa steg.
Varning 2: När jag ville visa värdena som har hittats av BMP180 tog jag inledningsvis en två tråd LCD gränssnitt att jag hade bygga med 164 skiftregister som jag hade pass tillgänglig. Därefter försökte jag räkna ut för flera timmar varför jag inte var att få någon anständig läste ut. I själva verket ändra inte läsa ut Väder jag ansluten till BMP180 eller inte. Efter många många prövningar jag började misstänka min display gränssnitt och beslutade att koppla in en I2C LCD. Det fungerade som smort. LiquidCrystal_I2C från Francisco Malpertida fungerar inte på Attiny85. Jag använde den klassiska LiquidCrystal_I2C som är Anpassad av Bro Hogan till arbeta på Attiny85. Han gjorde det genom att ändra raden:
#include <Wire.h>
i#if defined(__AVR_ATtiny85__) || (__AVR_ATtiny2313__) #include "TinyWireM.h"
// include this if ATtiny85 or ATtiny2313
#else
#include <Wire.h> // original lib include #endif // original lib include #endif