April 2023
Microswitch ..
Adafruit Product #1270 1.2" tall, 4 digit, 7 segment, display. Communication via I2C (address of 0x70 to 0x77, selectable with jumpers). It prefers 5V power and 5V logic. Must set the jumper between the backpack 'IO' pin and +5V to indicate I2C will be 5V logic. My notes on how to use the display.
At full brightness, I measured a maximum current consumption of less than 31 mA (spikes may be higher). The Arduino UNO 5V connection can provide up to 400 mA. 31 mA is too much for a digital output to be used to supply power to the display.
After testing some IR emitter and receivers, the Adafruit 3 mm IR Break Beam Sensor allows the emitter and receiver was chosen because of its robust performance in ambient light, and each pair of emitter / detector was insensitive to a nearby pair. The emitter/detector pairs may be separated up to 25 cm (10 inches) apart. Placing the emitter below in the track and then sensing above the track with the detector provides the best isolation from ambient light noise. The signal from the detector is LOW or 0 VDC when the emitter is not detected, and approximately 2.0 VDC (HIGH) when detected. This works well with Arduino 5V digital input logic.
Power of 5 VDC and at least 500 mA is supplied to the Arduino Uno. The 5V output from the Arduino provides power to the display, IR emitter, and the IR detector.
The display communicates with the Arduino via I2C on pins A4/SDA and A5/SCL.
Each IR detector has a 10k ohm pullup resistor. This causes the signal to the Arduino to be LOW when IR is not detected, and HIGH when IR is detected. The IR detector will be HIGH when unobstructed, and then LOW (briefly) when a car passes between the IR emitter and IR detector.
The microswitch common terminal is connected to 5 VDC, and the other normally open (NO) contact is connected to a 10k ohm pulldown resistor. The digital input to the Arduino on D6 sees 0 VDC when the switch is open (or LOW), and +5 VDC when the switch is closed. The resistor keeps the current limited to 0.5 mA (I = V/R = 5.0/10000 = 0.005 A).
/*
proj_BSA_Pinewood_Derby.ino
Digital inputs:
Adafruit 3 mm IR Break Beam Sensor PN 2167
Microswitch
3x of Adafruit Product #1270 1.2" tall, 4 digit, 7 segment, display
In Arduino IDE:
Set board to 'Arduino Uno'
Set programmer to 'AVRISP mkii'
Set COM port
*/
/////////////////////////////////////////////////////////////////////////
// Built in LED on D13
const uint8_t pinBuiltInLED = 13;
/////////////////////////////////////////////////////////////////////////
// Adafruit #1270 1.2" tall, 4 digit, 7 segment, display
// Download the "Adafruit LED Backpack" library and the "Adafruit GFX library" from the Arduino library manager.
#include <Wire.h> // Enable this line if using Arduino Uno, Mega, etc.
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
Adafruit_7segment matrix1 = Adafruit_7segment();
Adafruit_7segment matrix2 = Adafruit_7segment();
Adafruit_7segment matrix3 = Adafruit_7segment();
void MatrixTest(Adafruit_7segment matrix){
// A quick test that the 7-segment LED is working.
matrix.clear(); matrix.writeDisplay(); // Clear the display (includes colon)
uint8_t counter = 0;
for (uint8_t d=0; d<25; d++) {
// paint one LED per row. The HT16K33 internal memory looks like
// a 8x16 bit matrix (8 rows, 16 columns)
for (uint8_t i=0; i<8; i++) {
// draw a diagonal row of pixels
matrix.displaybuffer[i] = _BV((counter+i) % 16) | _BV((counter+i+8) % 16) ;
}
// write the changes we just made to the display
matrix.writeDisplay();
delay(100);
counter++;
if (counter >= 16) counter = 0;
delay(1);
}
matrix.clear(); matrix.writeDisplay(); // Clear the display (includes colon)
} // MatrixTest()
/////////////////////////////////////////////////////////////////////////
// Derby specific hardware
const uint8_t PIN_IR_LANE1 = 12;
const uint8_t PIN_IR_LANE2 = 10;
const uint8_t PIN_IR_LANE3 = 8;
const uint8_t PIN_MICROSW = 6;
uint8_t state_microsw = LOW;
uint8_t state_microsw_last = LOW;
uint8_t state_microsw_boot = LOW;
uint8_t mode_derby = 0;
// 0 = boot / reset
// 1 = test mode
// 2 = awaiting race start
// 3 = race underway
// 4 = race over
uint8_t place_lane1 = 0;
uint8_t place_lane2 = 0;
uint8_t place_lane3 = 0;
uint8_t last_place = 0;
uint32_t race_timer_ms = 0;
uint32_t lane1_timer_s = 0;
uint32_t lane2_timer_s = 0;
uint32_t lane3_timer_s = 0;
void minSecDigits(uint8_t num, uint8_t* tens, uint8_t* ones) {
// Breaks num into two digits where tens represents the
// value divided by ten, and ones is the remainder.
// Used for a matrix display.
// Ex. minSecDigits(9, tens, ones); tens = 0, ones = 9
// Ex. minSecDigits(21, tens, ones); tens = 2, ones = 1
uint8_t dig1 = static_cast<uint8_t>(num / 10);
if (dig1 > 0) {
*ones = num - (dig1 * 10);
} else {
*ones = num;
}
*tens = dig1;
} // minSecDigits()
void TestMode(uint8_t state_lane1, uint8_t state_lane2, uint8_t state_lane3) {
matrix1.clear();
matrix1.drawColon(false);
matrix1.writeDigitNum(1, state_lane1, false);
matrix1.writeDisplay();
matrix2.clear();
matrix2.drawColon(false);
matrix2.writeDigitNum(1, state_lane2, false);
matrix2.writeDisplay();
matrix3.clear();
matrix3.drawColon(false);
matrix3.writeDigitNum(1, state_lane3, false);
matrix3.writeDisplay();
} // TestMode()
void RaceStartWait(Adafruit_7segment matrix) {
matrix.print(12345, DEC); // "----"
matrix.writeDisplay();
} // RaceStartWait()
void RaceStarted(Adafruit_7segment matrix) {
matrix.clear(); matrix.writeDisplay(); // Clear the display
matrix.blinkRate(1); // fast blink
matrix.drawColon(true); matrix.writeDisplay();
} // RaceStarted()
void CarAtFinish(Adafruit_7segment matrix, uint8_t place) {
matrix.writeDigitNum(1, place, false);
matrix.blinkRate(0); // stop blinking
matrix.drawColon(false);
matrix.writeDisplay();
} // TestMode()
void DisplayRaceTime(uint32_t lane_time_s, Adafruit_7segment matrix) {
matrix.clear();
uint8_t race_min = static_cast<uint8_t>(lane_time_s / 60);
//OR: uint8_t race_min = (uint8_t)(lane_time_s / 60);
uint8_t race_sec = 0;
//Serial.print(lane_time_s); Serial.print(" s = \t"); Serial.print(race_min); Serial.print(" min\t"); Serial.print(race_sec); Serial.println(" sec");
if (race_min > 0) {
race_sec = lane_time_s - (race_min * 60);
} else {
race_sec = lane_time_s;
}
//Serial.print(lane_time_s); Serial.print(" s = \t"); Serial.print(race_min); Serial.print(" min\t"); Serial.print(race_sec); Serial.println(" sec");
// Update the seconds on the display
uint8_t tens = 0;
uint8_t ones = 0;
// minSecDigits(uint8_t num, &uint8_t tens, &uint8_t ones)
minSecDigits(race_sec, &tens, &ones);
//Serial.print(race_sec); Serial.print("\t"); Serial.print(tens); Serial.print("\t"); Serial.print(ones); Serial.println("\n");
matrix.writeDigitNum(3, tens, false);
matrix.writeDigitNum(4, ones, false);
// Update the minutes on the display
tens = 0; ones = 0;
minSecDigits(race_min, &tens, &ones);
matrix.writeDigitNum(0, tens, false);
matrix.writeDigitNum(1, ones, false);
matrix.drawColon(true);
matrix.writeDisplay();
} // DisplayRaceTime()
/////////////////////////////////////////////////////////////////////////
void setup() {
pinMode(pinBuiltInLED, OUTPUT);
Serial.begin(9600);
while (!Serial) {
digitalWrite(pinBuiltInLED, HIGH);
delay(1);
}
digitalWrite(pinBuiltInLED, LOW);
Serial.println("\nSerial ready");
pinMode(PIN_IR_LANE1, INPUT);
pinMode(PIN_IR_LANE2, INPUT);
pinMode(PIN_IR_LANE3, INPUT);
pinMode(PIN_MICROSW, INPUT);
// Adafruit #1270 1.2" tall, 4 digit, 7 segment, display
// Set the address below according to the A0, A1, A2 jumpers on the
// back of the display.
matrix1.begin(0x70); // Initialize the display
matrix2.begin(0x71); // Initialize the display
matrix3.begin(0x73); // Initialize the display
// Get the state of PIN_MICROSW during boot
state_microsw_boot = digitalRead(PIN_MICROSW);
// Power On / Reset
//MatrixTest(matrix1); // Show a test pattern
//MatrixTest(matrix2); // Show a test pattern
//MatrixTest(matrix3); // Show a test pattern
Serial.println("Setup complete\n");
} // setup()
void loop() {
uint8_t state_lane1 = digitalRead(PIN_IR_LANE1);
uint8_t state_lane2 = digitalRead(PIN_IR_LANE2);
uint8_t state_lane3 = digitalRead(PIN_IR_LANE3);
if (mode_derby == 0 && state_microsw_boot == HIGH) {
mode_derby = 1; // test mode
Serial.println("Test mode");
} else if (mode_derby == 0 && state_microsw_boot == LOW) {
if (state_lane1 == HIGH && state_lane2 == HIGH && state_lane3 == HIGH) {
// None of the IR emitter/detector lanes are obstructed.
mode_derby = 2; // awaiting race start
Serial.println("Awaiting race start");
RaceStartWait(matrix1);
RaceStartWait(matrix2);
RaceStartWait(matrix3);
} else {
// One or more of the IR emitter/detector lanes is obstructed. Go into Test Mode.
mode_derby = 1; // test mode
Serial.println("IR ERROR - now in test mode");
}
}
if (mode_derby == 1) {
// test mode (it will not get out of test mode until restart)
TestMode(state_lane1, state_lane2, state_lane3);
} // test mode
state_microsw = digitalRead(PIN_MICROSW);
// Detect a change in the state of the microswitch (LOW to HIGH)
if (state_microsw != state_microsw_last && state_microsw == HIGH) {
// state_microsw == HIGH && state_microsw_last == LOW
state_microsw_last = HIGH;
}
if (mode_derby == 2 && state_microsw == HIGH) {
// race underway
race_timer_ms = millis();
mode_derby = 3; // race underway
RaceStarted(matrix1);
RaceStarted(matrix2);
RaceStarted(matrix3);
}
if (mode_derby == 3) {
// race underway
digitalWrite(pinBuiltInLED, HIGH);
if (place_lane1 > 0 && place_lane2 > 0 && place_lane3 > 0) {
mode_derby = 4; // race over
}
if (last_place >= 3) {
mode_derby = 4; // race over
}
if (place_lane1 == 0 && state_lane1 == LOW) {
// A car has briefly obstructed the IR emitter/detector at Lane #1
last_place++;
place_lane1 = last_place;
lane1_timer_s = (millis() - race_timer_ms) / 1000;
//Serial.print("\tplace_lane1: "); Serial.print(place_lane1); Serial.print("\tTime [s]: "); Serial.println(lane1_timer_s);
CarAtFinish(matrix1, place_lane1);
}
if (place_lane2 == 0 && state_lane2 == LOW) {
// A car has briefly obstructed the IR emitter/detector at Lane #2
last_place++;
place_lane2 = last_place;
lane2_timer_s = (millis() - race_timer_ms) / 1000;
//Serial.print("\tplace_lane2: "); Serial.print(place_lane2); Serial.print("\tTime [s]: "); Serial.println(lane2_timer_s);
CarAtFinish(matrix2, place_lane2);
}
if (place_lane3 == 0 && state_lane3 == LOW) {
// A car has briefly obstructed the IR emitter/detector at Lane #3
last_place++;
place_lane3 = last_place;
lane3_timer_s = (millis() - race_timer_ms) / 1000;
//Serial.print("\tplace_lane3: "); Serial.print(place_lane3); Serial.print("\tTime [s]: "); Serial.println(lane3_timer_s);
CarAtFinish(matrix3, place_lane3);
}
}
if (mode_derby == 4) {
// Race over
digitalWrite(pinBuiltInLED, LOW);
// Show race time
DisplayRaceTime(lane1_timer_s, matrix1);
DisplayRaceTime(lane2_timer_s, matrix2);
DisplayRaceTime(lane3_timer_s, matrix3);
while (true) {}
}
} // loop()
Do you need help developing or customizing a IoT product for your needs? Send me an email requesting a free one hour phone / web share consultation.
The information presented on this website is for the author's use only. Use of this information by anyone other than the author is offered as guidelines and non-professional advice only. No liability is assumed by the author or this web site.