Reusable Classes
1. Button Class 'btn'
2. Button Press and Hold Class 'btnph'
3. Toggle Class 'toggle'
4. Accumulator Class 'accum'
5. PWM Class 'pwm'
6. PID Class 'pid'
7. ADC Class 'adc'
8. Switch Class 'sw'
9. Matrix Class 'matrix'
10. Pin Class 'pin'
11. Print Class 'prt'
12. Keyer Class 'keyer'
13. Linear Regression Class 'reg'
14. Sockets Class 'sock'
The following are classes that are portable across multiple processors and environments.

1. Button Class 'btn'

The btn class is an implementation of a simple button metaphor. The btn class is similar to the sw class except the default button state is assumed to be Released, where a sw's default state is either open or closed depending on the switch's physical reality.

1.1 btn Code

The interface file.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
[GPLv2]
 
#ifndef btn_h
#define btn_h
 
#include "oosmos.h"
#include "pin.h"
 
typedef struct btnTag btn;
 
extern btn * btnNew(pin * pPin);
 
extern void btnSubscribeReleasedEvent(btn * pButton, oosmos_sQueue * pQueue, int ReleasedEventCode, void * pContext);
extern void btnSubscribePressedEvent (btn * pButton, oosmos_sQueue * pQueue, int PressedEventCode, void * pContext);
 
#endif
oosmos/Classes/btn.h
The implementation file.
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
[GPLv2]
 
#ifndef btnMaxButtons
#define btnMaxButtons 2
#endif
 
#ifndef btnMaxPressedSubscribers
#define btnMaxPressedSubscribers 1
#endif
 
#ifndef btnMaxReleasedSubscribers
#define btnMaxReleasedSubscribers 1
#endif
 
//===================================
 
#include "oosmos.h"
#include "pin.h"
#include "btn.h"
#include <stdbool.h>
#include <stddef.h>
 
struct btnTag
{
pin * m_pPin;
 
oosmos_sObjectThread m_ObjectThread;
oosmos_sSubscriberList m_PressedEvent[btnMaxPressedSubscribers];
oosmos_sSubscriberList m_ReleasedEvent[btnMaxReleasedSubscribers];
};
 
static void Thread(const btn * pButton, oosmos_sState * pState)
{
oosmos_POINTER_GUARD(pButton);
oosmos_POINTER_GUARD(pState);
 
oosmos_ThreadBegin();
for (;;) {
oosmos_ThreadWaitCond(pinIsOn(pButton->m_pPin));
oosmos_SubscriberListNotify(pButton->m_PressedEvent);
 
oosmos_ThreadWaitCond(pinIsOff(pButton->m_pPin));
oosmos_SubscriberListNotify(pButton->m_ReleasedEvent);
}
oosmos_ThreadEnd();
}
 
extern void btnSubscribePressedEvent(btn * pButton, oosmos_sQueue * pQueue, int PressedEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pButton);
 
oosmos_SubscriberListAdd(pButton->m_PressedEvent, pQueue, PressedEventCode, pContext);
}
 
extern void btnSubscribeReleasedEvent(btn * pButton, oosmos_sQueue * pQueue, int ReleasedEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pButton);
 
oosmos_SubscriberListAdd(pButton->m_ReleasedEvent, pQueue, ReleasedEventCode, pContext);
}
 
extern btn * btnNew(pin * pPin)
{
oosmos_Allocate(pButton, btn, btnMaxButtons, NULL);
 
pButton->m_pPin = pPin;
 
oosmos_ObjectThreadInit(pButton, m_ObjectThread, Thread, true);
 
oosmos_SubscriberListInit(pButton->m_PressedEvent);
oosmos_SubscriberListInit(pButton->m_ReleasedEvent);
 
return pButton;
}
oosmos/Classes/btn.c

2. Button Press and Hold Class 'btnph'

The btnph class has specialized button behavior that will generate an event code when the button is pressed then immediately released; and then a different code if the button remains pressed for a configurable amount of time.

2.1 btnph Code

The interface file.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
[GPLv2]
 
#ifndef btnph_h
#define btnph_h
 
#include "oosmos.h"
#include "pin.h"
#include <stdbool.h>
#include <stdint.h>
 
typedef struct btnphTag btnph;
 
extern btnph * btnphNew(pin * pPin, uint32_t HoldTimeMS);
 
extern void btnphSubscribeReleasedEvent(btnph * pSwitch, oosmos_sQueue * pQueue, int ReleasedEventCode, void * pContext);
extern void btnphSubscribeHeldEvent (btnph * pSwitch, oosmos_sQueue * pQueue, int HeldEventCode, void * pContext);
extern void btnphSubscribePressedEvent (btnph * pSwitch, oosmos_sQueue * pQueue, int PressedEventCode, void * pContext);
 
#endif
oosmos/Classes/btnph.h
The implementation file.
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
[GPLv2]
 
#ifndef btnphMaxButtons
#define btnphMaxButtons 2
#endif
 
#ifndef btnphMaxPressedSubscribers
#define btnphMaxPressedSubscribers 1
#endif
 
#ifndef btnphMaxHeldSubscribers
#define btnphMaxHeldSubscribers 1
#endif
 
#ifndef btnphMaxReleasedSubscribers
#define btnphMaxReleasedSubscribers 1
#endif
 
//===================================
 
#include "oosmos.h"
#include "pin.h"
#include "btnph.h"
#include <stdbool.h>
#include <stddef.h>
 
struct btnphTag
{
pin * m_pPin;
uint32_t m_HoldTimeMS;
 
oosmos_sObjectThread m_ObjectThread;
oosmos_sSubscriberList m_PressedEvent[btnphMaxPressedSubscribers];
oosmos_sSubscriberList m_HeldEvent[btnphMaxHeldSubscribers];
oosmos_sSubscriberList m_ReleasedEvent[btnphMaxReleasedSubscribers];
};
 
static void Thread(const btnph * pButton, oosmos_sState * pState)
{
bool TimedOut;
 
oosmos_POINTER_GUARD(pButton);
oosmos_POINTER_GUARD(pState);
 
oosmos_ThreadBegin();
for (;;) {
oosmos_ThreadWaitCond(pinIsOn(pButton->m_pPin));
oosmos_ThreadWaitCond_TimeoutMS(pinIsOff(pButton->m_pPin), pButton->m_HoldTimeMS, &TimedOut);
 
if (TimedOut) {
oosmos_SubscriberListNotify(pButton->m_HeldEvent);
oosmos_ThreadWaitCond(pinIsOff(pButton->m_pPin));
} else {
oosmos_SubscriberListNotify(pButton->m_PressedEvent);
}
 
oosmos_SubscriberListNotify(pButton->m_ReleasedEvent);
}
oosmos_ThreadEnd();
}
 
extern void btnphSubscribePressedEvent(btnph * pButton, oosmos_sQueue * pQueue, int PressedEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pButton);
 
oosmos_SubscriberListAdd(pButton->m_PressedEvent, pQueue, PressedEventCode, pContext);
}
 
extern void btnphSubscribeHeldEvent(btnph * pButton, oosmos_sQueue * pQueue, int HeldEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pButton);
 
oosmos_SubscriberListAdd(pButton->m_HeldEvent, pQueue, HeldEventCode, pContext);
}
 
extern void btnphSubscribeReleasedEvent(btnph * pButton, oosmos_sQueue * pQueue, int ReleasedEventCode, void * pContext)
{
oosmos_POINTER_GUARD(pButton);
 
oosmos_SubscriberListAdd(pButton->m_ReleasedEvent, pQueue, ReleasedEventCode, pContext);
}
 
extern btnph * btnphNew(pin * pPin, uint32_t HoldTimeMS)
{
oosmos_Allocate(pButton, btnph, btnphMaxButtons, NULL);
 
pButton->m_pPin = pPin;
pButton->m_HoldTimeMS = HoldTimeMS;
 
oosmos_ObjectThreadInit(pButton, m_ObjectThread, Thread, true);
 
oosmos_SubscriberListInit(pButton->m_PressedEvent);
oosmos_SubscriberListInit(pButton->m_HeldEvent);
oosmos_SubscriberListInit(pButton->m_ReleasedEvent);
 
return pButton;
}
oosmos/Classes/btnph.c

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

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

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

3.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 Object Thread feature.

3.4 Classic State-Based Version

toggle Statechart Using States
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
37
40
41
42
43
44
45
46
47
48
84
85
86
87
88
89
90
91
94
95
96
97
98
99
100
101
[GPLv2]
 
#include "oosmos.h"
#include "toggle.h"
#include "pin.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.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

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

3.6 Object Thread Version

This version of toggle demonstrates the ability to use an Object Thread to reduce the aggregate number of states in the system.
See the thread code on lines 45-59 and its initialization on line 65.
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
[GPLv2]
 
#ifndef toggleMAX
#define toggleMAX 5
#endif
 
//===================================
 
#include "oosmos.h"
#include "toggle.h"
#include "pin.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
 
struct toggleTag
{
oosmos_sObjectThread m_ObjectThread;
 
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();
}
 
extern toggle * toggleNew(pin * pPin, uint32_t TimeOnMS, uint32_t TimeOffMS)
{
oosmos_Allocate(pToggle, toggle, toggleMAX, NULL);
 
oosmos_ObjectThreadInit(pToggle, m_ObjectThread, ToggleThread, true);
 
pToggle->m_pPin = pPin;
pToggle->m_TimeOnMS = TimeOnMS;
pToggle->m_TimeOffMS = TimeOffMS;
 
return pToggle;
}
oosmos/Classes/toggle.c

4. 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_GetFreeRunningUS() 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.

5. PWM Class 'pwm'

The pwm class allows you to control a Pulse Width Modulation signal. The emphasis of this interface is on portability. Specifically, we control the percentage (0.0% to 100.0%) of the duty cycle and not vary the magnitude of the duty cycle resolution, which is inherently platform-specific.
Only the interface is portable. The implementation may utilize #ifdef's to select an implementation that may be platform specific.
Some platforms allow better control over things like PWM signal frequency and/or the duty cycle resolution (see ESP32). For these platforms, we extend the interface in (hopefully) portable way.

5.1 pwm Code

The interface file.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
[GPLv2]
 
#ifndef pwm_h
#define pwm_h
 
#include <stdint.h>
 
typedef struct pwmTag pwm;
 
extern pwm * pwmNew(unsigned PinNumber, double DutyCyclePercent);
extern void pwmSetDutyCyclePercent(pwm * pPWM, double DutyCyclePercent); // 0.0%-100.0%
 
extern void pwmOn(pwm * pPWM);
extern void pwmOff(pwm * pPWM);
 
#if defined(ESP32)
extern double pwmGetSignalFrequency(pwm * pPWM);
extern void pwmSetSignalFrequency(pwm * pPWM, double SignalFrequency);
 
extern uint32_t pwmGetDutyCycleResolution(pwm * pPWM);
extern void pwmSetDutyCycleResolution(pwm * pPWM, uint32_t Resolution);
#endif
 
#endif
oosmos/Classes/pwm.h
The implementation file.
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
[GPLv2]
 
#ifndef pwmMAX
#define pwmMAX 2
#endif
 
//===================================
 
#include "pwm.h"
#include "oosmos.h"
#include "prt.h"
#include <stdint.h>
 
//
// This pwm class provides a more intuitive and portable interface to control
// PWM signals. By specifying PWM duty cycle units as a percentage (0.0%-100.0%), the
// user intent is clear. The alternative is non-portable value in your application
// whose magnitude varies from platform to platform. The code below is a bit messy,
// but it is preferable to hide the multi-platform ugliness in the PWM implementation
// rather than pollute the principle application logic.
//
// Some processors have more capabilities than others. For example,
// the Arduino ESP32 allows for complete control over the PWM SignalFrequency where
// others do not without a lot of low-level timer reconfiguration.
// For those platforms, more APIs are exposed and and there is a more complete pwmNew()
// constructor provided.
//
// This class can be compiled cleanly on Windows, although it only outputs debug
// information.
//
// To better read through all the preprocessor conditionals, it is recommended that
// you view the code using Visual Studio Code and collapse the sections that are
// not important to you.
//
// It has been tested on the following boards: ESP32, ESP8266, ChipKIT family,
// Arduino AVR family, ST Nucleo family. Add your board with the appropriate
// #ifdef's if it is currently not supported.
//
 
struct pwmTag
{
unsigned m_PinNumber;
uint32_t m_DutyCycleValue;
 
#if defined(ESP32)
unsigned m_ChannelNumber;
unsigned m_DutyCycleResolution;
double m_SignalFrequency;
#endif
};
 
