During this week we have finished placing our window sensor prototype in the window mockup built weeks ago. We have modified the mockup to be able to screw the sensor and place a 9v battery behind it. We have also added a numerical ruler and several equally spaced magnets so that we can test our sensor. Several programs have been carried out to visually demonstrate the operation of the sensor obtaining the positioning by means of the linear encoder made with the magnetic reed sensors.
Tracking System for Classroom Ventilation Routines
A STEM project for classrooms
the VenTTracker project - Blog #07 - Adjusting the window sensor
In the following video we can see graphically the operation of our sensor by a progress bar on the OLED DisplayOLED Display. Our final version of the sensor will not have the graphical display, only two LEDs located at the bottom. The LED will remain on the design off and only switched to temporarily indicate the movement of the window or opening or closing events. In this way we can minimize energy consumption and extend the useful life of the battery.
Visualizing the degree of openness
When the sensor detects that the window is closed it displays the logo of our design challenge, :
The window has been opened. The progress bar shows an approximate 25% open.
We continue to open the window halfway. The progress bar reaches the middle indicating an approximate 50% open.
Once the second magnet has passed, we are already at approximately 75% opening.
And finally we can consider that the window is already completely open.
Depending on the number of magnets that we place, we can increase or decrease the precision of our sensor.
For our final version we consider that two magnets will not be enough.
Being able to have a scale that indicates:
- Closed window
- Window open below 33% open
- Open window between 33% and 66% open
- and open window between 66% and 100% open.
The battery in the final design will be a 12v battery. A23 A23 batteries are dry cell type batteries.
The battery will be placed under the Arduino Nano 33 IoTArduino Nano 33 IoT module as shown in the previous blog VenTTracker #06 - Window Sensor Prototype.
That battery measures 28.5mm long and 10.3mm in diameter, and weighs approximately 8 grams.
For the mockup we have placed a 9v battery at the back of the sliding part of the window.
We have prepared a bed for the battery and it is held with an elastic band.
When the sensor is started, the design challenge slogan and logo are displayed on the screen.
The code for the demo
Sketch uses 25032 bytes (9%) of program storage space. Maximum is 262144 bytes.
Global variables use 3340 bytes (10%) of dynamic memory, leaving 29428 bytes for local variables. Maximum is 32768 bytes.
/************************************************************************** This is an example for testing the VenTTracker Window Sensor Displays a progress bar indicating the window openes on a Monochrome OLED display based on SSD1306 drivers. This example is for a 128x32 pixel display using I2C to communicate 3 pins are required to interface (two I2C and one reset). Lights LED according sensor events: | LEFT LED | RIGHT LED | WINDOW OPEN EVENT | OFF | OFF | WINDOW CLOSED EVENT | ON | ON | TO THE LEFT MOVEMENT | ON | OFF | TO THE RIGHT MOVEMENT| ON | OFF | Hardware. Pinout 8 A4/SDA Analog ADC in; I2C SDA; SDA to SDA OLED Display 9 A5/SCL Analog ADC in; I2C SCL to SCK OLED Display 13 RST Digital In Active low reset input RESET to push button. Other end to ground. 14 GND Power Power Ground to Battery (-) 15 VIN Power In Vin Power input VIN to Baterry (+) 20 D2 Digital GPIO - to Open/Closed Reed Switches 25 D7 Digital; Right LED with a 100 Ohm resitor to ground 26 D8 Digital GPIO to Left LED with a 100 Ohm resitor to ground 27 D9/PWM Digital GPIO to Right Reed Switch. Other end to ground. 28 D10/PWM Digital GPIO; to Left Reed Switch. Other end to ground. Author: Enrique Albertos Date: 2021-04-17 **************************************************************************/ #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Wire.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 32 // OLED display height, in pixels enum WindowStateType {OPEN = LOW, CLOSED = HIGH}; int windowState = CLOSED; // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Port connections int leftSwitchPort = 10; // switch to ground + internal pull-up resistor. // Negative logic LOW when switch is closed int rightSwitchPort = 9; // switch to ground + internal pull-up resistor. // Negative logic LOW when switch is closed int leftLedPort = 8; // positive logic. HIGH turn on the LED int rightLedPort = 7; // positive logic. HIGH turn on the LED int openClosedSwitchPort = 2; // switch to ground + internal pull-up resistor. // Negative logic LOW when switch is closed typedef enum direction_t { RIGHT = 0x00, LEFT = 0xFF }; volatile direction_t lastWindowDirection = LEFT; volatile int8_t encoderPosition = 0; // don't know where is our encoder, we'll need an absolute reference // let's assume we are in closed position and window opens from left to right volatile bool encoderChangePending = false; volatile bool openCloseChangePending = true; unsigned long lastLeftSwitchDebounceTime = 0; // the last time the input left encoder pin was toggled unsigned long debounceDelay = 150; // the debounce time in millis #define LOGO_HEIGHT 32 #define LOGO_WIDTH 64 // logo 8 pixels per Byte Little Endian Horizontal static const unsigned char PROGMEM logoDesignForACause[] ={ 0x00, 0x00, 0x0F, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xC0, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF8, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x03, 0xFF, 0xFC, 0x1F, 0xFF, 0xE0, 0x00, 0x00, 0x07, 0xFF, 0xFE, 0x3F, 0xFF, 0xF0, 0x00, 0x00, 0x0F, 0xFF, 0xFE, 0x3F, 0xFF, 0xFC, 0x00, 0x00, 0x1F, 0xF9, 0xFE, 0x3F, 0xEF, 0xFC, 0x00, 0x00, 0x3F, 0xF0, 0x3E, 0x3E, 0x07, 0xFE, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x7F, 0x80, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x80, 0xF0, 0x0F, 0x10, 0xFF, 0x80, 0x00, 0xFF, 0x83, 0xFC, 0x3F, 0xC0, 0xFF, 0xC0, 0x01, 0xFF, 0x07, 0x1E, 0x70, 0xE0, 0x7F, 0xC0, 0x01, 0xFF, 0x0E, 0x07, 0xE0, 0x60, 0x7F, 0xE0, 0x03, 0xFF, 0x0C, 0x03, 0xC4, 0x30, 0x7F, 0xE0, 0x03, 0xFE, 0x0C, 0xE1, 0x8E, 0x30, 0x3F, 0xE0, 0x07, 0xFF, 0x0C, 0x03, 0x86, 0x30, 0x7F, 0xF0, 0x07, 0xFF, 0x0C, 0x03, 0xC4, 0x70, 0x7F, 0xF0, 0x0F, 0xFF, 0x8E, 0x07, 0xE0, 0x60, 0xFF, 0xF8, 0x1F, 0xFF, 0x87, 0xBE, 0x79, 0xE0, 0xFF, 0xFC, 0x1F, 0xFF, 0xC3, 0xF8, 0x3F, 0x81, 0xFF, 0xFC, 0x3F, 0xFF, 0xE0, 0x60, 0x06, 0x03, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x06, 0x30, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF }; #define TITLE_HEIGHT 32 #define TITLE_WIDTH 112 // title logo 8 pixels per Byte Little Endian Horizontal static const unsigned char PROGMEM designChallengeTitle[] ={ 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x01, 0x80, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0C, 0xC0, 0x00, 0x00, 0x00, 0x67, 0x3C, 0x79, 0x1F, 0x3E, 0x0F, 0x3C, 0xD0, 0x38, 0x18, 0xCF, 0x11, 0x1C, 0x78, 0x63, 0x7E, 0xD9, 0x9F, 0x3E, 0x0F, 0x6E, 0xF0, 0x6C, 0x18, 0x1F, 0xBB, 0x36, 0x7C, 0x63, 0x66, 0xC9, 0xB3, 0x37, 0x06, 0x66, 0xE0, 0x0C, 0x18, 0x01, 0xBB, 0x72, 0xCC, 0x63, 0x7E, 0xE1, 0xB3, 0x37, 0x06, 0x66, 0xC0, 0x3C, 0x18, 0x07, 0xBB, 0x38, 0xFC, 0x63, 0x7E, 0x79, 0xB3, 0x37, 0x06, 0x66, 0xC0, 0x7C, 0x18, 0xDF, 0xBB, 0x1E, 0xFC, 0x67, 0x60, 0x1D, 0xB3, 0x37, 0x06, 0x66, 0xC0, 0xCC, 0x18, 0xD9, 0xBB, 0x06, 0xC0, 0x66, 0x66, 0xDD, 0x9F, 0x37, 0x06, 0x66, 0xC0, 0xEC, 0x0D, 0xD9, 0xBB, 0x76, 0xEC, 0x7C, 0x3C, 0xF9, 0x9F, 0x37, 0x06, 0x7C, 0xC0, 0x7C, 0x0F, 0x9F, 0x9F, 0x3E, 0x78, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x10, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x30, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xB0, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xBF, 0x1E, 0x66, 0x38, 0xB8, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3F, 0x3F, 0x66, 0x7D, 0xF8, 0xF9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x33, 0x03, 0x66, 0xCC, 0xCD, 0x99, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x33, 0x1F, 0x66, 0xFC, 0xCD, 0x9B, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0xB3, 0x3B, 0x66, 0xE1, 0xCD, 0x9B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xB3, 0x33, 0x66, 0xC4, 0xCD, 0x99, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0xB3, 0x37, 0x66, 0xEC, 0xCC, 0xF9, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x33, 0x3F, 0x66, 0x7C, 0xCC, 0xF9, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // double buffer for the display GFXcanvas1 canvas(SCREEN_WIDTH, SCREEN_HEIGHT); // 128x32 pixel canvas void isrFallingLeftSwitchPort(); // ISR for leftSwitchPort void isrChangeOpenClosedSwitchPort(); // ISR for open/close switches void setup() { Serial.begin(9600); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } // set port switches with internal 20K pull up resistors pinMode(leftSwitchPort, INPUT_PULLUP); pinMode(rightSwitchPort, INPUT_PULLUP); pinMode(openClosedSwitchPort, INPUT_PULLUP); // set debugging LEDs ports pinMode(leftLedPort, OUTPUT); pinMode(rightLedPort, OUTPUT); blinkLogo(designChallengeTitle, TITLE_WIDTH, TITLE_HEIGHT); blinkLogo(logoDesignForACause, LOGO_WIDTH, LOGO_HEIGHT); // detect falling edges the switch chages from open to closed. It is negative // logic attachInterrupt(digitalPinToInterrupt(leftSwitchPort), isrFallingLeftSwitchPort, FALLING); // detect switch chages from open to closed. It is negative logic attachInterrupt(digitalPinToInterrupt(openClosedSwitchPort), isrChangeOpenClosedSwitchPort, CHANGE); } void loop() { // if there is a new event from the encoder acknowledge it and do pending // actions if (encoderChangePending) { encoderChangePending = false; // turn on left led when window direction is LEFT digitalWrite(leftLedPort, lastWindowDirection == LEFT); // and turn on right led when window direction is RIGHT digitalWrite(rightLedPort, lastWindowDirection == RIGHT); // log encoder position to serial port Serial.println(encoderPosition); if (windowState == OPEN) { drawProgressBar(encoderPosition); } else { drawLogo(logoDesignForACause, LOGO_WIDTH, LOGO_HEIGHT); } } if (openCloseChangePending) { openCloseChangePending = false; // Light LEDs when window closed digitalWrite(leftLedPort, windowState); digitalWrite(rightLedPort, windowState); if (windowState == OPEN) { drawProgressBar(encoderPosition); } else { drawLogo(logoDesignForACause, LOGO_WIDTH, LOGO_HEIGHT); } } } /** Interrupt Service Routine for Falling Edge in Left Switch Port */ void isrFallingLeftSwitchPort() { if ((millis() - lastLeftSwitchDebounceTime) > debounceDelay) { if (digitalRead(rightSwitchPort) == HIGH) { if (encoderPosition > 0) { encoderPosition--; } lastWindowDirection = LEFT; } else { if (encoderPosition < 3) { encoderPosition++; } lastWindowDirection = RIGHT; } lastLeftSwitchDebounceTime = millis(); encoderChangePending = true; } } /** Interrupt Service Routine for Changing Edge in Open Close Switches when the window is closed light the two LEDS */ void isrChangeOpenClosedSwitchPort() { windowState = !digitalRead(openClosedSwitchPort); if ((millis() - lastLeftSwitchDebounceTime) > debounceDelay) { encoderPosition = 0; openCloseChangePending = true; lastLeftSwitchDebounceTime = millis(); } } /** * Print the progress bar */ void drawProgressBar(const int counter) { display.clearDisplay(); canvas.fillScreen(BLACK); for(int i = 0; i < 4; ++i) { canvas.drawRect(i * SCREEN_WIDTH / 4,0,SCREEN_WIDTH /4 , SCREEN_HEIGHT , WHITE ); } for (int i = 0 ; i <= counter;++i) { canvas.fillRect(i * SCREEN_WIDTH / 4+3,4,SCREEN_WIDTH /4 -6, SCREEN_HEIGHT -8, WHITE ); } display.drawBitmap(0, 0, canvas.getBuffer(), 128, 32, WHITE, BLACK); // Copy to screen display.display(); } /** Send a logo to the Display and center it */ void drawLogo(const unsigned char* logo, const int width, const int height ) { display.clearDisplay(); display.drawBitmap( (display.width() - width ) / 2, (display.height() - height) / 2, logo, width, height, 1); display.display(); } /** blinks a logo on the display */ void blinkLogo(const unsigned char* logo, const int width, const int height) { // After reset blink the logo display.clearDisplay(); drawLogo(logo, width, height); //Invert and restore display, pausing in-between display.invertDisplay(true); delay(1000); display.invertDisplay(false); delay(2000); }
Adapting our mockup for the prototype.
We disassemble the mockup to make the holes where our sensor will be located and screwed.
After drilling the moving part and checking that everything fits without friction, we reassemble the model.
We prepare a bed for the 9v battery at the rear of the moving part of our model. The bed is glued.
The battery cables pass through a hole also made in the moving part.
The battery fits well in its bed.
To prevent movement will be secured with a rubber band.
Then we glued a graduated ruler and three equally spaced magnets for our linear position encoder.
And a fourth magnet for the detection of the closed window state.
The sensor is now ready for the first tests.
Finally we tested the prototype already assembled in the model.
It seems that everything works as we wished and we can estimate the position of the window using our linear position encoder.
This will help us gauge our version of the sensor based on accelerometer and automate the labeling of samples for our learning model machining.
The code for the first demo
Sketch uses 23572 bytes (8%) of program storage space. Maximum is 262144 bytes.
Global variables use 3332 bytes (10%) of dynamic memory, leaving 29436 bytes for local variables. Maximum is 32768 bytes.
/************************************************************************** This is an example for testing the VenTTracker Window Sensor Displays the element14 logo in a Monochrome OLEDs based on SSD1306 drivers This example is for a 128x32 pixel display using I2C to communicate 3 pins are required to interface (two I2C and one reset). Lights LED according sensor events: | LEFT LED | RIGHT LED | WINDOW OPEN EVENT | OFF | OFF | WINDOW CLOSED EVENT | ON | ON | TO THE LEFT MOVEMENT | ON | OFF | TO THE RIGHT MOVEMENT| ON | OFF | Hardware. Pinout 8 A4/SDA Analog ADC in; I2C SDA; SDA to SDA OLED Display 9 A5/SCL Analog ADC in; I2C SCL to SCK OLED Display 13 RST Digital In Active low reset input RESET to push button. Other end to ground. 14 GND Power Power Ground to Battery (-) 15 VIN Power In Vin Power input VIN to Baterry (+) 20 D2 Digital GPIO - to Open/Closed Reed Switches 25 D7 Digital; Right LED with a 100 Ohm resitor to ground 26 D8 Digital GPIO to Left LED with a 100 Ohm resitor to ground 27 D9/PWM Digital GPIO to Right Reed Switch. Other end to ground. 28 D10/PWM Digital GPIO; to Left Reed Switch. Other end to ground. Author: Enrique Albertos Date: 2021-04-16 **************************************************************************/ #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Wire.h> #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 32 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // Port connections int leftSwitchPort = 10; // switch to ground + internal pull-up resistor. // Negative logic LOW when switch is closed int rightSwitchPort = 9; // switch to ground + internal pull-up resistor. // Negative logic LOW when switch is closed int leftLedPort = 8; // positive logic. HIGH turn on the LED int rightLedPort = 7; // positive logic. HIGH turn on the LED int openClosedSwitchPort = 2; // switch to ground + internal pull-up resistor. // Negative logic LOW when switch is closed #define LOGO_HEIGHT 20 #define LOGO_WIDTH 128 // element14 logo 8 pixels per Byte Little Endian Horizontal static const unsigned char PROGMEM logo_bmp[] = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x07, 0x00, 0xE0, 0x1C, 0x00, 0x0C, 0x03, 0x00, 0x07, 0x00, 0x01, 0x80, 0x70, 0x06, 0x18, 0x00, 0x3F, 0xE0, 0xE0, 0xFF, 0x83, 0x3F, 0x9F, 0xE0, 0x3F, 0xE0, 0x6F, 0xF1, 0xFC, 0x06, 0x18, 0x00, 0x70, 0x70, 0xE1, 0xC1, 0xC3, 0xE1, 0xF8, 0x70, 0x70, 0x70, 0x78, 0x78, 0x70, 0x06, 0x18, 0x00, 0x60, 0x38, 0xE3, 0x80, 0xE3, 0xC0, 0xE0, 0x30, 0x60, 0x38, 0x70, 0x18, 0x60, 0x06, 0x18, 0x00, 0xE0, 0x18, 0xE3, 0x00, 0x63, 0x80, 0xE0, 0x30, 0xE0, 0x18, 0x60, 0x18, 0x60, 0x06, 0x18, 0x18, 0xC0, 0x18, 0xE3, 0x00, 0x63, 0x80, 0xE0, 0x30, 0xC0, 0x18, 0x60, 0x18, 0x60, 0x06, 0x18, 0x18, 0xC0, 0x18, 0xE3, 0x00, 0x63, 0x80, 0xE0, 0x30, 0xC0, 0x18, 0x60, 0x18, 0x60, 0x06, 0x18, 0x18, 0xFF, 0xF8, 0xE3, 0xFF, 0xE3, 0x80, 0xC0, 0x30, 0xFF, 0xF8, 0x60, 0x18, 0x60, 0x06, 0x18, 0x18, 0xFF, 0xF8, 0xE3, 0xFF, 0xE3, 0x80, 0xC0, 0x30, 0xFF, 0xF8, 0x60, 0x18, 0x60, 0x06, 0x18, 0x18, 0xC0, 0x00, 0xE3, 0x00, 0x03, 0x80, 0xC0, 0x30, 0xC0, 0x00, 0x60, 0x18, 0x60, 0x06, 0x18, 0x18, 0xC0, 0x00, 0xE3, 0x00, 0x03, 0x80, 0xC0, 0x30, 0xC0, 0x00, 0x60, 0x18, 0x60, 0x06, 0x1C, 0x18, 0xE0, 0x00, 0xE3, 0x00, 0x03, 0x80, 0xC0, 0x30, 0xC0, 0x00, 0x60, 0x18, 0x60, 0x06, 0x0F, 0xFF, 0xE0, 0x00, 0xE3, 0x80, 0x03, 0x80, 0xC0, 0x30, 0xE0, 0x00, 0x60, 0x18, 0x60, 0x06, 0x03, 0xFE, 0x60, 0x00, 0xE1, 0x80, 0x03, 0x80, 0xC0, 0x30, 0x60, 0x00, 0x60, 0x18, 0x70, 0x06, 0x00, 0x18, 0x7F, 0xF0, 0xE1, 0xFF, 0xC3, 0x80, 0xC0, 0x30, 0x7F, 0xF0, 0x60, 0x18, 0x3E, 0x06, 0x00, 0x18, 0x1F, 0xE0, 0x40, 0x7F, 0x83, 0x80, 0xC0, 0x30, 0x1F, 0xE0, 0x60, 0x18, 0x1E, 0x06, 0x00, 0x18}; typedef enum direction_t { RIGHT = 0x00, LEFT = 0xFF }; volatile direction_t lastWindowDirection = LEFT; volatile int8_t encoderPosition = 0; // don't know where is our encoder, we'll need an absolute reference // let's assume we are in closed position and window opens from left to right volatile bool encoderChangePending = false; unsigned long lastLeftSwitchDebounceTime = 0; // the last time the input left encoder pin was toggled unsigned long debounceDelay = 50; // the debounce time void isrFallingLeftSwitchPort(); // ISR for leftSwitchPort void isrChangeOpenClosedSwitchPort(); // ISR for open/close switches void drawLogo(void); // draws logo_bmp centered on the OLED Display void setup() { Serial.begin(9600); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 Serial.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } // set port switches with internal 20K pull up resistors pinMode(leftSwitchPort, INPUT_PULLUP); pinMode(rightSwitchPort, INPUT_PULLUP); pinMode(openClosedSwitchPort, INPUT_PULLUP); // set debugging LEDs ports pinMode(leftLedPort, OUTPUT); pinMode(rightLedPort, OUTPUT); // After reset blink the logo display.clearDisplay(); drawLogo(); // Invert and restore display, pausing in-between display.invertDisplay(true); delay(1000); display.invertDisplay(false); delay(1000); // detect falling edges the switch chages from open to closed. It is negative // logic attachInterrupt(digitalPinToInterrupt(leftSwitchPort), isrFallingLeftSwitchPort, FALLING); // detect switch chages from open to closed. It is negative logic attachInterrupt(digitalPinToInterrupt(openClosedSwitchPort), isrChangeOpenClosedSwitchPort, CHANGE); } void loop() { // if there is a new event from the encoder acknowledge it and do pending // actions if (encoderChangePending) { encoderChangePending = false; // turn on left led when window direction is LEFT digitalWrite(leftLedPort, lastWindowDirection == LEFT); // and turn on right led when window direction is RIGHT digitalWrite(rightLedPort, lastWindowDirection == RIGHT); // scroll the logo according direction if (lastWindowDirection == RIGHT) { display.startscrollright(0x00, 0x07); } else { display.startscrollleft(0x00, 0x07); } // log encoder position to serial port Serial.println(encoderPosition); } } /** Interrupt Service Routine for Falling Edge in Left Switch Port */ void isrFallingLeftSwitchPort() { if ((millis() - lastLeftSwitchDebounceTime) > debounceDelay) { if (digitalRead(rightSwitchPort) == HIGH) { encoderPosition++; lastWindowDirection = RIGHT; } else { encoderPosition--; lastWindowDirection = LEFT; } lastLeftSwitchDebounceTime = millis(); encoderChangePending = true; } } /** Interrupt Service Routine for Changing Edge in Open Close Switches when the window is closed light the two LEDS */ void isrChangeOpenClosedSwitchPort() { int switchState = !digitalRead(openClosedSwitchPort); if ((millis() - lastLeftSwitchDebounceTime) > debounceDelay) { // Light LEDs when window closed digitalWrite(leftLedPort, switchState); digitalWrite(rightLedPort, switchState); if (switchState) { display.stopscroll(); } lastLeftSwitchDebounceTime = millis(); } } /** Send logo to the Display and center it then wait 1 second */ void drawLogo(void) { display.clearDisplay(); display.drawBitmap((display.width() - LOGO_WIDTH) / 2, (display.height() - LOGO_HEIGHT) / 2, logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1); display.display(); delay(1000); }
Next Steps
- Next week we will continue to play with our new toy.
- We will try to get the positioning of the window using only the accelerometer and the magnetic reed switch used to detect that the window is closed as described in our second blog VenTTracker #02 - Analyzing window types.. The linear position encoder will be used to automatically adjust the new version of the sensor and then to calculate the error of our sensor.
<< Previous VenTTracker Blog | Next VenTTracker Blog >> |
---|---|
VenTTracker #06 - Window Sensor Prototype | VenTTracker #08 - Trying to make a measuring device with the accelerometer |