diff --git a/bme280.ino b/bme280.ino index 93271c8..c39f033 100644 --- a/bme280.ino +++ b/bme280.ino @@ -1,23 +1,50 @@ #include "Seeed_BME280.h" +enum { + LOWER = 0, + HIGHER +}; + BME280 sensor; -float highTemp = 0.0, lowTemp = 0.0; -const float blindZone = 0.03; +float bounds[2][SENSORS] = {}; +const float hysteresisWidth[SENSORS] = {0.03, 0.5, 0.5}; void initBME280() { sensor.init(); } -void readTemperature() { - float newTemperature = sensor.getTemperature(); +void readSensors() { + float newValue; - if (newTemperature > highTemp) { - temperature = newTemperature; - highTemp = newTemperature; - lowTemp = highTemp - blindZone; - } else if (newTemperature < lowTemp) { - temperature = newTemperature; - lowTemp = newTemperature; - highTemp = lowTemp + blindZone; + for (uint i = 0; i < SENSORS; i++) { + switch (i) { + case TEMPERATURE: + newValue = sensor.getTemperature(); + break; + + case HUMIDITY: + newValue = sensor.getHumidity(); + break; + + case PRESSURE: + newValue = sensor.getPressure(); + break; + } + + if (newValue > bounds[HIGHER][i]) { + sensorValue[i] = newValue; + bounds[HIGHER][i] = newValue; + bounds[LOWER][i] = bounds[HIGHER][i] - hysteresisWidth[i]; + if ((countdown <= 0) && (displaySensor == i)) { + displayNeedsUpdate = true; + } + } else if (newValue < bounds[LOWER][i]) { + sensorValue[i] = newValue; + bounds[LOWER][i] = newValue; + bounds[HIGHER][i] = bounds[LOWER][i] + hysteresisWidth[i]; + if ((countdown <= 0) && (displaySensor == i)) { + displayNeedsUpdate = true; + } + }; }; } diff --git a/button.ino b/button.ino index f5a68cf..b422bda 100644 --- a/button.ino +++ b/button.ino @@ -1,6 +1,6 @@ volatile unsigned long lastButtonPress = millis(); -// Debouncing na timerze: +// Debouncing using timer: // https://github.com/khoih-prog/TimerInterrupt/blob/master/examples/SwitchDebounce/SwitchDebounce.ino void initButton() { pinMode(BUTTON_PIN, INPUT_PULLUP); @@ -9,17 +9,20 @@ void initButton() { attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonISRstate, RISING); } +// FIXME: runMetronome ISR should be started/stopped by button. Otherwise fraction of first PERIOD_MS is lost from countdown. void buttonISRstate() { if ((millis() - lastButtonPress) > 100) { if (countdown < 0) // TODO: enable metronome timer countdown = 0; - else if (countdown == 0) + else if (countdown == 0) { // TODO: restart metronome timer to align countdown countdown = COUNTDOWN; - else { + updateTube(); + } else { countdown = -1; digitalWrite(BUTTON_LED_PIN, LOW); + updateTube(); // TODO: disable metronome timer } } diff --git a/doc/BME280 datashet, rev. 1.24.pdf b/doc/BME280 datashet, rev. 1.24.pdf new file mode 100644 index 0000000..36ef4e0 Binary files /dev/null and b/doc/BME280 datashet, rev. 1.24.pdf differ diff --git a/metronom.ino b/metronom.ino index 718812f..f6e8db9 100644 --- a/metronom.ino +++ b/metronom.ino @@ -4,24 +4,52 @@ SAMDTimer TaskTimer(TIMER_TC3); SAMD_ISR_Timer TasksISRs; -void TasksHandler(void) { TasksISRs.run(); } - +enum { + TEMPERATURE = 0, + HUMIDITY, + PRESSURE, + SENSORS +}; +// COUNTDOWN is effectively multiplied by METRONOME_INTERVAL const int COUNTDOWN = 600; -const int PERIOD_MS = 1000; -const int BUZZER_FREQ = 300; +// INTERVALS and TIMESHARES given in [ms] +const uint TASK_HANDLER_INTERVAL = 20; +const uint METRONOME_INTERVAL = 1000; +const uint SENSOR_INTERVAL = 1000; +const uint SENSOR_TIMESHARE[SENSORS] = {90000, 20000, 10000}; +// FREQUENCY in [Hz] +const uint BUZZER_FREQ = 300; const uint BUTTON_PIN = 6; const uint BUTTON_LED_PIN = 5; const uint BUZZER_PIN = 0; /* Metronome state is expressed by countdown: - * -1 - IDLE - * 0 - BEATING - * >0 - COUNTDOWN - */ + -1 - IDLE + 0 - BEATING + >0 - COUNTDOWN + Unless metronome is in COUNTDOWN mode, display rotates through sensor readings. +*/ volatile int countdown = -1; -volatile float temperature = 0.0; +// Units: temperature [C], humidity [%], pressure [P] +volatile float sensorValue[SENSORS] = {}; +volatile uint displaySensor = TEMPERATURE; +volatile bool displayNeedsUpdate = false; +volatile bool tubeRotated = false; +int tubeTimerID; + +void TasksHandler(void) { + TasksISRs.run(); + + if (displayNeedsUpdate || tubeRotated) + updateTube(); + + if (tubeRotated) { + TasksISRs.changeInterval(tubeTimerID, SENSOR_TIMESHARE[displaySensor]); + tubeRotated = false; + } +} void setup() { initButton(); @@ -29,11 +57,13 @@ void setup() { initBuzzer(); initTube(); - TaskTimer.attachInterruptInterval_MS(20, TasksHandler); - TasksISRs.setInterval(PERIOD_MS, runMetronome); - TasksISRs.setInterval(PERIOD_MS, readTemperature); - // TODO: move display from ISR to loop() - TasksISRs.setInterval(200, updateTube); + readSensors(); + updateTube(); + + TaskTimer.attachInterruptInterval_MS(TASK_HANDLER_INTERVAL, TasksHandler); + TasksISRs.setInterval(METRONOME_INTERVAL, runMetronome); + TasksISRs.setInterval(SENSOR_INTERVAL, readSensors); + tubeTimerID = TasksISRs.setInterval(SENSOR_TIMESHARE[displaySensor], rotateTube); } void runMetronome() { @@ -41,6 +71,8 @@ void runMetronome() { countdown -= 1; digitalWrite(BUTTON_LED_PIN, countdown % 2 ? HIGH : LOW); + + displayNeedsUpdate = true; } else if (countdown == 0) { TasksISRs.setTimeout(100, noBuzz); buzz(); diff --git a/tube.ino b/tube.ino index 9d998d7..d39f81c 100644 --- a/tube.ino +++ b/tube.ino @@ -10,21 +10,42 @@ void initTube() { tube.setBlinkRate(BLINK_OFF); } +void rotateTube() { + displaySensor = (displaySensor + 1) % SENSORS; + tubeRotated = true; +} + void updateTube() { char newText[5]; bool highPoint = false, lowPoint = false; + uint value = countdown; if (countdown <= 0) { - sprintf(newText, "%4u", (unsigned int) (temperature * 100.0)); - lowPoint = true; - } else { - sprintf(newText, "%4u", countdown); + switch (displaySensor) { + case TEMPERATURE: + value = (uint) (sensorValue[TEMPERATURE] * 100); + lowPoint = true; + break; + case HUMIDITY: + // TODO: add % sign? + value = (uint) (sensorValue[HUMIDITY]); + break; + case PRESSURE: + value = (uint) (sensorValue[PRESSURE] / 100); + break; + } } + sprintf(newText, "%4u", value); + // TODO: In the current mode of display refresh (displayNeedsUpdate) it should not be necessary to check previous text + // Maybe add LED switching for case when the strings match as a rough check before removing comparison? + // Or LED switching when display is updated? if (strcmp(tubeText, newText)) { strcpy(tubeText, newText); tube.displayString(tubeText); tube.setPoint(highPoint, lowPoint); tube.display(); } + + displayNeedsUpdate = false; }