extern void pwmSetDutyCyclePercent(pwm * pPWM, double DutyCyclePercent)
{
#if defined(ARDUINO_ESP8266_NODEMCU)
const uint32_t DutyCycleValue = (uint32_t) oosmos_AnalogMapAccurate(DutyCyclePercent, 0.0, 100.0, 0.0, 1023.0);
#elif defined(ESP32)
const uint32_t DutyCycleValue = (uint32_t) oosmos_AnalogMapAccurate(DutyCyclePercent, 0.0, 100.0, 0.0, (1 << pPWM->m_DutyCycleResolution) - 1);
#else
const uint32_t DutyCycleValue = (uint32_t) oosmos_AnalogMapAccurate(DutyCyclePercent, 0.0, 100.0, 0.0, 255.0);
#endif
 
#if defined(ESP32)
ledcWrite(pPWM->m_ChannelNumber, DutyCycleValue);
#elif defined(ARDUINO)
analogWrite(pPWM->m_PinNumber, DutyCycleValue);
#elif defined(_MSC_VER)
// Do nothing
#else
#error pwm.cpp: Unsupported platform.
#endif
 
pPWM->m_DutyCycleValue = DutyCycleValue;
 
#if defined(pwm_DEBUG)
prtFormatted("pwmDutyCycle: pPWM: %p, pinNumber: %d, DutyCyclePercent: %f, DutyCycleValue: %u\n",
pPWM, pPWM->m_PinNumber, DutyCyclePercent, (unsigned) pPWM->m_DutyCycleValue);
#endif
}
 
extern void pwmOn(pwm * pPWM)
{
#if defined(ESP32)
ledcWrite(pPWM->m_ChannelNumber, pPWM->m_DutyCycleValue);
#elif defined(ARDUINO)
analogWrite(pPWM->m_PinNumber, pPWM->m_DutyCycleValue);
#elif defined(_MSC_VER)
// Do nothing
oosmos_UNUSED(pPWM);
#else
#error pwm.cpp: Unsupported platform.
oosmos_UNUSED(pPWM);
#endif
}
 
extern void pwmOff(pwm * pPWM)
{
#if defined(ESP32)
ledcWrite(pPWM->m_ChannelNumber, 0);
#elif defined(ARDUINO)
analogWrite(pPWM->m_PinNumber, 0);
#elif defined(_MSC_VER)
// Do nothing
oosmos_UNUSED(pPWM);
#else
#error: pwm.cpp: Unsupported platform.
oosmos_UNUSED(pPWM);
#endif
}
 
extern pwm * pwmNew(unsigned PinNumber, double DutyCyclePercent)
{
oosmos_Allocate(pPWM, pwm, pwmMAX, NULL);
pPWM->m_PinNumber = PinNumber;
 
pwmSetDutyCyclePercent(pPWM, DutyCyclePercent);
 
#if defined(ARDUINO)
pinMode(pPWM->m_PinNumber, OUTPUT);
#endif
 
#if defined(ESP32)
static unsigned Channels = 0;
 
pPWM->m_ChannelNumber = Channels++;
pPWM->m_DutyCycleResolution = 15;
pPWM->m_SignalFrequency = 1000.0;
 
ledcSetup(pPWM->m_ChannelNumber, pPWM->m_SignalFrequency, pPWM->m_DutyCycleResolution);
ledcAttachPin(pPWM->m_PinNumber, pPWM->m_ChannelNumber);
#endif
 
return pPWM;
}
 
#if defined(ESP32)
extern double pwmGetSignalFrequency(pwm * pPWM)
{
return pPWM->m_SignalFrequency;
}
 
extern void pwmSetSignalFrequency(pwm * pPWM, double SignalFrequency)
{
pPWM->m_SignalFrequency = SignalFrequency;
ledcSetup(pPWM->m_ChannelNumber, pPWM->m_SignalFrequency, pPWM->m_DutyCycleResolution);
}
 
extern uint32_t pwmGetDutyCycleResolution(pwm * pPWM)
{
return pPWM->m_DutyCycleResolution;
}
 
extern void pwmSetDutyCycleResolution(pwm * pPWM, uint32_t DutyCycleResolution)
{
pPWM->m_DutyCycleResolution = DutyCycleResolution;
ledcSetup(pPWM->m_ChannelNumber, pPWM->m_SignalFrequency, pPWM->m_DutyCycleResolution);
}
#endif
oosmos/Classes/pwm.c

6. PID Class 'pid'

The pid class is an implementation of a PID Controller as described in Wikipedia.

6.1 PID Code

The interface file.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
[GPLv2]
 
#ifndef PID_H
#define PID_H
 
typedef struct pidTag pid;
 
extern pid * pidNew(float Kp, float Ki, float Kd, float SetPoint);
 
extern float pidAdjustOutput(pid * pPID, float Input);
 
extern void pidSet_SetPoint(pid * pPID, float SetPoint);
extern void pidSet_Kp(pid * pPID, float Kp);
extern void pidSet_Ki(pid * pPID, float Ki);
extern void pidSet_Kd(pid * pPID, float Kd);
 
#endif
oosmos/Classes/pid.h
The implementation file.
1
22
23
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
[GPLv2]
 
[...]
 
extern float pidAdjustOutput(pid * pPID, float Input)
{
const float Error = pPID->m_SetPoint - Input;
 
uint64_t NowUS = oosmos_GetFreeRunningUS();
 
//
// Handle uint32_t wrap around.
//
if (NowUS < pPID->m_PreviousTimeUS) {
NowUS += 0x100000000;
}
 
const float P = Error * pPID->m_Kp;
float I;
float D;
 
uint32_t dtMS;
 
if (pPID->m_bFirst) {
pPID->m_SumOfErrors = Error;
I = 0.0f;
D = 0.0f;
dtMS = 0;
pPID->m_bFirst = false;
 
#if defined(pid_DEBUG)
printf("Input,Error,P,I,D,Integral,Output,SetPoint,dtMS\n");
#endif
} else {
const uint32_t tdUS = (uint32_t) (NowUS - pPID->m_PreviousTimeUS);
dtMS = oosmos_US2MS_Rounded(tdUS);
 
pPID->m_SumOfErrors += Error * dtMS;
I = pPID->m_SumOfErrors * pPID->m_Ki;
 
const float Derivative = oosmos_Divide_Integral_Rounded(Error - pPID->m_PreviousError, (float) dtMS);
D = Derivative * pPID->m_Kd;
}
 
const float Output = P + D + I;
 
#if defined(pid_DEBUG)
printf("%f,%f,%f,%f,%f,%f,%f,%f,%u\n", (double) Input, (double) Error,
(double) P, (double) I, (double) D,
(double) pPID->m_SumOfErrors, (double) Output,
(double) pPID->m_SetPoint, dtMS);
#else
oosmos_UNUSED(dtMS);
#endif
 
pPID->m_PreviousError = Error;
pPID->m_PreviousTimeUS = NowUS;
 
return Output;
}
 
[...]
oosmos/Classes/pid.c

6.2 Testing the pid Class

We take advantage of orthogonal states as well as state threads to run through some common PID tests, described at https://controlstation.com/perform-step-test.
pidtest Statechart
The following is the complete PID test source file.
Note the two highlighted sections which are the PidThread and TestThread.
1
22
23
24
25
26
27
28
29
30
31
32
35
36
37
38
39
40
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
155
156
157
158
159
160
161
162
164
165
166
167
168
169
170
171
172
173
174
[GPLv2]
 
#include "pid.h"
#include "pidtest.h"
#include "oosmos.h"
#include <stdlib.h>
#include <stdbool.h>
 
#define BASELINE 25
 
//>>>EVENTS
[Code Generated by OOSMOS]
//<<<EVENTS
 
struct pidtestTag
{
//>>>DECL
[Code Generated by OOSMOS]
//<<<DECL
 
#if defined(pid_DEBUG)
oosmos_sObjectThread m_ObjectThread;
#endif
 
pid * m_pPID;
float m_Output;
float m_Sample;
};
 
static int RandBetween(int Lower, int Upper)
{
return rand() % (Upper - Lower + 1) + Lower;
}
 
static void PidThread(pidtest * pPidTest, oosmos_sState * pState)
{
//
// Input is always a different unit of measure than the Output. For example,
// you measure speed for the input, but the output unit would be the PWM duty cycle
// that drives the motor.
//
// Attempt to simulate that the input is affected by each output.
//
// Also, vary the amount of time between each sample to test the dt element of the PID.
//
oosmos_ThreadBegin();
for (;;) {
pPidTest->m_Sample = oosmos_Divide_Integral_Rounded(pPidTest->m_Output, 2);
pPidTest->m_Output += pidAdjustOutput(pPidTest->m_pPID, pPidTest->m_Sample);
oosmos_ThreadDelayMS(RandBetween(10, 15));
}
oosmos_ThreadEnd();
}
 
