-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStackHack.ino
More file actions
197 lines (167 loc) · 6.15 KB
/
StackHack.ino
File metadata and controls
197 lines (167 loc) · 6.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
The basic idea is: the signal is going to switch a bunch, then go idle for a while, and repeat.
The idle periods are always at least 10 milliseconds long, so we do the following:
We listen to the input and record the timing between each signal switch into a buffer.
Whenever it has been more than 10 ms from the last signal switch, we read the buffer contents,
parse the message to get the time, go back to the beginning of the buffer
and wait for the signal to start switching again to repeat the whole process.
*/
// Input from the timer
#define TIMER_IN 4
// Size of array that will keep track of timing between signal switches
#define BUFFER_SIZE 80
// Time (in microseconds) between input messages to start processing last message
#define TIME_BETWEEN_MESSAGES 10000
// Time (in microseconds) since last detected signal switch to enter standby
#define STANDBY_TIME 500000
// Serial reading variables
volatile unsigned long durationBuffer[BUFFER_SIZE] = { 0 };
volatile unsigned long currentSignalStart = 0;
volatile bool signalBuffer[BUFFER_SIZE] = { 0 };
volatile int currentBufferIndex = 0;
// Output display variables
uint8_t receivedDigits[6] = { 0 }; // Digits that were last received from the timer
bool parsed = false;
bool standby = false;
void IRAM_ATTR OnSignalSwitch()
{
// Update duration
unsigned long now = micros();
durationBuffer[currentBufferIndex] = now - currentSignalStart; // Store time since last signal switch
currentSignalStart = now;
// Update signal
signalBuffer[currentBufferIndex] = !digitalRead(TIMER_IN); // Previous signal is the opposite of the current signal
// Increment index
currentBufferIndex = currentBufferIndex + 1;
}
void setup()
{
// Input pin that receives the timer signal
pinMode(TIMER_IN, INPUT);
attachInterrupt(TIMER_IN, OnSignalSwitch, CHANGE);
Serial.begin(115200);
}
void InterpretDigits()
{
/*
Parse message received from timer through UART in the TIMER_IN pin.
The timer sends 10 bits per byte, the first 2 always being "1 0", marking the start of the byte, and the next 8 being the byte itself.
The signal stays idle between messages, so the first index of the buffers is skipped to avoid looping through a huge signal duration.
The first bit that mark the start of the byte is ignored (as explained above), and the second one is bit 0.
The first byte is composed by bits 1 to 8. The second one is bits 11 to 18, and so on.
The bits of each byte are sent from LSB to MSB, so they need to be inverted when parsed.
The MSB (last bit sent) of every byte is always 0, because no bytes sent by the timer are ever greater than 127.
Each message is composed of 10 bytes:
Byte 0 (bits 1 to 8): Timer status (running = 32, stopped/idle = 73, different timer brands might have more statuses).
Bytes 1 to 6 (bits 11 to 18, 21 to 28 and so on): Timer digits, in ASCII, from the minute to the millisecond digit.
Byte 7 (bits 71 to 78): A checksum of each digit value (raw value, not in ASCII), offsetted by 32.
Bytes 8 and 9: Control characters CR LF (\r\n), ignored.
All information is parsed
*/
int bitCounter = 0;
uint8_t parsedDigits[7] = { 0 };
uint8_t checksum = 0;
// Start from buffer index 1
for (int i = 1; i < currentBufferIndex; i++)
{
// Considering a baud rate of 1200 bits per second, calculate how many equal bits were passed at this buffer location
int signalDuration = int((float(durationBuffer[i]) / (1000000.0f / 1200.0f)) + 0.5f);
// Count each equal bit within the current signal duration
for (int j = 0; j < signalDuration; j++)
{
// Parse each digit
for (int digitIndex = 0; digitIndex < 6; digitIndex++)
{
// Only need first 4 bits because values go from 0 to 9
// Get bits 11 to 14, 21 to 24, 31 to 34 and so on until 61 to 64
const int startIndex = digitIndex * 10 + 11;
const int endIndex = digitIndex * 10 + 14;
if ((bitCounter >= startIndex) && (bitCounter <= endIndex))
{
// Get byte by OR-ing the bits that are set to HIGH
// Write byte in reverse direction because message is sent from LSB to MSB
parsedDigits[digitIndex] |= signalBuffer[i] << (bitCounter - startIndex);
}
}
// Parse checksum (only need first 6 bits because checksum goes from 0 to 50 = 9+5+9+9+9+9)
if ((bitCounter >= 71) && (bitCounter <= 76))
{
checksum |= signalBuffer[i] << (bitCounter - 71);
}
bitCounter++;
}
}
// Check if sum of digits equals checksum by subtracting each digit from the checksum
for (int i = 0; i < 6; i++)
{
checksum -= parsedDigits[i];
}
// Only copy 'parsedDigits' to 'receivedDigits' if parsed digits sum to the checksum
if (checksum == 0)
{
for (int i = 0; i < 6; i++)
{
receivedDigits[i] = parsedDigits[i];
}
}
/*
Serial.print(parsedDigits[0]);
Serial.print(":");
Serial.print(parsedDigits[1]);
Serial.print(parsedDigits[2]);
Serial.print(".");
Serial.print(parsedDigits[3]);
Serial.print(parsedDigits[4]);
Serial.print(parsedDigits[5]);
Serial.println(" ");
*/
}
void UpdateDisplay()
{
// TODO: output to hex, interpolation/extrapolation
if (!standby)
{
Serial.print(receivedDigits[0]);
Serial.print(":");
Serial.print(receivedDigits[1]);
Serial.print(receivedDigits[2]);
Serial.print(".");
Serial.print(receivedDigits[3]);
Serial.print(receivedDigits[4]);
Serial.print(receivedDigits[5]);
Serial.println(" ");
}
else
{
Serial.println("Standby");
}
}
void loop()
{
unsigned long timeSinceLastSwitch = micros() - currentSignalStart;
if (timeSinceLastSwitch < TIME_BETWEEN_MESSAGES) // Still receiving message
{
parsed = false;
standby = false;
}
else if (timeSinceLastSwitch < STANDBY_TIME) // Finished receiving message
{
// If more than 10 milliseconds have passed since the last signal switch, the message has finished
if (!parsed)
{
// Parse the message if it hasn't been yet
// If the message is valid, update the received digits
InterpretDigits();
parsed = true;
currentBufferIndex = 0;
}
}
else // Enter standby
{
// More than half a second has passed since the last signal switch
// Assume that the timer is turned off or disconnected, and enter standby mode
standby = true;
}
// Update the LED display
UpdateDisplay();
}