Skip to content

Instantly share code, notes, and snippets.

@andrew-wilkes
Created December 13, 2020 10:25
Show Gist options
  • Save andrew-wilkes/b131bdc4cbdc9c8be817fdb5a0745fcf to your computer and use it in GitHub Desktop.
Save andrew-wilkes/b131bdc4cbdc9c8be817fdb5a0745fcf to your computer and use it in GitHub Desktop.
Arduino Cycling Computer
/*
* 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