Reusable Classes
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of toggle
1.4 Classic State-Based Version
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'accum'
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of toggle
1.4 Classic State-Based Version
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'accum'
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of toggle
1.4 Classic State-Based Version
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'accum'
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of toggle
1.4 Classic State-Based Version
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'accum'
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of toggl...
1.4 Classic State-Based Version
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'accum...
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of togg...
1.4 Classic State-Based Version
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'accu...
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of tog...
1.4 Classic State-Based Versio...
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'acc...
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
1. The Toggle Class 'toggle'
1.1 toggle Requirements
1.2 Common toggle Interface
1.3 Two Implementations of to...
1.4 Classic State-Based Versi...
1.5 toggle Execution
1.6 State Thread Version
2. The Pin Class 'pin'
2.1 pin.h
2.2 pin.c
3. The Accumulator Class 'ac...
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 Statechart
7.2 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
9. The Sockets Class 'sock'
9.1 sock Code
9.1.1 sock.h
9.1.2 sock.c
The following are classes that are portable across multiple processors and environments.

1. 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. It's particularly useful to toggle an on-board LED to act as a software health monitor during development.

1.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 that is "On" shall turn Off after a specified number of "On" milliseconds.
  • A toggle object that is "Off" shall turn On after a specified number of "Off" milliseconds.

1.2 Common toggle Interface

toggle.h defines only 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
33
[GPLv2]
 
#ifndef toggle_h
#define toggle_h
 
#include "pin.h"
#include <stdint.h>
 
typedef struct toggleTag toggle;
 
extern toggle * toggleNew(pin * pPin, uint32_t TimeOnMS, uint32_t TimeOffMS);
 
#endif
oosmos/Classes/toggle.h

1.3 Two Implementations of toggle

We present two implementations of the same interface that satisfies the same requirements. One that uses classic states that will transition between the On and Off states and one that uses the OOSMOS State Thread feature.

1.4 Classic State-Based Version

toggle Statechart Using a State Thread
toggle_state.c, below, 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 generated functions On_State_Code and Off_State_Code.
Refer to the OOSMOS User's Guide to see how an OOSMOS class is built.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
39
40
41
42
43
44
45
46
47
83
84
85
86
87
88
89
90
93
94
95
96
97
98
99
100
[GPLv2]
 
#include "oosmos.h"
#include "toggle.h"
#include "pin.h"
#include <stdbool.h>
#include <stdint.h>
 
#ifndef toggleMAX
#define toggleMAX 4
#endif
 
struct toggleTag
{
//>>>DECL
[Code Generated by OOSMOS]
//<<<DECL
 
pin * m_pPin;
uint32_t m_TimeOnMS;
uint32_t m_TimeOffMS;
};
 
//>>>CODE
[Code Generated by OOSMOS]
//<<<CODE
 
extern toggle * toggleNew(pin * pPin, uint32_t TimeOnMS, uint32_t TimeOffMS)
{
oosmos_Allocate(pToggle, toggle, toggleMAX, NULL);
 
//>>>INIT
[Code Generated by OOSMOS]
//<<<INIT
 
pToggle->m_pPin = pPin;
pToggle->m_TimeOnMS = TimeOnMS;
pToggle->m_TimeOffMS = TimeOffMS;
 
return pToggle;
}
oosmos/Classes/toggle_state.c

1.5 toggle Execution

Figure 1, 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 1. Snapshot of two toggle objects during execution

1.6 State Thread Version

This version of toggle demonstrates the ability to use a State Thread to reduce the aggregate number of states in the system.
toggle Statechart Using a State Thread
See the thread code on lines 48-62.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
78
79
80
81
82
83
84
85
87
88
89
90
91
92
93
94
[GPLv2]
 
#ifndef toggleMAX
#define toggleMAX 5
#endif
 
//===================================
 
#include "oosmos.h"
#include "toggle.h"
#include "pin.h"
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
 
struct toggleTag
{
//>>>DECL
[Code Generated by OOSMOS]
//<<<DECL
 
pin * m_pPin;
uint32_t m_TimeOnMS;
uint32_t m_TimeOffMS;
};
 
static void ToggleThread(const toggle * pToggle, oosmos_sState * pState)
{
oosmos_POINTER_GUARD(pToggle);
oosmos_POINTER_GUARD(pState);
 
oosmos_ThreadBegin();
for (;;) {
pinOn(pToggle->m_pPin);
oosmos_ThreadDelayMS(pToggle->m_TimeOnMS);
 
pinOff(pToggle->m_pPin);
oosmos_ThreadDelayMS(pToggle->m_TimeOffMS);
}
oosmos_ThreadEnd();
}
 
//>>>CODE
[Code Generated by OOSMOS]
//<<<CODE
 
extern toggle * toggleNew(pin * pPin, uint32_t TimeOnMS, uint32_t TimeOffMS)
{
oosmos_Allocate(pToggle, toggle, toggleMAX, NULL);
 
//>>>INIT
[Code Generated by OOSMOS]
//<<<INIT
 
pToggle->m_pPin = pPin;
pToggle->m_TimeOnMS = TimeOnMS;
pToggle->m_TimeOffMS = TimeOffMS;
 
return pToggle;
}
oosmos/Classes/toggle.c

2. 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 45-46 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:

2.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
64
65
66
67
68
69
70
[GPLv2]
 
#ifndef pin_h
#define pin_h
 
#include "oosmos.h"
#include <stdbool.h>
#include <stdint.h>
 
typedef struct pinTag pin;
 
typedef enum
{
pinOut = 1,
pinIn,
pinInOut
} pin_eDirection;
 
typedef enum
{
pinActiveLow = 1,
pinActiveHigh
} pin_eLogic;
 
extern void pinOn(const pin * pPin);
extern void pinOff(const pin * pPin);
 
extern bool pinIsOn(const pin * pPin);
extern bool pinIsOff(const 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);
#elif defined(__IAR_SYSTEMS_ICC__)
extern pin * pinNew(GPIO_TypeDef* Port, uint16_t Pin, const pin_eDirection Direction, const pin_eLogic Logic);
extern pin * pinNew_Debounce(GPIO_TypeDef* Port, const uint16_t Bit, const pin_eDirection Direction, const pin_eLogic Logic, const uint8_t DebounceTimeMS);
#elif defined(_MSC_VER)
extern pin * pinNew(char Key, pin_eLogic Logic);
#endif
 
#endif
 
oosmos/Classes/pin.h

2.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
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
456
457
458
459
460
[GPLv2]
 
#ifndef pinMAX
#define pinMAX 18
#endif
 
//===================================
 
#include "pin.h"
#include "oosmos.h"
#include <stdbool.h>
#include <stdint.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;
#elif defined(__IAR_SYSTEMS_ICC__)
GPIO_TypeDef* m_Port;
uint16_t m_Pin;
#elif defined(_MSC_VER)
char m_Key;
#endif
 
oosmos_sActiveObject m_ActiveObject;
oosmos_sTimeout m_Timeout;
uint8_t m_DebounceTimeMS;
unsigned int m_State:4; // eStates
unsigned int m_Logic:4; // pin_eLogic
unsigned int m_Direction:4; // pin_eDirection
};
 
static bool IsPhysicallyOn(const pin * pPin);
 
static bool IsPhysicallyOff(const pin * pPin)
{
return !IsPhysicallyOn(pPin);
}
 
static void RunStateMachine(void * pObject)
{
oosmos_POINTER_GUARD(pObject);
 
pin * pPin = (pin *) pObject;
 
switch (pPin->m_State) {
case On_State: {
if (IsPhysicallyOff(pPin)) {
pPin->m_State = (unsigned int) ConfirmingOff_State;
oosmos_TimeoutInMS(&pPin->m_Timeout, pPin->m_DebounceTimeMS);
}
 
break;
}
 
case Off_State: {
if (IsPhysicallyOn(pPin)) {
pPin->m_State = (unsigned int) 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 = (unsigned int) Unknown_State;
break;
}
 
pPin->m_State = (unsigned int) On_State;
break;
}
 
case ConfirmingOff_State: {
if (!oosmos_TimeoutHasExpired(&pPin->m_Timeout)) {
break;
}
 
if (!IsPhysicallyOff(pPin)) {
pPin->m_State = (unsigned int) Unknown_State;
break;
}
 
pPin->m_State = (unsigned int) Off_State;
break;
}
 
case Unknown_State: {
pPin->m_State = (unsigned int) (IsPhysicallyOn(pPin) ? ConfirmingOn_State : ConfirmingOff_State);
oosmos_TimeoutInMS(&pPin->m_Timeout, pPin->m_DebounceTimeMS);
break;
}
 
default: {
oosmos_FOREVER();
}
}
}
 
extern bool pinIsOn(const pin * pPin)
{
oosmos_POINTER_GUARD(pPin);
 
if (pPin->m_DebounceTimeMS == 0) {
return IsPhysicallyOn(pPin);
}
 
return pPin->m_State == (unsigned int) On_State;
}
 
extern bool pinIsOff(const pin * pPin)
{
return !pinIsOn(pPin);
}
 
#if defined(ARDUINO) || defined(oosmos_RASPBERRY_PI)
 
static bool IsPhysicallyOn(const pin * pPin)
{
const int PinValue = digitalRead(pPin->m_PinNumber);
return PinValue == (pPin->m_Logic == pinActiveHigh ? HIGH : LOW);
}
 
extern void pinOn(const 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(const 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 = (unsigned int) Logic;
pPin->m_State = (unsigned int) Unknown_State;
pPin->m_Direction = (unsigned int) 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(const 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(const pin * pPin)
{
(pPin->m_Logic == pinActiveHigh ? PORTSetBits : PORTClearBits)(pPin->m_Port, pPin->m_Bit);
}
 
extern void pinOff(const 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 = (unsigned int) Logic;
pPin->m_State = (unsigned int) Unknown_State;
pPin->m_Direction = (unsigned int) 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 = (unsigned int) Logic;
pPin->m_State = (unsigned int) Unknown_State;
pPin->m_Direction = (unsigned int) 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(const pin * pPin)
{
DigitalOut * pDigitalOut = (DigitalOut *) pPin->m_Pin;
const int PinValue = pDigitalOut->read();
return PinValue == (pPin->m_Logic == pinActiveHigh ? 1 : 0);
}
 
extern void pinOn(const pin * pPin)
{
DigitalOut * pDigitalOut = (DigitalOut *) pPin->m_Pin;
pDigitalOut->write(pPin->m_Logic == pinActiveHigh ? 1 : 0);
}
 
extern void pinOff(const 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;
}
#elif defined(__IAR_SYSTEMS_ICC__)
static bool IsPhysicallyOn(const pin * pPin)
{
oosmos_POINTER_GUARD(pPin);
 
const GPIO_PinState PinValue = HAL_GPIO_ReadPin(pPin->m_Port, pPin->m_Pin);
return (pPin->m_Logic == pinActiveHigh) ? (PinValue == GPIO_PIN_SET) : (PinValue == GPIO_PIN_RESET);
}
 
extern void pinOn(const pin * pPin)
{
oosmos_POINTER_GUARD(pPin);
 
HAL_GPIO_WritePin(pPin->m_Port, pPin->m_Pin, pPin->m_Logic == pinActiveHigh ? GPIO_PIN_SET : GPIO_PIN_RESET);
}
 
extern void pinOff(const pin * pPin)
{
oosmos_POINTER_GUARD(pPin);
 
HAL_GPIO_WritePin(pPin->m_Port, pPin->m_Pin, pPin->m_Logic == pinActiveHigh ? GPIO_PIN_RESET : GPIO_PIN_SET);
}
 
extern pin * pinNew(GPIO_TypeDef* Port, uint16_t Pin, const pin_eDirection Direction, const pin_eLogic Logic)
{
oosmos_Allocate(pPin, pin, pinMAX, NULL);
 
pPin->m_Port = Port;
pPin->m_Pin = Pin;
pPin->m_Logic = (unsigned int) Logic;
pPin->m_State = (unsigned int) Unknown_State;
pPin->m_Direction = (unsigned int) Direction;
pPin->m_DebounceTimeMS = 0;
 
pinOff(pPin);
return pPin;
}
 
extern pin * pinNew_Debounce(GPIO_TypeDef* Port, const uint16_t 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(_MSC_VER)
#include <windows.h>
#include <stdio.h>
 
static bool pinFirst = true;
static bool KeyIsDown[0xFF];
static HANDLE hStdin;
 
static bool IsPhysicallyOn(const pin * pPin)
{
oosmos_POINTER_GUARD(pPin);
 
DWORD NumRead;
INPUT_RECORD InputRecord;
 
while (PeekConsoleInput(hStdin, &InputRecord, 1, &NumRead) && NumRead > 0) {
if (InputRecord.EventType == KEY_EVENT) {
const KEY_EVENT_RECORD KER = InputRecord.Event.KeyEvent;
const CHAR Char = KER.uChar.AsciiChar;
 
KeyIsDown[Char] = (KER.bKeyDown == TRUE);
}
 
(void) ReadConsoleInput(hStdin, &InputRecord, 1, &NumRead);
}
 
const bool IsDown = KeyIsDown[pPin->m_Key];
return pPin->m_Logic == ((unsigned int) pinActiveHigh) ? IsDown : !IsDown;
}
 
extern pin * pinNew(char Key, const pin_eLogic Logic)
{
oosmos_Allocate(pPin, pin, pinMAX, NULL);
 
pPin->m_Key = Key;
pPin->m_Logic = (unsigned int) Logic;
pPin->m_State = (unsigned int) Unknown_State;
pPin->m_DebounceTimeMS = 0;
 
if (pinFirst) {
memset(KeyIsDown, false, sizeof(KeyIsDown));
 
hStdin = GetStdHandle(STD_INPUT_HANDLE);
pinFirst = false;
}
 
return pPin;
}
 
extern void pinOn(const pin * pPin)
{
printf("%p Pin ON\n", pPin);
}
 
extern void pinOff(const pin * pPin)
{
printf("%p Pin OFF\n", pPin);
}
#else
#error pin.c: Unsupported platform.
#endif
oosmos/Classes/pin.c

3. The Accumulator Class 'accum'

The accum class allows you to store and react to large time values, with no real-world practical limit.
The accum class can hold 18,446,744,073,709,551,615 microseconds where the results of oosmos_GetFreeRunningMicroseconds() can hold only 4,294,967,295 microseconds, which is only a little over an hour. The accum class will properly handle each wrap around from 4,294,967,295 to zero.
Here is an example of how you might use the accum class in one of your objects, where you enter and stay in the Waiting state for three days and then exit. (See the code at oosmos\Classes\Tests\Windows.)
accumtest Statechart
See the exhaustive set of time conversion APIs at the API documentation page.

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
42
[GPLv2]
 
#ifndef sw_h
#define sw_h
 
#include "oosmos.h"
#include "pin.h"
#include <stdbool.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
[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"
#include <stdbool.h>
#include <stddef.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_RegisterActiveObject(pSwitch, swRunStateMachine, &pSwitch->m_ActiveObject);
 
oosmos_SubscriberListInit(pSwitch->m_CloseEvent);
oosmos_SubscriberListInit(pSwitch->m_OpenEvent);
 
return pSwitch;
}
 
extern sw * swNew(pin * pPin)
{
sw * pSwitch = swNewDetached(pPin);
return pSwitch;
}
 
extern void swSubscribeCloseEvent(sw * pSwitch, oosmos_sQueue * pQueue, int CloseEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pSwitch);
 
oosmos_SubscriberListAdd(pSwitch->m_CloseEvent, pQueue, CloseEventCode, pContext);
}
 
extern void swSubscribeOpenEvent(sw * pSwitch, oosmos_sQueue * pQueue, int OpenEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pSwitch);
 
oosmos_SubscriberListAdd(pSwitch->m_OpenEvent, pQueue, OpenEventCode, pContext);
}
 
static eStates PhysicalSwitchState(const sw * pSwitch)
{
oosmos_POINTER_GUARD(pSwitch);
 
return pinIsOn(pSwitch->m_pPin) ? Closed_State : Open_State;
}
 
extern bool swIsOpen(const sw * pSwitch)
{
return !swIsClosed(pSwitch);
}
 
extern bool swIsClosed(const sw * pSwitch)
{
oosmos_POINTER_GUARD(pSwitch);
 
if (pSwitch->m_State == Unknown_State) {
return PhysicalSwitchState(pSwitch) == Closed_State;
}
else {
return pSwitch->m_State == Closed_State;
}
}
 
extern void swRunStateMachine(void * pObject)
{
oosmos_POINTER_GUARD(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
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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
[GPLv2]
 
#ifndef matrixMAX
#define matrixMAX 1
#endif
 
#ifndef matrixMAX_ROWS
#define matrixMAX_ROWS 8
#endif
 
#ifndef matrixMAX_COLS
#define matrixMAX_COLS 8
#endif
 
static const int RowOnSettleTimeUS = 50;
static const int RowOffSettleTimeUS = 50;
 
#include "oosmos.h"
#include "matrix.h"
#include "sw.h"
#include "pin.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
 
struct matrixTag
{
//>>>DECL
[Code Generated by OOSMOS]
//<<<DECL
 
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(const matrix * pMatrix)
{
const int RowIndex = pMatrix->m_CurrentRowIndex;
const int Columns = pMatrix->m_Columns;
 
for (int ColumnIndex = 0; ColumnIndex < Columns; ColumnIndex++) {
sw * pSwitch = pMatrix->m_pSwitch[RowIndex][ColumnIndex];
 
if (pSwitch != NULL) {
swRunStateMachine(pSwitch);
}
}
}
 
static void Thread(matrix * pMatrix, oosmos_sState * pState)
{
oosmos_ThreadBegin();
for (;;) {
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_ThreadDelayMS(RowOnSettleTimeUS);
 
InterrogateColumns(pMatrix);
 
pinOff(pMatrix->m_pRowPins[pMatrix->m_CurrentRowIndex]);
oosmos_ThreadDelayMS(RowOffSettleTimeUS);
}
}
oosmos_ThreadEnd();
}
 
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;
}
}
 
//>>>CODE
[Code Generated by OOSMOS]
//<<<CODE
 
extern matrix * matrixNew(int Rows, int Columns, ...)
{
oosmos_Allocate(pMatrix, matrix, matrixMAX, NULL);
 
pMatrix->m_Rows = 0;
pMatrix->m_Columns = 0;
 
for (int RowIndex = 0; RowIndex < matrixMAX_ROWS; RowIndex++) {
pMatrix->m_pRowPins[RowIndex] = NULL;
 
for (int ColumnIndex = 0; ColumnIndex < matrixMAX_COLS; ColumnIndex++) {
pMatrix->m_pColumnPins[ColumnIndex] = NULL;
pMatrix->m_pSwitch[RowIndex][ColumnIndex] = NULL;
}
}
 
//>>>INIT
[Code Generated by OOSMOS]
//<<<INIT
 
va_list ArgList;
va_start(ArgList, Columns);
 
for (int Row = 1; Row <= Rows; Row += 1) {
AddRow(pMatrix, Row, va_arg(ArgList, pin *));
}
 
for (int 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, int Row, 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) {
for (;;) {
continue;
}
}
 
pMatrix->m_pSwitch[RowIndex][ColumnIndex] = pSwitch;
}
 
extern pin * matrixGetColumnPin(const matrix * pMatrix, 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 30 and prt.c line 50) 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 27 as well as examples Switch and Matrix.
Code for prt:
1
22
23
24
25
26
27
28
29
30
31
32
[GPLv2]
 
#ifndef prt_h
#define prt_h
 
#if defined(ARDUINO)
extern unsigned long prtArduinoBaudRate;
#endif
 
extern void 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 "prt.h"
#include "oosmos.h"
#include <stdbool.h>
#include <stdarg.h>
#include <stdio.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 void prtFormatted(const char * pFormat, ...)
{
char Buffer[MaxBuffer];
 
va_list ArgList; //lint -e438
va_start(ArgList, pFormat);
(void) 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
}
oosmos/Classes/prt.c

7. The Keyer Class 'keyer'

The keyer object is common and portable to multiple target implementations because of the multi-platform pin class. There is even a Windows version for educational purposes.

7.1 keyer Statechart

Figure 4. Keyer Statechart

7.2 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
33
[GPLv2]
 
#ifndef keyer_h
#define keyer_h
 
#include "pin.h"
#include <stdint.h>
 
typedef struct keyerTag keyer;
 
extern keyer * keyerNew(pin * pDahPin, pin * pDitPin, pin * pSpeakerPin, uint32_t 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
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
204
205
206
207
208
209
210
211
219
220
221
222
223
224
225
226
227
228
229
[GPLv2]
 
#include "oosmos.h"
#include "keyer.h"
#include "pin.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
 
struct keyerTag
{
//>>>DECL
[Code Generated by OOSMOS]
//<<<DECL
 
pin * m_pDahPin;
pin * m_pDitPin;
pin * m_pSpeakerPin;
 
uint32_t m_DitTimeMS;
uint32_t m_DahTimeMS;
uint32_t m_SpaceTimeMS;
 
bool m_DitWasPressed;
bool m_DahWasPressed;
};
 
static void CheckDahIsPressedPoll(keyer * pKeyer)
{
if (!pKeyer->m_DahWasPressed && pinIsOn(pKeyer->m_pDahPin)) {
pKeyer->m_DahWasPressed = true;
}
}
 
static void CheckDitIsPressedPoll(keyer * pKeyer)
{
if (!pKeyer->m_DitWasPressed && pinIsOn(pKeyer->m_pDitPin)) {
pKeyer->m_DitWasPressed = true;
}
}
 
static bool DahWasPressed(const keyer * pKeyer)
{
return pKeyer->m_DahWasPressed;
}
 
static bool DitWasPressed(const keyer * pKeyer)
{
return pKeyer->m_DitWasPressed;
}
 
static bool IsDitPressed(const keyer * pKeyer)
{
return pinIsOn(pKeyer->m_pDitPin);
}
 
static bool IsDahPressed(const keyer * pKeyer)
{
return pinIsOn(pKeyer->m_pDahPin);
}
 
static void DitThread(const keyer * pKeyer, oosmos_sState * pState)
{
oosmos_ThreadBegin();
pinOn(pKeyer->m_pSpeakerPin);
oosmos_ThreadDelayMS(pKeyer->m_DitTimeMS);
pinOff(pKeyer->m_pSpeakerPin);
 
oosmos_ThreadDelayMS(pKeyer->m_SpaceTimeMS);
oosmos_ThreadEnd();
}
 
static void DahThread(const keyer * pKeyer, oosmos_sState * pState)
{
oosmos_ThreadBegin();
pinOn(pKeyer->m_pSpeakerPin);
oosmos_ThreadDelayMS(pKeyer->m_DahTimeMS);
pinOff(pKeyer->m_pSpeakerPin);
 
oosmos_ThreadDelayMS(pKeyer->m_SpaceTimeMS);
oosmos_ThreadEnd();
}
 
//>>>CODE
[Code Generated by OOSMOS]
//<<<CODE
 
extern keyer * keyerNew(pin * pDahPin, pin * pDitPin, pin * pSpeakerPin, uint32_t WPM)
{
oosmos_Allocate(pKeyer, keyer, 1, NULL);
 
//>>>INIT
[Code Generated by OOSMOS]
//<<<INIT
 
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;
 
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
39
40
[GPLv2]
 
#ifndef reg_h
#define reg_h
 
#include <stdint.h>
 
typedef struct regTag reg;
 
typedef struct
{
float X;
float Y;
} regSample;
 
extern reg * regNew(void);
extern void regSamples(reg * pReg, const regSample * pSamples, uint32_t 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
88
89
90
[GPLv2]
 
#include "reg.h"
#include "oosmos.h"
#include <stdint.h>
 
struct regTag
{
float m_Intercept;
float m_Slope;
};
 
static float MeanOfX(const regSample * pSamples, uint32_t Samples)
{
float Sum = 0.0f;
 
for (uint32_t SampleIndex = 0; SampleIndex < Samples; SampleIndex++) {
Sum += pSamples[SampleIndex].X;
}
 
return Sum / Samples;
}
 
static float MeanOfY(const regSample * pSamples, uint32_t Samples)
{
float Sum = 0.0f;
 
for (uint32_t 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, uint32_t Samples)
{
const float MeanX = MeanOfX(pSamples, Samples);
const float MeanY = MeanOfY(pSamples, Samples);
 
float SumXY = 0.0f;
float SumXX = 0.0f;
 
for (uint32_t 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;
}
 
oosmos_ASSERT(SumXX > 0.0f);
 
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

9. 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.

9.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.

9.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
52
53
54
55
56
[GPLv2]
 
#ifndef sock_h
#define sock_h
 
#include "oosmos.h"
#include <stddef.h>
#include <stdint.h>
#include <stdbool.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

9.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
456
457
458
459
460
461
462
463
464
465
466
467
468
[GPLv2]
 
#includes...
#include <ctype.h>
#include <stdint.h>
#include <stdbool.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>
#include <minwindef.h> // for MAKEWORD, WORD
#include <winerror.h> // for WSAEWOULDBLOCK, WSAECONNREFUSED, WSAECONNABORTED
#include <inaddr.h> // for IN_ADDR, s_addr
 
typedef SOCKET sock_tSocket;
 
typedef int socklen_t;
typedef uint32_t sendsize_t;
typedef uint32_t recvsize_t;
#define IOCTL ioctlsocket
#define CLOSE closesocket
 
#define sockEWOULDBLOCK WSAEWOULDBLOCK
#define sockECONNRESET WSAECONNRESET
#define sockECONNREFUSED WSAECONNREFUSED
#define sockEINPROGRESS WSAEINPROGRESS
#define sockECONNABORTED WSAECONNABORTED
#define sockINVALID_SOCKET INVALID_SOCKET
#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>
#include <sys/select.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
#define sockINVALID_SOCKET -1
#endif
 
#define RECEIVE_BUFFER_SIZE 500
 
struct sockTag
{
sock_tSocket m_Socket;
 
// Connect...
bool m_FirstConnect;
 
// Send...
const char * m_pSendData;
int 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);
(void) WSAStartup(wVersionRequested, &wsaData);
Started = true;
}
#else
signal(SIGPIPE, SIG_IGN);
#endif
}
 
static bool WouldBlock(sock * pSock, recvsize_t BytesReceived)
{
if (BytesReceived == (recvsize_t) -1) {
return sockGetLastError() == sockEWOULDBLOCK;
}
 
oosmos_UNUSED(pSock);
 
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(sock_tSocket Socket)
{
sock * pSock = (sock *) malloc(sizeof(sock));
 
//
// Make the socket non-blocking.
//
{
unsigned long IsNonBlocking = 1;
(void) 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)
{
unsigned int A, B, C, D;
sscanf(pDot, "%u.%u.%u.%u", &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((unsigned short) Port);
 
if (bind(pSock->m_Socket, (struct sockaddr *) &Listener, sizeof(Listener)) < 0) {
perror("bind failed. Error");
return false;
}
 
(void) listen(pSock->m_Socket, Backlog);
return true;
}
 
extern bool sockAccepted(sock * pListenerSock, sock ** ppNewSock)
{
struct sockaddr_in Server;
 
socklen_t ServerSize = sizeof(Server);
const sock_tSocket ServerSocket = accept(pListenerSock->m_Socket, (struct sockaddr *) &Server, &ServerSize);
 
if (ServerSocket == sockINVALID_SOCKET) {
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);
(void) memcpy(pBuffer, pSock->m_pReceiveBuffer, Span);
*pBytesReceived = Span;
 
// Shift...
pSock->m_BytesReceived -= Span;
(void) 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 recvsize_t 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);
(void) memcpy(pBuffer, pSock->m_pReceiveBuffer, Span);
*pBytesReceived = Span;
 
// Shift...
pSock->m_BytesReceived -= Span;
(void) 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 = (const 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)
{
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_set fd_out;
FD_ZERO(&fd_out);
FD_SET(pSock->m_Socket, &fd_out);
 
const sock_tSocket largest_sock = pSock->m_Socket;
 
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
 
select(largest_sock+1, NULL, &fd_out, NULL, &tv);
const int Writable = FD_ISSET(pSock->m_Socket, &fd_out);
 
char Code;
socklen_t SizeofCode = sizeof(Code);
 
getsockopt(pSock->m_Socket, SOL_SOCKET, SO_ERROR, &Code, &SizeofCode);
 
if (Writable && Code == 0) {
pSock->m_FirstConnect = true;
return true;
}
 
if (Code == sockECONNREFUSED) {
(void) sockClose(pSock);
}
 
return false;
}
 
extern bool sockClose(sock * pSock)
{
CLOSE(pSock->m_Socket);
pSock->m_Closed = true;
 
return true;
}
 
extern sock * sockNew(void)
{
Init();
 
sock_tSocket Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 
return New(Socket);
}
 
extern void sockDelete(sock * pSock)
{
free(pSock->m_pReceiveBuffer);
(void) sockClose(pSock);
}
oosmos/Classes/sock.c
Copyright © 2014-2019  OOSMOS, LLC