/* Use a modified low cost clock analog movement BME280 module address 0x76 Hardware connections: GND -> GND 5.0 -> 5.0 SDA -> A4 SCL -> A5 Scale 990 hPa to 1040 hPa Use 50 seconds (300 degrees, like an aneroid barometer) on a clock scale Pressure scale 990-1040 on a clock 35 -> 25 by 1 hPa step (~ min - max where I leave) Thermometer scale 15-40 on a clock 0 -> 25 by 1 ℃ step (right side) humidity scale 0-100 on a clock 20 -> 0 by 5 % step (left side) Button to put system in thermometer mode and show temperature Button to put system in hygrometer mode and show percentage of humidity Button to show the tendency (around 12 O'clock +- 5 steps) using last 6 hours of pressure variation Button +- to compensate the pressure display against altitude Press the button to display value (5 seconds) and return to barometer mode Make one revolution at startup if BME280 is connected Set manually the needle to actual condition (i.e using METAR) Power down mode used, watchdog used for wake up */ /* Note: This project use a low cost modified clock movement Lavet-type stepping motor jumping second. */ #include #include #include #include #include #include #include #include #include #define LED PB5 #define CLOCKA PD6 // pin number where motor #define CLOCKB PD7 // is connected #define INTERRUPT PD2 // interrupt pin pushbutton array #define THERMOMETER 0 // PC0/A0 thermometer pushbutton pin #define HUMIDITY 1 // PC1/A1 hygrometer pushbutton pin #define TREND 2 // PC2/A2 trend pushbutton pin #define OFFSETP 4 // PD4 compensation+ pushbutton pin #define OFFSETM 5 // PD5 compensation- pushbutton pin const char copyright[] PROGMEM = {"2022 VA2CPJ j-p.cornut@va2cpj.ca / va2cpj@gmail.com https://va2cpj.ca Free for Use."}; const char connected[] PROGMEM = {"Two-wire I2C interface initialized, Bosch Sensortech BME280 (Adafruit) connected, address 0x76."}; const char preset[] PROGMEM = {"Now it is time to set needle at 1015 hPa, you have 30 seconds."}; const char adjust[] PROGMEM = {"Wait for sampling then using +- compensation to set the needle to the current pressure at the altitude (i.e. using METAR, WeatherWX, etc..)."}; const int PULSE_LENGTH = 150; // motor pulse 100 ms const int PULSE_PERIOD = 100; const int BME280_ADDR = 0x76; // BME280 module default address const int RATE = 75; // sampling rate (75 * 8 seconds) = 600 seconds 10 minutes const int ALTITUDE = 100; // arbitrary const int HISTORY_SIZE = 6; // history size const float ATM = 1013.25; // standard atmosphere pressure const float CRITICAL = 2.776445105; // for trend calculation int hPa = 1015; // atmospheric pressure int compensation = 0; // altitude compensation value int humidity = 0; // humidity value int barometer_position = 25; // current pressure position int saved_position = 25; // previous pressure position int trend; // trend position around +-5 position around 0'Oclock float data[HISTORY_SIZE] = {0.0}; // historical data buffer float celsius; // temperature value float pressure; // pressure value byte TICKPIN = CLOCKA; byte history = 0; // history counter, incremented every ~hour byte index = 0; // index into historical array volatile byte WDT_COUNT = 1; // sampling rate counter ( SAMPLE * 8 seconds) volatile boolean WDT_FLAG = false; // watchdog flag volatile boolean BAROMETER_FLAG = false; // barometer function status volatile boolean THERMOMETER_FLAG = false; // thermometer function status volatile boolean HYGROMETER_FLAG = false; // hygrometer function status volatile boolean TREND_FLAG = false; // trend function status Adafruit_BME280 bme; void setup() // Setup routine { pinMode(LED,OUTPUT); // connected to A0 low power LED pinMode(CLOCKA, OUTPUT); // motor drive pins, A1 and pinMode(CLOCKB, OUTPUT); // A2 as output pinMode(INTERRUPT, INPUT); // PD2 input external interrupt, external pull-up pinMode(A0, INPUT_PULLUP); // thermometer function pushbutton pinMode(A1, INPUT_PULLUP); // hygrometer function pushbutton pinMode(A2,INPUT_PULLUP); // trend function pushbutton pinMode(OFFSETP, INPUT_PULLUP); // compensation compensation function pushbutton pinMode(OFFSETM, INPUT_PULLUP); // compensation compensation function pushbutton pinMode(LED, OUTPUT); // optional low power led digitalWrite(CLOCKA, LOW); digitalWrite(CLOCKB, LOW); digitalWrite(LED,LOW); clock_prescale_set(clock_div_16); // run at 1 mHz Serial.begin(38400); // at 1mHz output at 2400 bauds if(ARDUINO){ Serial.flush(); } if(ARDUINO){ Serial.println("");Serial.println(""); } if(ARDUINO){ Serial.println("");Serial.println ((const __FlashStringHelper *) copyright); } if(ARDUINO){ Serial.flush(); } Wire.begin(); if(!bme.begin(BME280_ADDR)){ while (1) Delay(10); } if(ARDUINO){ Serial.println ((const __FlashStringHelper *) connected); } Step(60); // bme280 is connected, execute one revolution if(ARDUINO){ Serial.println ((const __FlashStringHelper *) adjust); } if(ARDUINO){ Serial.println ((const __FlashStringHelper *) preset); } if((byte)EEPROM.read(0) == 255){ EEPROM.write(0, (byte)0); } // first run clear, first eeprom location Delay(30000); // wait 30 seconds, the time to adjust needle to 1015 hPa,15°C,100% if(ARDUINO){ Serial.flush(); } cli(); Setup_Bme() // disable interrupt Setup_WatchDog(); Setup_Interrupt(); sei(); // allow interrupt } void loop() { // main loop routine if(WDT_FLAG == true) // wait until the watchdog have triggered a wake up { Barometer(); Monitor(); digitalWrite(LED, HIGH); Delay(100); digitalWrite(LED, LOW); WDT_FLAG = false; WDT_COUNT = RATE; } Sleep(); // re-enter sleep mode. } void Read_Bme() // read bme280 { bme.takeForcedMeasurement(); pressure = bme.readPressure() / 100; // read pressure hPa = int(pressure + 0.5); celsius = bme.readTemperature(); // read temperature humidity = bme.readHumidity(); // read humidity compensation = EEPROM.read(0); // get saved compensation barometer_position = (hPa - 990) + compensation; // convert hPa to position on scale +- compensation // set bme280 in forced mode if(hPa > 1040){ hPa = 1040; } // upper full scale if(hPa < 990){ hPa = 990; } // lower full scale // convert hPa to position on scale +- compensation if(celsius > 40) { celsius = 40; } // upper full scale if(celsius < 15) { celsius = 15; } // lower full scale } void Tick() // energize the motor electromagnet { digitalWrite(TICKPIN, HIGH); digitalWrite(LED, HIGH); Delay(PULSE_LENGTH); digitalWrite(TICKPIN, LOW); digitalWrite(LED, LOW); Delay(PULSE_PERIOD); // slowly if(TICKPIN == CLOCKA){ TICKPIN = CLOCKB; } // switch the direction for next time else { TICKPIN = CLOCKA; } } void Step(int step) { if(step < 0){ step = 60 - abs(step); } // backward while (step != 0){ Tick(); step--; } } void Setup_Bme() { bme.setSampling(Adafruit_BME280::MODE_FORCED, Adafruit_BME280::SAMPLING_X1, // temperature on Adafruit_BME280::SAMPLING_X1, // pressure on Adafruit_BME280::SAMPLING_X1, // humidity on Adafruit_BME280::FILTER_OFF); } void Setup_WatchDog() // setup the watchdog timer { MCUSR &= ~(1< CRITICAL) { if(slope < 0.0){trend = 20; } if(slope > 0.0){trend = 30; } } else trend = 25; } } void Trend() // display trend for 5 seconds { int step; wdt_reset(); // ok to display trend, don't disturb by watchdog if(trend == 0){ return; } // do nothing step = trend - barometer_position; Step(step); Delay(5000); // wait 5 seconds step = barometer_position - trend; Step(step); } void Barometer() { int step; if(!THERMOMETER_FLAG || !HYGROMETER_FLAG || !TREND_FLAG || !BAROMETER_FLAG) { wdt_reset(); // avoid disturbing by watchdog BAROMETER_FLAG = true; Read_Bme(); step = barometer_position - saved_position; Step(step); saved_position = barometer_position; Analyze(); BAROMETER_FLAG = false; } } void Temperature() // display temperature for 5 seconds { int step,temperature_position; wdt_reset(); // don't disturb by watchdog temperature_position = int(celsius + 10 + 0.5); // temperature position step = temperature_position - barometer_position; // last 25 positions, right scale Step(step); Delay(5000); // delay 5 seconds step = barometer_position - temperature_position; // return to current barometer position Step(step); } void Humidity() // display hygrometry for 5 seconds { int step,humidity_position; wdt_reset(); // ok to display hygrometry, don't disturb by watchdog humidity_position = (humidity + 0.5) / 4; // first 25 positions, left scale step = humidity_position - barometer_position; // number step to do Step(step); Delay(5000); // delay 5 seconds step = barometer_position - humidity_position; // return to current barometer position Step(step); } float Atm() // convert local pressure to sea-level { float hl; hl = ALTITUDE * 0.0065; return (pressure / pow(1.0 - hl/(celsius + hl + 273.15),5.257)); } void Zambretti() // Zambretti algorithm forecaster { // not yet implemented } void Monitor() { int i; { Serial.print("Pressure: "); Serial.print(pressure + compensation); Serial.print(" Temperature: "); Serial.print(celsius); Serial.print(" Humidity: "); Serial.print(humidity); Serial.print(" Compensation: "); Serial.print(compensation); Serial.print(" Position: "); Serial.print(barometer_position); Serial.print(" Trend: "); Serial.print(trend); Serial.print(" History: "); for(i = 0; i < HISTORY_SIZE; i++){ Serial.print(data[i]); Serial.print(" "); } Serial.println(""); Serial.flush(); } }