Skip to content

Commit fbba628

Browse files
authored
Merge pull request #2 from outadoc/develop
First prototype in all-working finished state
2 parents 75431a0 + 7c1e0d4 commit fbba628

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+2375
-124
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.vscode/*
2+
!.vscode/settings.json
3+
!.vscode/tasks.json
4+
!.vscode/launch.json
5+
!.vscode/extensions.json
6+
!.vscode/arduino.json
7+
8+
build/

.gitmodules

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[submodule "libraries/SevSeg"]
2+
path = libraries/SevSeg
3+
url = git@github.com:outadoc/SevSeg.git
4+
[submodule "libraries/MsTimer2"]
5+
path = libraries/MsTimer2
6+
url = git@github.com:PaulStoffregen/MsTimer2.git
7+
[submodule "libraries/TimerOne"]
8+
path = libraries/TimerOne
9+
url = git@github.com:PaulStoffregen/TimerOne.git
10+
[submodule "libraries/NeoSWSerial"]
11+
path = libraries/NeoSWSerial
12+
url = git@github.com:SlashDevin/NeoSWSerial.git
13+
[submodule "libraries/narcoleptic"]
14+
path = libraries/narcoleptic
15+
url = git@github.com:brabl2/narcoleptic.git

.vscode/arduino.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"board": "arduino:avr:nano",
3+
"configuration": "cpu=atmega328",
4+
"port": "/dev/cu.wchusbserialfd120",
5+
"sketch": "DigitalSpeedometer/DigitalSpeedometer.ino",
6+
"output": "build"
7+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* Arduino-based replica of the Back to the Future DeLorean Speedometer
2+
Copyright (C) 2017 Baptiste Candellier
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>. */
16+
17+
#ifndef DIGITAL_SPEEDOMETER_H
18+
#define DIGITAL_SPEEDOMETER_H
19+
20+
#include <Arduino.h>
21+
22+
#define STATE_DISCONNECTED 0x0
23+
#define STATE_CONNECTED 0x2
24+
#define STATE_SLEEPING 0x3
25+
26+
#define UNIT_MPH HIGH
27+
#define UNIT_KMH LOW
28+
29+
#define TIMER_INTERVAL_DISP_REFRESH_MS 10
30+
#define TIMER_INTERVAL_DISP_INC_MS 500
31+
32+
#define IDLE_SLEEP_DELAY_MS 8000
33+
34+
#define PIN_SEG_A 5
35+
#define PIN_SEG_B 6
36+
#define PIN_SEG_C 7
37+
#define PIN_SEG_D 8
38+
#define PIN_SEG_E 9
39+
#define PIN_SEG_F 10
40+
#define PIN_SEG_G 11
41+
#define PIN_SEG_DP 12
42+
43+
#define PIN_DIG_1 A1
44+
#define PIN_DIG_2 A2
45+
46+
#define PIN_SPEED_ADJUST A0
47+
48+
#define PIN_UNIT_SELECT 2
49+
#define PIN_SLEEP_ENABLE 3
50+
51+
//#define MODE_SIMULATION
52+
53+
typedef uint8_t speed_t;
54+
typedef uint8_t state_t;
55+
56+
void setup_timers();
57+
void setup_interrupts();
58+
void setup_display();
59+
void setup_obd_connection();
60+
61+
void isr_display();
62+
void isr_refresh_display();
63+
void isr_check_display_unit();
64+
void isr_check_sleep_mode();
65+
66+
speed_t adjust_speed(speed_t);
67+
void set_displayed_speed(speed_t);
68+
void probe_current_speed();
69+
70+
int analog_read_avg(const int, const int, const long);
71+
72+
void enter_sleep_mode();
73+
void leave_sleep_mode();
74+
75+
#endif // !DIGITAL_SPEEDOMETER_H
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/* Arduino-based replica of the Back to the Future DeLorean Speedometer
2+
Copyright (C) 2017 Baptiste Candellier
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>. */
16+
17+
#include <Arduino.h>
18+
19+
#include <avr/sleep.h>
20+
#include <TimerOne.h>
21+
#include <MsTimer2.h>
22+
#include <OBD2UART.h>
23+
#include <SevSeg.h>
24+
#include <Narcoleptic.h>
25+
26+
#include "DigitalSpeedometer.h"
27+
28+
SevSeg sevseg;
29+
30+
#ifndef MODE_SIMULATION
31+
COBD obd;
32+
#endif
33+
34+
volatile state_t state;
35+
volatile speed_t target_read_speed;
36+
37+
volatile float modifier;
38+
volatile int unit_select;
39+
40+
void setup() {
41+
state = STATE_DISCONNECTED;
42+
target_read_speed = 0;
43+
44+
pinMode(PIN_UNIT_SELECT, INPUT_PULLUP);
45+
pinMode(PIN_SLEEP_ENABLE, INPUT_PULLUP);
46+
47+
// Read speed modifier (1.0 keeps raw speed read from OBD)
48+
// OBD speed can be a bit different from real life speed, so play with
49+
// the potentiometer to adjust to real speed
50+
modifier = (float)map(analog_read_avg(PIN_SPEED_ADJUST, 20, 20),
51+
0, 1024, 9000, 12000) / (float)10000.0;
52+
53+
#ifdef MODE_SIMULATION
54+
Serial.begin(9600);
55+
Serial.println(modifier, 4);
56+
#endif
57+
58+
setup_display();
59+
setup_timers();
60+
setup_interrupts();
61+
62+
// Initialize display unit switch and sleep mode interrupts
63+
isr_check_display_unit();
64+
isr_check_sleep_mode();
65+
}
66+
67+
void loop() {
68+
noInterrupts();
69+
isr_check_sleep_mode();
70+
state_t loc_state = state;
71+
interrupts();
72+
73+
// Yay, finite state machine!
74+
switch (loc_state) {
75+
case STATE_DISCONNECTED:
76+
// If we got disconnected (or on first loop), try reconnecting
77+
setup_obd_connection();
78+
break;
79+
case STATE_SLEEPING:
80+
// If we switched the switch to OFF, time for sleepies
81+
enter_sleep_mode();
82+
break;
83+
case STATE_CONNECTED:
84+
// We should be connected. PROBE!
85+
probe_current_speed();
86+
break;
87+
}
88+
}
89+
90+
void enter_sleep_mode() {
91+
noInterrupts();
92+
93+
// Blank the display and wait a little before sleeping
94+
sevseg.blank();
95+
delay(500);
96+
97+
#ifndef MODE_SIMULATION
98+
if (state == STATE_CONNECTED) {
99+
obd.enterLowPowerMode();
100+
}
101+
#endif
102+
103+
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
104+
sleep_enable(); // enables the sleep bit in the mcucr register
105+
interrupts(); // interrupts allowed now, next instruction WILL be executed
106+
sleep_cpu(); // here the device is put to sleep
107+
}
108+
109+
void leave_sleep_mode() {
110+
// Wakey wakey, eggs and bakey
111+
sleep_disable();
112+
113+
noInterrupts();
114+
state = STATE_DISCONNECTED;
115+
interrupts();
116+
}

DigitalSpeedometer/display.ino

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/* Arduino-based replica of the Back to the Future DeLorean Speedometer
2+
Copyright (C) 2017 Baptiste Candellier
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>. */
16+
17+
#include <Arduino.h>
18+
#include "DigitalSpeedometer.h"
19+
20+
void setup_display() {
21+
byte numDigits = 2;
22+
byte digitPins[] = {PIN_DIG_1, PIN_DIG_2};
23+
byte segmentPins[] = {
24+
PIN_SEG_A,
25+
PIN_SEG_B,
26+
PIN_SEG_C,
27+
PIN_SEG_D,
28+
PIN_SEG_E,
29+
PIN_SEG_F,
30+
PIN_SEG_G,
31+
PIN_SEG_DP
32+
};
33+
34+
// If you change these you might need to hack around in SevSeg.cpp to
35+
// replay my crappy hacks
36+
bool resistorsOnSegments = true;
37+
byte hardwareConfig = COMMON_CATHODE;
38+
bool updateWithDelays = false;
39+
bool leadingZeros = false;
40+
41+
sevseg.begin(hardwareConfig, numDigits, digitPins, segmentPins,
42+
resistorsOnSegments, updateWithDelays, leadingZeros);
43+
44+
sevseg.blank();
45+
}
46+
47+
void setup_timers() {
48+
// Timer that will refresh the display (interpolation using SevSeg)
49+
Timer1.initialize(TIMER_INTERVAL_DISP_REFRESH_MS * 1000);
50+
Timer1.attachInterrupt(isr_refresh_display);
51+
52+
// Timer that will display the speed read from OBD every xxx ms,
53+
// interpolating if needed
54+
MsTimer2::set(TIMER_INTERVAL_DISP_INC_MS, isr_display);
55+
MsTimer2::start();
56+
}
57+
58+
speed_t adjust_speed(speed_t speed) {
59+
// Adjust read speed using modifier (set via potentiometer) and take
60+
// unit into account
61+
if (unit_select == UNIT_MPH) {
62+
// Convert from km/h to mph
63+
return round(modifier * (float)speed * 0.621371);
64+
}
65+
66+
return round(modifier * (float)speed);
67+
}
68+
69+
void set_displayed_speed(speed_t speed) {
70+
// Display number with a decimal point
71+
sevseg.setNumber(speed, 0);
72+
}
73+
74+
void isr_display() {
75+
// Speed currently displayed; will be incremented to reach target speed
76+
static speed_t curr_disp_speed = 0;
77+
78+
if (state != STATE_CONNECTED) {
79+
return;
80+
}
81+
82+
// Make copy of target speed
83+
const speed_t target_speed = adjust_speed(target_read_speed);
84+
85+
// We want to increment speed one by one until we hit the target speed
86+
// on a relatively short duration
87+
double interval_between_incs = TIMER_INTERVAL_DISP_INC_MS / (abs(target_speed - curr_disp_speed));
88+
89+
// Enable interrupts for the rest of the procedure,
90+
// because we want the display to still be refreshed
91+
interrupts();
92+
93+
if (curr_disp_speed == target_speed) {
94+
set_displayed_speed(curr_disp_speed);
95+
}
96+
97+
// Until we've hit the target speed, increment, display and pause
98+
while (curr_disp_speed != target_speed) {
99+
if (curr_disp_speed < target_speed) {
100+
curr_disp_speed++;
101+
} else {
102+
curr_disp_speed--;
103+
}
104+
105+
// Display and pause execution for a fixed amount of time between each iteration
106+
set_displayed_speed(curr_disp_speed);
107+
delay(interval_between_incs);
108+
}
109+
}
110+
111+
// Called every 10 ms
112+
void isr_refresh_display() {
113+
sevseg.refreshDisplay();
114+
}

0 commit comments

Comments
 (0)