Skip to content

Commit fa4c0d2

Browse files
committed
add the ability to examine a pin's history as an array or as ascii
1 parent 5dd9965 commit fa4c0d2

File tree

7 files changed

+221
-19
lines changed

7 files changed

+221
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
88
### Added
99
- Yaml files can have either `.yml` or `.yaml` extensions
1010
- Yaml files support select/reject critera for paths of unit tests for targeted testing
11+
- Pins now track history and can report it in Ascii (big- or little-endian) for digital sequences
1112

1213
### Changed
1314
- Unit test executables print to STDERR just in case there are segfaults. Uh, just in case I ever write any.

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,61 @@ unittest(two_flips)
127127
}
128128
```
129129

130+
Of course, it's possible that your code might flip the bit more than once in a function. For that scenario, you may want to examine the history of a pin's commanded outputs:
131+
132+
```C++
133+
unittest(pin_history)
134+
{
135+
GodmodeState* state = GODMODE();
136+
int myPin = 3;
137+
state->reset(); // pin will start LOW
138+
digitalWrite(myPin, HIGH);
139+
digitalWrite(myPin, LOW);
140+
digitalWrite(myPin, LOW);
141+
digitalWrite(myPin, HIGH);
142+
digitalWrite(myPin, HIGH);
143+
144+
assertEqual(6, state->digitalPin[1].size());
145+
bool expected[6] = {LOW, HIGH, LOW, LOW, HIGH, HIGH};
146+
bool actual[6];
147+
148+
// move history queue into an array because at the moment, reading
149+
// the history is destructive -- it's a linked-list queue. this
150+
// means that if toArray or hasElements fails, the queue will be in
151+
// an unknown state and you should reset it before continuing with
152+
// other tests
153+
int numMoved = state->digitalPin[myPin].toArray(actual, 6);
154+
assertEqual(6, numMoved);
155+
156+
// verify each element
157+
for (int i = 0; i < 6; ++i) {
158+
assertEqual(expected[i], actual[i]);
159+
}
160+
```
161+
162+
Finally, there are some cases where you want to use a pin as a serial port. There are history functions for that too.
163+
164+
```C++
165+
int myPin = 3;
166+
167+
// digitial history as serial data, big-endian
168+
bool bigEndian = true;
169+
bool binaryAscii[24] = {
170+
0, 1, 0, 1, 1, 0, 0, 1, // Y
171+
0, 1, 1, 0, 0, 1, 0, 1, // e
172+
0, 1, 1, 1, 0, 0, 1, 1 // s
173+
};
174+
175+
// "send" these bits
176+
for (int i = 0; i < 24; digitalWrite(myPin, binaryAscii[i++]));
177+
178+
// The first bit in the history is the initial value, which we will ignore
179+
int offset = 1;
180+
181+
// We should be able to parse the bits as ascii
182+
assertEqual("Yes", state->digitalPin[myPin].toAscii(offset, bigEndian));
183+
```
184+
130185
## More Documentation
131186

132187
This software is in alpha. But [SampleProjects/DoSomething](SampleProjects/DoSomething) has a decent writeup and is a good bare-bones example of all the features.

SampleProjects/TestSomething/test/good-godmode.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,68 @@ unittest(pins)
6565
assertEqual(56, analogRead(1));
6666
}
6767

68+
unittest(pin_history)
69+
{
70+
GodmodeState* state = GODMODE();
71+
state->reset();
72+
73+
// history for digital pin
74+
digitalWrite(1, HIGH);
75+
digitalWrite(1, LOW);
76+
digitalWrite(1, LOW);
77+
digitalWrite(1, HIGH);
78+
digitalWrite(1, HIGH);
79+
80+
assertEqual(6, state->digitalPin[1].size());
81+
bool expectedD[6] = {LOW, HIGH, LOW, LOW, HIGH, HIGH};
82+
bool actualD[6];
83+
int numMoved = state->digitalPin[1].toArray(actualD, 6);
84+
assertEqual(6, numMoved);
85+
86+
for (int i = 0; i < 6; ++i) {
87+
assertEqual(expectedD[i], actualD[i]);
88+
}
89+
90+
// history for analog pin
91+
analogWrite(1, 11);
92+
analogWrite(1, 22);
93+
analogWrite(1, 33);
94+
analogWrite(1, 44);
95+
analogWrite(1, 55);
96+
97+
assertEqual(6, state->analogPin[1].size());
98+
int expectedA[6] = {0, 11, 22, 33, 44, 55};
99+
int actualA[6];
100+
numMoved = state->analogPin[1].toArray(actualA, 6);
101+
assertEqual(6, numMoved);
102+
103+
for (int i = 0; i < 6; ++i) {
104+
assertEqual(expectedA[i], actualA[i]);
105+
}
106+
107+
// digitial history as serial data, big-endian
108+
bool binaryAscii[24] = {
109+
0, 1, 0, 1, 1, 0, 0, 1,
110+
0, 1, 1, 0, 0, 1, 0, 1,
111+
0, 1, 1, 1, 0, 0, 1, 1
112+
};
113+
114+
for (int i = 0; i < 24; digitalWrite(2, binaryAscii[i++]));
115+
116+
assertEqual("Yes", state->digitalPin[2].toAscii(1, true));
117+
118+
// digitial history as serial data, little-endian
119+
bool binaryAscii2[16] = {
120+
0, 1, 1, 1, 0, 0, 1, 0,
121+
1, 1, 1, 1, 0, 1, 1, 0
122+
};
123+
124+
for (int i = 0; i < 16; digitalWrite(3, binaryAscii2[i++]));
125+
126+
assertEqual("No", state->digitalPin[3].toAscii(1, false));
127+
128+
}
129+
68130
#ifdef HAVE_HWSERIAL0
69131

70132
void smartLightswitchSerialHandler(int pin) {

cpp/arduino/AvrMath.h

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,7 @@
44
#define constrain(x,l,h) ((x)<(l)?(l):((x)>(h)?(h):(x)))
55
#define map(x,inMin,inMax,outMin,outMax) (((x)-(inMin))*((outMax)-(outMin))/((inMax)-(inMin))+outMin)
66

7-
#ifdef abs
8-
#undef abs
9-
#endif
10-
#define abs(x) ((x)>0?(x):-(x))
11-
12-
#ifdef max
13-
#undef max
14-
#endif
15-
#define max(a,b) ((a)>(b)?(a):(b))
16-
17-
#ifdef min
18-
#undef min
19-
#endif
20-
#define min(a,b) ((a)<(b)?(a):(b))
7+
#include "Forced.h"
218

229
#define sq(x) ((x)*(x))
2310

cpp/arduino/Forced.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Stuff where we very explicitly override std C++
2+
// Note lack of "pragma once"
3+
4+
#ifdef abs
5+
#undef abs
6+
#endif
7+
#define abs(x) ((x)>0?(x):-(x))
8+
9+
#ifdef max
10+
#undef max
11+
#endif
12+
#define max(a,b) ((a)>(b)?(a):(b))
13+
14+
#ifdef min
15+
#undef min
16+
#endif
17+
#define min(a,b) ((a)<(b)?(a):(b))

cpp/arduino/Godmode.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "ArduinoDefines.h"
33
#include <avr/io.h>
44
#include "WString.h"
5+
#include "PinHistory.h"
56

67
#define MOCK_PINS_COUNT 256
78

@@ -29,14 +30,14 @@ class GodmodeState {
2930
unsigned long micros;
3031
unsigned long seed;
3132
// not going to put pinmode here unless its really needed. can't think of why it would be
32-
bool digitalPin[MOCK_PINS_COUNT];
33-
int analogPin[MOCK_PINS_COUNT];
33+
PinHistory<bool> digitalPin[MOCK_PINS_COUNT];
34+
PinHistory<int> analogPin[MOCK_PINS_COUNT];
3435
struct PortDef serialPort[NUM_SERIAL_PORTS];
3536

3637
void resetPins() {
3738
for (int i = 0; i < MOCK_PINS_COUNT; ++i) {
38-
digitalPin[i] = LOW;
39-
analogPin[i] = 0;
39+
digitalPin[i].reset(LOW);
40+
analogPin[i].reset(0);
4041
}
4142
}
4243

@@ -54,7 +55,6 @@ class GodmodeState {
5455
return NUM_SERIAL_PORTS;
5556
}
5657

57-
5858
GodmodeState()
5959
{
6060
reset();

cpp/arduino/PinHistory.h

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#pragma once
2+
#include <queue>
3+
#include "Forced.h"
4+
5+
// pins with history.
6+
template <typename T>
7+
class PinHistory {
8+
private:
9+
std::queue<T> q;
10+
11+
void clear() { while (!q.empty()) q.pop(); }
12+
13+
public:
14+
PinHistory() {}
15+
16+
void reset(T val) {
17+
clear();
18+
q.push(val);
19+
}
20+
21+
unsigned int size() { return q.size(); }
22+
23+
operator T() const { return q.back(); }
24+
25+
T &operator=(const T i) {
26+
q.push(i);
27+
return q.back();
28+
}
29+
30+
// destructively move elements to an array, up to a given length
31+
// return the number of elements moved
32+
int toArray(T* arr, unsigned int length) {
33+
int ret = 0;
34+
for (int i = 0; i < length && q.size(); ++i) {
35+
arr[i] = q.front();
36+
q.pop();
37+
++ret;
38+
}
39+
return ret;
40+
}
41+
42+
// destructively see if the array matches the elements in the queue
43+
bool hasElements(T* arr, unsigned int length) {
44+
int i;
45+
for (i = 0; i < length && q.size(); ++i) {
46+
if (q.front() != arr[i]) return false;
47+
q.pop();
48+
}
49+
return i == length;
50+
}
51+
52+
// destructively convert the pin history to a string as if it was Serial comms
53+
// start from offset, consider endianness
54+
String toAscii(unsigned int offset, bool bigEndian) {
55+
String ret = "";
56+
57+
while (offset) {
58+
q.pop();
59+
--offset;
60+
}
61+
62+
if (offset) return ret;
63+
64+
// 8 chars at a time, form up
65+
while (q.size() >= 8) {
66+
unsigned char acc = 0x00;
67+
for (int i = 0; i < 8; ++i) {
68+
int shift = bigEndian ? 7 - i : i;
69+
T val = q.front();
70+
unsigned char bit = val ? 0x1 : 0x0;
71+
acc |= (bit << shift);
72+
q.pop();
73+
}
74+
ret.append(String((char)acc));
75+
}
76+
77+
return ret;
78+
}
79+
};
80+

0 commit comments

Comments
 (0)