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 | |
---|---|
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 |
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; | |
} |
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.
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 |
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; | |
} |
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.
LED
, whatever. It's particularly
useful to toggle an on-board LED to act as a software health monitor during development.
toggle
object turns a pin On, then Off, then On again, forever.toggle
object is started in the On
state.toggle
object that is "On" shall turn
Off
after a specified number of "On" milliseconds.toggle
object that is "Off" shall turn
On
after a specified number of "Off" milliseconds.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 |
On
and Off
states
and one that uses the OOSMOS Object Thread feature.
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
.
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; | |
} |
toggle
demonstrates the ability to use an Object Thread to reduce the aggregate number
of states in the system.
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; | |
} |
accum
class allows you to store and
react to large time values, with no real-world practical limit.
accum
class will properly handle each wrap around
from 4,294,967,295 to zero.
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
.)
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.
#ifdef
's to
select an implementation that may be platform specific.
PWM
signal frequency and/or the
duty cycle resolution (see ESP32). For these platforms, we extend the interface in (hopefully)
portable way.
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 |
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 |
pid
class is an implementation of a
PID Controller as described in Wikipedia.
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 |
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; | |
} | |
[...] |
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; | |
} |
adc
class is an implementation of an analog to digital conversion.
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 |
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 |
sw
) allows an object
to react when
a switch is pressed and/or released.
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.
sw
object shall accept multiple close event subscribers.sw
object shall accept multiple open event subscribers.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 |
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; | |
} | |
} | |
} |
sw
object will notify the
user when the switch closes or opens.)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 |
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]; | |
} |
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.
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
.)
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).
pin.h
and 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 | |
[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 |
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 |
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.
Arduino
platforms, you will need to specify
the baud rate for the serial port. The default value is on line 33 of prt.c
.
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 |
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 | |
} |
pin
class. There is even a Windows
version for educational purposes.
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 |
C
code, which
allows you to see an overall map of the functions that make up the
keyer implementation.
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; | |
} |
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.
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 |
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; | |
} |
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); | |
} |
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 | |
FORECAST
on the same values in our test case. FORECAST
does the
same thing as our regPredictY
function.
FORECAST
formula for each value of column labeled
Predict
Time
,
resulting
in the values in column labeled
Position
At
Time
.
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.
sock
class is below. There is a
good overview and example of how to use sock.h
and sock.c
in the Sockets example.
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 |
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); | |
} |