Saat arduino donanım platformu ile kapalı hava istasyonu. Arduino için BMP085 Barometrik Basınç Sensörü Modülü (veya kendi ellerinizle bir hava istasyonu nasıl yapılır)

  • 17.05.2019

DIY hava istasyonu.

Akşam oldu, yılbaşından sonra yapacak bir şey yoktu. Her zamanki gibi, kış Yeni Yıl tatillerinde, başınızı ve ellerinizi yararlı ve yaratıcı bir şeyle meşgul etmek istersiniz. Bu Yeni Yıl tatillerinde kendi ellerimle bir meteoroloji istasyonu yapmaya karar verdim. Önceden hazırlanmaya başladım, tüm bileşenleri yeni yıldan önce satın alıp monte ettim ve tatillerde ana programlamayı yaptım.

(kesimin altında bir sürü fotoğraf var!)

İlk olarak, bileşenleri gözden geçireceğim, eBay'de (kişisel hesabımda) mallar arşivlendiğinden bağlantı vermeyeceğim. Bileşenlerin çoğunu eBay'den yavaş yavaş satın aldım. Her zaman "şimdi satın al" almadan önce müzayedeyi ilk kez denedim. Ne diyebilirim ki, satın alma konusunda aceleniz yoksa, bazı bileşenler daha ucuza satın alınabilir (fark bazen iki katına çıkar).

Basınç sensörü ВМР085
Bu ana sensördür. eBay'de gördüğümde, bir ev hava durumu istasyonu inşa etmek istediğimi fark ettim.
Sensör, içine balonlu naylonla yapıştırılmış sıradan bir zarf içinde geldi.

Zarfın içinde satıcının kartviziti ve sensör vardı, antistatik bir torbada paketlenmiş ve başka bir yumru tabakasına sarılmıştı.

Antistatik torba, uçuş sırasındaki nemin sensörü tehdit etmemesi için mühürlenmiştir.

Sensörü çıkarıyoruz. Bir tarafta, bükülmeyecek şekilde köpüğe yerleştirilmiş bir temas hattı lehimlenmiştir. Diğer tarafta sensörün kendisi ve kontak işaretleri bulunur.




Her şey yolunda, ancak temas işaretleri yansıtılıyor.
Sensör, I2C veri yolu üzerinden bağlanır ve 3,3 V'tan güç alır. Yani normal çalışma için 4 kabloya ihtiyacınız vardır (+, -, SDA, SCL)
Sensörü 2 şekilde yoklayabilirsiniz: ya kitaplık aracılığıyla ya da doğrudan çizimdeki işlevleri kullanarak.
Örnek program:

#Dahil etmek

#define BMP085_ADDRESS 0x77 // BMP085'in I2C adresi

Const imzasız karakter OSS = 0; // Aşırı Örnekleme Ayarı

// Kalibrasyon değerleri
int ac1;
int ac2;
int ac3;
imzasız int ac4;
imzasız int ac5;
imzasız int ac6;
int b1;
int b2;
int mb;
int mc;
int md;

Kısa sıcaklık;
uzun basınç;

Geçersiz kurulum ()
{
Seri.başlangıç ​​(9600);
tel.başla ();
bmp085Kalibrasyon ();
}

Boş döngü ()
{
sıcaklık = bmp085GetTemperature (bmp085ReadUT ());
basınç = bmp085GetPressure (bmp085ReadUP ());
Seri.print ("Sıcaklık:");
Seri.baskı (sıcaklık / 10.0, Aralık);
Seri.println (“C”);
Serial.print (“Basınç:„);
Seri.baskı (basınç / 133.322, Aralık);
Seri.println (“mm Hg”);
Seri.println ();
gecikme (1000);
}

Void bmp085Kalibrasyon ()
{
ac1 = bmp085ReadInt (0xAA);
ac2 = bmp085ReadInt (0xAC);
ac3 = bmp085ReadInt (0xAE);
ac4 = bmp085ReadInt (0xB0);
ac5 = bmp085ReadInt (0xB2);
ac6 = bmp085ReadInt (0xB4);
b1 = bmp085ReadInt (0xB6);
b2 = bmp085ReadInt (0xB8);
mb = bmp085ReadInt (0xBA);
mc = bmp085ReadInt (0xBC);
md = bmp085ReadInt (0xBE);
}

Kısa bmp085GetTemperature (imzasız int ut)
{
uzun x1, x2;
x1 = (((uzun) ut - (uzun) ac6) * (uzun) ac5) >> 15;
x2 = ((uzun) mc<< 11)/(x1 + md);
b5 = x1 + x2;

Dönüş ((b5 + 8) >> 4);
}

Uzun bmp085GetPressure (imzasız uzun yukarı)
{
uzun x1, x2, x3, b3, b6, p;
imzasız uzun b4, b7;
b6 = b5 - 4000;
// B3'ü hesapla
x1 = (b2 * (b6 * b6) >> 12) >> 11;
x2 = (ac2 * b6) >> 11;
x3 = x1 + x2;
b3 = (((((uzun) ac1) * 4 + x3)<>2;
// B4'ü hesapla
x1 = (ac3 * b6) >> 13;
x2 = (b1 * ((b6 * b6) >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (ac4 * (işaretsiz uzun) (x3 + 32768)) >> 15;
b7 = ((işaretsiz uzun) (yukarı - b3) * (50000 >> OSS));
eğer (b7< 0x80000000)
p = (b7<<1)/b4;
Başka
p = (b7 / b4)<<1;
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
p + = (x1 + x2 + 3791) >> 4;
dönüş p;
}

// "address" adresindeki BMP085'ten 1 bayt oku
char bmp085Read (imzasız karakter adresi)
{
imzasız karakter verileri;

Wire.write (adres);
Wire.endTransmission ();
Wire.requestFrom (BMP085_ADDRESS, 1);
while (! Wire.mevcut ())
;
geri dön Wire.read();
}

Int bmp085ReadInt (imzasız karakter adresi)
{
imzasız karakter msb, lsb;
Wire.beginTransmission (BMP085_ADDRESS);
Wire.write (adres);
Wire.endTransmission ();
Wire.requestFrom (BMP085_ADDRESS, 2);
while (Wire.mevcut ()<2)
;
msb = Wire.read();
lsb = Wire.read();
dönüş (int) msb<<8 | lsb;
}

// Telafi edilmemiş sıcaklık değerini oku
imzasız int bmp085ReadUT ()
{
imzasız int ut;
// 0x2E'yi Register 0xF4'e yaz
// Bu, bir sıcaklık okuması ister
Wire.beginTransmission (BMP085_ADDRESS);
Wire.write (0xF4);
Wire.write (0x2E);
Wire.endTransmission ();
// En az 4.5ms bekleyin
gecikme (5);
// 0xF6 ve 0xF7 kayıtlarından iki bayt oku
ut = bmp085ReadInt (0xF6);
geri dön;
}

// Telafi edilmemiş basınç değerini oku
imzasız uzun bmp085ReadUP ()
{
imzasız karakter msb, lsb, xlsb;
işaretsiz uzun yukarı = 0;
// 0x34 + (OSS yaz<<6) into register 0xF4
// Aşırı örnekleme ayarıyla birlikte bir basınç okuması isteyin
Wire.beginTransmission (BMP085_ADDRESS);
Wire.write (0xF4);
Wire.write (0x34 + (OSS)<<6));
Wire.endTransmission ();
// Dönüşüm için bekleyin, OSS'ye bağlı gecikme süresi
gecikme (2 + (3<// 0xF6 (MSB), 0xF7 (LSB) ve 0xF8 (XLSB) kaydını oku
Wire.beginTransmission (BMP085_ADDRESS);
Wire.write (0xF6);
Wire.endTransmission ();
Wire.requestFrom (BMP085_ADDRESS, 3);
// Verilerin kullanılabilir hale gelmesini bekleyin
while (Wire.mevcut ()< 3)
;
msb = Wire.read();
lsb = Wire.read();
xlsb = Wire.read();
up = (((işaretsiz uzun) msb<< 16) | ((unsigned long) lsb << 8) | (unsigned long) xlsb) >> (8-OSS);
geri dön;
}


Ek olarak, sensörün basınç dengelemesi için kendi termo sensörü ve bir altimetre vardır.

Arduino Nano v3.0
Bu, tüm hava durumu istasyonunun kalbidir. Basitçe söylemek gerekirse, denetleyici minyatür boyuttadır.
Satın almak
Bu zaten benden önce yapıldığı için kontrolör hakkında ayrıntılı olarak konuşmayacağım:


Lightake'li paket monte edildi, kontrolör bir USB kablosu ve bir Arduino ile birlikte kapalı bir anti-statik çanta içinde geldi.

Boyutu tahmin etmek için Arduino'nun yanına 1 ruble jeton koydum.

Kontrol panosu yakın



Ferrit halkalı USB kablosu iyidir. USB kablosu ile Arduino tarafından desteklenmektedir. Geliştirme ortamı indirilebilir (indirme sayfası). "C" benzeri bir dil, işte çok kullandığım için mastering ile ilgili herhangi bir sorun olmadı.

LCD ekran
Depolarda çalışırken uyumlu bir LCD 1602 ekran buldum. Bunun için bir veri sayfası bulamadığım için bağlantıyla uğraşmak zorunda kaldım. Sonuç olarak, LCD çalışmaya başladı.

Ancak kısa bir işlemden sonra bu ekranın benim için yeterli olmadığını ve her biri 16 karakterden oluşan 2 satır olduğu için daha fazla veri göstermenin mümkün olmayacağını fark ettim. İlk başta bu parametreler yeterli gibi görünse de programlamaya başladığınızda maksimum 3-4 parametre sığdırabileceğinizi fark ediyorsunuz. Ve bir menü yaparsanız (sonuçta bu ekranda bir menü yapmayı düşünüyordum), o zaman 1-2 parametre için boş alan kalır.
Sonuç olarak, başka bir ekran aramaya başladım. İlk başta Nokia 3310'un grafik ekranına yakından baktım ve hatta onu satın almak için bir eBay müzayedesine katıldım, ancak işe yaramadı (ki bu beni çok mutlu etti), bu yüzden bu ekrandan vazgeçmek zorunda kaldım. Şimdi, karşılaştırılacak bir şey olduğundan, amaçlarım için çok küçük olacağını anlıyorum.
Arduino üzerinde ki shieldlerden yanlışlıkla bakarken ST7920 controller üzerinde 12864 grafik ekran ile karşılaştım. Bu ekran benim ihtiyaçlarıma hem uygun bir boyuta hem de iyi bir çözünürlüğe sahip (128x64). Yani 6-7 satırlık 20 karakterlik normal okunabilir bir yazı tipine güvenle yerleştirebilirsiniz. Ekran grafiksel olduğu için farklı fontlardaki metinlerin yanı sıra grafikler de yerleştirebilirsiniz. Kısacası tam da ihtiyacım olan buydu, bu ekranda her şey mevcuttu, ben de bozulup sipariş verdim.
Paket hızlı bir şekilde geldi ve standart bir şekilde paketlendi: bir kabarcıklı zarf, içinde başka bir kabarcık sargısı katmanı ve antistatik bir torbada bir ekran var:






Boyutu tahmin etmek için LCD'nin yanına 1 ruble jeton koydum.




Ekranı Arduino'ya hızlı bir şekilde bağlamak için LCD pinlerine bir dizi pin lehimledim. LCD, seri veri yolu ve paralel ile bağlanabilir. Çok az sayıda ücretsiz Arduino bağlantısı olduğu için ilk seçeneği seçtim.
Bağlantı (ağdan alınmıştır):

- Pin 1 (GND) ortak veri yoluna bağlanır
- Pin 2 (VCC) + 5V güç rayına bağlıdır ve akım tüketimi nispeten küçüktür ve ekran dahili Arduino sabitleyiciden güç alabilir.
- 4, 5 ve 6 numaralı pinler Arduino'nun dijital çıkışlarına bağlanarak bir seri SPI arayüzü oluşturur:
pin 4 - (RS) - CS hattına karşılık gelir (örneğin 7)
pin 5 - (RW) - MOSI hattına karşılık gelir (örneğin 8)
pin 6 - (E) - SCK hattına karşılık gelir (örneğin 3)
Arduino pin numaraları herhangi biri olabilir, asıl şey, ekranı başlatırken bunları program metninde doğru bir şekilde belirtmeyi unutmamaktır.
- Pin 15 (PSB) ortak veri yoluna bağlanır.
- 19 (A) ve 20 (K) pinleri arka ışık kaynağıdır (sırasıyla + 5V ve GND). Arka ışığın parlaklığını ayarlamak için, güç ve GND veriyolları arasına bağlı 10kOhm'luk bir değişken direnç kullanabilirsiniz. Motorundan gelen voltaj, ekranın 19. pinine uygulanır.
Bu talimatı izleyerek arka ışık dışındaki her şeyi bağladım. Arka ışığa güç sağlamak için bir Arduino PWM kullandım.
LCD'yi Arduino'ya programlı olarak bağlamak için u8glib kütüphanesi kullanılır. İndirebilirsin. İndirme ile ilgili sorunlar varsa, kütüphaneyi narod.ru'ya yükleyebilirim.
Kütüphanenin kendisi karmaşık değildir ve metni farklı yazı tiplerinde görüntülemenize, bir çizgi çizmenize, en basit geometrik şekilleri (dikdörtgen, daire) çizmenize, özel bir şekilde hazırlanmış resimlerinizi görüntülemenize olanak tanır. Prensip olarak, bu araç çoğu görev için yeterlidir.
İşte basit bir programın sonucu:

Programın kendisi:

#include "U8glib.h"

U8GLIB_ST7920_128X64 u8g (3, 9, 8, U8G_PIN_NONE); // SPI E = 3, RW = 9, RS = 8

// Boş hafızayı belirlemek için alt program
int freeRam() (
extern int __heap_start, * __ brkval;
televizyonda;
dönüş (int) & v - (__brkval == 0? (int) & __ heap_start: (int) __brkval);
}

Void kurulumu (void) (
u8g.setFont (u8g_font_6x10); // yazı tipi
u8g.setRot180 (); // Ekranı çevir
analogWrite (6, 115); // Ekranın parlaklığını ayarlayın (arka ışık anodu 6 pin olarak)
}

Geçersiz döngü (boşluk) (
u8g.ilkSayfa ();
yapmak (

u8g.setPrintPos (1, 12); // konum
u8g.print ("Merhaba !!!"); // metin çıktısı
u8g.drawBox (0.22,128.9); // Dikdörtgeni beyaza boya
u8g.setColorIndex (0); // beyaz mürekkep, siyah arka plan
u8g.setPrintPos (1, 30); // konum
u8g.print ("Kelime ..."); // metin çıktısı

U8g.setColorIndex (1); // beyaz mürekkep, siyah arka plan
u8g.setPrintPos (1, 50); // konum
u8g.print ("Başladıktan sonra ="); // metin çıktısı
u8g.setPrintPos (85, 50); // konum
u8g.print (milis () / 1000); // başladıktan sonraki saniye sayısını çıktıla
u8g.setPrintPos (1, 64); // konum
u8g.print (freeRam()); // ne kadar bellek kullanıldığını göster
) while (u8g.nextPage ());

Gecikme (200);
}

Gerçek Zamanlı Saat DS1307
Hava durumu istasyonum için başka bir bileşen. Bu kalkanın gerçek zamanlı bir saati var. Onları eBay'de sipariş etti. Satıcı, gerçekçi olmayan büyük bir kutuda bir saat eşarp gönderdi


Kutunun içinde iki adet A4 reklam sayfası ve selofanla sarılmış bir saat mendili vardı.


Ücretin 2 rubleyi geçmediğini belirtmek isterim. bozuk para ve kutu boyutu 13x15x5 cm idi.
Tahta, anti-statik bir torbada paketlenmiştir.

şal ​​yakın çekim



Bu modülle uğraşmak zorunda kaldım. İlk olarak, bağlantı zorlukları vardı. İkincisi, bu tahtada kuvars yok. Modüle çok fazla zaman harcayacağımı bilseydim, büyük olasılıkla, ağ şemalarla dolu olduğu için kendim monte ederdim. En basit devre 4-5 bileşen içerir.
Bağlantı hakkında. I2C arayüzünün Arduino'nun (A4 ve A5) normal analog girişlerine değil, herhangi bir ayrık girişe bağlanabileceğini söyleyen bir kitaplık buldum. Yazıldığı gibi, öyle yaptı. İlk başta hiçbir şey işe yaramadı, tef ile uzun bir danstan sonra saat kuruldu. İşte bu kadar diye düşündüm, sorunlar bitti ama aynı modülü başka bir Arduino'ya bağlamaya çalıştıktan sonra tef ile dans etmeye devam ettim. Bu soruna bir çözüm bulmak için çok zaman harcadım ve hemen hemen her yerde ya yanlış bağlantı ya da SCL ve SDA kontaklarında pull-up dirençlerinin olmadığı belirtildi. Zaten bir havya ile tahtaya tırmanmak istedim, ancak bir forumda SCL ve SDA'nın Arduino'daki standart I2C bağlantı noktalarına bağlanması gerektiği söylenen bir koda rastladım. Standart bağlantıdan sonra her şey hemen çalıştı.
Şimdi kuvars hakkında. Çinlilerin oraya ne tür bir kuvars koyduğunu bilmiyorum ama böyle bir kuvarslı bir saat günde 10-11 saniye kaçtı. Bu hata ayda 5 dakika ve yılda 1 saattir. Nafig böyle bir saate gerek yok. Tekrar çevrimiçi olmak ve bu hatayı nasıl düzelteceğimi aramak zorunda kaldım. Ortaya çıkan ilk çözüm, kuvarsı topraklamanız gerektiğini söylüyor. Yaptı - sonuç sıfır. Ayrıca eski bir anakart bulmam ve oradan saat kuvarsını buharlaştırmam gereken bir yer buldum. Ben yaptım - bir sonuç var. Şimdi saat 10-11 saniye değil, günde 1,5 saniye geçiyor. Diyelim ki daha iyi oldu, ama ideal olmaktan uzak. Havya ile uğraşmak daha isteksiz olduğundan, saatin programlı olarak ayarlanmasına, yani günde bir kez saatin gerekli değere ayarlanmasına karar verildi. 10 gün sonra, saat bir saniyeden fazla kalmadı. Yöntem iyidir, ancak yalnızca Arduino senkronizasyon cihazı güce bağlı olduğunda, aksi takdirde saat pil gücüyle çalışır ve yine de kaçar.
Küçük test programı:

#include "Wire.h"
#define DS1307_I2C_ADDRESS 0x68 // SDA A4, SCL A5

Bayt decToBcd (bayt değeri)
{
dönüş ((val / 10 * 16) + (%val% 10));
}

Bayt bcdToDec (bayt değeri)
{
dönüş ((val / 16 * 10) + (%val% 16));
}

Void setDateDs1307 (bayt saniye, // 0-59
bayt dakika, // 0-59
bayt saat) // 0-99
{

Tel.yaz (0);
Wire.write (decToBcd (saniye));
Wire.write (decToBcd (dakika));
Wire.write (decToBcd (saat));
Wire.endTransmission ();
}

Void getDateDs1307 (bayt * saniye,
bayt * dakika,
bayt * saat)
{

Wire.beginTransmission (DS1307_I2C_ADDRESS);
Tel.yaz (0);
Wire.endTransmission ();

Wire.requestFrom (DS1307_I2C_ADDRESS, 3);

* saniye = bcdToDec (Wire.read ());
* dakika = bcdToDec (Wire.read ());
* saat = bcdToDec (Wire.read ());
}

Geçersiz kurulum ()
{
bayt saniye, dakika, saat;
tel.başla ();
Seri.başlangıç ​​(9600);

ikinci = 45;
dakika = 5;
saat = 16;

SetDateDs1307 (saniye, dakika, saat);
}

Boş döngü ()
{
bayt saniye, dakika, saat;

GetDateDs1307 (& saniye, & dakika ve saat);
Serial.print (saat, Aralık);
Seri.baskı (":");
Serial.print (dakika, Aralık);
Seri.baskı (":");
Serial.println (ikinci, Aralık);

Gecikme (1000);
}


Kitaplık burada kullanılmaz ve işlevler okuma ve yazma süresi için kısaltılır.

DHT11 sıcaklık ve nem sensörü
Bu sensör hakkında söylenecek bir şey yok. Neme ihtiyacım olmasa kullanmazdım. Ne yazık ki, aldığımda fotoğrafını çekmedim, bu yüzden fotoğraf olmayacak. Sensörün fotoğrafları, Arduino'ya bağladığım aşağıda görülebilir. Sensör bağlantısı basittir (+, dijital çıkış, -). Genellikle sensörler dört kontaktan oluşur. Bu form faktörü ile üçüncü kişi hiçbir şeye bağlı değildir.
Arduino'ya bağlanmak için bir kütüphane kullanılabilir. İndirebilirsin.
LCD ekrana 1602 bilgi çıkışı olan küçük bir test programı:

// kitaplık kodunu dahil et:
#Dahil etmek
#Dahil etmek

// nesneleri bildir
dht11 DHT11;
LiquidCrystal lcd (12, 11, 6, 5, 4, 3);

#define DHT11PIN 7
int i;

Geçersiz kurulum ()
{
lcd.başlangıç ​​(16, 2);
lcd.print ("Durum:");
ben = 0;
}

Boş döngü ()
{
int chk = DHT11.read (DHT11PIN);
lcd.setCursor (8, 0);
anahtar (chk)
{
durum 0: lcd.print (“Tamam”); ara; // lcd.setCursor (11, 0); lcd.print (milis () / 2000); kırmak;
durum -1: lcd.print (“Kontrol toplamı hatası”); mErr(); kırmak;
durum -2: lcd.print ("Zaman aşımı hatası"); mErr(); kırmak;
varsayılan: lcd.print ("Bilinmeyen hata"); mErr(); kırmak;
}
gecikme (500);
lcd.setCursor (15, 0);
(i) anahtarı
{
durum 0: lcd.print ("^"); lcd.setCursor (15, 1); lcd.print (""); ara;
durum 1: lcd.print ("v"); lcd.setCursor (15, 1); lcd.print (""); ara;
varsayılan: lcd.setCursor (15, 1); lcd.print ("E"); kırmak;
}
ben = ben + 1;
eğer (i> 1) ben = 0;
lcd.setCursor (0, 1);
lcd.print ("H =");
lcd.setCursor (2, 1);
lcd.print ((yüzer) DHT11.nem, 0);
lcd.setCursor (4, 1);
lcd.print ("%");
lcd.setCursor (8, 1);
lcd.print ("T =");
lcd.setCursor (10, 1);
lcd.print ((kayan) DHT11.temperature, 0);
lcd.setCursor (12, 1);
lcd.print ("C");

Geçersiz mErr ()
{
lcd.setCursor (2, 1);
lcd.print ("**");
lcd.setCursor (10, 1);
lcd.print ("**");
ben = 5;
}


Sensörün dezavantajları vardır - sensörden gelen veriler yalnızca tam sayılardadır ve aralık zayıftır.

Görünüşe göre tüm bileşenler hakkında yazdım. Her şeyi tek bir bütün halinde toplamak için kalır.
Oops, neredeyse unutuyordum! Her şeyin cihazı monte etmesi için bir kasaya ihtiyacınız var. Ebay'den de davayı sipariş ettim. Satıcı İngiltere'dendi. Paket hızlı bir şekilde geldi, ancak fotoğrafını çekmedi. Kasaya ait tüm fotoğraflar aşağıdadır.

İlk önce masadaki her şeyi özel teller kullanarak topladım. Bir test programı yazdım ve kontrolöre yükledim.



Aslında, mavi arka ışık çok daha parlaktır. Minimum parlaklıkta (Parlak = 5) bile çerçeve açığa çıkar.

Her şeyi kablo olmadan monte etmek için mini bir anakart yapılmasına karar verildi ve konektörlere Arduino kartı ve kalkanlar yerleştirildi. Bu durumda, hızlı bir şekilde kolayca çıkarılabilirler. Ayrıca kontrol için LCD ekranı ve düğmeleri bağlamaya karar verdim, sadece kablolardaki sıcaklık sensörünü lehimleyin.
atkı böyle çıktı



Son fotoğrafta, akıyı henüz tamamen yıkamadım. Kalkanların altında, konektörlerin yanında, en azından bir tür destek olacak şekilde gözenekli kauçuk yapıştırdım. Aslında, kontaklardaki konektörlerdeki kalkanlar mükemmel bir şekilde yapışır.

Kalkanlı anakart ve Arduino kartı takılı.

Anakarta tam bir bağlantı böyle görünüyor.


Düğmeler yerine, devre tahtasına lehimlenmiş ev yapımı bir kalkan kullandım. Düğme olarak eski farelerdeki düğmeleri kullandım.
Gördüğünüz gibi, tel sayısı azaldı.

Kasaya yerleştirmenin ana sorunu, LCD ekran için oluğu kesmektir. Ne kadar uğraşırsam uğraşayım yine de mükemmel olmadı. Bazı yerlerde yuvalar 1 mm'den biraz fazlaydı. Her şeyin düzgün görünmesi için akvaryum için siyah bir dolgu macunu aldım ve tüm çatlakları doldurdum, aynı zamanda ekranı bu dolgu macununa tutturdum. Sızdırmazlık maddesi kuruduktan sonra, fazlalığı dışarıdan kesin. Parlak ışıkta dolgu macunu görünür ve normal ışıkta her şey vücutla birleşir.
LCD ekran ve anakart takılıyken kasa içeriden böyle görünüyor.

Dışarıdan parlak ışıkta böyle görünüyor (parmak izleri için kusura bakmayın, fotoğrafları incelerken gördüm).

Düğmeleri kasaya nasıl sığdıracağımı ve en önemlisi hangi düğmeleri kullanacağımı uzun süre düşündüm...
Radyoelektronik mağazalarında uzun saç tokası olan düğmeyi ve bu saç tokasına takılan uçları beğendiler. Bu düğmeler panoya lehimlemek için kullanılır. Her şey yolunda, ancak bir dezavantajı var - basma darbesi çok küçük ve yüksek.
Butonların iki aşamada yerleştirilmesi gerekiyordu: Birincisi butonları tahtaya yerleştirmek, ikincisi bu tahtayı bir tahtaya daha monte etmekti. Ve sonra tüm bunları kılavuzlardaki vücuda koyun.

Düğmeli bir eşarp şöyle görünür:



Tutucu panosu şöyle görünür:


Burada düğmeli panonun yerleştirildiği kılavuzları görebilirsiniz. Tahtaya sağlamlık kazandırmak için bazı elemanlar lehimlenmiştir.

Şimdi her şeyi kasaya koyuyoruz
Düğmeleri bağlamadan:


Düğme bağlantısı ile:

Kasayı kapatıp açıyoruz. Her şey harika çalışıyor, düğmeler gerektiği gibi çalışıyor.

Sonunda, cihazın farklı modlarda çalışmasının küçük bir videosunu yayınlıyorum:
http://www.youtube.com/watch?v=KsiVaUWkXNA&feature=youtu.be
Kimin videosu yok burada, işte link

İncelemeyi bitirmenin zamanı geldi.
Program hakkında biraz yazacağım ve ardından kısa sonuçlar yazacağım. Programı yazdığımda 30720 byte limitine çok çabuk gireceğimi düşünmemiştim.


Kodu optimize etmem gerekiyordu. Alt programlara birçok kod parçası koydum. Derlenmiş biçimde bir switch ... case ifadesinin birkaç if ... else'den daha fazla yer kapladığını asla düşünmezdim. Değişkenlerin doğru beyanı ayrıca yerden tasarruf sağlar. Bir diziyi uzun bildirirseniz, bayt ile geçinmek oldukça mümkün olsa da, dizinin boyutuna bağlı olarak aşırı bellek kullanımı 500 bayta ulaşır. Bir program yazdığınızda, onu düşünmezsiniz ve ancak daha sonra programı analiz ettiğinizde bazı şeyleri yanlış yaptığınızı fark eder ve kodu optimize etmeye başlarsınız. Programın boyutuyla ilgili sorunlar çözüldükten sonra RAM sınırlamasıyla karşılaştım. Bu, programın yüklendikten sonra askıda kalmaya başlamasıyla ifade edildi. Boş RAM'i hesaplamak için bir alt program sunmam gerekiyordu. Sonuç olarak, ekranda simgeleri göstermesi gerektiğinden bir hava durumu tahmin algoritmasından vazgeçmek zorunda kaldım. Algoritmanın kendisi çalışıyor, ancak simgelerin çıktısının geçersiz kılınması gerekiyordu. Kodun nasıl optimize edileceğine dair hala fikirlerim var, ancak yakın gelecekte performansı değerlendirmek ve tüm hataları belirlemek için cihazı olduğu gibi çalışır durumda bırakacağım.

Şimdi küçük sonuçlar
eksiler
1) Fiyat. Bu eksi için bahane, bir hobinin asla ucuz olmamasıdır.

profesyoneller
1) Cihazın mükemmel işlevselliği
2) Fonksiyonların genişletilmesi sadece kullanılan kontrolör ve kendi iradesi ile sınırlıdır.
3) Tefekkürden estetik zevk ve bu cihazı hala toplayıp tamamlamamdan gelen ahlaki zevk

+85 almayı planlıyorum Favorilere ekle incelemeyi beğendim +137 +304

Hava istasyonumuzu geliştirmeye devam ediyoruz.

Güncellemeye geçmeden önce biraz açıklığa kavuşturmak istiyorum.

Meslektaşlarımızdan biri bana bekçi köpeğinin neden tanıtıldığını sordu.

Bekçi zamanlayıcısı acil durumda. Pratikte görüldüğü gibi, ENC28J60 daha fazla çekmez (eğer bellek arızalanmazsa) 4 eşzamanlı bağlantı. Kaç servis bağlantısı olduğu göz önüne alındığında, sürekli olarak ağın çalışmasını ve her türlü ev oyuncağı tarafından yaratılan sol trafiği (örneğin, modern TV'ler ağdaki mevcut ana bilgisayarları tarar ve onlar için bağlantı noktalarını açar) sürdürmek gerçekleşir. tasarım basitçe bir stupor haline gelir. ENC28J60, ağ protokolleriyle bağımsız olarak nasıl çalışacağını bilmiyor ve her şey kütüphanelerde uygulanıyor. Belki onların içindedir.
Mevcut tüm kütüphaneleri ve farklı modülleri kontrol ettim (birdenbire bir evlilik), ancak uzun süre istikrarlı bir çalışma elde edemedim. Maksimum süre yaklaşık 3-4 haftaydı.
Bunun için "köpek" orada dönüyor ve bir şey olursa, kontrolör sarsılıyor. Ondan sonra sorun ortadan kalktı.
Ayrıca, ev ağımda belirli nüansların veya sorunların olabileceğini de inkar etmiyorum. Ama bir sorunum olduğu için başka bir kişiyle ortaya çıkabilir. Şimdiye kadar sadece böyle bir çözüm buldum.
Bildiğim kadarıyla Wiznet yongaları (W5100 ve üstü) buna sahip değil ya da sadece kötü görünüyorlardı.

Güncellemeye devam

En önemlisi, çipi bırakıyoruz ENC28J60 Ve git W5100... Her şeyi eski bir çip üzerinde uygulamaya çalıştım, ancak çok büyük kütüphaneler nedeniyle yeterli mikrodenetleyici belleği yok. ENC28J60... Yeni bir çip kullanırken standart kütüphaneler geliştiriciden ve yapılan tüm değişikliklerden daha fazlası var 20% ücretsiz mikrodenetleyici belleği ATMega328... Ve bu, yeni çörekler!

Bu sürüm (ikinci diyelim), frekansı kullanarak sensörlerden gelen okumaları kablosuz olarak iletme yeteneğini ekledi. 433 MHz... Modülleri Çinlilerden aldım, işaretledim XY-MK-5V... İletim kalitesinin mükemmel olmaktan uzak olduğunu belirtmek isterim. Sinyal kaybı, gürültü, aynı anda iletememe vb. olasıdır. Ancak fiyatları (set başına 1 dolardan az) bu dezavantajları telafi ediyor. Bu (en ucuz) modüllerin ev kullanımı için birçok markalı hava istasyonunda olduğuna dair bir sır vereceğim. Vay, beklenmedik mi?

Baz istasyonuyla başlayalım

taşınıyoruz Arduino UNO'su ve Ethernet Kalkanı(ilk versiyon) çipe dayalı W5100... Bu bir sandviç ve onu tarif etmenin bir anlamı yok. Modüller için yalnızca ek olarak kullanılan kişileri tanımlayacağım XY-MK-5V.

Verici modülü güç kullanıyor 5V, GND(o zaman annesiz nerede) ve D2 denetleyiciye takın. Kişiyi değiştir D2 (VERİ) işlevi kullanabilirsin vw_set_tx_pin vw kitaplığından.

Önceki çizimden farklı olarak, bu iki ek kitaplığı içerir:

#Dahil etmek #Dahil etmek

Çizimin kendisi

Gizli metin

#Dahil etmek #Dahil etmek #Dahil etmek #Dahil etmek #Dahil etmek #Dahil etmek #Dahil etmek #Dahil etmek #define DHTTYPE DHT22 #define DHTPIN 5 DHT dht (DHTPIN, DHTTYPE); bayt mac = (0x54, 0x34, 0x31, 0x31, 0x31, 0x31); char sunucusu = "narodmon.ru"; int bağlantı noktası = 8283; IPAdresi ip (192,168,0,201); EthernetClient istemcisi; BMP085 dps = BMP085 (); uzun Sıcaklık = 0, Basınç = 0; yüzer H, dP, dPt; bool aralığı = doğru; EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE (byte ID; // Device ID int Sıcaklık; // Sıcaklık kayan nokta Basınç; // Basınç kayan nokta Nem; // Nem kayan nokta çiğ Noktası; // Çiy / donma noktası); SEND_DATA_STRUCTURE yayını; void setup () (// Watchdog zamanlayıcısını başlat wdt_disable (); delay (8000); wdt_enable (WDTO_8S); // Konsol Serial.begin (9600); // DHT sensörünü başlat dht.begin (); / / 433 MHz modülünün başlatılması ET.begin (detaylar (yayın)); vw_set_ptt_inverted (true); vw_set_tx_pin (2); vw_setup (2000); // DHCP sunucusundan veri beklemediysek ağı başlatın , sonra // kendimize bir adres atayın if (Ethernet.begin (mac) == 0) Ethernet.begin (mac, ip); // 1-Wire Wire.begin'i Başlat (); gecikme (200); // BMP180'i başlat yükseklik düzeltmeli // dps.init (MODE_STANDARD, 3200, true); // BMP180'i başlat dps.init (); Serial.println (Ethernet.localIP ()); // İlk verileri cihazı açtıktan hemen sonra gönder send_info (true);) // dewPoint işlevi NOAA // referans (1): http://wahiduddin.net/calc/density_algorithms.htm // referans (2): http://www.colorado.edu/geography/weather_station /Geog_site/about.htm çift çiy Noktası (çift santigrat, çift nem) (// (1) Doygunluk Buhar Basıncı = ESGG (T) çift ORAN = 373.15 / (273.15 + santigrat); çift ​​RHS = -7.90298 * (ORAN - 1); RHS + = 5.02808 * log10 (ORANI); RHS + = -1.3816e-7 * (pow (10, (11.344 * (1 - 1 / ORAN))) - 1); RHS + = 8.1328e-3 * (pow (10, (-3.49149 * (ORANI - 1))) - 1); RHS + = log10 (1013.246); // faktör -3, birimleri ayarlamaktır - Buhar Basıncı SVP * nem double VP = pow (10, RHS - 3) * nem; // (2) DEWPOINT = F (Buhar Basıncı) double T = log (VP / 0.61078); // geçici değişken dönüş (241.88 * T) / (17.558 - T); ) void send_info (bool eth) (bool fail = true; while (fail) (// Sonucu alana kadar // DHT nem sensöründen veri okumaya çalışıyoruz. Vakaların %90'ında her şey yolunda gidiyor, ancak ihtiyacımız var %100 if ((H = dht.readHumidity ())> = 0) (// BMP180 sensöründen nem ve sıcaklığın alınması dps.getPressure (& Basınç); dps.getTemperature (& Sıcaklık); // Çiği hesaplayın sıcaklık 0 santigrat derecenin üzerindeyse işaretleyin // ve 0'ın üzerinde bir sonuç bekleyin, aksi takdirde 0 çıktısı alın. Bu, kış mevsiminde yanlış yönlendirilmemek için // gereklidir. // dP = Sıcaklık> 0? ((dPt = çiğNoktası (Sıcaklık * 0.1, H))<0?0:dPt):0; dP = dewPoint(Temperature*0.1, H); // Отправляем данные в эфир 433 мГц broadcast.ID = 1; broadcast.Temperature = floor(Temperature*0.1); broadcast.Pressure = floor(Pressure/133.3*10)/10; broadcast.Humidity = floor(H*10)/10; broadcast.dewPoint = floor(dP*10)/10; ET.sendData(); delay(250); if(eth) { // Подключаемся к серверу "Народный мониторинг" if(client.connect(server, port)) { // Начинаем передачу данных // адрес_устройства_в_проекте, имя_устройства, GPS широта, GPS долгота client.print(F("#fe-31-31-0e-5a-3b#Arduino Uno#71.344699#27.200014\n")); // Температура client.print(F("#T0#")); client.print(Temperature*0.1); client.print(F("#Температура\n")); // Давление client.print("#P1#"); client.print(Pressure/133.3); client.print(F("#Давление\n")); // Влажность client.print("#H1#"); client.print(H); client.print(F("#Влажность\n")); // Точка росы\инея client.print("#T1#"); client.print(dP); client.print((dP <= 0)? F("#Точка инея\n"):F("#Точка росы\n")); //client.print(F("#Точка росы\n")); // Отправляем конец телеграммы client.print("##"); // Даем время отработать Ethernet модулю и разрываем соединение delay(250); client.stop(); } } // Останавливаем цикл, если передача завершена fail = !fail; break; } delay(250); } } void loop() { // Каждые 4 секунды сбрасываем сторожевой таймер микроконтроллера // Каждые 6 минут отправляем данные на "Народный мониторинг" // Каждые 30 секунд отсылаем данные в эфир 433 if(!(millis()%1000)) wdt_reset(); if(!(millis()%360000)) send_info(true); if(!(millis()%30000)) send_info(false); }

Modüllerin kendilerine bir anten eklenmelidir. İçin 433 MHz normal bir uzun bakır tel yeterlidir 17 cm... Anten olmadan normal çalışmayı unutabilirsiniz.

Bu güncellemenin en önemli kısmına geçiyoruz - yerel kablosuz istasyon

Bunu uygulamak için (dizimde), bir analog kullandım Arduino NANO(temelde ATMega328) ve TFTçipte göster ST7735S izinle 128x160

Gizli metin



Pin çıkışı ekranı -> denetleyici

============================= LED | 3.3V SCK | SCK (13) SDA | MOSI (11) A0 | DC (9) SIFIRLA | RST (8) CS | CS (10) GND | GND VCC | 5V =============================

Alıcı modülü, bir verici ile aynı şekilde bağlanır, yalnızca VERİ sabitlemek D7.

Nasıl göründüğüne dair birkaç resim:

Gizli metin

alıcı kroki

Gizli metin

#Dahil etmek #Dahil etmek #Dahil etmek #Dahil etmek int x, y; int w = 128, h = 160; int boyutu; // 433 EasyTransferVirtualWire ET; struct SEND_DATA_STRUCTURE (byte ID; // Device ID int Sıcaklık; // Sıcaklık kayan nokta Basınç; // Basınç kayan nokta Nem; // Nem kayan nokta çiğ Noktası; // Çiy / donma noktası); SEND_DATA_STRUCTURE yayını; int Log_Sıcaklık = -1; float Log_Pressure = -1; float Log_Nem = -1; float Log_dewPoint = -1; // TFT #define cs 10 #define dc 9 #define ilk 8 char Sıcaklık, Basınç, Nem, Çiy Noktası; Dizi bilgisi; TFT TFTekran = TFT (cs, dc, rst); void setup () (Serial.begin (9600); // 433 MHz modülünün başlatılması ET.begin (ayrıntılar (yayın)); vw_set_ptt_inverted (true); vw_set_rx_pin (7); vw_setup (2000); vw_rx_start (); / / Başlatma ve ilk görüntü ayarı TFTscreen.begin (); TFTscreen.setRotation (2); TFTscreen.background (0, 0, 0); // Statik elemanlar çizin // 1. Bizi ziyaret edin TFTscreen.stroke (255, 255, 255); TFTscreen.setTextSize (1); TFTscreen.text ("", 10, 10); // 2. TFTscreen.text ("mmHg", w / 2 + 5, 80); TFTscreen sensörlerinden alınan değerlerin açıklaması. text ("%", w / 2 + 5, 100); TFTscreen.text ("C", w / 2 + 5, 120); yayın.Sıcaklık = 0; yayın.Basınç = 0; yayın.Nem = 0; yayın .dewPoint = 0; TFTPrint ();) geçersiz döngü () (if (ET.receiveData ()) (if (broadcast.ID == 1) TFTPrint (); / * Seri.println (yayın.Sıcaklık); Seri println (yayın.Basınç); Serial.println (yayın.Nem); Seri.println (yayın.dewPoint); Seri.println (); * /) void değişiklikleri (int boyut, int x , int y, bool up, bool clear = false) (if (clear) TFTscreen.stroke (0, 0, 0); else (değişiklikler (boyut, x, y,! up, true); TFTscreen.stroke ((yukarı)? 0: 255, 0, (yukarı)? 255: 0);) if ((boyut% 2) == 0 ) beden ++; while (size> 0) (TFTscreen.line (x, y, x + (size--), y); ++ x, (yukarı)? - y: ++ y, --size;) / * while ( size> 0) (TFTscreen.line (x, y, (yukarı)? x + size-1: x, (yukarı)? y: y + size-1); ++ x, ++ y, --size; ) * /) int x_center (int w, int uzunluk, int boyut) (dönüş kat ((w-uzunluk * (boyut * 5) + boyut * 2) / 2);) int x_alignment_right (int w, int uzunluk, int boyut) (dönüş tavanı (w-uzunluk * (boyut * 5) + boyut * 2);) void TFTPrint () (boyut = 3; // ================ ================================================= =============== // Sıcaklık okumalarını görüntüleme // ============================ ================================================= ==== if (broadcast.Temperature! = Log_Temperature) (TFTscreen.setTextSize (size); // Eski verileri sil String info = String (Log_Temperature); info.concat ("C"); if (Log_Temperature> 0) info = "+" + bilgi; info.toCharArray (Sıcaklık, bilgi.uzunluk () + 1); TFTscreen.stroke (0, 0, 0); TFTscreen.text (Sıcaklık, x_center (w, bilgi.uzunluk () + 1) , boyut), 35); // Yeni okumaları göster i info = Dize (yayın. Sıcaklık); info.concat ("C"); if (yayın.Sıcaklık> 0) bilgi = "+" + bilgi; info.toCharArray (Sıcaklık, info.length () + 1); // Sıcaklığın kendisine bağlı olarak sıcaklık değerinin rengini değiştirin int r, g = 0, b; if (yayın.Sıcaklık> 0) (r = harita (yayın.Sıcaklık, 0, 40, 255, 150); // Kırmızı b = harita (yayın.Sıcaklık, 0, 40, 30, 0); // Değiştir sıfırdan daha görsel bir geçiş için renk tonu) else (r = harita (yayın.Sıcaklık, -40, 0, 0, 30); // Sıfırdan daha görsel bir geçiş için tonu değiştirin b = harita (yayın.Sıcaklık, - 40, 0, 150, 255); // Mavi) TFTscreen.stroke (b, g, r); // UYARI: kitaplıkta renk konumları karışmış, RGB alanı BGR tarafından kullanılıyor! TFTscreen.text (Sıcaklık, x_center (w, info.length () + 1, boyut), 35); ) boyut = 1; // ============================================== ================================= // Basınç okumalarını görüntüleyin // ========= ================================================= ======================= if (broadcast.Pressure! = Log_Pressure) (TFTscreen.setTextSize (boyut); // Eski verilerin üzerine yaz bilgi = String (Log_Pressure) ); info.toCharArray (Basınç, bilgi.uzunluk ()); TFTscreen.stroke (0, 0, 0); TFTscreen.text (Basınç, x_alignment_right (w / 2-5, info.length (), boyut), 80 ); // Yeni okumaları göster info = String (broadcast.Pressure); info.toCharArray (Basınç, info.length ()); TFTscreen.stroke (255, 255, 255); TFTscreen.text (Basınç, x_alignment_right (w / 2-5, info.length (), size), 80); değişiklikler (10, 106, 85, (broadcast.Pressure> Log_Pressure)? True: false);) else (değişiklikler (10, 106, 85, true, true); değişiklikler (10, 106, 85, false, true);) // =============================== ================================================= = // Nem okumalarını göster // =================== ================================================= ============== if (broadcast.Humidity! = Log_Humidity) (TFTscreen.setTextSize (size); // Eski verileri sil bilgisi = String (Log_Humidity); info.toCharArray (Nem, info.length ()); TFTscreen.stroke (0, 0, 0); TFTscreen.text (Nem, x_alignment_right (w / 2-5, info.length (), boyut), 100); // Yeni okumaları yazdır info = String (yayın.Nem); info.toCharArray (Nem, info.length ()); TFTscreen.stroke (255, 255, 255); TFTscreen.text (Nem, x_alignment_right (w / 2-5, info.length (), boyut), 100); değişiklikler (10, 106, 105, (yayın.Nem> Günlük_Nem)? true: false); ) else (değişiklikler (10, 106, 105, true, true); değişiklikler (10, 106, 105, false, true);) // ================== ================================================= ============== // Çiy / donma noktası okumalarını görüntüleme // ========================== ================================================= ====== if (broadcast.dewPoint! = Log_dewPoint) (TFTscreen.setTextSize (size); // Güncel olmayan verileri sil info = String (Log_dewPoint); info.toCharArray (dewPoint, info.length ()); TFTscreen.stroke (0, 0, 0); TFTscreen.text (dewPoint, x_alignment_right (w / 2-5, info.length (), size), 120); // Yeni okumaları görüntüleme info = String (broadcast.dewPoint); info.toCharArray (dewPoint, info.length ()); TFTscreen.stroke (255, 255, 255); TFTscreen.text (dewPoint, x_alignment_right (w / 2-5, info.length (), size), 120); değişiklikler (10, 106, 125, (broadcast.dewPoint>) Log_dewPoint)? True: false);) else (değişiklikler (10, 106, 125, true, true); değişiklikler (10, 106, 125, false, true);) // Sonrakiler için günlüklerdeki değerleri güncelleyin okumaların karşılaştırılması Log_Temperature = yayın.Sıcaklık; Log_Pressure = yayın.Basınç; Log_Humidity = yayın.Nem; Log_dewPoint = yayın.dewPoint; )

Göstergeler oldukça kompakt bir şekilde gösteriliyor, ancak uygulamanın gösterdiği gibi (ve yoldaşlarımın tavsiyesi) - "tat ve renk açısından, karım bile bir arkadaşım değil." Bir sürü tavsiye ve öneri dinledim ama bunlar birbiriyle çelişiyor. Bu yüzden zevkinize göre yapın.

Bana öyle geldi ki tasarım, projenin çoğu zaman alan kısmı!

Gizli metin

Bazı veriler, bazı tasarım öğelerini görüntülemek için üretilir.

Ekrandaki eserler, bunlar ekranın içinde bulunduğu uzun süre boyunca biriken toz ve diğer kirler ... orada bir yerde, ... peki, nereden aldığımı hatırlamıyorum! Beni yalnız bırakın!

Çizimin konumlandırma işlevleri vardır. Oldukça ilkeldirler, ancak belirli efektler elde etmenize izin verirler.

  1. x_center
  2. x_alignment_right

Birincisi metni ortalar ve ikincisi onu belirtilen bölgenin sağına hizalar. Tüm hesaplamalar, ifadeye dayalı olarak belirtilen metnin boyutuna göre yapılır. 1 boyut = 1PX x 1PX yazı tipi bölümü.

Ekran ayrıca, okumaların şu veya bu değerindeki artış veya azalmaya karşılık gelen öğeleri de gösterir. Üçgen olarak görüntülenirler. Ama fonksiyon kodunda değişiklikler 45 derece döndürülmüş üçgen şeklinde alternatif bir ekran var. Okumalar artarsa, eleman kırmızı, aksi takdirde mavidir.

Bu arada, ana sıcaklığın rengi ve gölgesi, sıcaklığın kendisine bağlı olarak değişir. Oldukça tartışmalı bir karar, ancak bence görsel olarak rahat. Bir süre bunun için uğraştım ve fonksiyondaki değerlerin felç, TFT görüntüleme nesneleri yanlış sırada listeleniyor. BGR bir yer RGB... Bu bir geliştiricinin hatası veya anlamadığım bir şey.

not: Her şey oldukça ilginç, ama bence daha fazla gelişmeyi hak ediyor. Bir süre sonra ne yapacağız.

Çoğu çalışan insan gibi, kendi projelerinizi yapmak, kalan tek boş zamanı alır. Bu nedenle, uzun süre yaratmadım ve bir şeyler yapmak için "ellerimi kaşındım". Bu fırsat üniversitede garip bir şekilde ortaya çıktı. Pencerenin dışında, 4 Eylül ve devre üzerine yaklaşan kurs var. Bize kursların iki şekilde yapılabileceği söylendi: kağıt ve donanım.

5 yıl boyunca Üniversitemizde kağıt kursları "eskileri al topla" ilkesine göre yapılmıştır. Bu yaklaşım rutini ile bana uymadı, bu yüzden hemen donanımdaki kursu seçtim. Kurs çalışmasının kalbi olarak, öğrenme kolaylığı nedeniyle Arduino mikrodenetleyici önerildi. Dersin türünü belirledikten sonra bir soru daha vardı: tam olarak ne yapmalı. Mikrodenetleyici programlama konusunda tecrübem olmadığı için hemen Google'ı açtım ve mevcut projeleri incelemeye başladım. Pek çok proje var, bazıları oldukça basit, bazıları dahice (örneğin 3D tarayıcı), ancak ezici çoğunluğun pratik bir uygulaması yoktu. Ben de tam olarak sonradan rafta durup orada toz toplamayacak olanı istedim. Arduino dünyasına yapılan yarım saatlik bir geziden sonra, ev hava istasyonları konusuyla ilgilendim ve projelerin uygulanması çok zor görünmüyordu (temelde acemilere rüşvet verdi).

Dönem ödevi konusu bu şekilde seçildi ve zamanla sorunlar planlanmadı.

Bileşen seçimi

Farklı projelere bakınca Nano hatta Pro Mini bile benim için yeterli olacağını fark ettim ama yine de Arduino için programlama yapmak isteyip ilerde birkaç proje daha hayata geçirmek umuduyla Arduino Uno'yu seçtim. Daha önce hiç havya tutmamıştım, bu yüzden daha kolay geliştirme için Sensor Shield v4'ü satın almaya karar verdim.

Daha fazla detay

Kart, sensörlerin, modüllerin, servo motorların, Seri ve I2C arabirimlerinin hızlı bağlantısını kolaylaştırır ve ayrıca Duemilanova / Uno form faktörünün denetleyicisinin tüm bağlantı noktalarını görüntüler (mega serisine de bağlanabilir, ancak sınırlamalar ve sonraki sonuçlar). Kendi üzerine diğer kalkanları destekler.


Meteorolojik veriler için kaynak olarak aşağıdaki sensörleri seçtim:


Sensörlere karar verdim. Ancak sensörlerden gelen verilerle ne yapmalı. sergilemeye karar verdim. Renkli bir resim istedim, bu yüzden monokrom çözümleri hemen bıraktım. Birkaç dakikalık aramadan sonra 1.8 inçlik ST7735 TFT ekran seçildi.

Daha fazla detay

Ekran, iletişim için 4 kablolu bir SPI protokolü kullandığından ve kendi piksel adreslenebilir çerçeve arabelleğine sahip olduğundan, her türlü mikro denetleyici ile kullanılabilir. 1.8 inçlik ekran 128x160 renkli piksellere sahiptir. Ayrıca microSD hafıza kartı için bir yuva vardır, bu nedenle bir microSD kartın FAT16 / FAT32 dosya sisteminden tam renkli bitmap'leri yüklemek kolaydır.

Özellikler:

  • Ekran diyagonal - 1,8 inç, çözünürlük 128x160 piksel, 18 bit renk (262.144 renk)
  • Video belleği arabelleğinin yerleşik piksel adreslemeli denetleyicisi
  • Dahili microSD yuvası - 2'den fazla dijital hat kullanır
  • 3.3 ve 5V ile uyumlu
  • Boyutlar: 34 mm x 56 mm x 6,5 m


Arduino denetleyici programlama

Meteoroloji istasyonunun bileşenlerine karar verdikten sonra kontrolörü programlamaya başlayalım. Arduino IDE, Arduino'yu flaş etmek için kullanıldı. Ayrıca Adafruit kütüphanelerinden de yararlanılmıştır.

Çizime geçmeden önce, işlevselliğe bir göz atalım:

  • Her 10 saniyede bir sensörlerden okumalar alınır ve ekranda sadece önceki ölçüme göre değişen göstergeler güncellenir.
  • COM portu üzerinden uygulanan veri aktarımı

Kroki

#Dahil etmek // I2C cihazlarıyla iletişim için kütüphane #include // Tüm sensörler için çekirdek kitaplık #include // BMP180 için kitaplık #include // Çekirdek grafik kitaplığı #include // Donanıma özel kitaplık #include // SPI cihazlarıyla iletişim için kitaplık #include "dht.h" // DHT için kitaplık #define DHT22_PIN 2 // DHT22'nin veri pinini 2 dijital pine bağlayın #define TFT_CS 10 // TFT'nin CS pinini 10 dijital pine bağlayın #define TFT_RST 9 // TFT'nin RST pinini 9 dijital pine bağlayın // bunu Arduino sıfırlamasına da bağlayabilirsiniz // bu durumda bu #define pinini 0 yapın! #define TFT_DC 8 // TFT'nin DC pinini 8 dijital pinine bağlayın Adafruit_ST7735 tft = Adafruit_ST7735 (TFT_CS, TFT_DC, TFT_RST); // TFT'yi başlat #define TFT_SCLK 13 // TFT'nin SCLK pinini 13 dijital pine bağlayın #define TFT_MOSI 11 // TFT'nin MOSI pinini 11 dijital pin dht DHT'ye bağlayın; Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified (10085); // BMP180'i başlat int bmpFlag = 0; struct (uint32_t toplam; uint32_t tamam; uint32_t crc_error; uint32_t zaman aşımı; uint32_t bağlantı; uint32_t ack_l; uint32_t ack_h; uint32_t bilinmiyor;) stat = (0,0,0,0,0,0,0,0); // dht status void kurulumu için yapı (void) (Serial.begin (9600); Serial.println ("Meteo Testi"); Seri.println (""); if (! bmp.begin ()) // bağlantıyı kontrol edin BMP180 için (Serial.print ("Hata, BMP180 algılanmadı ... Kablolamanızı veya I2C ADDR'nizi kontrol edin!"); bmpFlag = 1;) tft.initR (INITR_BLACKTAB); // TFT'yi başlatın ve siyah renk tft.fillScreen ile doldurun (ST7735_BLACK); tft.setRotation (tft.getRotation () + 1); tft.setTextSize (1.5); gecikme (500); // TFT'nin başlatılmasını sağlamak için gecikme) // son ölçülen veri float eskiSıcaklık = 0 , eskiYükseklik = 0, eskiBasınç = 0, eskiDHTHnemlilik = 0, eskiDHTSıcaklık; bool wasUpdate = yanlış; void loop (void) (if (Serial.available ()> 0) // Verilerimiz Seri port (Serial.read (); // seri porttan bayt oku ve en son ölçülen verileri gönder printValue ("Basınç", eskiBasınç) , "hPa", false); printValue ("Sıcaklık", eskiSıcaklık, "C", yanlış); printValue ("Rakım", eskiAltitude, "m", yanlış); printValue ("Nem", eskiDHTHumidity, "%", false); printValue ("DHT_temperature", oldDHTtemperature, "C", false); Serial.println ("END_TRANSMISSION");)ensor_event_t event; float sıcaklık, yükseklik; if (bmpFlag == 0) (bmp.getEvent (& event) ) ; // if (event.pressure) (bmp.getTemperature (& sıcaklık); float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA; height = bmp.pressureToAltitude (seaLevelPressure, event.pressure, sıcaklık);) BMP180'den veri alın;) else (Serial.println ( "Sensör hatası ");)) uint32_t start = mikros (); int chk = DHT.read22 (DHT22_PIN); // DHT22'den veri al uint32_t stop = mikros (); stat.total ++; geçiş (chk) // durumunu kontrol et DHT22 (DHTLIB_OK durumu: stat.ok ++; kırmak; durum DHTLIB_ERROR_CHECKSUM: stat.crc_error ++; Serial.print ("Doğrulama hatası, \ t"); kırmak; durum DHTLIB_ERROR_TIMEOUT: stat.time_out ++; Serial.print ("Zaman aşımı hatası, \ t"); kırmak; durum DHTLIB_ERROR_CONNECT: stat.connect ++; Serial.print ("Bağlantı hatası, \ t"); kırmak; durum DHTLIB_ERROR_ACK_L: stat.ack_l ++; Serial.print ("Düşük Hatayı Onayla, \ t"); kırmak; durum DHTLIB_ERROR_ACK_H: stat.ack_h ++; Serial.print ("Yüksek Hatayı Onaylayın, \ t"); kırmak; varsayılan: stat.unknown ++; Serial.print ("Bilinmeyen hata, \ t"); kırmak; ) if (bmpFlag! = 0 || ! event.pressure) // verileri güncelle (tft.fillRect (0, 30, 160, 6, ST7735_BLACK); tft.setCursor (0, 30); tft.setTextColor (ST7735_RED); printValue ("ERROR BMP INITIALIZATION", 0 , "", true);) else (if (event.pressure! = oldPressure) (tft.fillRect (0, 30, 160, 7, ST7735_BLACK); tft.setCursor (0, 30); tft.setTextColor (ST7735_RED) ; printValue ("Basınç", event.pressure, "hPa", true); oldPressure = event.pressure; wasUpdate = true;) if (sıcaklık! = eskiSıcaklık) (tft.fillRect (0, 38, 160, 7, ST7735_BLACK) ); tft.setCursor (0, 38); tft.setTextColor (ST7735_WHITE); printValue ("Sıcaklık", sıcaklık, "C", true); oldTemperature = sıcaklık; wasUpdate = true;) if (rakım! = eskiİrtifa) ( tft.fillRect (0, 46, 160, 7, ST7735_BLACK); tft.setCursor (0, 46); tft.setTextColor (ST7735_BLUE); printValue ("Rakım", yükseklik, "m", gerçek); oldAltitude = yükseklik; wasUpdate = true;)) if (DHT.humidity! = oldDHTHumidity) (tft.fillRect (0, 54, 160, 7, ST7735_BLACK); t ft.setCursor (0, 54); tft.setTextColor (ST7735_GREEN); printValue ("Nem", DHT.humidity, "%", doğru); eskiDHTHumidity = DHT.humidity; wasUpdate = doğru; ) if (DHT.temperature! = oldDHTTemperature) (tft.fillRect (0, 80, 160, 7, ST7735_BLACK); tft.setCursor (0, 80); tft.setTextColor (ST7735_YELLOW); printValue ("DHT_temperature", DHT. sıcaklık, "C", true); oldDHTtemperature = DHT.temperature; wasUpdate = true;) if (wasUpdate) (Serial.println ("END_TRANSMISSION");) wasUpdate = false; gecikme (10000); ) void printValue (char * başlık, çift değer, char * ölçü, bool tftPrint) (if (tftPrint) // verileri TFT'ye yazdır (tft.print (başlık); tft.print (":"); tft.print ( değer); tft.println (ölçü);) Serial.print (başlık); // Seri port Serial.print'e veri gönder (":"); Seri.print (değer); Serial.println (ölçüm);)

Davayı toplamanın zamanı geldi

Kursun ana koşulu, prezentabl bir biçimde çalışan bir prototipti. Bu nedenle, bir dava satın almak zorunda kaldım ve bir dosyayla donanmış olarak, hava istasyonunu herhangi bir şekilde davaya soktum.

Yerel bir elektronik mağazasından bir konut satın alındı.

Çerçeve

(Fotoğrafta durum biraz farklı. Şeffaf bir kapağım var)



Ardından, bir dosya kullanılarak, sensörlerin çıkışı ve güç kaynağı için delikler açıldı. Sensörleri dışarı çıkarmaya karar verdim çünkü kasa olmadan sistemi test ederken ekranın arkasının çok ısındığını ve bunun kasa içindeki sıcaklığı etkileyeceğini fark ettim.

Sensörler ve güç kaynağı için delikli muhafaza



Bacakları 2 sensöre lehimlemem gerektiğinden ve bunlardan birinde pisti yaktığımdan, kaderi kışkırtmamaya ve kabloları sensörlere lehimlememeye karar verdim (başka bir şey üzerinde çalışacağım) ve bağlantı için az çok güvenilir olmak için elektrik bandını geri sarmaya karar verdim.

Muhafazaya "itmeden" önce sistem



Kasa Arduino'dan çok daha büyük olduğu için (daha küçüğü yoktu), anakartın kasanın içinde gezinmemesi için bir destek bulmam gerekti. Ayrıca, kasanın iç kısımlarını gizlemek için paralondan bir şekil ve içinde ekran için bir dikdörtgen kesildi. Elimde süper yapıştırıcı yoktu, bu yüzden çift taraflı bant üzerine oturmak zorunda kaldım.

Harika Yuda balina balığı



Kapağı tutturuyoruz, güç kaynağını bağlayıp bekliyoruz.

Binada tamamlanmış hava istasyonu



Sonuçları ekranda görüntüledikten sonra, nem ölçümünde hoş olmayan bir hata ortaya çıkıyor: DHT22 ısrarla %99,90 rakamı veriyor (%1.00 için son derece nadirdir). Sorunun ne olduğunu anlamaya başlıyoruz. Yaptığımız ilk şey, değerlerin COM portuna çıktısına bakmaktır. İyi hissettiriyor. Kasanın birkaç dolumu, demontajı ve montajından sonra, Google'da bir cevap aramak aklıma geliyor. Beklendiği gibi, Rus Google mantıklı bir şey söylemedi. TAMAM. İngilizce aramaya başlıyoruz ve forumlardan birinde benzer bir sorunu olan adamlarla karşılaşıyoruz. Tartışmanın ilk dört sayfası pratik bir şey vermiyor ama beşinci sayfada sorumuzun cevabını buluyoruz:
Nem sensörleri, yanlış gazlardan veya çok uzun süre yüksek neme IIRC maruz kalmaktan kolayca etkilenebilir. Veri sayfasında sensörün nasıl "sıfırlanacağı" ile ilgili bir prosedür vardır, bunu deneyebilirsiniz.

Tek soru, DHT22'ye ne zaman ve nasıl zarar verdiğimdi. Ama ders alma zamanı gelmişti ve ben de bu sorunun çözümünü sonraya bıraktım.

son söz

Kurs teslim edildi. Meteoroloji istasyonu, üniversitedeki tüm kuyruklar kapanana kadar süresiz olarak ertelendi. Ancak, tahmin ettiğimden daha erken hava istasyonuna dönmek zorunda kaldık. Öyle oldu ki Kasım ortasında iş yerimi değiştirdim ve yeni ekipte Arduino platformu ve benzerleriyle ilgilenen insanlarla tanıştım. Dolayısıyla soğumaya vakit bulamayınca bu platforma olan ilgim yeniden alevlendi. Hava durumu istasyonumu çıkardım, bir bilgisayara bağladım ve COM portu üzerinden Arduino'dan veri aktarımı uyguladığımı hatırladım. Sonra Arduino'dan COM portu üzerinden veri alan bir program yazmak ve bu verileri kamu izlemesine aktarmak fikri geldi, ama bu tamamen farklı bir hikaye.

Ayrıca kablosuz sensörlere sahip olmak ve yine de Arduino Pro Mini'de bir hava durumu istasyonu uygulamak istiyorum. Bu nedenle 3.3V güç kaynağı, 4 nRF24L01+ radyo modülü ve diğer bazı ek sensörlere sahip 4 adet Arduino Pro Mini sipariş ettim, bir dahaki sefere sizlere de anlatmaya çalışacağım. Bu arada paketi bekliyorum, client ile bağlantı olmaması şartıyla, datanın güncelleme zamanını ve datanın kendisini microSD karta kaydedebilmek için gerçek zamanlı bir saat bağlantısı kurmayı planlıyorum. COM bağlantı noktası aracılığıyla.

Sitenin geliştirilmesine yardımcı olabilir ve bazı fonları transfer edebilirsiniz.



Şehirde dolaşırken yeni bir elektronik mağazasının açıldığını gördüm. Girdikten sonra Arduina için çok sayıda kalkan buldum. Evde bir Arduino Uno vardı ve bir Arduino Nano hemen uzaktan sinyal vericilerle oynama fikrini aldı. En ucuz 433 MHz verici ve alıcıyı almaya karar verdim:

Sinyal vericisi.


Sinyal alıcısı.

Veri iletiminin en basit taslağını yazdıktan sonra (buradan bir örnek alınmıştır), iletim cihazlarının sıcaklık, nem gibi en basit verileri iletmek için oldukça uygun olabileceği ortaya çıktı.

Verici aşağıdaki özelliklere sahiptir:
1. Model: MX -FS - 03V
2. Hareket yarıçapı (engelleyen nesnelerin varlığına bağlıdır): 20-200 metre
3. Çalışma voltajı: 3.5 -12V
4. Modül boyutları: 19*19 mm
5. Sinyal modülasyonu: AM
6. Verici gücü: 10mW
7. Frekans: 433 MHz
8. Gerekli harici anten uzunluğu: 25cm
9. Bağlanması kolay (sadece üç kablo): VERİ; VCC; Toprak.

Alıcı modül özellikleri:
1. Çalışma voltajı: DC 5V
2. Akım: 4mA
3. Çalışma frekansı: 433.92 MHz
4. Hassasiyet: - 105dB
5. Modül ölçüleri: 30*14*7 mm
6. Harici anten gerekli: 32 cm.

İnternetin uçsuz bucaksızlığında 2Kb/s'deki bilgi aktarım aralığının 150m'ye kadar çıkabildiği söyleniyor. Kendim kontrol etmedim, ancak iki odalı bir dairede her yeri kabul ediyor.

Ev hava istasyonu donanımı

Birkaç denemeden sonra Arduino Nano'ya sıcaklık, nem sensörü ve verici bağlamaya karar verdim.


DS18D20 sıcaklık sensörü arduinoya şu şekilde bağlanır:

1) Mikrodenetleyicinin eksi GND'si.
2) Bir pull-up direnci aracılığıyla toprağa ve Arduino'nun D2 pinine DQ
3) Vdd ila + 5V.

Verici modülü MX-FS - 03V 5 Volt ile beslenir, veri çıkışı (ADATA) D13 pinine bağlanır.

Arduino Uno'ya bir LCD ekran ve bir BMP085 barometre bağladı.


arduino uno'ya bağlantı şeması

Sinyal alıcısı, D10 pinine bağlanır.

BMP085 modülü, dijital bir atmosferik basınç sensörüdür. Sensör, sıcaklık, basınç ve yüksekliği ölçmenizi sağlar. Bağlantı arayüzü: I2C. Sensör besleme gerilimi 1.8-3.6 V

Modül, Arduino'ya diğer I2C cihazlarıyla aynı şekilde bağlanır:

  • VCC - VCC (3.3V);
  • GND - GND;
  • SCL - analog pin 5'e;
  • SDA - analog pin 4'e.
  • Çok düşük maliyetli
  • Güç ve G/Ç 3-5V
  • %5 doğrulukla %20-80 nem tayini
  • 0-50 derece sıcaklık tayini. %2 doğrulukla
  • Örnekleme hızı 1 Hz'den fazla değil (her 1 saniyede bir defadan fazla değil)
  • Boyutlar 15.5mm x 12mm x 5.5mm
  • 4 pin aralığı 0.1"

DHT'nin 4 pini vardır:

  1. Vcc (3-5V güç kaynağı)
  2. Veri çıkışı - Veri çıkışı
  3. Kullanılmamış
  4. Genel

D8 Arduino'ya bağlanır.

Ev hava istasyonunun yazılım kısmı

Verici modül, sıcaklığı her 10 dakikada bir ölçer ve iletir.

Program aşağıdadır:

/ * Sketch version 1.0 Her 10 dakikada bir sıcaklık gönder. * / #include #include #include #define ONE_WIRE_BUS 2 // Dallas OneWire oneWire sensörünü (ONE_WIRE_BUS) bağlamak için pin; DallasSıcaklık sensörleri (& oneWire); CihazAdresi içerideTermometre; void setup (void) (//Serial.begin(9600); vw_set_ptt_inverted (true); // DR3100 için gerekli vw_setup (2000); // Baud hızını (bps) ayarlayın sensörler.begin (); if (! sensörler .getAddress (insideThermometer, 0)); printAddress (insideThermometer);ensor.setResolution (insideThermometer, 9);) void printTemperature (DeviceAddress deviceAddress) (float tempC = sensor.getTempC (deviceAddress); //Serial.print(Temp C : ") ; //Serial.println(tempC); // int numarası göndermek için veri oluşturma = tempC; char sembolü =" c "; // Bunun bir sensör olduğunu belirlemek için servis sembolü String strMsg =" z "; strMsg + = symbol ; strMsg + = ""; strMsg + = sayı; strMsg + = ""; char msg; strMsg.toCharArray (mesaj, 255); vw_send ((uint8_t *) msg, strlen (mesaj)); vw_wait_tx (); / / Transferin gecikmeyi bitirmesini bekliyoruz (200);) void loop (void) (for (int j = 0; j)<= 6; j++) { sensors.requestTemperatures(); printTemperature(insideThermometer); delay(600000); } } //Определение адреса void printAddress(DeviceAddress deviceAddress) { for (uint8_t i = 0; i < 8; i++) { if (deviceAddress[i] < 16); //Serial.print("0"); //Serial.print(deviceAddress[i], HEX); } }

