element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Members
    Members
    • Achievement Levels
    • Benefits of Membership
    • Feedback and Support
    • Members Area
    • Personal Blogs
    • What's New on element14
  • Learn
    Learn
    • eBooks
    • Learning Center
    • Learning Groups
    • STEM Academy
    • Webinars, Training and Events
  • Technologies
    Technologies
    • 3D Printing
    • Experts & Guidance
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Arduino Projects
    • Design Challenges
    • element14 presents
    • Project14
    • Project Groups
    • Raspberry Pi Projects
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Or choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
Design For A Cause 2021
  • Challenges & Projects
  • Design Challenges
  • Design For A Cause 2021
  • More
  • Cancel
Design For A Cause 2021
Blog VenTTracker #07 - Adjusting the window sensor
  • Blog
  • Forum
  • Documents
  • Polls
  • Files
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Blog Post Actions
  • Subscribe by email
  • More
  • Cancel
  • Share
  • Subscribe by email
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: javagoza
  • Date Created: 18 Apr 2021 4:47 PM Date Created
  • Views 620 views
  • Likes 6 likes
  • Comments 0 comments
  • design_challenge
  • venttracker
  • covid-19
  • window_sensor
  • arduino nano 33 iot
  • arduino
  • design_for_a_cause_2021
  • natural ventilation
Related
Recommended

VenTTracker #07 - Adjusting the window sensor

javagoza
javagoza
18 Apr 2021

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.

 

image

  Design for A Cause 2021 the VenTTracker project

  01 - Intro and motivation. | 02 - Analyzing Window Types (I) | 03 - Analyzing Window Types (II) | 04 - Playing with the IMU | 05-Sliding Window Mock-up | 06 -  Window Sensor Prototype | 07 - Adjusting the window sensor | 08 - Trying to make a measuring device with the accelerometer | 09 - Checking and updating WiFiNINA Firmware | 10 - Ventilation Monitor on Arduino IoT Cloud | 11 - Wireless Environmental Monitor on Arduino Nano 33 IoT | 12 - Window Anomaly Detection. Edge Impulse & Arduino Nano 33 IoT | 14 - Environmental Monitor Revisited - Version 2 | 13 - Project wrap-up and Lessons learned

 

Tracking System for Classroom Ventilation Routines

A STEM project for classrooms

 

image

 

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.

 

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

Visualizing the degree of openness

 

When the sensor detects that the window is closed it displays the logo of our design challenge, :

 

image

 

The window has been opened. The progress bar shows an approximate 25% open.

image

We continue to open the window halfway. The progress bar reaches the middle indicating an approximate 50% open.

image

Once the second magnet has passed, we are already at approximately 75% opening.

image

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:

  1. Closed window
  2. Window open below 33% open
  3. Open window between 33% and 66% open
  4. and open window between 66% and 100% open.

image

 

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.

 

image

 

When the sensor is started, the  design challenge slogan and logo are displayed on the screen.

image

 

image

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.

image

After drilling the moving part and checking that everything fits without friction, we reassemble the model.

image

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.

image

The battery fits well in its bed.

image

To prevent movement will be secured with a rubber band.

image

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.

image

The sensor is now ready for the first tests.

Finally we tested the prototype already assembled in the model.

 

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

 

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.

 

VenTTracker Blogs
VenTTracker #01 - Intro and motivation.
VenTTracker #02 - Analyzing window types.
VenTTracker #03 - Analyzing window types II
VenTTracker #04 - Playing with the IMU
VenTTracker #05 - Sliding Window Mock-up
VenTTracker #06 - Window Sensor Prototype
VenTTracker #07 - Adjusting the window sensor
VenTTracker #08 - Trying to make a measuring device with the accelerometer
VenTTracker #09 - Checking and updating WiFiNINA Firmware
VenTTracker #10 - Ventilation Monitor on Arduino IoT Cloud
VenTTracker #11 - Wireless Environmental Monitor on Arduino Nano 33 IoT
VenTTracker #12 - Window Anomaly Detection. Edge Impulse & Arduino Nano 33 IoT
VenTTracker #14 (aka #11 bis) - Environmental Monitor Revisited - Version 2
VenTTracker #13 - Project wrap-up and Lessons learned

 

 

<< Previous VenTTracker BlogNext VenTTracker Blog  >>
VenTTracker #06 -  Window Sensor PrototypeVenTTracker #08 - Trying to make a measuring device with the accelerometer
  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2023 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube