/* BME280 module I2C address 0x76 Hardware connections: GND -> GND 5.0 -> 5.0 SDA -> A4 SCL -> A5 LCD/PCF8574 module I2C address 0x72 Scale 950 hPa to 1050 hPa Button +- to compensate the pressure display against altitude */ #include #include #include #include #include #include #include #define LED LED_BUILTIN #define INTERRUPT0 PD2 // interrupt pin optical sensor stopper #define INTERRUPT1 PD3 // interrupt pin pushbutton array #define OFFSETP 8 // PD8 offset+ pushbutton pin #define OFFSETM 9 // PD9 offset- pushbutton pin #define SAMPLE 60 // store new pressure every 60 minutes byte CELSIUS[8] = { 0b11000,0b11000,0b00011,0b00100,0b00100,0b000100,0b00011,0b00000 }; byte PROZENT[8] = { 0b01100,0b01101,0b00011,0b00110,0b01100,0b110000,0b10110,0b00110 }; byte COPYRIGHT[8] = { 0b01110,0b10101,0b11001,0b00110,0b11001,0b11011,0b10101,0b01110 }; const char message0[] PROGMEM = {"2022 VA2CPJ"}; const char message1[] PROGMEM = {"j-p.cornut@va2cpj.ca"}; const char message2[] PROGMEM = {"https://va2cpj.ca"}; const char message3[] PROGMEM = {"Free for Use."}; const char connect0[] PROGMEM = {"I2C initialized."}; const char connect1[] PROGMEM = {"BME280 connected."}; const int SPR = 200; // steps per revolution for NEMA 17 const int BME280 = 0x76; // BME280 module default address const int LCD = 0x72; // LCD module address int step_count; // number of steps the motor has taken int offset; // altitude offset value int humidity; // humidity value int position; // current pressure position on scale int sample = SAMPLE; // compute trend int historycount = 0; float pressure; // pressure value float celsius; // temperature value const char* Training = "TRAINING"; const char* Falling = "FALLING "; const char* Steady = "STEADY "; const char* Rising = "RISING "; const int historysize = 6; const double Critical = 2.776445105; double pressures[historysize] = { }; double n = 1.0 * historysize; double sum_x = 0.0; double sum_xx = 0.0; double sum_y = 0.0; double sum_xy = 0.0; double slope = (sum_x*sum_y - n*sum_xy) / (sum_x*sum_x - n*sum_xx); double intercept = (sum_y -slope*sum_x) / n; double SSE = 0.0; double Observed = fabs(slope/(sqrt(SSE / (n-2.0)) / sqrt(sum_xx - sum_x*sum_x/n))); char *trend = Training; volatile boolean OPTO_FLAG = false; // end of course volatile boolean SCAN_FLAG = false; // activated switch Adafruit_BME280 bme; // initialize bme280 library SerLCD lcd; // Initialize the library with default I2C address 0x72 Stepper Dostep(SPR, 8, 9, 10, 11); // initialize the stepper library on pins 8 through 11 void Delay(long msec) // non timer delay in milliseconds { for(long i = 0; i < msec; i++){delayMicroseconds(1000); } // clock prescaler set at 1, 1 millisecond = 1000 microseconds } void Setup_Bme() { bme.setSampling(Adafruit_BME280::MODE_FORCED, // force reading after delayTime Adafruit_BME280::SAMPLING_X1, // temperature sampling set to 1 Adafruit_BME280::SAMPLING_X1, // pressure sampling set to 1 Adafruit_BME280::SAMPLING_X1, // humidity sampling set to 1 Adafruit_BME280::FILTER_OFF); // filter off, immediate 100% step response } void Read_Bme() // read bme280 { bme.takeForcedMeasurement(); pressure = bme.readPressure() / 100; // read pressure if(pressure > 1050){ pressure = 1050.0; } // upper full scale if(pressure < 950){ pressure = 950.0; } celsius = bme.readTemperature(); // read temperature humidity = bme.readHumidity(); // read humidity offset = EEPROM.read(0); // get saved offset position = (pressure - 950) + offset; // convert hPa to position on scale and +- offset } void Rotate(int turn) { if(turn > 0){ Dostep.step(SPR); } if(turn < 0){ Dostep.step(-SPR); } } ISR(Stopper) { OPTO_FLAG = true; } ISR(Scan) { SCAN_FLAG = true; } void Offsetp() { offset = EEPROM.read(0); if(offset == +20){ return; } offset++; EEPROM.write(0, (byte)offset); } void Offsetm() { offset = EEPROM.read(0); if(offset == -20){ return; } offset--; EEPROM.write(0, (byte)offset); } void setup() // setup routine { if((byte)EEPROM.read(0) == 255){ EEPROM.write(0, (byte)0); } // first run clear, first eeprom location (offset) pinMode(INTERRUPT0, INPUT); // PD2 as input external interrupt, external pull-up pinMode(INTERRUPT1, INPUT); // PD3 input external interrupt, external pull-up pinMode(LED, OUTPUT); Wire.begin(); lcd.begin(Wire); // set up the LCD for I2C communication Wire.setClock(400000); // set I2C SCL to High Speed Mode of 400kHz lcd.setFastBacklight(0, 0, 255); // set backlight to bright blue lcd.setContrast(150); // set contrast lcd.createChar(1,CELSIUS); // creating custom character lcd.createChar(2,PROZENT); lcd.createChar(2,COPYRIGHT); lcd.clear(); // clear the display - this moves the cursor to home position as well lcd.print((const __FlashStringHelper *) message0); lcd.setCursor(0, 1); lcd.print((const __FlashStringHelper *) message1); lcd.setCursor(0, 2); lcd.print((const __FlashStringHelper *) message2); Delay(3000); Serial.begin(57600); Serial.flush(); if((byte)EEPROM.read(0) == 255){ EEPROM.write(0, (byte)0); } // clear first eeprom location if first run if(!bme.begin(BME280)) { while(1) { digitalWrite(LED, !digitalRead(LED)); Delay(100); } } digitalWrite(LED, LOW); Setup_Bme(); lcd.clear(); lcd.print((const __FlashStringHelper *) connect0); lcd.setCursor(0, 1); lcd.print((const __FlashStringHelper *) connect1); Delay(3000); cli(); // disable interrupt attachInterrupt(digitalPinToInterrupt(INTERRUPT0),Stopper,FALLING); // optical detector interrupt, low limit (950 hPa) switch stopper attachInterrupt(digitalPinToInterrupt(INTERRUPT1),Scan,FALLING); // switch array interrupt sei(); // while (OPTO_FLAG == false){ Rotate(-1); } // now step down until low limit switch detected // allow interrupt Read_Bme(); Display(); } // Calculating barometric pressure trend // Reference // https://gist.github.com/Paraphraser/c5609f85cc7ee6ecd03ce179fb7f7edb const char* Trend(double newpressure) { if (historycount < historysize) { pressures[historycount] = newpressure; historycount++; } else { for (int i = 1; i < historysize; i++) pressures[i-1] = pressures[i]; pressures[historysize-1] = newpressure; } if (historycount < historysize) return Training; for (int i = 0; i < historysize; i++) { double x = 1.0 * i; double y = pressures[i]; sum_x = sum_x + x; sum_xx = sum_xx + x * x; sum_y = sum_y + y; sum_xy = sum_xy + x * y; } for (int i = 0; i < historysize; i++) { double y = pressures[i]; double residual = y - (intercept + slope * i); SSE = SSE + residual * residual; } if (Observed > Critical) { if (slope < 0.0) return Falling; else return Rising; } return Steady; } void loop() { if(millis() % 60000U == 0) // 60 seconds elapsed { Read_Bme(); Display(); // update display if(--sample == 0) { sample = SAMPLE; trend = Trend(pressure + offset); } } } void Display() { lcd.setCursor(0, 0); lcd.print("Pressure: "); lcd.print(pressure + offset); lcd.setCursor(0, 1); lcd.print("Temperature: "); lcd.print(celsius); lcd.setCursor(0, 2); lcd.print("Humidity: "); lcd.print(humidity); lcd.setCursor(5, 3); lcd.println(trend); Serial.print(" offset: "); Serial.print(offset); Serial.print(" Position: "); Serial.print(position); Serial.print(" Trend: "); Serial.println(trend); Serial.flush(); }