Alıcı cihaz verileri alır, odadaki basınç ve sıcaklığı ölçer ve ekrana iletir.

#include #include LiquidCrystal lcd (12, 10, 5, 4, 3, 2); #dht11 sensörünü dahil et; #define DHT11PIN 8 #include #include BMP085 dps = BMP085 (); uzun Sıcaklık = 0, Basınç = 0, Rakım = 0; void setup () (Serial.begin (9600); vw_set_ptt_inverted (true); // DR3100 için gerekli vw_setup (2000); // Alma hızını ayarlayın vw_rx_start (); // Yayın lcd.begin'i izlemeye başlayın (16, 2 ); Wire.begin (); gecikme (1000); dps.init (); //lcd.setCursor(14,0); //lcd.write(byte(0)); //lcd.home (); ) void loop () (uint8_t arabelleği; // Mesaj için arabellek uint8_t buflen = VW_MAX_MESSAGE_LEN; // Tampon uzunluğu if (vw_get_message (buf, & buflen)) // Bir mesaj alınırsa (// Ayrıştırmayı başlat int i; // Eğer mesaj bize iletilmediyse çıkın if (buf! = "z") (return;) char komut = buf; // Komut dizin 2'de // Sayısal parametre dizin 4'te başlar i = 4; int sayı = 0; // Aktarım karakter karakter olduğundan, karakter kümesini bir sayıya dönüştürmeniz gerekir while (buf [i]! = "") (sayı * = 10; sayı + = buf [i] - "0"; i ++;) dps.getPressure (& Basınç); dps.getAltitude (& Rakım); dps.getTemperature (& Sıcaklık); //Serial.print(komut); Serial.print (""); Seri.println (sayı); lcd.print ("T ="); lcd.setCursor (2.0); lcd.print (sayı); lcd.setCursor (5.0); lcd.print ("P ="); lcd.print (Basınç / 133.3); lcd.print ("mmH"); lcd.setCursor (0,1); lcd.print ("T ="); lcd.print (Sıcaklık * 0.1); lcd.print ("H ="); lcd.print (sensör.nem); lcd.home (); // gecikme (2000); int chk = sensör.oku (DHT11PIN); geçiş (chk) (durum DHTLIB_OK: //Serial.println("OK "); break; case DHTLIB_ERROR_CHECKSUM: //Serial.println("Checksum error"); break; case DHTLIB_ERROR_TIMEOUT: //Serial.println("Zaman aşımı error "); break; varsayılan: //Serial.println("Bilinmeyen hata"); break;)))

not Gelecekte, aşağıdakileri eklemeyi planlıyorum:
- vericiye nem sensörü, veri aktarım algoritmasını yeniden çalışın
- rüzgarın hızını ve yönünü ölçmek için bir sensör.
- alıcı cihaza başka bir ekran ekleyin.
- alıcı ve vericiyi ayrı bir mikro denetleyiciye aktarın.

Aşağıda yaşananların bir fotoğrafını ekliyorum:

radyo elementlerin listesi

atama Bir çeşit mezhep Miktar NotMağazaBenim defterim
İletim kısmı.
Arduino kurulu

Arduino Nano 3.0

1 not defterine
Sıcaklık sensörü

DS18B20

1 not defterine
direnç

220 ohm

1 not defterine
verici modülüMX-FS-03V (433 MHz)1 not defterine
Radyo alıcı kısmı.
Arduino kurulu

Arduino Uno

1 not defterine
düzeltici direnç 1 not defterine
direnç