#if defined(pid_DEBUG)
static void TestThread(pidtest * pPidTest, oosmos_sState * pState)
{
//
// Change the set point to various values and then let the PID controller settle
// between each one.
//
oosmos_ThreadBegin();
// Reset
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(1000);
 
// Bump Test
pidSet_SetPoint(pPidTest->m_pPID, BASELINE + 25);
oosmos_ThreadDelayMS(1000);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
 
// Reset
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(1000);
 
// Step Test
pidSet_SetPoint(pPidTest->m_pPID, BASELINE + 25);
oosmos_ThreadDelayMS(1000);
 
// Reset
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(1000);
 
// Doublet Test
pidSet_SetPoint(pPidTest->m_pPID, BASELINE + 25);
oosmos_ThreadDelayMS(500);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE - 10);
oosmos_ThreadDelayMS(500);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(500);
 
// Reset
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(1000);
 
// PRBA Test
pidSet_SetPoint(pPidTest->m_pPID, BASELINE + 25);
oosmos_ThreadDelayMS(500);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(500);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE + 25);
oosmos_ThreadDelayMS(750);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(250);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE + 25);
oosmos_ThreadDelayMS(500);
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
 
// Reset
pidSet_SetPoint(pPidTest->m_pPID, BASELINE);
oosmos_ThreadDelayMS(1000);
 
oosmos_EndProgram(1);
oosmos_ThreadEnd();
}
#endif
 
//>>>CODE
[Code Generated by OOSMOS]
//<<<CODE
 
extern pidtest * pidtestNew(void)
{
oosmos_Allocate(pPidTest, pidtest, 1, NULL);
 
//>>>INIT
[Code Generated by OOSMOS]
//<<<INIT
 
pPidTest->m_pPID = pidNew(1.0f, 0.0f, 0.0f, BASELINE);
pPidTest->m_Output = 0.0f;
 
#if defined(pid_DEBUG)
oosmos_ObjectThreadInit(pPidTest, m_ObjectThread, TestThread, true);
#endif
 
return pPidTest;
}
oosmos/Classes/Tests/pidtest.c

7. ADC Class 'adc'

The adc class is an implementation of an analog to digital conversion.

7.1 adc Code

The interface file.
1
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[GPLv2]
 
#ifndef adc_h
#define adc_h
 
#include <stdint.h>
 
typedef struct adcTag adc;
 
extern uint32_t adcRead(const adc * pADC);
 
#if defined(ARDUINO)
extern adc * adcNew(unsigned PinNumber);
#elif defined(_MSC_VER)
extern adc * adcNew(unsigned PinNumber);
#else
#error adc.h: Unsupported platform.
#endif
 
#endif
oosmos/Classes/adc.h
The implementation file.
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
[GPLv2]
 
#ifndef adcMAX
#define adcMAX 2
#endif
 
//===================================
 
#include "adc.h"
#include "oosmos.h"
#include <stdint.h>
#include <stddef.h>
 
struct adcTag
{
#if defined(ARDUINO)
unsigned m_PinNumber;
#elif defined(_MSC_VER)
unsigned m_PinNumber;
#else
#error adc.c: Unsupported platform.
#endif
};
 
#if defined(ARDUINO)
extern uint32_t adcRead(const adc * pADC)
{
return analogRead(pADC->m_PinNumber);
}
 
extern adc * adcNew(unsigned PinNumber)
{
oosmos_Allocate(pADC, adc, adcMAX, NULL);
pinMode(PinNumber, OUTPUT);
pADC->m_PinNumber = PinNumber;
 
return pADC;
}
#elif defined(_MSC_VER)
extern uint32_t adcRead(const adc * pADC)
{
oosmos_UNUSED(pADC);
return 0;
}
 
extern adc * adcNew(unsigned PinNumber)
{
oosmos_Allocate(pADC, adc, adcMAX, NULL);
pADC->m_PinNumber = PinNumber;
 
return pADC;
}
#else
#error adc.c: Unsupported platform.
#endif
oosmos/Classes/adc.c

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

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

8.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_ActiveObjectInit(pSwitch, m_ActiveObject, swRunStateMachine);
 
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

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

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

9.2 matrix Statechart

Figure 3. matrix Statechart

