One of the main components of our ventilation routine monitoring project is the sensor for the status of doors, windows and blinds. During this week we have given a boost to the creation of that sensor. The blog describes the work of these days consolidating a simple design and the construction of a first prototype that will allow us to fine-tune our software and start taking data to develop a machine learning model. We have equipped the device with several sensors that will help us to automatically label the samples for the training of our machine learning models.
Tracking System for Classroom Ventilation Routines
A STEM project for classrooms
the VenTTracker project - Blog #06 - Window Sensor Prototype
Our first window sensor protoype:
The Design
What are we building?
We are building a Wireless door and window opening sensor. This is an IOT device that can be placed on windows, shutters, blinds and doors to be able to keep track of whether they are open or not and in what position they are: Fully open, semi-open, closed ... Our ventilation routines monitor system will use several of these devices. The device uses an Arduino Nano 33 IoT. It can estimate the degree of opening of the window. To obtain an origin reference that marks the closed position and the full opened position, it uses reed switches. The device can be adapted to multiple types of windows, blinds, shutters and doors.
Changes from previous versions
This revision of our sensor:
- Add two reed switches to detect open and close window events and acts as an absolute reference for the linear position encoder and the accelerometer.
- Maintains the magnetic linear encoder made with another two reed switches.
- All the switches are attached to ports that support interruptions to allow us to work with multithreading.
- All the switches use Internal 20K pull-ups. The logic of the switches in our design is negative.
- Add an OLED Display for debugging purposes.
- Add a reset button.
- It is built on a pad-board.
The schematic
Components
- C1: 100nF
- C2: 10uF
- DC1: DC005-2.0MM
- DP1: OLED DISPLAY 128x32 0.91" I2C SSD1306
- H1: HDR-F-2.54_1x2
- 2x16 Female Header
- J2: HDR-M-2.54_1x4
- LED1,LED2: LED-TH-3mm_G
- R1,R2: 100 Ohm
- S1,S2,S3,S4: Plastic Reed Switch Reed Contact Normally Open (N/O)
- SW1 Push Button - 6x6mm - 2 pins
Bill of Materials
My apologies that the product descriptions are in Spanish. I am a Farnell Spain customer and they decide for me. I can't change the language or send the BOM list to Farnell UK those are the problems of such smart websites . As the news say "the descriptions are in Spanish due to a computer error" Software developers like me are always the problem!
Product Name | Manufacturer | Quantity | Buy KitBuy Kit |
---|---|---|---|
MPCC100V104KY5U-CAPACITOR, 0.1UF, 100V, 10%, Y5U, RAD | MULTICOMP PRO | 1 | Buy NowBuy Now |
ECA2GHG100-Condensador Electrolítico, 10 µF, 400 V, Serie NHG, ± 20%, Con Conexión Radial, 1000 horas a 105°C | PANASONIC | 1 | Buy NowBuy Now |
MACD-14-15-20-REED SWITCH, SPST-NO, 0.5A, 200V, AXIAL | LITTELFUSE | 4 | Buy NowBuy Now |
CES-116-01-T-S-Conector Hembra para PCB, Simple, Placa a Placa, 2.54 mm, 1 Filas, 16 Contactos | SAMTEC | 2 | Buy NowBuy Now |
CES-102-01-T-S-Conector Hembra para PCB, Simple, Placa a Placa, 2.54 mm, 1 Filas, 2 Contactos | SAMTEC | 1 | Buy NowBuy Now |
ABX00032-PLACA NANO 33 IOT, CONECTOR DE PINES | ARDUINO | 1 | Buy NowBuy Now |
CES-104-01-T-S-Conector Hembra para PCB, Simple, Placa a Placa, 2.54 mm, 1 Filas, 4 Contactos | SAMTEC | 1 | Buy NowBuy Now |
HLMP-1401-LED, Amarillo, Agujero Pasante, T-1 (3mm), 10 mA, 2 V, 585 nm | BROADCOM | 2 | Buy NowBuy Now |
MCF 0.25W 100R-Resistencia de Agujero Pasante, 100 ohm, Serie MCF, 250 mW, ± 5%, Axial, 250 V | MULTICOMP PRO | 2 | Buy NowBuy Now |
LS-00003-Product Range:- | OSEPP | 1 | Buy NowBuy Now |
Pantalla OLED Gráfica, 128 x 32 Píxeles, Blanco sobre Negro, 3 V, I2C, 35,8mm x 12mm, -40 °C | MIDAS | 1 | Buy NowBuy Now |
Battery Clip | KEYSTONE | 2 | Buy NowBuy Now |
9V Battery Snap Connector | MCM | 1 | Buy NowBuy Now |
Additional Parts
Product Name | Manufacturer | Quantity |
---|---|---|
PCB | 1 |
Software debounce
As we have seen in a previous blog we will need a low pass filter to avoid bouncing on the switches. See VenTTracker #03 - Analyzing window types II
Bouncing is due to mechanical vibration in the sliding motion of the window. This vibratory movement that can move the magnet towards and away from the switch reeds, producing unwanted transitions.
We are using a microcontroller so we are going to use "software debounce" technique for our switches.
The microcontroller we are using include internal pullups and a simple interrupt driven code can look for a stable change of state.
This is why it is very important that our sensors are connected to ports that do support the programming of interrupt services routines.
Pad-board prototype
Before going to PCB let's make a pad-board prototype. For this prototype we are using a 9v battery.
Connections
Assembling our first prototype
This are the minimum configuration for our sensor. Nothing else is necessary, the rest of the components are to facilitate debugging and sampling with automatic labeling.
Checking arduino headers
Checking the two encoder magnetic reed switches
Left and Right Open/Closed Detection Magnetic Reed Sensors and two headers for the display.
Bottom view
Checking Display and IMU/Display relative orientation. We use the developed for the 04 blog: VenTTracker #04 - Playing with the IMU
Everything works!
Window Sensor Images
Let's add some standoffs and put a transparent protective cover
Right View: You can see the left magnetic reed switch used for detecting open/close events in windows where the magnet is on the right side of the sensor.
You can see the 12v battery under the Arduino Nano 33 IoT between the two headers.
Left View: You can see the left magnetic reed switch used for detecting open/close events in windows where the magnet is on the left side of the sensor.
A close view:
Placing the sensor in our Sliding Window Mockup
Our Venttracker Window Sensor Prototype Assembled On the Sliding Window Mock-up: VenTTracker #05 - Sliding Window Mock-up
Adding a reset button and some LEDs
The reset button is not accessible . Let's add a reset button and two indicator LEDs as in our old prototype: VenTTracker #03 - Analyzing window types II
At the bottom right the reset button.
Between the reed switches the new indicator LEDs
Enough empty space for a 12v battery. We need clips for this kind of battery.
And finally a big thank you to the entire element14 community!
The demo code
See explanation about the ISR and interrupts in the past blog: VenTTracker #03 - Analyzing window types II
/************************************************************************** This is an example for testting 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 | Author: Enrique Albertos Date: 2021-04-10 **************************************************************************/ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.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 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 and 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); }
Future improvements
For the next revision we will consider:
- make a PCB and a case.
- replace the reed switch sensor by Hall Digital Switches like A1120LUA-T
Next Steps
- Start taking and labeling samples and design a ML model.
- Continue with the design of our Indoor Air Quality Desktop Monitor.
<< Previous VenTTracker Blog | Next VenTTracker Blog >> |
---|---|
VenTTracker #05 - Sliding Window Mock-up | VenTTracker #07 - Adjusting the window sensor |
Top Comments