Created
December 13, 2020 10:25
-
-
Save andrew-wilkes/b131bdc4cbdc9c8be817fdb5a0745fcf to your computer and use it in GitHub Desktop.
Arduino Cycling Computer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Bicyle computer Arduino Uno code | |
* Author: Andrew M Wilkes | |
* 2016-08-12 | |
*/ | |
/* | |
* Notes | |
* Switch/sensor debounce is implemented by assigning an INT value to the switch variable that is set on the initial falling edge of the switch. | |
* The switch action is then ignored until the value has been counted down to zero. | |
* A value of 1 is used to judge the time of the rising edge of the reset switch i.e. the switch was released. Most actions only care about when a switch is pressed. | |
*/ | |
// Set up the IO pins | |
#define DISPLAY_CLK 6 | |
#define DISPLAY_DIO 7 | |
#define RESET_SW 4 | |
#define MODE_SW 5 | |
#define SPEED_SENSOR 2 | |
#define CADENCE_SENSOR 3 | |
#define SLOPE_SENSOR 0 | |
#define TEMPERATURE_SENSOR 1 | |
// Set up timing values | |
#define LOOP_DELAY_MS 100 // The basic unit of timing periods | |
#define ONE_SECOND 10 // The number of loops in 1s | |
#define REPORT_PERIOD 5 | |
#define DEBOUNCE_PERIOD 5 | |
#define RESET_PERIOD 20 | |
#define ALERT_TIME 10 | |
#define BLINK_PERIOD 5 | |
// We are using a 4-segment LED digit display with a colon in the middle. It uses a driver library and serial comms. | |
#include "TM1637.h" | |
TM1637 tm1637(DISPLAY_CLK, DISPLAY_DIO); | |
// Set up our global variables | |
int state = 0; | |
int speed = 0; | |
int slope = 0; | |
int temp = 0; | |
// Timers | |
int modeTimer = 0; | |
int pauseTimer = 0; | |
int resetTimer = 0; | |
int alertTimer = 0; | |
int blinkTimer = 0; | |
int reportTimer = 0; | |
// State of what is being displayed on the LED digits | |
int mode = 0; // Display off | |
// Time-related vars | |
int tick = 0; | |
int secs = 0; | |
int mins = 0; | |
int hours = 0; | |
int distance = 0; | |
// Set up constant display chrs for mode change alerts | |
int8_t mode1[] = {5,18,12,10}; // Speed, cadence (SP:CA) | |
int8_t mode2[] = {16,17,16,12}; // Slope temperature (%:'C) !! Need to hard code 'F if American and change the calibration factors | |
boolean logging = true; | |
boolean colon = true; | |
// Volatile type variables that are used by interrupt service routines that can alter their value at any time in the program flow | |
volatile int wheelSw = 0; | |
volatile int crankSw = 0; | |
void setup() | |
{ | |
Serial.begin(9600); | |
pinMode(RESET_SW, INPUT_PULLUP); | |
pinMode(MODE_SW, INPUT_PULLUP); | |
pinMode(SPEED_SENSOR, INPUT_PULLUP); | |
pinMode(CADENCE_SENSOR, INPUT_PULLUP); | |
pinMode(LED_BUILTIN, OUTPUT); | |
attachInterrupt(digitalPinToInterrupt(SPEED_SENSOR), speedIsr, FALLING); | |
attachInterrupt(digitalPinToInterrupt(CADENCE_SENSOR), cadenceIsr, FALLING); | |
tm1637.init(); | |
tm1637.set(BRIGHT_TYPICAL);//BRIGHT_TYPICAL = 2,BRIGHT_DARKEST = 0,BRIGHTEST = 7; | |
} | |
void loop() | |
{ | |
// Timing updates | |
if (alertTimer) alertTimer--; // alertTimer == true if > 0 This fact is used later in the mode switch statement code for simplicity | |
if (DEBOUNCE_PERIOD == wheelSw) | |
{ | |
speed++; | |
} | |
if (wheelSw > 0) wheelSw--; | |
if (DEBOUNCE_PERIOD == crankSw) | |
{ | |
speed--; | |
} | |
if (crankSw > 0) crankSw--; | |
// Logging or paused state operations | |
if (logging) // Perform updates | |
{ | |
updateTime(); | |
distance++; | |
colon = true; | |
} | |
else // Blink colon | |
{ | |
if (--blinkTimer <= 0) // Ensure that the colon goes off soon after entering Pause | |
{ | |
blinkTimer = BLINK_PERIOD; | |
colon = ! colon; | |
} | |
} | |
// Items to continuously monitor even when paused | |
slope = analogRead(SLOPE_SENSOR); | |
temp = analogRead(TEMPERATURE_SENSOR); | |
// Update the LED display | |
if (mode > 0) // Display is active | |
{ | |
switch(mode) | |
{ | |
case 1: | |
// Speed : Cadence | |
tm1637.point(colon); | |
if (alertTimer) | |
tm1637.display(mode1); | |
else | |
tm1637.display(mode1); | |
break; | |
case 2: | |
// Slope : Temperature | |
tm1637.point(colon); | |
if (alertTimer) | |
tm1637.display(mode2); | |
else | |
tm1637.display(mode2); | |
break; | |
case 3: | |
// Distance | |
tm1637.point(!colon); | |
displayDistance(); | |
break; | |
case 4: | |
// time | |
tm1637.point(colon); | |
displayTime(); | |
break; | |
} | |
} | |
// Handle the Mode switch | |
if (0 == modeTimer && switchPressed(MODE_SW)) | |
{ | |
modeTimer = DEBOUNCE_PERIOD; | |
// Change mode | |
mode++; | |
if (mode > 0) | |
displayIsActive = true; | |
if (mode > 4) mode = 1; | |
alertTimer = ALERT_TIME; | |
} | |
else | |
{ | |
if (modeTimer > 0) modeTimer--; | |
} | |
// Handle the Reset/pause switch | |
if (switchPressed(RESET_SW)) | |
{ | |
if (0 == resetTimer) | |
{ | |
pauseTimer = DEBOUNCE_PERIOD; | |
resetTimer = RESET_PERIOD; | |
} | |
if (1 == resetTimer) | |
{ | |
// Reset | |
tm1637.clearDisplay(); | |
resetTimer = RESET_PERIOD; | |
tick = 0; | |
secs = 0; | |
mins = 0; | |
hours = 0; | |
distance = 0; | |
mode = 1; | |
} | |
} | |
else | |
{ | |
if (pauseTimer == 1) | |
{ | |
resetTimer = 0; | |
// Stop or pause | |
logging = ! logging; | |
blinkTimer = 0; | |
} | |
} | |
if (pauseTimer > 0) pauseTimer--; | |
if (resetTimer > 0) resetTimer--; | |
// Handle serial IO reports | |
reportTimer++; | |
if (REPORT_PERIOD == reportTimer) | |
{ | |
reportTimer = 0; | |
Serial.println("Speed:" + String(speed, DEC) + " Slope: " + String(slope, DEC) + " Temp: " + String(temp, DEC)); | |
// Add a report of being paused | |
} | |
delay(LOOP_DELAY_MS); | |
} | |
int switchPressed(int sw) | |
{ | |
return (LOW == digitalRead(sw)); | |
} | |
void speedIsr() | |
{ | |
if (0 == wheelSw) wheelSw = DEBOUNCE_PERIOD; | |
} | |
void cadenceIsr() | |
{ | |
if (0 == crankSw) crankSw = DEBOUNCE_PERIOD; | |
} | |
void updateTime() | |
{ | |
tick++; | |
if (ONE_SECOND == tick) | |
{ | |
tick = 0; | |
secs++; | |
if (60 == secs) | |
{ | |
secs = 0; | |
mins++; | |
if (60 == mins) | |
{ | |
mins = 0; | |
hours++; | |
if (hours > 99) | |
hours = 0; | |
} | |
} | |
} | |
} | |
void displayTime() | |
{ | |
int leftTime = mins; | |
int rightTime = secs; | |
if (hours > 0) | |
{ | |
leftTime = hours; | |
rightTime = mins; | |
} | |
tm1637.display(0, leftTime / 10); | |
tm1637.display(1, leftTime % 10); | |
tm1637.display(2, rightTime / 10); | |
tm1637.display(3, rightTime % 10); | |
} | |
void displayDistance() | |
{ | |
int thousands = distance / 1000; | |
int hundreds = (distance - thousands * 1000) / 100; // or = distance % 1000 / 100 maybe? | |
int tens = (distance - hundreds * 100) / 10; | |
// Don't display leading zeros | |
if (0 == thousands) | |
{ | |
thousands = 0x7f; | |
if (0 == hundreds) | |
{ | |
hundreds = 0x7f; | |
if (0 == tens) tens = 0x7f; | |
} | |
} | |
tm1637.display(0, thousands); | |
tm1637.display(1, hundreds); | |
tm1637.display(2, tens); | |
tm1637.display(3, distance % 10); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment