Мобильная метеостанция на Arduino

от автора

Наверное, каждый кто начинает свое знакомство с Arduino, поморгав светодиодом и подключив кнопку, переходит к созданию своей метеостанции. Не исключением стал и я.

Ознакомившись с различными проектами в интернете, были составлены определенные требования. Устройство должно отображать время, температуру, давление и график его изменения. Также очень хотелось рассчитывать фазы луны и время восхода-захода солнца. А самым важным требованием было – мобильность. Создаваемая портативная метеостанция предполагалась использоваться в помощь для рыбалки, поэтому должна была иметь компактный размер и обладать определенной автономностью.

Создание корпуса для любой самоделки является довольно сложной задачей. В закромах был найден мобильный телефон Motorola T192.

image

Оказалось, что в него отлично становится экран от Nokia 3310 (конечно использовать саму Nokia 3310, возможно, было бы удобнее).
Управлять всем было поручено Arduino Pro Mini.

image

Чтобы не проваливались кнопки, была вставлена макетная плата. При переклеивании клавиатуры, было замечено, что шаг отверстий макетки почти совпал с кнопками. В дальнейшем это позволило использовать кнопки.

image

Для измерения давления применен датчик bmp180. В качестве часов был взят модуль ds1302, но для экономии места с него была взята только микросхема. Кварц был выпаян из материнской платы, а батарейку вынул с ноутбука (конечно же, можно и обычную cr2032 в термокембрике).

image

Так как имелись свободные выходы, то подсветку дисплея повесил на один из свободных и кнопку на второй.

image

Для зарядки литиевого аккумулятора был использован модуль заряда Li-ion на TP4056.

image

Итоговый вид устройства (провода, торчащие справа, для прошивки, поэтому вскоре будут убраны.)

image

При написании программы использовались обычные средства. Единственное хотелось бы обратить внимание на библиотеку TimeLord.h (https://github.com/probonopd/TimeLord). С помощью ее функций, указав дату, координаты и часовой пояс можно определить время восхода-захода солна, фазу луны. Описание можно скачать по ссылке: TimeLord.pdf.

Касаемо времени автономной работы, то непродолжительное тестирование показало, что неделю устройство проработает точно. Этот результат вполне устраивает, поэтому дальнейшие эксперименты по энергосбережению пока отложены.

Скетч на данный момент использует 81% памяти устройства, поэтому есть возможность добавить что-нибудь еще.

image

На экране отображается антенна (индикатор уровня сигнала). Это оставлен задел для тестирования радио модуля на 433 МГц, для получения правдивой температуры и влажности от внешнего модуля.

Исходник

#include <BMP085.h> #include <EEPROM.h> #include <Adafruit_GFX.h> #include <Adafruit_PCD8544.h> #include <DS1302.h> #include <TimeLord.h> #include <Wire.h>  //84x48 Adafruit_PCD8544 display = Adafruit_PCD8544(3, 4, 5, 6, 7); // Create a DS1302 object. DS1302 rtc(12, 11, 10);// Chip Enable, Input/Output, Serial Clock Time t = rtc.time(); String dayAsString(const Time::Day day) {   switch (day) {     case Time::kSunday: return "Sunday";     case Time::kMonday: return "Monday";     case Time::kTuesday: return "Tuesday";     case Time::kWednesday: return "Wednesday";     case Time::kThursday: return "Thursday";     case Time::kFriday: return "Friday";     case Time::kSaturday: return "Saturday";   }   return "(unknown day)"; } //------------- TimeLord tardis; float const LATITUDE = 52.70; float const LONGITUDE = 25.40; //byte today[] = {0,0,12,22,03,16}; byte today[6]; //------------- //long previousMillis = 0, previousMillis2 = 0;      // храним время последней команды //long interval = 1800000;           // интервал между командами //long interval = 3600000;           // интервал между командами //long interval2 = 1000;           // интервал между командами byte prevSecond = 99, prevHour = 99; //------------- BMP085 dps = BMP085(); long Temperature = 0, Pressure = 0; float t_f = 0;  //-------------buttons-------------------- int buttonPushCounter = 0; int buttonState = 0; int lastButtonState = 0;  //-------------moon---------------------- //0.0 New Moon 0.99 - Almost new const unsigned char PROGMEM moon0[] = { B00000111, B11100000,   B00001000, B00010000,   B00010000, B00001000,   B00100000, B00000100,   B01000000, B00000010,   B10000000, B00000001,   B10000000, B00000001,   B10000000, B00000001,   B10000000, B00000001,   B10000000, B00000001,   B01000000, B00000010,   B00100000, B00000100,   B00010000, B00001000,   B00001000, B00010000,   B00000111, B11100000 };  //Waxing Crescent const unsigned char PROGMEM moon1[] = { B00000111, B11100000,   B00001000, B01110000,   B00010000, B00111000,   B00100000, B00011100,   B01000000, B00001110,   B10000000, B00001111,   B10000000, B00001111,   B10000000, B00001111,   B10000000, B00001111,   B10000000, B00001111,   B01000000, B00001110,   B00100000, B00011100,   B00010000, B00111000,   B00001000, B01110000,   B00000111, B11100000 };  //0.25 First Quarter const unsigned char PROGMEM moon2[] = { B00000111, B11100000,   B00001000, B11110000,   B00010000, B11111000,   B00100000, B11111100,   B01000000, B11111110,   B10000000, B11111111,   B10000000, B11111111,   B10000000, B11111111,   B10000000, B11111111,   B10000000, B11111111,   B01000000, B11111110,   B00100000, B11111100,   B00010000, B11111000,   B00001000, B11110000,   B00000111, B11100000 };  //Waxing Gibbous const unsigned char PROGMEM moon3[] = { B00000111, B11100000,   B00001011, B11110000,   B00010111, B11111000,   B00101111, B11111100,   B01001111, B11111110,   B10001111, B11111111,   B10001111, B11111111,   B10001111, B11111111,   B10001111, B11111111,   B10001111, B11111111,   B01001111, B11111110,   B00101111, B11111100,   B00010111, B11111000,   B00001011, B11110000,   B00000111, B11100000 };  //0.5 Full Moon const unsigned char PROGMEM moon4[] = { B00000111, B11100000,   B00001111, B11110000,   B00011111, B11111000,   B00111111, B11111100,   B01111111, B11111110,   B11111111, B11111111,   B11111111, B11111111,   B11111111, B11111111,   B11111111, B11111111,   B11111111, B11111111,   B01111111, B11111110,   B00111111, B11111100,   B00011111, B11111000,   B00001111, B11110000,   B00000111, B11100000 };  //Waning Gibbous const unsigned char PROGMEM moon5[] = { B00000111, B11100000,   B00001111, B11010000,   B00011111, B11101000,   B00111111, B11110100,   B01111111, B11110010,   B11111111, B11110001,   B11111111, B11110001,   B11111111, B11110001,   B11111111, B11110001,   B11111111, B11110001,   B01111111, B11110010,   B00111111, B11110100,   B00011111, B11101000,   B00001111, B11010000,   B00000111, B11100000 };  //0.75 Third Quarter  (Last Quarter) const unsigned char PROGMEM moon6[] = { B00000111, B11100000,   B00001111, B00010000,   B00011111, B00001000,   B00111111, B00000100,   B01111111, B00000010,   B11111111, B00000001,   B11111111, B00000001,   B11111111, B00000001,   B11111111, B00000001,   B11111111, B00000001,   B01111111, B00000010,   B00111111, B00000100,   B00011111, B00001000,   B00001111, B00010000,   B00000111, B11100000 };   //Waning Crescent const unsigned char PROGMEM moon7[] = { B00000111, B11100000,   B00001110, B00010000,   B00011100, B00001000,   B00111000, B00000100,   B01110000, B00000010,   B11110000, B00000001,   B11110000, B00000001,   B11110000, B00000001,   B11110000, B00000001,   B11110000, B00000001,   B01110000, B00000010,   B00111000, B00000100,   B00011100, B00001000,   B00001110, B00010000,   B00000111, B11100000 }; //===================================================================================== void drawMoon(int moon_x, int moon_y, int phase) {   display.fillRect(moon_x, moon_y, 16, 15, WHITE);   display.drawBitmap(moon_x, moon_y, moon4,  16, 15, WHITE);   switch (phase) {     case 0:       display.drawBitmap(moon_x, moon_y, moon0,  16, 15, BLACK);       break;     case 1:       display.drawBitmap(moon_x, moon_y, moon1,  16, 15, BLACK);       break;     case 2:       display.drawBitmap(moon_x, moon_y, moon2,  16, 15, BLACK);       break;     case 3:       display.drawBitmap(moon_x, moon_y, moon3,  16, 15, BLACK);       break;     case 4:       display.drawBitmap(moon_x, moon_y, moon4,  16, 15, BLACK);       break;     case 5:       display.drawBitmap(moon_x, moon_y, moon5,  16, 15, BLACK);       break;     case 6:       display.drawBitmap(moon_x, moon_y, moon6,  16, 15, BLACK);       break;     case 7:       display.drawBitmap(moon_x, moon_y, moon7,  16, 15, BLACK);       break;     default:       display.drawBitmap(moon_x, moon_y, moon4,  16, 15, WHITE);   } } //=========================================================================================== void drawMoonDate(int moon_x, int moon_y, uint8_t * datetoday) {   float phase;   phase = tardis.MoonPhase(datetoday);   if (phase >= 0.0   && phase <= 0.0625)  {     drawMoon(moon_x, moon_y, 0);   }; //0.000  New moon   if (phase > 0.0625 && phase <= 0.1875)  {     drawMoon(moon_x, moon_y, 1);   }; //0,125   if (phase > 0.1875 && phase <= 0.3125 ) {     drawMoon(moon_x, moon_y, 2);   }; //0.250  First Quarter   if (phase > 0.3125 && phase <= 0.4375)  {     drawMoon(moon_x, moon_y, 3);   }; //0,375   if (phase > 0.4375 && phase <= 0.5625)  {     drawMoon(moon_x, moon_y, 4);   }; //0.500  Full   if (phase > 0.5625 && phase <= 0.6875)  {     drawMoon(moon_x, moon_y, 5);   }; //0,625   if (phase > 0.6875 && phase <= 0.8125)  {     drawMoon(moon_x, moon_y, 6);   }; //0.750  Last Quarter   if (phase > 0.8125 && phase <= 0.9375)  {     drawMoon(moon_x, moon_y, 7);   }; //0.875   if (phase > 0.9375 && phase <= 1)       {     drawMoon(moon_x, moon_y, 0);   }; //0.990  Almost new } //===================================================================================== void drawSignal(float streng) {   display.fillRect(0, 0, 12, 6, WHITE);   display.drawTriangle(0, 0, 8, 0, 4, 4, BLACK);   display.drawLine(4, 0, 4, 6, BLACK);    display.drawLine(6, 5, 6, 6, BLACK);   display.drawLine(8, 4, 8, 6, BLACK);   display.drawLine(10, 2, 10, 6, BLACK);   display.drawLine(12, 0, 12, 6, BLACK);  } //===================================================================================== void drawBatteryState(float v_bat) {   display.fillRect(68, 0, 16, 7, WHITE);   display.drawRect(83, 2, 1, 3, BLACK);   display.drawRoundRect(68, 0, 15, 7, 2, BLACK);   // 3, 44  4, 2 0, 76  0, 152   //4,200 full  4   //4,048       3   //3,896       2   //3,744       1   //3,592       0   //3,440 zero  -    if (v_bat > 4500)   {     display.fillRect(70, 2, 10, 3, BLACK);   }   if (v_bat > 4048)   {     display.drawRect(79, 2, 2, 3, BLACK);   }   if (v_bat > 3896)   {     display.drawRect(76, 2, 2, 3, BLACK);   }   if (v_bat > 3744)    {     display.drawRect(73, 2, 2, 3, BLACK);   }   if (v_bat > 3592)    {     display.drawRect(70, 2, 2, 3, BLACK);   } } //===================================================================================== void drawTime(byte x, byte y) {   display.fillRect(0 + x, 0 + y, 48, 7, WHITE);   //display.fillRect(0+x, 0+y, 30, 7, WHITE);   Time t = rtc.time();   display.setTextColor(BLACK);   display.setTextSize(1);   display.setCursor(x, y);   display.print(t.hr);   display.print(":");   display.print(t.min);   display.print(":");   display.print(t.sec); } //=========================================================================================== void updateMoonSunDate() {   Time t = rtc.time();   today[0] = 0;   today[1] = 0;   today[2] = 12;   today[3] = t.date;   today[4] = t.mon;   today[5] = t.yr - 2000; } //===================================================================================== void drawSunRiseSet(byte x, byte y) {   updateMoonSunDate();   display.setTextSize(1);   display.setTextColor(BLACK);   display.fillRect(x, y, 33, y + 8, WHITE);    if (tardis.SunRise(today)) // if the sun will rise today (it might not, in the [ant]arctic)   {     display.setCursor(x, y);     display.print((int) today[tl_hour]);     display.print(":");     display.println((int) today[tl_minute]);   }   if (tardis.SunSet(today)) // if the sun will set today (it might not, in the [ant]arctic)   {     display.setCursor(x, y + 8);     display.print((int) today[tl_hour]);     display.print(":");     display.println((int) today[tl_minute]);   }  } //===================================================================================== void drawPressure(byte x, byte y) {   display.setTextSize(1);   display.setTextColor(BLACK);   display.fillRect(x, y, 33, y + 8, WHITE);   display.setCursor(x, y);   t_f = Temperature;   display.println( t_f / 10, 1);   display.setCursor(x, y + 8);   //display.println(ceil(Pressure / 133.3), 0);   display.println(Pressure / 133.3, 1); } //===================================================================================== long readVcc() {   // Read 1.1V reference against AVcc   // set the reference to Vcc and the measurement to the internal 1.1V reference #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)   ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)   ADMUX = _BV(MUX5) | _BV(MUX0); #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)   ADMUX = _BV(MUX3) | _BV(MUX2); #else   ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); #endif    delay(75); // Wait for Vref to settle   ADCSRA |= _BV(ADSC); // Start conversion   while (bit_is_set(ADCSRA, ADSC)); // measuring    uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH   uint8_t high = ADCH; // unlocks both    long result = (high << 8) | low;    //scale_constant = internal1.1Ref * 1023 * 1000   //где   //internal1.1Ref = 1.1 * Vcc1 (показания_вольтметра) / Vcc2 (показания_функции_readVcc())   //4.967/4600-------1215079.369565217   // result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*100000   result = 1132060 / result;    return result; // Vcc in millivolts   //http://blog.unlimite.net/?p=25 } //=========================================================================================== void drawVcc(byte x, byte y, word vcc) {   display.setTextSize(1);   display.setTextColor(BLACK);   display.fillRect(x, y, 33, y + 8, WHITE);   display.setCursor(x, y);   display.println(vcc); } //===================================================================================== void pressure_drawLine(byte x, byte h) {   display.drawLine(x, 47 - h, x, 47, BLACK); } void pressure_drawGraph() {   display.fillRect(0, 25, 83, 47, WHITE);   for (int i = 0; i <= 83; i++) {     pressure_drawLine(i, EEPROM.read(i));   } }  //=========================================================================================== void setup() {   Serial.begin(9600);     pinMode(8, INPUT_PULLUP);   pinMode(9, INPUT_PULLUP);   pinMode(2, OUTPUT);   //-----------------------------------------------------------------------------   //rtc.writeProtect(false);   //rtc.halt(false);   String day = dayAsString(t.day);   //-----------------------------------------------------------------------------   tardis.TimeZone(3 * 55);   tardis.Position(LATITUDE, LONGITUDE);   //-----------------------------------------------------------------------------   Wire.begin();   display.begin();   display.setContrast(55);   display.clearDisplay();    display.drawLine(0, display.height() / 2, display.width(), display.height() / 2, BLACK);   dps.init();    updateMoonSunDate();   drawMoonDate(34, 8, today);   pressure_drawGraph();   display.display();    prevHour = rtc.time().hr;   //EEPROM.write(0, 9);   // for (int i=0; i <= 83; i++){   // EEPROM.write(i, random(1, 23));   //   pressure_drawLine(i,EEPROM.read(i));   //     Serial.println(EEPROM.read(i));   // }  } //===================================================================================== void loop() {   unsigned long currentMillis = millis();   unsigned long currentMillis2 = millis();   byte temp;    //timer---------------------- 1 hour   //  if (currentMillis - previousMillis > interval) {   //  previousMillis = currentMillis;   if (rtc.time().hr != prevHour) {     prevHour = rtc.time().hr;      dps.getPressure(&Pressure);     dps.getTemperature(&Temperature);      for (int i = 0; i <= 82; i++) {       temp = EEPROM.read(i + 1);       EEPROM.write(i, temp);     }     EEPROM.write(83, ceil(Pressure / 133.3) - 740);     pressure_drawGraph();     display.display();     }   //timer---------------------- 1 sec   // if (currentMillis2 - previousMillis2 > interval2) {   //   previousMillis2 = currentMillis2;    if (rtc.time().sec != prevSecond) {     prevSecond = rtc.time().sec;      dps.getPressure(&Pressure);     dps.getTemperature(&Temperature);     updateMoonSunDate();      drawPressure(0, 8);     drawTime(17, 0);     drawSunRiseSet(53, 8);     drawMoonDate(34, 8, today);     drawBatteryState(readVcc());     drawSignal(5);     //    drawVcc(0, 16, readVcc());     display.display();   }   //timer----------------------    buttonState = digitalRead(8);   // Serial.println(buttonState);   if (buttonState != lastButtonState) {     if (buttonState == HIGH) {       buttonPushCounter++;     }   }   lastButtonState = buttonState;   if (buttonPushCounter % 2 == 0) {     digitalWrite(2, HIGH);   } else {     digitalWrite(2, LOW);   } } //===================================================================================== 

ссылка на оригинал статьи https://geektimes.ru/post/274258/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *