// display stuff
#include <Arduino.h>
#include <SoftwareSerial.h>
#define USE_SERIAL_2004_LCD
#include "LCDBigNumbers.hpp"
#define LCD_I2C_ADDRESS 0x27
#include "LiquidCrystal_I2C.h"
LiquidCrystal_I2C myLCD(LCD_I2C_ADDRESS, LCD_COLUMNS, LCD_ROWS);
// Available big number fonts are: BIG_NUMBERS_FONT_1_COLUMN_2_ROWS_VARIANT_1, BIG_NUMBERS_FONT_2_COLUMN_2_ROWS_VARIANT_1,
// BIG_NUMBERS_FONT_3_COLUMN_2_ROWS_VARIANT_1, BIG_NUMBERS_FONT_3_COLUMN_2_ROWS_VARIANT_2, BIG_NUMBERS_FONT_3_COLUMN_2_ROWS_VARIANT_3,
// BIG_NUMBERS_FONT_2_COLUMN_3_ROWS_VARIANT_1, BIG_NUMBERS_FONT_2_COLUMN_3_ROWS_VARIANT_2, BIG_NUMBERS_FONT_3_COLUMN_3_ROWS_VARIANT_1,
// BIG_NUMBERS_FONT_3_COLUMN_4_ROWS_VARIANT_1, BIG_NUMBERS_FONT_3_COLUMN_4_ROWS_VARIANT_2
LCDBigNumbers bigNumberLCD(&myLCD, BIG_NUMBERS_FONT_3_COLUMN_3_ROWS_VARIANT_1); // Use 3x4 numbers, 1. variant
// end display stuff
#define HALL_SENSOR_PIN 3
#define ENCODER_SW 6
#include "ui.h"
#include "button.h"
#include "timer.h"
#include "env.h"
#ifdef DEBUG
#define Sprint(a) (Serial.println(a))
#define Sprintln(a) (Serial.print(a))
#else
#define Sprint(a)
#define Sprintln(a)
#endif
// variables for the business logic
const long circumference = 1857;
const long hourSeconds = 3600;
float secondsElapsed;
float lastBikeSpeed;
float bikeSpeed;
unsigned long lastRead = 0;
unsigned long currentRead;
unsigned long elapsedRead;
int lastState = HIGH;
int speedDigits = 4;
int uiMode = 0;
bool menuOpen = false;
bool zeroedOut = false;
bool cursorMove = false;
bool cursorDirection = false;
unsigned long menuTimer;
int menuTimeout = 3000;
int wheelSpeedTimeout = 3000;
bool timerModeStatus = false;
bool timerButtonStatus = false;
Button timerButton(ENCODER_SW);
bool inSpeedoMode() {
return (uiMode == 0) ?true :false;
}
bool inMenuMode() {
return (uiMode == 1) ?true :false;
}
bool inTimerMode() {
return (uiMode == 2) ?true :false;
}
void setUiMode() {
switch (uiMode) {
case 0:
// speedometer
showMain();
break;
case 1:
// menu
showMenu();
break;
case 2:
// timer
timerMode();
break;
}
}
// for comparisons, not for actually showing.
int whole(float flotsam) {
int wholeInt;
wholeInt = flotsam * 100;
return wholeInt;
}
float getSpeed(float rpm) {
float kmh = (((rpm * circumference)/1000) * 60)/1000;
kmh *= 100;
kmh = round(kmh);
kmh /= 100;
return kmh;
}
void zeroOut() {
bikeSpeed = 0;
zeroedOut = true;
}
void showMain() {
myLCD.clear();
myLCD.setCursor(0,0);
myLCD.print("GP shift");
myLCD.setCursor(0,1);
myLCD.print(speedUnitName);
}
void showSpeed() {
int leftMargin = 12;
int currentSpeedDigits = 2;
int decimalPlaces = (speedUnitNumber == 2) ? 2 : 1;
lastBikeSpeed = bikeSpeed;
if (bikeSpeed != 0) {
zeroedOut = false;
}
if (bikeSpeed >= 100) {
leftMargin = 4;
currentSpeedDigits = 4;
} else if (bikeSpeed >= 10 || speedUnitNumber == 2) {
leftMargin = 8;
currentSpeedDigits = 3;
}
if (currentSpeedDigits < speedDigits) {
int spaces = (speedDigits - currentSpeedDigits);
bigNumberLCD.setBigNumberCursor(4, 1);
if (spaces = 2) {
bigNumberLCD.print(" ");
} else {
bigNumberLCD.print(" ");
}
}
speedDigits = currentSpeedDigits;
bigNumberLCD.setBigNumberCursor(leftMargin, 1);
bigNumberLCD.print(bikeSpeed, decimalPlaces);
}
void readEncoder() {
int dtValue = digitalRead(ENCODER_DT);
menuTimer = millis();
cursorMove = true;
if (dtValue == HIGH) {
cursorDirection = true;
}
if (dtValue == LOW) {
cursorDirection = false;
}
}
void setup()
{
Serial.begin(115200);
#ifdef WOKWI
Sprintln("WOKWI defined");
pinMode(HALL_SENSOR_PIN, INPUT_PULLUP);
#else
Sprintln("WOKWI not defined");
pinMode(HALL_SENSOR_PIN, INPUT);
#endif
// set up the lcd
myLCD.init();
myLCD.clear();
myLCD.backlight();
bigNumberLCD.begin();
// set up the encoder
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING);
setUiMode();
zeroOut();
}
void loop() {
int pinValue = digitalRead((HALL_SENSOR_PIN));
if (timerButton.isPressed() != timerButtonStatus) {
timerButtonStatus = timerButton.isPressed();
if (timerButton.isPressed()) {
uiMode = (uiMode == 2) ?0 :2;
setUiMode();
Sprint("Timer mode status: ");
Sprint(inTimerMode());
}
}
if (cursorMove) {
cursorMove = false;
if (!inMenuMode()) {
Sprintln("opening menu");
uiMode = 1;
setUiMode();
drawSelector();
}
redrawSelector(cursorDirection);
}
if (inMenuMode()) {
if ((millis() - menuTimer) > menuTimeout) {
Sprintln("closing menu");
uiMode = 0;
setUiMode();
showSpeed();
return;
}
}
if (!zeroedOut && !inMenuMode()) {
if (((millis() - lastRead) > wheelSpeedTimeout)) {
zeroOut();
showSpeed();
if (inTimerMode() && timerStarted) {
checkSpeed(bikeSpeed);
}
return;
}
}
if (lastState != pinValue) {
lastState = pinValue;
currentRead = millis();
if (pinValue == LOW) {
if (inTimerMode()){
if (!timerDone && !timerStarted) {
startTimer();
}
}
elapsedRead = currentRead - lastRead;
lastRead = currentRead;
secondsElapsed = elapsedRead/1000.0;
float rpm = 60/secondsElapsed;
bikeSpeed = getSpeed(rpm);
bikeSpeed = getConvertedSpeed(bikeSpeed);
// if we are not in menu mode and the speed has changed
if (!inMenuMode() && (whole(bikeSpeed) != whole(lastBikeSpeed))) {
showSpeed();
}
if (inTimerMode()){
if (timerArmed()) {
startTimer();
}
if (timerRunning()) {
checkSpeed(bikeSpeed);
}
}
}
}
}
uno:A5.2
uno:A4.2
uno:AREF
uno:GND.1
uno:13
uno:12
uno:11
uno:10
uno:9
uno:8
uno:7
uno:6
uno:5
uno:4
uno:3
uno:2
uno:1
uno:0
uno:IOREF
uno:RESET
uno:3.3V
uno:5V
uno:GND.2
uno:GND.3
uno:VIN
uno:A0
uno:A1
uno:A2
uno:A3
uno:A4
uno:A5
lcd1:GND
lcd1:VCC
lcd1:SDA
lcd1:SCL
encoder1:CLK
encoder1:DT
encoder1:SW
encoder1:VCC
encoder1:GND
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r