Reusable Classes
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requirements
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Class 'reg'
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requirements
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Class 're...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requirements
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Class 'r...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requirement...
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Class '...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requiremen...
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Class ...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requireme...
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Class...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Requirem...
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Clas...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
1 The Pin Class 'pin'
1.1 pin.h
1.2 pin.c
2 The Sockets Class 'sock'
2.1 sock Code
2.1.1 sock.h
2.1.2 sock.c
3 The Toggle Class 'toggle'
3.1 toggle Requirements
3.2 toggle Statechart
3.3 toggle Execution
4 The Switch Class 'sw'
4.1 sw Requirements
4.2 sw Code
5 The Matrix Class 'matrix'
5.1 matrix Requirements
5.2 matrix Statechart
5.3 matrix Code
6 The Print Class 'prt'
7 The Keyer Class 'keyer'
7.1 keyer Notes & Require...
7.2 keyer Statechart
7.3 keyer Code
8 The Linear Regression Cla...
8.1 reg Overview
8.2 reg Requirements
8.3 reg Code
8.3.1 reg.h
8.3.2 reg.c
8.3.3 regtest.c
8.3.4 regtest Results
The following are classes that are portable across multiple processors and environments.

1 The Pin Class 'pin'

Common to all the small microcontroller boards are a set of digital I/O pins. However, not all boards have the same pins or even the same way of addressing them.
Our pin class implements common traits and make it easier to write programs that are more portable from board to board. We demonstrate this by using the same toggle object on ten different platforms in the Blink Example.
When creating the pin object, you specify whether the hardware characteristic of the pin is active high or active low. Once the pin class has this information, you can use the more intuitive nomenclature of pinOn and pinOff in your logic. (See lines 43-44 in pin.h.)
In other words, if the pin object is created as active high, turning the pin On will make the pin go high (active). If it is created as active low, then turning the pin On will make the pin go low (active).
Here is the source code for pin.h and pin.c:

1.1 pin.h

1
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
[GPLv2]
 
#ifndef pin_h
#define pin_h
 
#include "oosmos.h"
 
typedef struct pinTag pin;
 
typedef enum
{
pinOut = 1,
pinIn,
pinInOut,
} pin_eDirection;
 
typedef enum
{
pinActiveLow = 1,
pinActiveHigh,
} pin_eLogic;
 
extern void pinOn(pin * pPin);
extern void pinOff(pin * pPin);
 
extern bool pinIsOn(pin * pPin);
extern bool pinIsOff(pin * pPin);
 
#if defined(ARDUINO) || defined(oosmos_RASPBERRY_PI)
extern pin * pinNew(int PinNumber, pin_eDirection, pin_eLogic Logic);
extern pin * pinNew_Debounce(int PinNumber, pin_eDirection, pin_eLogic Logic, uint8_t DebounceTimeMS);
extern int pinGetPinNumber(pin * pPin);
#elif defined(__PIC32MX)
extern pin * pinNew(IoPortId Port, int Bit, pin_eDirection Direction, pin_eLogic Logic);
extern pin * pinNew_Debounce(IoPortId Port, int Bit, pin_eDirection, pin_eLogic Logic, uint8_t DebounceTimeMS);
#elif defined(__MBED__)
extern pin * pinNew(PinName Pin, pin_eDirection, pin_eLogic Logic);
extern pin * pinNew_Debounce(PinName Pin, pin_eDirection, pin_eLogic Logic, uint8_t DebounceTimeMS);
extern int pinGetPinName(pin * pPin);
#endif
 
#endif
 
oosmos/Classes/pin.h

1.2 pin.c

1
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
[GPLv2]
 
#ifndef pinMAX
#define pinMAX 20
#endif
 
#include "oosmos.h"
#include "pin.h"
 
typedef enum {
Unknown_State = 1,
On_State,
Off_State,
ConfirmingOn_State,
ConfirmingOff_State,
} eStates;
 
struct pinTag
{
#if defined(ARDUINO) || defined(oosmos_RASPBERRY_PI)
uint8_t m_PinNumber;
#elif defined(__PIC32MX)
IoPortId m_Port;
uint16_t m_Bit;
#elif defined(__MBED__)
uint8_t m_Pin[sizeof(DigitalOut)];
PinName m_PinName;
#endif
 
oosmos_sActiveObject m_ActiveObject;
oosmos_sTimeout m_Timeout;
uint8_t m_DebounceTimeMS;
eStates m_State:4;
pin_eLogic m_Logic:4;
pin_eDirection m_Direction:4;
};
 
static bool IsPhysicallyOn(pin * pPin);
 
static bool IsPhysicallyOff(pin * pPin)
{
return !IsPhysicallyOn(pPin);
}
 
static void RunStateMachine(void * pObject)
{
pin * pPin = (pin *) pObject;
 
switch (pPin->m_State) {
case On_State:
if (IsPhysicallyOff(pPin)) {
pPin->m_State = ConfirmingOff_State;
oosmos_TimeoutInMS(&pPin->m_Timeout, pPin->m_DebounceTimeMS);
}
 
break;
case Off_State:
if (IsPhysicallyOn(pPin)) {
pPin->m_State = ConfirmingOn_State;
oosmos_TimeoutInMS(&pPin->m_Timeout, pPin->m_DebounceTimeMS);
}
 
break;
case ConfirmingOn_State:
if (!oosmos_TimeoutHasExpired(&pPin->m_Timeout))
break;
 
if (!IsPhysicallyOn(pPin)) {
pPin->m_State = Unknown_State;
break;
}
 
pPin->m_State = On_State;
break;
case ConfirmingOff_State:
if (!oosmos_TimeoutHasExpired(&pPin->m_Timeout))
break;
 
if (!IsPhysicallyOff(pPin)) {
pPin->m_State = Unknown_State;
break;
}
 
pPin->m_State = Off_State;
break;
case Unknown_State:
pPin->m_State = IsPhysicallyOn(pPin) ? ConfirmingOn_State : ConfirmingOff_State;
oosmos_TimeoutInMS(&pPin->m_Timeout, pPin->m_DebounceTimeMS);
break;
}
}
 
extern bool pinIsOn(pin * pPin)
{
if (pPin->m_DebounceTimeMS == 0)
return IsPhysicallyOn(pPin);
 
return pPin->m_State == On_State;
}
 
extern bool pinIsOff(pin * pPin)
{
return !pinIsOn(pPin);
}
 
#if defined(ARDUINO) || defined(oosmos_RASPBERRY_PI)
 
static bool IsPhysicallyOn(pin * pPin)
{
const int PinValue = digitalRead(pPin->m_PinNumber);
return PinValue == (pPin->m_Logic == pinActiveHigh ? HIGH : LOW);
}
 
extern void pinOn(pin * pPin)
{
const int PinNumber = pPin->m_PinNumber;
 
if (pPin->m_Direction == pinInOut) {
pinMode(PinNumber, pPin->m_Logic == pinActiveHigh ? INPUT : OUTPUT);
}
 
digitalWrite(PinNumber, pPin->m_Logic == pinActiveHigh ? HIGH : LOW);
}
 
extern void pinOff(pin * pPin)
{
const int PinNumber = pPin->m_PinNumber;
 
if (pPin->m_Direction == pinInOut) {
pinMode(PinNumber, pPin->m_Logic == pinActiveHigh ? OUTPUT : INPUT);
}
 
digitalWrite(PinNumber, pPin->m_Logic == pinActiveHigh ? LOW : HIGH);
}
 
extern int pinGetPinNumber(pin * pPin)
{
return pPin->m_PinNumber;
}
 
extern pin * pinNew(const int PinNumber, const pin_eDirection Direction, const pin_eLogic Logic)
{
#if defined(oosmos_RASPBERRY_PI)
static int WiringPi_Initialized = 0;
 
if (!WiringPi_Initialized) {
wiringPiSetup();
WiringPi_Initialized = 1;
}
#endif
 
oosmos_Allocate(pPin, pin, pinMAX, NULL);
 
pPin->m_PinNumber = PinNumber;
pPin->m_Logic = Logic;
pPin->m_State = Unknown_State;
pPin->m_Direction = Direction;
pPin->m_DebounceTimeMS = 0;
 
switch (Direction) {
case pinIn:
case pinInOut:
pinMode(PinNumber, INPUT);
break;
case pinOut:
pinMode(PinNumber, OUTPUT);
break;
}
 
return pPin;
}
 
extern pin * pinNew_Debounce(const int PinNumber, const pin_eDirection Direction, const pin_eLogic Logic, const uint8_t DebounceTimeMS)
{
pin * pPin = pinNew(PinNumber, Direction, Logic);
 
pPin->m_DebounceTimeMS = DebounceTimeMS;
 
if (DebounceTimeMS > 0)
oosmos_RegisterActiveObject(pPin, RunStateMachine, &pPin->m_ActiveObject);
 
return pPin;
}
 
#elif defined(__PIC32MX)
 
static bool IsPhysicallyOn(pin * pPin)
{
const uint32_t PinValue = PORTReadBits(pPin->m_Port, pPin->m_Bit);
return (pPin->m_Logic == pinActiveHigh) ? PinValue != 0 : PinValue == 0;
}
 
extern void pinOn(pin * pPin)
{
(pPin->m_Logic == pinActiveHigh ? PORTSetBits : PORTClearBits)(pPin->m_Port, pPin->m_Bit);
}
 
extern void pinOff(pin * pPin)
{
(pPin->m_Logic == pinActiveHigh ? PORTClearBits : PORTSetBits)(pPin->m_Port, pPin->m_Bit);
}
 
extern pin * pinNew(const IoPortId Port, const int Bit, const pin_eDirection Direction, const pin_eLogic Logic)
{
oosmos_Allocate(pPin, pin, pinMAX, NULL);
 
pPin->m_Port = Port;
pPin->m_Bit = Bit;
pPin->m_Logic = Logic;
pPin->m_State = Unknown_State;
pPin->m_Direction = Direction;
pPin->m_DebounceTimeMS = 0;
 
switch (pPin->m_Direction) {
case pinOut:
pinOff(pPin);
PORTSetPinsDigitalOut(Port, Bit);
break;
case pinIn:
pinOff(pPin);
PORTSetPinsDigitalIn(Port, Bit);
break;
}
 
return pPin;
}
 
extern pin * pinNew_Debounce(const IoPortId Port, const int Bit, const pin_eDirection Direction, const pin_eLogic Logic, const uint8_t DebounceTimeMS)
{
pin * pPin = pinNew(Port, Bit, Direction, Logic);
 
pPin->m_DebounceTimeMS = DebounceTimeMS;
 
if (DebounceTimeMS > 0)
oosmos_RegisterActiveObject(pPin, RunStateMachine, &pPin->m_ActiveObject);
 
return pPin;
}
 
#elif defined(__MBED__)
 
#include "mbed.h"
#include <new>
 
extern pin * pinNew(const PinName Pin, const pin_eDirection Direction, const pin_eLogic Logic)
{
oosmos_Allocate(pPin, pin, pinMAX, NULL);
::new(&pPin->m_Pin) DigitalOut(Pin);
 
pPin->m_PinName = Pin;
pPin->m_Logic = Logic;
pPin->m_State = Unknown_State;
pPin->m_Direction = Direction;
pPin->m_DebounceTimeMS = 0;
 
return pPin;
}
 
extern pin * pinNew_Debounce(const PinName PinNumber, const pin_eDirection Direction, const pin_eLogic Logic, const uint8_t DebounceTimeMS)
{
pin * pPin = pinNew(PinNumber, Direction, Logic);
 
pPin->m_DebounceTimeMS = DebounceTimeMS;
 
if (DebounceTimeMS > 0)
oosmos_RegisterActiveObject(pPin, RunStateMachine, &pPin->m_ActiveObject);
 
return pPin;
}
 
static bool IsPhysicallyOn(pin * pPin)
{
DigitalOut * pDigitalOut = (DigitalOut *) pPin->m_Pin;
const int PinValue = pDigitalOut->read();
return PinValue == (pPin->m_Logic == pinActiveHigh ? 1 : 0);
}
 
extern void pinOn(pin * pPin)
{
DigitalOut * pDigitalOut = (DigitalOut *) pPin->m_Pin;
pDigitalOut->write(pPin->m_Logic == pinActiveHigh ? 1 : 0);
}
 
extern void pinOff(pin * pPin)
{
DigitalOut * pDigitalOut = (DigitalOut *) pPin->m_Pin;
pDigitalOut->write(pPin->m_Logic == pinActiveHigh ? 0 : 1);
}
 
extern PinName pinGetPinName(pin * pPin)
{
return pPin->m_PinName;
}
#else
#error pin.c: Unsupported platform.
#endif
 
 
 
oosmos/Classes/pin.c

2 The Sockets Class 'sock'

The sock class implements a non-blocking TCP/IP interface that can be used cooperatively with the collection of OOSMOS sync functions to provide non-threaded, object-oriented communication capability.

2.1 sock Code

The source code for the sock class is below. There is a good overview and example of how to use sock.h and sock.c in the Sockets example.

2.1.1 sock.h

1
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
[GPLv2]
 
#ifndef sock_h
#define sock_h
 
typedef struct sockTag sock;
 
extern sock * sockNew(void);
extern void sockDelete(sock * pSock);
 
extern int sockGetLastError(void);
 
extern bool sockConnect(sock * pSock, uint32_t IP_HostByteOrder, int Port);
 
extern bool sockListen(sock * pSock, int Port, int Backlog);
extern bool sockAccepted(sock * pListenerSock, sock ** ppNewSock);
 
extern bool sockSend(sock * pSock, const void * pData, size_t Bytes);
 
extern bool sockClose(sock * pSock);
 
extern bool sockReceive(sock * pSock, void * pBuffer, size_t BufferSize, size_t * pBytesReceived);
extern bool sockReceiveUntilContent(sock * pSock, void * pBufferArg, size_t BufferSize, const void * pContent,
size_t ContentLength, size_t * pBytesReceived);
 
extern void sockSubscribeClosedEvent(sock * pSock, oosmos_sQueue * pEventQueue, int ClosedEventCode, void * pContext);
 
extern bool sockIsIpAddress(const char * pString);
extern uint32_t sockDotToIP_HostByteOrder(const char * pDot);
 
#endif
oosmos/Classes/sock.h

2.1.2 sock.c

1
22
23
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
[GPLv2]
 
#includes...
#include "oosmos.h"
#include "sock.h"
 
//
// The following preprocessor conditional is used to make the code
// that follows it portable. Tested on Windows and Linux.
//
 
#ifdef _WIN32
#include <winsock2.h>
 
typedef SOCKET sock_tSocket;
 
typedef int socklen_t;
typedef int sendsize_t;
typedef int recvsize_t;
#define IOCTL ioctlsocket
#define CLOSE closesocket
 
#define sockEWOULDBLOCK WSAEWOULDBLOCK
#define sockECONNRESET WSAECONNRESET
#define sockECONNREFUSED WSAECONNREFUSED
#define sockEINPROGRESS WSAEINPROGRESS
#define sockECONNABORTED WSAECONNABORTED
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <signal.h>
 
typedef int sock_tSocket;
 
// socklen_t is already defined in Linux
typedef ssize_t sendsize_t;
typedef ssize_t recvsize_t;
#define IOCTL ioctl
#define CLOSE close
 
#define sockEWOULDBLOCK EWOULDBLOCK
#define sockECONNRESET ECONNRESET
#define sockECONNREFUSED ECONNREFUSED
#define sockEINPROGRESS EINPROGRESS
#define sockECONNABORTED ECONNABORTED
#endif
 
#define RECEIVE_BUFFER_SIZE 500
 
struct sockTag
{
sock_tSocket m_Socket;
 
// Connect...
bool m_FirstConnect;
 
// Send...
const char * m_pSendData;
size_t m_BytesToSend;
 
bool m_Closed;
 
size_t m_BytesReceived;
char * m_pReceiveBuffer;
 
oosmos_sSubscriberList m_ClosedEvent[1];
};
 
static void Init(void)
{
#ifdef _WIN32
static bool Started = false;
 
if (!Started) {
WSADATA wsaData;
 
WORD wVersionRequested = MAKEWORD(1, 1);
WSAStartup(wVersionRequested, &wsaData);
Started = true;
}
#else
signal(SIGPIPE, SIG_IGN);
#endif
}
 
static bool WouldBlock(sock * pSock, recvsize_t BytesReceived)
{
if (BytesReceived == -1)
return sockGetLastError() == sockEWOULDBLOCK;
 
return false;
}
 
static bool CheckSendError(sock * pSock, sendsize_t BytesSent)
{
if (BytesSent <= 0) {
const int Error = sockGetLastError();
 
if (Error == sockEWOULDBLOCK)
return false;
 
oosmos_SubscriberListNotify(pSock->m_ClosedEvent);
pSock->m_Closed = true;
return true;
}
 
return false;
}
 
static bool CheckReceiveError(sock * pSock, recvsize_t BytesReceived)
{
if (BytesReceived == -1) {
const int Error = sockGetLastError();
 
if (Error == sockEWOULDBLOCK)
return false;
 
oosmos_SubscriberListNotify(pSock->m_ClosedEvent);
pSock->m_Closed = true;
return true;
}
else if (BytesReceived == 0) {
oosmos_SubscriberListNotify(pSock->m_ClosedEvent);
pSock->m_Closed = true;
return true;
}
 
return false;
}
 
static sock * New(int Socket)
{
sock * pSock = (sock *) malloc(sizeof(sock));
 
//
// Make the socket non-blocking.
//
{
unsigned long IsNonBlocking = 1;
IOCTL(Socket, FIONBIO, &IsNonBlocking);
}
 
pSock->m_Socket = Socket;
pSock->m_Closed = false;
pSock->m_FirstConnect = true;
pSock->m_pSendData = NULL;
 
pSock->m_BytesReceived = 0;
pSock->m_pReceiveBuffer = (char *) malloc(RECEIVE_BUFFER_SIZE);
 
oosmos_SubscriberListInit(pSock->m_ClosedEvent);
 
return pSock;
}
 
extern int sockGetLastError(void)
{
#ifdef _WIN32
return WSAGetLastError();
#else
return errno;
#endif
}
 
extern uint32_t sockDotToIP_HostByteOrder(const char * pDot)
{
int A, B, C, D;
sscanf(pDot, "%d.%d.%d.%d", &A, &B, &C, &D);
return A << 24 | B << 16 | C << 8 | D;
}
 
extern bool sockIsIpAddress(const char * pString)
{
while (*pString != '\0') {
if (!(*pString == '.' || isdigit(*pString)))
return false;
 
pString++;
}
 
return true;
}
 
extern void sockSubscribeClosedEvent(sock * pSock, oosmos_sQueue * pEventQueue, int ClosedEventCode, void * pContext)
{
oosmos_SubscriberListAdd(pSock->m_ClosedEvent, pEventQueue, ClosedEventCode, pContext);
}
 
extern bool sockListen(sock * pSock, int Port, int Backlog)
{
struct sockaddr_in Listener;
 
Listener.sin_family = AF_INET;
Listener.sin_addr.s_addr = INADDR_ANY;
Listener.sin_port = htons((short) Port);
 
if (bind(pSock->m_Socket, (struct sockaddr *) &Listener, sizeof(Listener)) < 0) {
perror("bind failed. Error");
return false;
}
 
listen(pSock->m_Socket, Backlog);
return true;
}
 
extern bool sockAccepted(sock * pListenerSock, sock ** ppNewSock)
{
struct sockaddr_in Server;
 
socklen_t ServerSize = sizeof(Server);
const int ServerSocket = accept(pListenerSock->m_Socket, (struct sockaddr *) &Server, &ServerSize);
 
if (ServerSocket == -1)
return false;
 
*ppNewSock = New(ServerSocket);
return true;
}
 
extern bool sockReceive(sock * pSock, void * pBuffer, size_t BufferSize, size_t * pBytesReceived)
{
if (pSock->m_Closed)
return false;
 
if (pSock->m_BytesReceived < BufferSize) {
recvsize_t NewBytesReceived = recv(pSock->m_Socket,
pSock->m_pReceiveBuffer+pSock->m_BytesReceived,
RECEIVE_BUFFER_SIZE-pSock->m_BytesReceived,
0);
 
if (WouldBlock(pSock, NewBytesReceived)) {
//
// If we would block, then there are no bytes available to read, but there
// may be bytes already in the receive buffer, so we need to continue to
// allow those bytes to be consumed.
//
NewBytesReceived = 0;
}
else if (CheckReceiveError(pSock, NewBytesReceived)) {
return false;
}
 
pSock->m_BytesReceived += NewBytesReceived;
}
 
if (pSock->m_BytesReceived > 0) {
// Consume...
const size_t Span = oosmos_Min(pSock->m_BytesReceived, BufferSize);
memcpy(pBuffer, pSock->m_pReceiveBuffer, Span);
*pBytesReceived = Span;
 
// Shift...
pSock->m_BytesReceived -= Span;
memcpy(pSock->m_pReceiveBuffer, pSock->m_pReceiveBuffer+Span, pSock->m_BytesReceived);
 
return true;
}
 
return false;
}
 
//
// Receive and return bytes up to and including the bytes specified by pContentArg & ContentLength.
//
extern bool sockReceiveUntilContent(sock * pSock, void * pBufferArg, size_t BufferSize,
const void * pContentArg, size_t ContentLength, size_t * pBytesReceived)
{
char * pBuffer = (char * ) pBufferArg;
char * pContent = (char * ) pContentArg;
 
const int BufferBytesAvailable = RECEIVE_BUFFER_SIZE-pSock->m_BytesReceived;
 
if (pSock->m_Closed)
return false;
 
if (BufferBytesAvailable > 0) {
recvsize_t NewBytesReceived = recv(pSock->m_Socket,
pSock->m_pReceiveBuffer+pSock->m_BytesReceived,
BufferBytesAvailable, 0);
 
if (WouldBlock(pSock, NewBytesReceived)) {
//
// If we would block, then there are no bytes available to read, but there
// may be bytes already in the receive buffer, so we need to continue to
// allow those bytes to be consumed.
//
NewBytesReceived = 0;
}
else if (CheckReceiveError(pSock, NewBytesReceived)) {
return false;
}
 
pSock->m_BytesReceived += NewBytesReceived;
}
 
if (pSock->m_BytesReceived >= ContentLength) {
const char * pFirst = pSock->m_pReceiveBuffer;
const char * pLast = pFirst + pSock->m_BytesReceived - ContentLength;
 
//
// Search for content bytes in the receive buffer.
//
for (; pFirst <= pLast; pFirst++) {
if (memcmp(pFirst, pContent, ContentLength) == 0) {
// Consume...
const size_t Span = oosmos_Min((pFirst-pSock->m_pReceiveBuffer)+ContentLength, BufferSize);
memcpy(pBuffer, pSock->m_pReceiveBuffer, Span);
*pBytesReceived = Span;
 
// Shift...
pSock->m_BytesReceived -= Span;
memcpy(pSock->m_pReceiveBuffer, pSock->m_pReceiveBuffer+Span, pSock->m_BytesReceived);
return true;
}
}
 
return false;
}
 
return false;
}
 
extern bool sockSend(sock * pSock, const void * pData, size_t Bytes)
{
if (pSock->m_Closed)
return false;
 
if (pSock->m_pSendData == NULL) {
pSock->m_pSendData = (char *) pData;
pSock->m_BytesToSend = Bytes;
}
 
{
const sendsize_t BytesSent = send(pSock->m_Socket, pSock->m_pSendData, pSock->m_BytesToSend, 0);
 
if (CheckSendError(pSock, BytesSent))
return false;
 
pSock->m_pSendData += BytesSent;
pSock->m_BytesToSend -= BytesSent;
 
if (pSock->m_BytesToSend == 0) {
pSock->m_pSendData = NULL;
return true;
}
}
 
return false;
}
 
extern bool sockConnect(sock * pSock, uint32_t IP_HostByteOrder, int Port)
{
fd_set fd_out;
struct timeval tv;
int largest_sock;
int Code;
int Writable;
socklen_t SizeofCode;
 
if (pSock->m_Closed)
return false;
 
if (pSock->m_FirstConnect) {
struct sockaddr_in server;
 
server.sin_addr.s_addr = htonl(IP_HostByteOrder);
server.sin_family = AF_INET;
server.sin_port = htons((uint16_t) Port);
 
connect(pSock->m_Socket, (struct sockaddr *) &server, sizeof(server));
 
pSock->m_FirstConnect = false;
}
 
FD_ZERO( &fd_out );
FD_SET( pSock->m_Socket, &fd_out );
 
largest_sock = pSock->m_Socket;
 
tv.tv_sec = 0;
tv.tv_usec = 0;
 
select(largest_sock+1, NULL, &fd_out, NULL, &tv);
Writable = FD_ISSET(pSock->m_Socket, &fd_out);
 
SizeofCode = sizeof(Code);
 
getsockopt(pSock->m_Socket, SOL_SOCKET, SO_ERROR, (char *) &Code, &SizeofCode);
 
if (Writable && Code == 0) {
pSock->m_FirstConnect = true;
return true;
}
 
if (Code == sockECONNREFUSED) {
sockClose(pSock);
}
 
return false;
}
 
extern bool sockClose(sock * pSock)
{
CLOSE(pSock->m_Socket);
pSock->m_Closed = true;
 
return true;
}
 
extern sock * sockNew(void)
{
int Socket;
 
Init();
 
Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
return New(Socket);
}
 
extern void sockDelete(sock * pSock)
{
free(pSock->m_pReceiveBuffer);
sockClose(pSock);
}
oosmos/Classes/sock.c

3 The Toggle Class 'toggle'

The portable toggle class is useful for toggling digital I/O pins. Because pin manipulation has been abstracted in the OOSMOS environment using the pin class, toggle can be used to toggle pins connected to any device on any supported board. Therefore you can toggle a relay, a buzzer, an LED, whatever.

3.1 toggle Requirements

  • A toggle object turns a pin On, then Off, then On again, forever.
  • A toggle object is started in the On state.
  • A toggle object in the On state shall transition to the Off state after a specified number of "On" milliseconds.
  • A toggle object in the Off state shall transition to the On state after a specified number of "Off" milliseconds.

3.2 toggle Statechart

Figure 1. toggle Statechart
toggle.h defines two things, the toggle data type and the method to create toggle objects, toggleNew.
1
22
23
24
25
26
27
28
29
30
31
32
[GPLv2]
 
#ifndef toggle_h
#define toggle_h
 
#include "pin.h"
 
typedef struct toggleTag toggle;
 
extern toggle * toggleNew(pin * pPin, int TimeOnMS, int TimeOffMS);
 
#endif
oosmos/Classes/toggle.h
toggle.c implements the toggleNew function that creates instances of toggle objects. It also contains the toggle state machine implementation for the two states On and Off. These are implemented in the two functions On_State_Code and Off_State_Code.
Refer to the OOSMOS User's Guide about how an OOSMOS class is built.
1
22
23
26
27
30
31
32
41
42
43
59
60
61
73
74
75
[GPLv2]
 
#include ...
 
#ifndef toggleMAX ...
 
struct toggleTag
{...}
 
static bool On_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{...}
 
static bool Off_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{...}
 
extern toggle * toggleNew(pin * pPin, const int TimeOnMS, const int TimeOffMS)
{...}
oosmos/Classes/toggle.c_state

3.3 toggle Execution

Figure 2, below, shows two instantiated, running toggle objects. Toggle object A is currently in the On state and Toggle object B is in the Off state.
Figure 2. Snapshot of two toggle objects during execution

4 The Switch Class 'sw'

The switch class (sw) allows an object to react when a switch is pressed and/or released.
It is implemented as an OOSMOS active object (as opposed to a passive or state machine object), so it is an excellent, uncluttered example of how to implement active objects.

4.1 sw Requirements

  • A sw object shall accept multiple close event subscribers.
  • A sw object shall accept multiple open event subscribers.
  • When the associated switch is depressed, publish the close event to all subscribers.
  • When the associated switch is released, publish the open event to all subscribers.

4.2 sw Code

1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[GPLv2]
 
#ifndef sw_h
#define sw_h
 
#include "oosmos.h"
#include "pin.h"
 
typedef struct swTag sw;
 
extern sw * swNew(pin * pPin);
extern sw * swNewDetached(pin * pPin);
 
extern void swSubscribeOpenEvent(sw * pSwitch, oosmos_sQueue * pQueue, int OpenEventCode, void * pContext);
extern void swSubscribeCloseEvent(sw * pSwitch, oosmos_sQueue * pQueue, int CloseEventCode, void * pContext);
 
extern bool swIsOpen(const sw * pSwitch);
extern bool swIsClosed(const sw * pSwitch);
extern void swRunStateMachine(void * pSwitch);
 
#endif
oosmos/Classes/sw.h
1
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
[GPLv2]
 
#ifndef swMaxSwitches
#define swMaxSwitches 20
#endif
 
#ifndef swMaxCloseSubscribers
#define swMaxCloseSubscribers 1
#endif
 
#ifndef swMaxOpenSubscribers
#define swMaxOpenSubscribers 1
#endif
 
//===================================
 
#include "oosmos.h"
#include "pin.h"
#include "sw.h"
 
typedef enum {
Unknown_State = 1,
Open_State,
Closed_State,
} eStates;
 
struct swTag
{
pin * m_pPin;
eStates m_State;
 
oosmos_sActiveObject m_ActiveObject;
oosmos_sSubscriberList m_CloseEvent[swMaxCloseSubscribers];
oosmos_sSubscriberList m_OpenEvent[swMaxOpenSubscribers];
};
 
extern sw * swNewDetached(pin * pPin)
{
oosmos_Allocate(pSwitch, sw, swMaxSwitches, NULL);
 
pSwitch->m_pPin = pPin;
pSwitch->m_State = Unknown_State;
 
oosmos_SubscriberListInit(pSwitch->m_CloseEvent);
oosmos_SubscriberListInit(pSwitch->m_OpenEvent);
 
return pSwitch;
}
 
extern sw * swNew(pin * pPin)
{
sw * pSwitch = swNewDetached(pPin);
 
oosmos_RegisterActiveObject(pSwitch, swRunStateMachine, &pSwitch->m_ActiveObject);
 
return pSwitch;
}
 
extern void swSubscribeCloseEvent(sw * pSwitch, oosmos_sQueue * pQueue, int CloseEventCode, void * pContext)
{
oosmos_SubscriberListAdd(pSwitch->m_CloseEvent, pQueue, CloseEventCode, pContext);
}
 
extern void swSubscribeOpenEvent(sw * pSwitch, oosmos_sQueue * pQueue, int OpenEventCode, void * pContext)
{
oosmos_SubscriberListAdd(pSwitch->m_OpenEvent, pQueue, OpenEventCode, pContext);
}
 
extern bool swIsOpen(const sw * pSwitch)
{
return pSwitch->m_State == Open_State;
}
 
extern bool swIsClosed(const sw * pSwitch)
{
return pSwitch->m_State == Closed_State;
}
 
extern void swRunStateMachine(void * pObject)
{
sw * pSwitch = (sw *) pObject;
 
switch (pSwitch->m_State) {
case Open_State:
if (pinIsOn(pSwitch->m_pPin)) {
pSwitch->m_State = Closed_State;
oosmos_SubscriberListNotify(pSwitch->m_CloseEvent);
}
 
break;
case Closed_State:
if (pinIsOff(pSwitch->m_pPin)) {
pSwitch->m_State = Open_State;
oosmos_SubscriberListNotify(pSwitch->m_OpenEvent);
}
 
break;
case Unknown_State:
pSwitch->m_State = pinIsOn(pSwitch->m_pPin) ? Closed_State : Open_State;
break;
}
}
oosmos/Classes/sw.c

5 The Matrix Class 'matrix'

This class implements a switch matrix. A switch matrix is an optimization technique that reduces the number of processor pins required to detect closure of a large number of switches.
If you have a small number of switches, then you can connect directly to processor pins but as the number of switches grows, it becomes more likely that the processor does not have a sufficient number of pins to support them.
This class uses a matrix of switches organized in rows and columns. Each row is connected to a processor pin. Likewise, each column is connected to a processor pin. As an example, using this technique, a 4 by 4 keypad matrix of 16 switches will use only 8 processor pins. If we didn't use this matrix approach, the same 16 switches would require 16 processor pins.

5.1 matrix Requirements

  • Energize each row in succession. Check columns for switch closure.
  • Run switch state machine only when row is fully energized. (The sw object will notify the user when the switch closes or opens.)

5.2 matrix Statechart

Figure 3. matrix Statechart

5.3 matrix Code

1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[GPLv2]
 
#ifndef matrix_h
#define matrix_h
 
#include "pin.h"
#include "sw.h"
 
typedef struct matrixTag matrix;
 
extern matrix * matrixNew(int Rows, int Columns, ...);
extern pin * matrixGetColumnPin(const matrix * pMatrix, int Column);
extern void matrixAssignSwitch(matrix * pMatrix, sw * pSwitch, int Row, int Column);
 
#endif
oosmos/Classes/matrix.h
1
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
[GPLv2]
 
#ifndef matrixMAX
#define matrixMAX 1
#endif
 
#ifndef matrixMAX_ROWS
#define matrixMAX_ROWS 8
#endif
 
#ifndef matrixMAX_COLS
#define matrixMAX_COLS 8
#endif
 
const static int RowOnSettleTimeUS = 50;
const static int RowOffSettleTimeUS = 50;
 
#include "oosmos.h"
#include "matrix.h"
#include "sw.h"
#include "pin.h"
#include <stdarg.h>
 
struct matrixTag
{
oosmos_sStateMachineNoQueue(StateMachine);
oosmos_sLeaf Running_State;
 
pin * m_pRowPins[matrixMAX_ROWS];
pin * m_pColumnPins[matrixMAX_COLS];
sw * m_pSwitch[matrixMAX_ROWS][matrixMAX_COLS];
 
int m_Rows;
int m_Columns;
 
//
// State variables...
//
int m_CurrentRowIndex;
};
 
static void InterrogateColumns(matrix * pMatrix)
{
const int RowIndex = pMatrix->m_CurrentRowIndex;
const int Columns = pMatrix->m_Columns;
 
int ColumnIndex;
 
for (ColumnIndex = 0; ColumnIndex < Columns; ColumnIndex++) {
sw * pSwitch = pMatrix->m_pSwitch[RowIndex][ColumnIndex];
 
if (pSwitch != NULL) {
swRunStateMachine(pSwitch);
}
}
}
 
static void AddRow(matrix * pMatrix, const int Row, pin * pPin)
{
const int RowIndex = Row - 1;
 
pMatrix->m_pRowPins[RowIndex] = pPin;
 
if (Row > pMatrix->m_Rows)
pMatrix->m_Rows = Row;
}
 
static void AddColumn(matrix * pMatrix, const int Column, pin * pPin)
{
const int ColumnIndex = Column - 1;
pMatrix->m_pColumnPins[ColumnIndex] = pPin;
 
if (Column > pMatrix->m_Columns)
pMatrix->m_Columns = Column;
}
 
static bool Running_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
matrix * pMatrix = (matrix *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE:
oosmos_AsyncBegin(pRegion);
while (true) {
for (pMatrix->m_CurrentRowIndex = 0; pMatrix->m_CurrentRowIndex < pMatrix->m_Rows; pMatrix->m_CurrentRowIndex++) {
if (pMatrix->m_pRowPins[pMatrix->m_CurrentRowIndex] == NULL)
continue;
 
pinOn(pMatrix->m_pRowPins[pMatrix->m_CurrentRowIndex]);
oosmos_AsyncDelayMS(pRegion, RowOnSettleTimeUS);
 
InterrogateColumns(pMatrix);
 
pinOff(pMatrix->m_pRowPins[pMatrix->m_CurrentRowIndex]);
oosmos_AsyncDelayMS(pRegion, RowOffSettleTimeUS);
}
}
oosmos_AsyncEnd(pRegion);
return true;
}
 
return false;
}
 
extern matrix * matrixNew(int Rows, int Columns, ...)
{
oosmos_Allocate(pMatrix, matrix, matrixMAX, NULL);
 
pMatrix->m_Rows = 0;
pMatrix->m_Columns = 0;
 
int RowIndex;
 
for (RowIndex = 0; RowIndex < matrixMAX_ROWS; RowIndex++) {
pMatrix->m_pRowPins[RowIndex] = NULL;
 
int ColumnIndex;
 
for (ColumnIndex = 0; ColumnIndex < matrixMAX_COLS; ColumnIndex++) {
pMatrix->m_pColumnPins[ColumnIndex] = NULL;
pMatrix->m_pSwitch[RowIndex][ColumnIndex] = NULL;
}
}
 
// StateName Parent Default
// =====================================================
oosmos_StateMachineInitNoQueue(pMatrix, StateMachine, NULL, Running_State);
oosmos_LeafInit (pMatrix, Running_State, StateMachine );
 
va_list ArgList;
va_start(ArgList, Columns);
 
int Row;
 
for (Row = 1; Row <= Rows; Row += 1)
AddRow(pMatrix, Row, va_arg(ArgList, pin *));
 
int Column;
 
for (Column = 1; Column <= Columns; Column += 1)
AddColumn(pMatrix, Column, va_arg(ArgList, pin *));
 
va_end(ArgList);
 
return pMatrix;
}
 
extern void matrixAssignSwitch(matrix * pMatrix, sw * pSwitch, const int Row, const int Column)
{
const int RowIndex = Row - 1;
const int ColumnIndex = Column - 1;
 
//
// Check if this Row/Column slot has already been assigned.
//
if (pMatrix->m_pSwitch[RowIndex][ColumnIndex] != NULL)
while (true);
 
pMatrix->m_pSwitch[RowIndex][ColumnIndex] = pSwitch;
}
 
extern pin * matrixGetColumnPin(const matrix * pMatrix, const int Column)
{
const int ColumnIndex = Column - 1;
return pMatrix->m_pColumnPins[ColumnIndex];
}
oosmos/Classes/matrix.c

6 The Print Class 'prt'

The prt class, short for print, exposes the single function prtFormatted (see prt.h line 32 and prt.c line 48) to allow for portable output. On platforms like Windows and Linux, you can use printf but platforms like Arduino use the serial port and the Serial object to display output. Using prtFormatted allows your code to be more portable. The prt class is used in many of the OOSMOS examples for this purpose.
Note that on Arduino platforms, you will need to specify the baud rate for the serial port. See prt.h line 29 as well as examples Switch and Matrix.
Code for prt:
1
22
23
24
25
26
27
28
29
30
31
32
33
34
[GPLv2]
 
#ifndef prt_h
#define prt_h
 
#include "oosmos.h"
 
#if defined(ARDUINO)
extern unsigned long prtArduinoBaudRate;
#endif
 
extern bool prtFormatted(const char * pFormat, ...);
 
#endif
oosmos/Classes/prt.h
1
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
[GPLv2]
 
#define MaxBuffer 100
 
#include <stdarg.h>
#include <stdio.h>
#include "oosmos.h"
#include "prt.h"
 
static void Init()
{
static bool First = true;
 
if (!First)
return;
 
First = false;
 
#if defined(ARDUINO)
{
Serial.begin(prtArduinoBaudRate);
}
#elif defined(__PIC32MX)
DBINIT();
#endif
}
 
extern bool prtFormatted(const char * pFormat, ...)
{
static char Buffer[MaxBuffer];
 
va_list ArgList;
va_start(ArgList, pFormat);
vsnprintf(Buffer, MaxBuffer, pFormat, ArgList);
va_end(ArgList);
 
Init();
 
#if defined(ARDUINO)
Serial.print(Buffer);
#elif defined(__PIC32MX)
DBPRINTF("%s", Buffer);
#else
printf("%s", Buffer);
#endif
 
return true;
}
oosmos/Classes/prt.c

7 The Keyer Class 'keyer'

The keyer object is common and portable to multiple target implementations because of the portable abstraction of the pin class.

7.1 keyer Notes & Requirements

Refer to the keyer statechart below as your read the following bullets.
  • When the state machine is started, it will enter the Active state and then immediately enter the default Idle state. The state machine stays in this state until either one of the paddles is pressed.
  • When the Dah paddle is tapped (pressed and immediately released), the state machine transitions to the Dah state which immediately enters the default Tone state.
  • At entry to the Tone state, the ENTER code is executed and the speaker is turned on. The state machine stays in the Tone state (and the tone continues to sound) until the state times out after m_DahTimeMS milliseconds.
  • As part of the transition from the Tone state to the Space state, the EXIT code of the Tone state is executed, turning off the speaker.
  • The state machine stays in the Space state until the timeout occurs after m_SpaceTimeMS milliseconds.
  • It is important to understand that while in either the Tone or Space state, we are also in the Dah state. Any time we are in the Dah state, when the state machine is executed the Dah state's oosmos_INSTATE event is fired, allowing us to determine if the Dit paddle has been pressed. If it has, we remember this occurrence for the conditional that comes later.
  • When the Space state times out, the state machine transitions from the Space state and then executes the conditional (diamond shape) to determine whether to transition to the Active/Idle state or to the Dit state.
  • The Dit state behaves very similar to the Dah state.

7.2 keyer Statechart

Figure 4. Keyer Statechart

7.3 keyer Code

As usual, the keyer object has the interface in the .h file and the implementation in the .c file.
1
22
23
24
25
26
27
28
29
30
31
32
[GPLv2]
 
#ifndef keyer_h
#define keyer_h
 
#include "pin.h"
 
typedef struct keyerTag keyer;
 
extern keyer * keyerNew(pin * pDahPin, pin * pDitPin, pin * pSpeakerPin, int WPM);
 
#endif
oosmos/Classes/keyer.h
Nearly all the content is collapsed in the following C code, which allows you to see an overall map of the functions that make up the keyer implementation.
Expand any of the functions that interest you by tapping the corresponding plus sign.
1
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
[GPLv2]
 
#include "oosmos.h"
#include "keyer.h"
#include "prt.h"
 
struct keyerTag
{
oosmos_sStateMachineNoQueue(StateMachine);
oosmos_sLeaf Idle_State;
oosmos_sComposite Dah_State;
oosmos_sLeaf Dah_Tone_State;
oosmos_sComposite Dit_State;
oosmos_sLeaf Dit_Tone_State;
 
pin * m_pDahPin;
pin * m_pDitPin;
pin * m_pSpeakerPin;
 
int m_DitTimeMS;
int m_DahTimeMS;
int m_SpaceTimeMS;
 
struct {
struct {
bool m_DitWasPressed;
} Dah;
 
struct {
bool m_DahWasPressed;
} Dit;
} m_StateData;
};
 
static void CheckDahIsPressed(keyer * pKeyer)
{
if (!pKeyer->m_StateData.Dit.m_DahWasPressed && pinIsOn(pKeyer->m_pDahPin))
pKeyer->m_StateData.Dit.m_DahWasPressed = true;
}
 
static void CheckDitIsPressed(keyer * pKeyer)
{
if (!pKeyer->m_StateData.Dah.m_DitWasPressed && pinIsOn(pKeyer->m_pDitPin))
pKeyer->m_StateData.Dah.m_DitWasPressed = true;
}
 
static bool Idle_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
keyer * pKeyer = (keyer *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE:
if (pinIsOn(pKeyer->m_pDahPin))
return oosmos_Transition(pRegion, &pKeyer->Dah_State);
else if (pinIsOn(pKeyer->m_pDitPin))
return oosmos_Transition(pRegion, &pKeyer->Dit_State);
}
 
return false;
}
 
static bool Dah_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
keyer * pKeyer = (keyer *) pObject;
 
switch (pEvent->Code) {
case oosmos_ENTER:
pKeyer->m_StateData.Dah.m_DitWasPressed = false;
return true;
case oosmos_INSTATE:
CheckDitIsPressed(pKeyer);
return true;
}
 
return false;
}
 
static bool Dah_Tone_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
keyer * pKeyer = (keyer *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE:
oosmos_AsyncBegin(pRegion);
pinOn(pKeyer->m_pSpeakerPin);
oosmos_AsyncDelayMS(pRegion, pKeyer->m_DahTimeMS);
pinOff(pKeyer->m_pSpeakerPin);
oosmos_AsyncDelayMS(pRegion, pKeyer->m_SpaceTimeMS);
oosmos_AsyncEnd(pRegion);
 
if (pKeyer->m_StateData.Dah.m_DitWasPressed)
return oosmos_Transition(pRegion, &pKeyer->Dit_State);
 
return oosmos_Transition(pRegion, &pKeyer->Idle_State);
}
 
return false;
}
 
static bool Dit_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
keyer * pKeyer = (keyer *) pObject;
 
switch (pEvent->Code) {
case oosmos_ENTER:
pKeyer->m_StateData.Dit.m_DahWasPressed = false;
return true;
case oosmos_INSTATE:
CheckDahIsPressed(pKeyer);
return true;
}
 
return false;
}
 
static bool Dit_Tone_State_Code(void * pObject, oosmos_sRegion * pRegion, const oosmos_sEvent * pEvent)
{
keyer * pKeyer = (keyer *) pObject;
 
switch (pEvent->Code) {
case oosmos_INSTATE:
oosmos_AsyncBegin(pRegion);
pinOn(pKeyer->m_pSpeakerPin);
oosmos_AsyncDelayMS(pRegion, pKeyer->m_DitTimeMS);
pinOff(pKeyer->m_pSpeakerPin);
oosmos_AsyncDelayMS(pRegion, pKeyer->m_SpaceTimeMS);
oosmos_AsyncEnd(pRegion);
 
if (pKeyer->m_StateData.Dit.m_DahWasPressed)
return oosmos_Transition(pRegion, &pKeyer->Dah_State);
 
return oosmos_Transition(pRegion, &pKeyer->Idle_State);
}
 
return false;
}
 
extern keyer * keyerNew(pin * pDahPin, pin * pDitPin, pin * pSpeakerPin, const int WPM)
{
oosmos_Allocate(pKeyer, keyer, 2, NULL);
 
// State Name Parent Default
// =====================================================
oosmos_StateMachineInitNoQueue(pKeyer, StateMachine, NULL, Idle_State );
oosmos_LeafInit (pKeyer, Idle_State, StateMachine );
oosmos_CompositeInit (pKeyer, Dah_State, StateMachine, Dah_Tone_State);
oosmos_LeafInit (pKeyer, Dah_Tone_State, Dah_State );
oosmos_CompositeInit (pKeyer, Dit_State, StateMachine, Dit_Tone_State);
oosmos_LeafInit (pKeyer, Dit_Tone_State, Dit_State );
 
pKeyer->m_pDahPin = pDahPin;
pKeyer->m_pDitPin = pDitPin;
pKeyer->m_pSpeakerPin = pSpeakerPin;
pKeyer->m_DitTimeMS = 1200 / WPM;
pKeyer->m_DahTimeMS = pKeyer->m_DitTimeMS * 3;
pKeyer->m_SpaceTimeMS = pKeyer->m_DitTimeMS;
 
oosmos_DebugCode(
oosmos_Debug(&pKeyer->StateMachine, true, NULL);
)
 
return pKeyer;
}
oosmos/Classes/keyer.c

8 The Linear Regression Class 'reg'

8.1 reg Overview

The reg class implements the ability to do linear regressions on a set of X and Y values and then predict new Y values along the slope given an X value.
See this wikipedia article for a complete description of the utility of this powerful tool.
We find this approach far superior to calculating a moving average because averages always yield information about the past, where a linear regression will not only yield contemporaneous data, it can actually predict the future.

8.2 reg Requirements

  • Shall accept a set of X and Y data values from which a linear regression slope and Y intercept will be computed.
  • An interface shall be provided to allow the prediction of a Y value along the slope of the linear regression given an X value.

8.3 reg Code

8.3.1 reg.h

1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[GPLv2]
 
#ifndef reg_h
#define reg_h
 
typedef struct regTag reg;
 
typedef struct
{
float X;
float Y;
} regSample;
 
extern reg * regNew(void);
extern void regSamples(reg * pReg, const regSample * pSamples, int Samples);
extern float regPredictY(const reg * pReg, float X);
 
#endif
oosmos/Classes/reg.h

8.3.2 reg.c

1
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
[GPLv2]
 
#include "reg.h"
 
struct regTag
{
float m_Intercept;
float m_Slope;
};
 
static float MeanOfX(const regSample * pSamples, int Samples)
{
float Sum = 0.0f;
int SampleIndex;
 
for (SampleIndex = 0; SampleIndex < Samples; SampleIndex++)
Sum += pSamples[SampleIndex].X;
 
return Sum / Samples;
}
 
static float MeanOfY(const regSample * pSamples, int Samples)
{
float Sum = 0.0f;
int SampleIndex;
 
for (SampleIndex = 0; SampleIndex < Samples; SampleIndex++)
Sum += pSamples[SampleIndex].Y;
 
return Sum / Samples;
}
 
extern reg * regNew(void)
{
static reg Reg[1];
reg * pReg = &Reg[0];
 
return pReg;
}
 
extern void regSamples(reg * pReg, const regSample * pSamples, int Samples)
{
const float MeanX = MeanOfX(pSamples, Samples);
const float MeanY = MeanOfY(pSamples, Samples);
 
float SumXY = 0.0f;
float SumXX = 0.0f;
int SampleIndex;
 
for (SampleIndex = 0; SampleIndex < Samples; SampleIndex++) {
const regSample * pSample = &pSamples[SampleIndex];
 
const float XiMinusMeanX = pSample->X - MeanX;
const float YiMinusMeanY = pSample->Y - MeanY;
 
SumXY += XiMinusMeanX * YiMinusMeanY;
SumXX += XiMinusMeanX * XiMinusMeanX;
}
 
pReg->m_Slope = SumXY / SumXX;
pReg->m_Intercept = MeanY - pReg->m_Slope * MeanX;
}
 
extern float regPredictY(const reg * pReg, float X)
{
return pReg->m_Slope * X + pReg->m_Intercept;
}
oosmos/Classes/reg.c

8.3.3 regtest.c

1
22
23
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
[GPLv2]
 
#includes...
 
static void Predict(reg * pReg, float Value)
{
prtFormatted("Predict value at time %.4f: %.4f\n", Value, regPredictY(pReg, Value));
}
 
extern void regtestNew(void)
{
static const regSample Samples[] = {
{ 2.0f, 3.0f },
{ 3.0f, 6.7f },
{ 4.0f, 7.0f },
{ 5.0f, 8.0f },
{ 6.0f, 9.0f },
};
 
reg * pReg = regNew();
 
regSamples(pReg, Samples, sizeof(Samples)/sizeof(Samples[0]));
 
Predict(pReg, 5.0f);
Predict(pReg, 6.0f);
Predict(pReg, 7.0f);
Predict(pReg, 8.0f);
Predict(pReg, 9.0f);
}
oosmos/Classes/Tests/regtest.c

8.3.4 regtest Results

1
2
3
4
5
6
7
 
Predict value at time 5.0000: 8.0700
Predict value at time 6.0000: 9.4000
Predict value at time 7.0000: 10.7300
Predict value at time 8.0000: 12.0600
Predict value at time 9.0000: 13.3900
 
regtest Results
To double check our results, we use the Excel formula FORECAST on the same values in our test case. FORECAST does the same thing as our regPredictY function.
To help you visualize the process, we graph the sample values (blue line) and then graph a linear regression trendline through those values (the black line). (See figure 5.) We then run the FORECAST formula for each value of column labeled Predict Time, resulting in the values in column labeled Position At Time.
(If you'd like to experiment, the Excel spreadsheet binary is here.)
Figure 5. Excel FORECAST Formula
Copyright © 2014-2016  OOSMOS, LLC