Skip to content

Commit 1d9bdc3

Browse files
committed
Implementation of threadsafe reading from the serial.
By duplicating received data in a separate read buffer per thread.
1 parent 5000a58 commit 1d9bdc3

File tree

3 files changed

+135
-5
lines changed

3 files changed

+135
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
#ifdef ARDUINO_PORTENTA_H7_M4
26+
SerialDispatcher Serial(Serial1); /* No SerialUSB for Portenta H7 / M4 Core */
27+
#else
28+
SerialDispatcher Serial(SerialUSB);
29+
#endif
30+
31+
/**************************************************************************************
32+
* SETUP/LOOP
33+
**************************************************************************************/
34+
35+
void setup()
36+
{
37+
/* Fire up some threads all accessing the LSM6DSOX */
38+
for(size_t i = 0; i < NUM_THREADS; i++)
39+
{
40+
snprintf(thread_name[i], sizeof(thread_name[i]), "Thread #%02d", i);
41+
rtos::Thread * t = new rtos::Thread(osPriorityNormal, OS_STACK_SIZE, nullptr, thread_name[i]);
42+
t->start(serial_thread_func);
43+
}
44+
}
45+
46+
void loop()
47+
{
48+
49+
}
50+
51+
/**************************************************************************************
52+
* FUNCTION DEFINITION
53+
**************************************************************************************/
54+
55+
void serial_thread_func()
56+
{
57+
Serial.begin(9600);
58+
59+
for(;;)
60+
{
61+
/* Sleep between 5 and 500 ms */
62+
rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(random(5,500)));
63+
64+
/* Read data from the serial interface into a String. */
65+
String serial_msg;
66+
while (Serial.available())
67+
serial_msg += (char)Serial.read();
68+
69+
/* Print thread id and chip id value to serial. */
70+
if (serial_msg.length())
71+
{
72+
char msg[64] = {0};
73+
snprintf(msg, sizeof(msg), "[%05lu] %s: %s ...", millis(), rtos::ThisThread::get_name(), serial_msg.c_str());
74+
Serial.block();
75+
Serial.println(msg);
76+
Serial.unblock();
77+
}
78+
}
79+
}

src/serial/SerialDispatcher.cpp

+50-4
Original file line numberDiff line numberDiff line change
@@ -99,19 +99,37 @@ void SerialDispatcher::end()
9999
int SerialDispatcher::available()
100100
{
101101
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
102-
return _serial.available();
102+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
103+
if (iter == std::end(_thread_customer_list)) return 0;
104+
105+
prepareSerialReader(iter);
106+
handleSerialReader();
107+
108+
return iter->rx_buffer->available();
103109
}
104110

105111
int SerialDispatcher::peek()
106112
{
107113
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
108-
return _serial.peek();
114+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
115+
if (iter == std::end(_thread_customer_list)) return 0;
116+
117+
prepareSerialReader(iter);
118+
handleSerialReader();
119+
120+
return iter->rx_buffer->peek();
109121
}
110122

111123
int SerialDispatcher::read()
112124
{
113125
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
114-
return _serial.read();
126+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
127+
if (iter == std::end(_thread_customer_list)) return 0;
128+
129+
prepareSerialReader(iter);
130+
handleSerialReader();
131+
132+
return iter->rx_buffer->read_char();
115133
}
116134

117135
void SerialDispatcher::flush()
@@ -203,4 +221,32 @@ std::list<SerialDispatcher::ThreadCustomerData>::iterator SerialDispatcher::find
203221
return std::find_if(std::begin(_thread_customer_list),
204222
std::end (_thread_customer_list),
205223
[thread_id](ThreadCustomerData const d) -> bool { return (d.thread_id == thread_id); });
206-
}
224+
}
225+
226+
void SerialDispatcher::prepareSerialReader(std::list<ThreadCustomerData>::iterator & iter)
227+
{
228+
iter->is_reader = true;
229+
if (!iter->rx_buffer)
230+
iter->rx_buffer.reset(new arduino::RingBuffer());
231+
}
232+
233+
void SerialDispatcher::handleSerialReader()
234+
{
235+
while (_serial.available())
236+
{
237+
int const c = _serial.read();
238+
239+
std::for_each(std::begin(_thread_customer_list),
240+
std::end (_thread_customer_list),
241+
[c](ThreadCustomerData & d)
242+
{
243+
if (!d.is_reader)
244+
return;
245+
246+
if (!d.rx_buffer->availableForStore())
247+
return;
248+
249+
d.rx_buffer->store_char(c);
250+
});
251+
}
252+
}

src/serial/SerialDispatcher.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
#include <list>
3131

32+
#include <SharedPtr.h>
33+
3234
/**************************************************************************************
3335
* CLASS DECLARATION
3436
**************************************************************************************/
@@ -72,13 +74,16 @@ class SerialDispatcher : public arduino::HardwareSerial
7274
osThreadId_t thread_id;
7375
arduino::RingBuffer tx_buffer;
7476
bool block_tx_buffer;
77+
bool is_reader; /* This thread has expressed interest to read from Serial via a call to either read() or available(). */
78+
mbed::SharedPtr<arduino::RingBuffer> rx_buffer; /* Only when a thread has expressed interested to read from serial a receive ringbuffer is allocated. */
7579
} ThreadCustomerData;
7680

7781
std::list<ThreadCustomerData> _thread_customer_list;
7882

7983
void threadFunc();
8084
std::list<ThreadCustomerData>::iterator findThreadCustomerDataById(osThreadId_t const thread_id);
81-
85+
void prepareSerialReader(std::list<ThreadCustomerData>::iterator & iter);
86+
void handleSerialReader();
8287
};
8388

8489
#endif /* SERIAL_DISPATCHER_H_ */

0 commit comments

Comments
 (0)