Skip to content

Commit a2f4e6d

Browse files
committed
Initial draft of SerialDispatched based on @facchinm's implementation within ArduinoThreads.
1 parent e20752a commit a2f4e6d

File tree

4 files changed

+349
-0
lines changed

4 files changed

+349
-0
lines changed

examples/ts_serial/ts_serial.ino

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**************************************************************************************
2+
* INCLUDE
3+
**************************************************************************************/
4+
5+
#include <Arduino_ThreadsafeIO.h>
6+
7+
/**************************************************************************************
8+
* CONSTANTS
9+
**************************************************************************************/
10+
11+
static size_t constexpr NUM_THREADS = 5;
12+
13+
/**************************************************************************************
14+
* FUNCTION DECLARATION
15+
**************************************************************************************/
16+
17+
void serial_thread_func();
18+
19+
/**************************************************************************************
20+
* GLOBAL VARIABLES
21+
**************************************************************************************/
22+
23+
static char thread_name[NUM_THREADS][32];
24+
#undef Serial
25+
SerialDispatcher Serial(SerialUSB);
26+
27+
/**************************************************************************************
28+
* SETUP/LOOP
29+
**************************************************************************************/
30+
31+
void setup()
32+
{
33+
/* Fire up some threads all accessing the LSM6DSOX */
34+
for(size_t i = 0; i < NUM_THREADS; i++)
35+
{
36+
snprintf(thread_name[i], sizeof(thread_name[i]), "Thread #%02d", i);
37+
rtos::Thread * t = new rtos::Thread(osPriorityNormal, OS_STACK_SIZE, nullptr, thread_name[i]);
38+
t->start(serial_thread_func);
39+
}
40+
}
41+
42+
void loop()
43+
{
44+
45+
}
46+
47+
/**************************************************************************************
48+
* FUNCTION DEFINITION
49+
**************************************************************************************/
50+
51+
void serial_thread_func()
52+
{
53+
Serial.begin(9600);
54+
55+
for(;;)
56+
{
57+
/* Sleep between 5 and 500 ms */
58+
rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(random(5,500)));
59+
/* Print thread id and chip id value to serial. */
60+
char msg[64] = {0};
61+
snprintf(msg, sizeof(msg), "[%05lu] %s: Lorem ipsum ...", millis(), rtos::ThisThread::get_name());
62+
Serial.println(msg);
63+
}
64+
}

src/Arduino_ThreadsafeIO.h

+1
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,6 @@
2626
#include "BusDevice.h"
2727
#include "spi/SpiBusDevice.h"
2828
#include "wire/WireBusDevice.h"
29+
#include "serial/SerialDispatcher.h"
2930

3031
#endif /* ARDUINO_THREADSAFE_IO_H_ */

src/serial/SerialDispatcher.cpp

+203
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
/*
2+
* This file is part of the Arduino_ThreadsafeIO library.
3+
* Copyright (c) 2021 Arduino SA.
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 2.1 of the License, or (at your option) any later version.
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
/**************************************************************************************
20+
* INCLUDE
21+
**************************************************************************************/
22+
23+
#include "SerialDispatcher.h"
24+
25+
/**************************************************************************************
26+
* CTOR/DTOR
27+
**************************************************************************************/
28+
29+
SerialDispatcher::SerialDispatcher(arduino::HardwareSerial & serial)
30+
: _is_initialized{false}
31+
, _mutex{}
32+
, _cond{_mutex}
33+
, _serial{serial}
34+
, _thread(osPriorityRealtime, 4096, nullptr, "SerialDispatcher")
35+
, _has_tread_started{false}
36+
, _terminate_thread{false}
37+
{
38+
39+
}
40+
41+
/**************************************************************************************
42+
* PUBLIC MEMBER FUNCTIONS
43+
**************************************************************************************/
44+
45+
void SerialDispatcher::begin(unsigned long baudrate)
46+
{
47+
begin(baudrate, SERIAL_8N1);
48+
}
49+
50+
void SerialDispatcher::begin(unsigned long baudrate, uint16_t config)
51+
{
52+
if (!_is_initialized)
53+
{
54+
_serial.begin(baudrate, config);
55+
_is_initialized = true;
56+
_thread.start(mbed::callback(this, &SerialDispatcher::threadFunc)); /* TODO: Check return code */
57+
while (!_has_tread_started) { }
58+
}
59+
60+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
61+
62+
/* Check if the thread calling begin is already in the list. */
63+
osThreadId_t const current_thread_id = rtos::ThisThread::get_id();
64+
if (findThreadCustomerDataById(rtos::ThisThread::get_id()) == std::end(_thread_customer_list))
65+
{
66+
/* Since the thread is not in the list yet we are
67+
* going to create a new entry to the list.
68+
*/
69+
ThreadCustomerData data;
70+
data.thread_id = current_thread_id;
71+
_thread_customer_list.push_back(data);
72+
}
73+
}
74+
75+
void SerialDispatcher::end()
76+
{
77+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
78+
79+
/* Retrieve the current thread id and remove
80+
* the thread data from the thread data list.
81+
*/
82+
osThreadId_t const current_thread_id = rtos::ThisThread::get_id();
83+
std::remove_if(std::begin(_thread_customer_list),
84+
std::end (_thread_customer_list),
85+
[current_thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == current_thread_id); });
86+
87+
/* If no thread consumers are left also end
88+
* the serial device alltogether.
89+
*/
90+
if (_thread_customer_list.size() == 0)
91+
{
92+
_terminate_thread = true;
93+
_thread.join();
94+
_serial.end();
95+
}
96+
}
97+
98+
int SerialDispatcher::available()
99+
{
100+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
101+
return _serial.available();
102+
}
103+
104+
int SerialDispatcher::peek()
105+
{
106+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
107+
return _serial.peek();
108+
}
109+
110+
int SerialDispatcher::read()
111+
{
112+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
113+
return _serial.read();
114+
}
115+
116+
void SerialDispatcher::flush()
117+
{
118+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
119+
_serial.flush();
120+
}
121+
122+
size_t SerialDispatcher::write(uint8_t const b)
123+
{
124+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
125+
126+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
127+
128+
/* If this thread hasn't registered yet
129+
* with the SerialDispatcher via 'begin'.
130+
*/
131+
if (iter == std::end(_thread_customer_list))
132+
return 0;
133+
134+
if (iter->tx_buffer.availableForStore())
135+
iter->tx_buffer.store_char(b);
136+
137+
/* Inform the worker thread that new data has
138+
* been written to a Serial transmit buffer.
139+
*/
140+
_cond.notify_one();
141+
142+
return 1;
143+
}
144+
145+
size_t SerialDispatcher::write(const uint8_t * data, size_t len)
146+
{
147+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
148+
149+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
150+
151+
/* If this thread hasn't registered yet
152+
* with the SerialDispatcher via 'begin'.
153+
*/
154+
if (iter == std::end(_thread_customer_list))
155+
return 0;
156+
157+
size_t bytes_written = 0;
158+
for (; (bytes_written < len) && iter->tx_buffer.availableForStore(); bytes_written++)
159+
iter->tx_buffer.store_char(data[bytes_written]);
160+
161+
/* Inform the worker thread that new data has
162+
* been written to a Serial transmit buffer.
163+
*/
164+
_cond.notify_one();
165+
166+
return bytes_written;
167+
}
168+
169+
/**************************************************************************************
170+
* PRIVATE MEMBER FUNCTIONS
171+
**************************************************************************************/
172+
173+
void SerialDispatcher::threadFunc()
174+
{
175+
_has_tread_started = true;
176+
177+
while(!_terminate_thread)
178+
{
179+
/* Prevent race conditions by multi-threaded
180+
* access to shared data.
181+
*/
182+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
183+
/* Wait for new data to be available */
184+
_cond.wait();
185+
/* Iterate over all list entries. */
186+
std::for_each(std::begin(_thread_customer_list),
187+
std::end (_thread_customer_list),
188+
[this](ThreadCustomerData & d)
189+
{
190+
while(d.tx_buffer.available())
191+
{
192+
_serial.write(d.tx_buffer.read_char());
193+
}
194+
});
195+
}
196+
}
197+
198+
std::list<SerialDispatcher::ThreadCustomerData>::iterator SerialDispatcher::findThreadCustomerDataById(osThreadId_t const thread_id)
199+
{
200+
return std::find_if(std::begin(_thread_customer_list),
201+
std::end (_thread_customer_list),
202+
[thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == thread_id); });
203+
}

src/serial/SerialDispatcher.h

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* This file is part of the Arduino_ThreadsafeIO library.
3+
* Copyright (c) 2021 Arduino SA.
4+
*
5+
* This library is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 2.1 of the License, or (at your option) any later version.
9+
* This library is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* Lesser General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU Lesser General Public
15+
* License along with this library; if not, write to the Free Software
16+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17+
*/
18+
19+
#ifndef SERIAL_DISPATCHER_H_
20+
#define SERIAL_DISPATCHER_H_
21+
22+
/**************************************************************************************
23+
* INCLUDE
24+
**************************************************************************************/
25+
26+
#include "api/HardwareSerial.h"
27+
28+
#include <mbed.h>
29+
30+
#include <list>
31+
32+
/**************************************************************************************
33+
* CLASS DECLARATION
34+
**************************************************************************************/
35+
36+
class SerialDispatcher : public arduino::HardwareSerial
37+
{
38+
39+
public:
40+
41+
SerialDispatcher(arduino::HardwareSerial & serial);
42+
43+
44+
virtual void begin(unsigned long baudrate) override;
45+
virtual void begin(unsigned long baudrate, uint16_t config) override;
46+
virtual void end() override;
47+
virtual int available() override;
48+
virtual int peek() override;
49+
virtual int read() override;
50+
virtual void flush() override;
51+
virtual size_t write(uint8_t const b) override;
52+
virtual size_t write(const uint8_t * data, size_t len) override;
53+
using Print::write;
54+
virtual operator bool() override { return _serial; }
55+
56+
57+
private:
58+
59+
bool _is_initialized;
60+
rtos::Mutex _mutex;
61+
rtos::ConditionVariable _cond;
62+
arduino::HardwareSerial & _serial;
63+
64+
rtos::Thread _thread;
65+
bool _has_tread_started;
66+
bool _terminate_thread;
67+
68+
typedef struct
69+
{
70+
osThreadId_t thread_id;
71+
arduino::RingBuffer tx_buffer;
72+
} ThreadCustomerData;
73+
74+
std::list<ThreadCustomerData> _thread_customer_list;
75+
76+
void threadFunc();
77+
std::list<ThreadCustomerData>::iterator findThreadCustomerDataById(osThreadId_t const thread_id);
78+
79+
};
80+
81+
#endif /* SERIAL_DISPATCHER_H_ */

0 commit comments

Comments
 (0)