Skip to content

Commit be84144

Browse files
committed
add SoftwareSerial support
1 parent ebfacba commit be84144

File tree

9 files changed

+231
-80
lines changed

9 files changed

+231
-80
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
1010
- Yaml files support select/reject critera for paths of unit tests for targeted testing
1111
- Pins now track history and can report it in Ascii (big- or little-endian) for digital sequences
1212
- Pins now accept an array (or string) of input bits for providing pin values across multiple reads
13+
- SoftwareSerial. That took a while.
1314

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

SampleProjects/TestSomething/test/pinhistory.cpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,13 @@
22
#include <Arduino.h>
33

44

5-
unittest(pin_read_history)
6-
{
5+
unittest(pin_read_history) {
76
PinHistory<int> phi;
87

98
const int future[6] = {33, 22, 55, 11, 44, 66};
109
assertEqual(0, phi.queueSize());
1110
phi.fromArray(future, 6);
12-
for (int i = 0; i < 6; ++i)
13-
{
11+
for (int i = 0; i < 6; ++i) {
1412
assertEqual(6 - i, phi.queueSize());
1513
assertEqual(future[i], phi.retrieve());
1614
assertEqual(6 - (i + 1), phi.queueSize());
@@ -20,19 +18,36 @@ unittest(pin_read_history)
2018
assertEqual(future[5], phi.retrieve());
2119

2220
PinHistory<bool> phb;
23-
assertEqual(0, phb.queueSize());
2421
phb.fromAscii("Yo", true);
25-
assertEqual(16, phb.queueSize());
22+
2623
// digitial history as serial data, big-endian
2724
bool binaryAscii[16] = {
28-
0, 1, 0, 1, 1, 0, 0, 1,
29-
0, 1, 1, 0, 1, 1, 1, 1
30-
};
25+
0, 1, 0, 1, 1, 0, 0, 1,
26+
0, 1, 1, 0, 1, 1, 1, 1};
3127

3228
for (int i = 0; i < 16; ++i) {
3329
assertEqual(binaryAscii[i], phb.retrieve());
3430
}
31+
32+
assertEqual("Yo", phb.toAscii(0, true));
3533
}
3634

35+
unittest(ascii_stuff) {
36+
PinHistory<bool> phb;
37+
assertEqual(0, phb.historySize());
38+
phb.reset(false);
39+
assertEqual(1, phb.historySize());
40+
41+
assertEqual(0, phb.queueSize());
42+
phb.fromAscii("Yo", true);
43+
assertEqual(16, phb.queueSize());
44+
assertEqual("Yo", phb.incomingToAscii(0, true));
45+
46+
phb.reset(false);
47+
assertEqual(0, phb.queueSize());
48+
assertEqual(1, phb.historySize());
49+
phb.outgoingFromAscii("hi", true);
50+
assertEqual("hi", phb.toAscii(1, true));
51+
}
3752

3853
unittest_main()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <ArduinoUnitTests.h>
2+
#include <SoftwareSerial.h>
3+
4+
unittest(software_input_output)
5+
{
6+
GodmodeState* state = GODMODE();
7+
state->reset();
8+
9+
SoftwareSerial ss(1, 2, false);
10+
11+
assertEqual(-1, ss.peek());
12+
13+
state->digitalPin[1].fromAscii("Holy crap ", true);
14+
state->digitalPin[1].fromAscii("this took a lot of prep work", true);
15+
16+
assertFalse(ss.isListening());
17+
assertEqual(-1, ss.peek());
18+
19+
ss.listen();
20+
assertTrue(ss.isListening());
21+
assertEqual(38, ss.available());
22+
assertEqual('H', ss.peek());
23+
assertEqual('H', ss.read());
24+
assertEqual('o', ss.read());
25+
assertEqual('l', ss.read());
26+
assertEqual('y', ss.read());
27+
28+
ss.write('b');
29+
ss.write('A');
30+
ss.write('r');
31+
assertEqual("bAr", state->digitalPin[2].toAscii(1, true));
32+
}
33+
34+
unittest(print) {
35+
GodmodeState* state = GODMODE();
36+
state->reset();
37+
38+
SoftwareSerial ss(1, 2, false);
39+
ss.listen();
40+
ss.print(1.3, 2);
41+
assertEqual("1.30", state->digitalPin[2].toAscii(1, true));
42+
}
43+
44+
unittest_main()

cpp/arduino/Arduino.cpp

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,2 @@
1-
#include "Arduino.h"
21
#include "Godmode.h"
32

4-
5-
void digitalWrite(unsigned char pin, unsigned char val) {
6-
GodmodeState* godmode = GODMODE();
7-
godmode->digitalPin[pin] = val;
8-
}
9-
10-
int digitalRead(unsigned char pin) {
11-
GodmodeState* godmode = GODMODE();
12-
return godmode->digitalPin[pin].retrieve();
13-
}
14-
15-
void analogWrite(unsigned char pin, int val) {
16-
GodmodeState* godmode = GODMODE();
17-
godmode->analogPin[pin] = val;
18-
}
19-
20-
int analogRead(unsigned char pin) {
21-
GodmodeState* godmode = GODMODE();
22-
return godmode->analogPin[pin].retrieve();
23-
}

cpp/arduino/Arduino.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,7 @@ typedef unsigned int word;
5757

5858
#define bit(b) (1UL << (b))
5959

60-
// io pins
61-
#define pinMode(...) _NOP()
62-
#define analogReference(...) _NOP()
63-
64-
void digitalWrite(uint8_t, uint8_t);
65-
int digitalRead(uint8_t);
66-
int analogRead(uint8_t);
67-
void analogWrite(uint8_t, int);
68-
#define analogReadResolution(...) _NOP()
69-
#define analogWriteResolution(...) _NOP()
60+
7061

7162

7263
// Get the bit location within the hardware port of the given virtual pin.

cpp/arduino/Godmode.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,27 @@ long random(long vmin, long vmax)
4646
return vmin < vmax ? (random(vmax - vmin) + vmin) : vmin;
4747
}
4848

49+
void digitalWrite(unsigned char pin, unsigned char val) {
50+
GodmodeState* godmode = GODMODE();
51+
godmode->digitalPin[pin] = val;
52+
}
53+
54+
int digitalRead(unsigned char pin) {
55+
GodmodeState* godmode = GODMODE();
56+
return godmode->digitalPin[pin].retrieve();
57+
}
58+
59+
void analogWrite(unsigned char pin, int val) {
60+
GodmodeState* godmode = GODMODE();
61+
godmode->analogPin[pin] = val;
62+
}
63+
64+
int analogRead(unsigned char pin) {
65+
GodmodeState* godmode = GODMODE();
66+
return godmode->analogPin[pin].retrieve();
67+
}
68+
69+
4970
// Serial ports
5071
#if defined(HAVE_HWSERIAL0)
5172
HardwareSerial Serial(&godmode.serialPort[0].dataIn, &godmode.serialPort[0].dataOut, &godmode.serialPort[0].readDelayMicros);

cpp/arduino/Godmode.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,17 @@ class GodmodeState {
8080

8181
};
8282

83+
// io pins
84+
#define pinMode(...) _NOP()
85+
#define analogReference(...) _NOP()
86+
87+
void digitalWrite(uint8_t, uint8_t);
88+
int digitalRead(uint8_t);
89+
int analogRead(uint8_t);
90+
void analogWrite(uint8_t, int);
91+
#define analogReadResolution(...) _NOP()
92+
#define analogWriteResolution(...) _NOP()
93+
94+
8395
GodmodeState* GODMODE();
8496

cpp/arduino/PinHistory.h

Lines changed: 53 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,49 @@ class PinHistory {
1414
qIn.clear();
1515
}
1616

17+
// enqueue ascii bits
18+
void a2q(Queue<T> &q, String input, bool bigEndian) {
19+
// 8 chars at a time, form up
20+
for (int j = 0; j < input.length(); ++j) {
21+
for (int i = 0; i < 8; ++i) {
22+
int shift = bigEndian ? 7 - i : i;
23+
unsigned char mask = (0x01 << shift);
24+
q.push(mask & input[j]);
25+
}
26+
}
27+
}
28+
29+
30+
// convert a queue to a string as if it was serial bits
31+
// start from offset, consider endianness
32+
String q2a(const Queue<T> &q, unsigned int offset, bool bigEndian) const {
33+
String ret = "";
34+
35+
Queue<T> q2(q);
36+
37+
while (offset) {
38+
q2.pop();
39+
--offset;
40+
}
41+
42+
if (offset) return ret;
43+
44+
// 8 chars at a time, form up
45+
while (q2.size() >= 8) {
46+
unsigned char acc = 0x00;
47+
for (int i = 0; i < 8; ++i) {
48+
int shift = bigEndian ? 7 - i : i;
49+
T val = q2.front();
50+
unsigned char bit = val ? 0x1 : 0x0;
51+
acc |= (bit << shift);
52+
q2.pop();
53+
}
54+
ret.append(String((char)acc));
55+
}
56+
57+
return ret;
58+
}
59+
1760
public:
1861
PinHistory() {}
1962

@@ -59,18 +102,17 @@ class PinHistory {
59102
}
60103

61104
// enqueue ascii bits
62-
void fromAscii(String input, bool bigEndian) {
63-
String ret = "";
105+
void fromAscii(String input, bool bigEndian) { a2q(qIn, input, bigEndian); }
64106

65-
// 8 chars at a time, form up
66-
for (int j = 0; j < input.length(); ++j) {
67-
for (int i = 0; i < 8; ++i) {
68-
int shift = bigEndian ? 7 - i : i;
69-
unsigned char mask = (0x01 << shift);
70-
qIn.push(mask & input[j]);
71-
}
72-
}
73-
}
107+
void outgoingFromAscii(String input, bool bigEndian) { a2q(qOut, input, bigEndian); }
108+
109+
// convert the queue of incoming data to a string as if it was Serial comms
110+
// start from offset, consider endianness
111+
String incomingToAscii (unsigned int offset, bool bigEndian) const { return q2a(qIn, offset, bigEndian); }
112+
113+
// convert the pin history to a string as if it was Serial comms
114+
// start from offset, consider endianness
115+
String toAscii (unsigned int offset, bool bigEndian) const { return q2a(qOut, offset, bigEndian); }
74116

75117
// copy elements to an array, up to a given length
76118
// return the number of elements moved
@@ -97,34 +139,5 @@ class PinHistory {
97139
return i == length;
98140
}
99141

100-
// convert the pin history to a string as if it was Serial comms
101-
// start from offset, consider endianness
102-
String toAscii (unsigned int offset, bool bigEndian) const {
103-
String ret = "";
104-
105-
Queue<T> q2(qOut);
106-
107-
while (offset) {
108-
q2.pop();
109-
--offset;
110-
}
111-
112-
if (offset) return ret;
113-
114-
// 8 chars at a time, form up
115-
while (q2.size() >= 8) {
116-
unsigned char acc = 0x00;
117-
for (int i = 0; i < 8; ++i) {
118-
int shift = bigEndian ? 7 - i : i;
119-
T val = q2.front();
120-
unsigned char bit = val ? 0x1 : 0x0;
121-
acc |= (bit << shift);
122-
q2.pop();
123-
}
124-
ret.append(String((char)acc));
125-
}
126-
127-
return ret;
128-
}
129142
};
130143

cpp/arduino/SoftwareSerial.h

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#pragma once
2+
#include "Print.h"
3+
#include "Stream.h"
4+
#include "Godmode.h"
5+
6+
// definitions neeeded for Serial.begin's config arg
7+
8+
class SoftwareSerial : public Stream
9+
{
10+
private:
11+
int mPinIn;
12+
int mPinOut;
13+
bool mInverse;
14+
bool mIsListening;
15+
GodmodeState* mState;
16+
unsigned long mOffset; // bits to offset stream
17+
18+
public:
19+
SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false) {
20+
mPinIn = receivePin;
21+
mPinOut = transmitPin;
22+
mIsListening = false;
23+
mOffset = 0; // godmode starts with 1 bit in the queue
24+
mState = GODMODE();
25+
}
26+
27+
~SoftwareSerial() {};
28+
29+
void setGodmodeOffset(unsigned long offset) {
30+
mOffset = offset;
31+
}
32+
33+
bool listen() { return mIsListening = true; }
34+
bool isListening() { return mIsListening; }
35+
bool stopListening() {
36+
bool ret = mIsListening;
37+
mIsListening = false;
38+
return ret;
39+
}
40+
void begin(long speed) { listen(); }
41+
void end() { stopListening(); }
42+
bool overflow() { return false; }
43+
44+
int peek() {
45+
if (!isListening()) return -1;
46+
String input = mState->digitalPin[mPinIn].incomingToAscii(mOffset, true);
47+
if (input.empty()) return -1;
48+
return input[0];
49+
}
50+
51+
virtual int read() {
52+
if (!isListening()) return -1;
53+
String input = mState->digitalPin[mPinIn].incomingToAscii(mOffset, true);
54+
if (input.empty()) return -1;
55+
int ret = input[0];
56+
for (int i = 0; i < 8; ++i) digitalRead(mPinIn);
57+
return ret;
58+
}
59+
60+
//using Print::write;
61+
62+
virtual size_t write(uint8_t byte) {
63+
mState->digitalPin[mPinOut].outgoingFromAscii(String((char)byte), true);
64+
return 1;
65+
}
66+
67+
virtual int available() { return mState->digitalPin[mPinIn].incomingToAscii(mOffset, true).length(); }
68+
virtual void flush() {}
69+
operator bool() { return true; }
70+
71+
static inline void handle_interrupt() {};
72+
73+
};
74+
75+

0 commit comments

Comments
 (0)