9.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(unsigned Rows, unsigned Columns, ...);
extern pin * matrixGetColumnPin(const matrix * pMatrix, unsigned Column);
extern void matrixAssignSwitch(matrix * pMatrix, sw * pSwitch, unsigned Row, unsigned 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
[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 unsigned RowOnSettleTimeUS = 50;
static const unsigned 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
{
oosmos_sObjectThread m_ObjectThread;
 
pin * m_pRowPins[matrixMAX_ROWS];
pin * m_pColumnPins[matrixMAX_COLS];
sw * m_pSwitch[matrixMAX_ROWS][matrixMAX_COLS];
 
unsigned m_Rows;
unsigned m_Columns;
 
//
// State variables...
//
unsigned m_CurrentRowIndex;
};
 
static void InterrogateColumns(const matrix * pMatrix)
{
const unsigned RowIndex = pMatrix->m_CurrentRowIndex;
const unsigned Columns = pMatrix->m_Columns;
 
for (unsigned 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 unsigned Row, pin * pPin)
{
const unsigned RowIndex = Row - 1;
 
pMatrix->m_pRowPins[RowIndex] = pPin;
 
if (Row > pMatrix->m_Rows) {
pMatrix->m_Rows = Row;
}
}
 
static void AddColumn(matrix * pMatrix, const unsigned Column, pin * pPin)
{
const unsigned ColumnIndex = Column - 1;
pMatrix->m_pColumnPins[ColumnIndex] = pPin;
 
if (Column > pMatrix->m_Columns) {
pMatrix->m_Columns = Column;
}
}
 
extern matrix * matrixNew(unsigned Rows, unsigned Columns, ...)
{
oosmos_Allocate(pMatrix, matrix, matrixMAX, NULL);
 
pMatrix->m_Rows = 0;
pMatrix->m_Columns = 0;
 
for (unsigned RowIndex = 0; RowIndex < matrixMAX_ROWS; RowIndex++) {
pMatrix->m_pRowPins[RowIndex] = NULL;
 
for (unsigned ColumnIndex = 0; ColumnIndex < matrixMAX_COLS; ColumnIndex++) {
pMatrix->m_pColumnPins[ColumnIndex] = NULL;
pMatrix->m_pSwitch[RowIndex][ColumnIndex] = NULL;
}
}
 
oosmos_ObjectThreadInit(pMatrix, m_ObjectThread, Thread, true);
 
va_list ArgList;
va_start(ArgList, Columns);
 
for (unsigned Row = 1; Row <= Rows; Row += 1) {
AddRow(pMatrix, Row, va_arg(ArgList, pin *));
}
 
for (unsigned 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, unsigned Row, unsigned Column)
{
const unsigned RowIndex = Row - 1;
const unsigned ColumnIndex = Column - 1;
 
//
// Check if this Row/Column slot has already been assigned.
//
if (pMatrix->m_pSwitch[RowIndex][ColumnIndex] != NULL) {
oosmos_FOREVER();
}
 
pMatrix->m_pSwitch[RowIndex][ColumnIndex] = pSwitch;
}
 
extern pin * matrixGetColumnPin(const matrix * pMatrix, unsigned Column)
{
const unsigned ColumnIndex = Column - 1;
return pMatrix->m_pColumnPins[ColumnIndex];
}
oosmos/Classes/matrix.c

10. 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:

10.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
71
[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(unsigned PinNumber, pin_eDirection, pin_eLogic Logic);
extern pin * pinNew_Debounce(unsigned PinNumber, pin_eDirection, pin_eLogic Logic, uint8_t DebounceTimeMS);
extern unsigned pinGetPinNumber(pin * pPin);
#elif defined(__PIC32MX)
extern pin * pinNew(IoPortId Port, unsigned Bit, pin_eDirection Direction, pin_eLogic Logic);
extern pin * pinNew_Debounce(IoPortId Port, unsigned 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 unsigned 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);
#else
#error pin.h: Unsupported platform.
#endif
 
#endif
oosmos/Classes/pin.h

10.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
461
462
463
464
465
466
467
[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;
#else
#error pin.c: Unsupported platform.
#endif
 
oosmos_sActiveObject m_ActiveObject;
oosmos_sTimeout m_Timeout;
uint8_t m_DebounceTimeMS;
unsigned m_State:4; // eStates
unsigned m_Logic:4; // pin_eLogic
unsigned 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) ConfirmingOff_State;
oosmos_TimeoutInMS(&pPin->m_Timeout, pPin->m_DebounceTimeMS);
}
 
break;
}
 
case Off_State: {
if (IsPhysicallyOn(pPin)) {
pPin->m_State = (unsigned) 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) Unknown_State;
break;
}
 
pPin->m_State = (unsigned) On_State;
break;
}
 
case ConfirmingOff_State: {
if (!oosmos_TimeoutHasExpired(&pPin->m_Timeout)) {
break;
}
 
if (!IsPhysicallyOff(pPin)) {
pPin->m_State = (unsigned) Unknown_State;
break;
}
 
pPin->m_State = (unsigned) Off_State;
break;
}
 
case Unknown_State: {
pPin->m_State = (unsigned) (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) 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 unsigned PinValue = digitalRead(pPin->m_PinNumber);
return PinValue == (pPin->m_Logic == pinActiveHigh ? HIGH : LOW);
}
 
extern void pinOn(const pin * pPin)
{
const unsigned 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 unsigned 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 unsigned pinGetPinNumber(pin * pPin)
{
return pPin->m_PinNumber;
}
 
extern pin * pinNew(const unsigned PinNumber, const pin_eDirection Direction, const pin_eLogic Logic)
{
#if defined(oosmos_RASPBERRY_PI)
static bool WiringPi_Initialized = false;
 
if (!WiringPi_Initialized) {
wiringPiSetup();
WiringPi_Initialized = true;
}
#endif
 
oosmos_Allocate(pPin, pin, pinMAX, NULL);
 
pPin->m_PinNumber = PinNumber;
pPin->m_Logic = (unsigned) Logic;
pPin->m_State = (unsigned) Unknown_State;
pPin->m_Direction = (unsigned) 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 unsigned 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_ActiveObjectInit(pPin, m_ActiveObject, RunStateMachine);
}
 
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 unsigned 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) Logic;
pPin->m_State = (unsigned) Unknown_State;
pPin->m_Direction = (unsigned) 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 unsigned 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_ActiveObjectInit(pPin, m_ActiveObject, RunStateMachine);
}
 
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) Logic;
pPin->m_State = (unsigned) Unknown_State;
pPin->m_Direction = (unsigned) 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_ActiveObjectInit(pPin, m_ActiveObject, RunStateMachine);
}
 
return pPin;
}
 
static bool IsPhysicallyOn(const pin * pPin)
{
DigitalOut * pDigitalOut = (DigitalOut *) pPin->m_Pin;
const unsigned 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) Logic;
pPin->m_State = (unsigned) Unknown_State;
pPin->m_Direction = (unsigned) 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_ActiveObjectInit(pPin, m_ActiveObject, RunStateMachine);
}
 
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) 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) Logic;
pPin->m_State = (unsigned) 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)
{
#if defined(pin_DEBUG)
printf("%p Pin ON\n", pPin);
#else
oosmos_UNUSED(pPin);
#endif
}
 
extern void pinOff(const pin * pPin)
{
#if defined(pin_DEBUG)
printf("%p Pin OFF\n", pPin);
#else
oosmos_UNUSED(pPin);
#endif
}
 
void (*pin_pDummy)(void *) = RunStateMachine; // To satisfy compiler
#else
#error pin.c: Unsupported platform.
#endif
oosmos/Classes/pin.c

11. Print Class 'prt'

The prt class, short for print, exposes the single function prtFormatted (see prt.h line 31 and prt.c line 36) 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. The default value is on line 33 of prt.c.
Code for prt:
1
22
23
24
25
26
27
28
29
30
31
32
33
[GPLv2]
 
#ifndef prt_h
#define prt_h
 
#if defined(ARDUINO)
#include <stdint.h>
extern uint32_t 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
[GPLv2]
 
#define MaxBuffer 100
 
#include "prt.h"
#include "oosmos.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
 
#if defined(ARDUINO)
#include <stdbool.h>
extern uint32_t prtArduinoBaudRate;
#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);
 
#if defined(ARDUINO)
static bool First = true;
 
if (First) {
First = false;
 
Serial.end();
Serial.begin(prtArduinoBaudRate);
}
 
Serial.print(Buffer);
Serial.flush();
#else
printf("%s", Buffer);
#endif
}
oosmos/Classes/prt.c

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

12.1 keyer Statechart

Figure 4. Keyer Statechart

12.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, unsigned 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, unsigned 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

13. Linear Regression Class 'reg'

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

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

13.3 reg Code

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

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

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

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

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

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

14.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, unsigned Port);
 
extern bool sockListen(sock * pSock, unsigned 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

14.1.2 sock.c

1
22
23
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...
 
//
// The following preprocessor conditional is used to make the code
// that follows it portable. Tested on Windows and Linux.
//
 
#if defined(_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)
{
#if defined(_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)
{
#if defined(_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, unsigned 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,
(int) (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 = (recvsize_t) (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 = (int) 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, unsigned 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((int) (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-2024  OOSMOS, LLC