Skip to content

Commit 5ee1bb2

Browse files
authored
Enable the injection of a prefined message before after a message. (#19)
1 parent d2a4fa4 commit 5ee1bb2

File tree

4 files changed

+148
-0
lines changed

4 files changed

+148
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
char const * thread_name = rtos::ThisThread::get_name();
60+
Serial.prefix([thread_name]() -> String
61+
{
62+
char msg[64] = {0};
63+
snprintf(msg, sizeof(msg), "[%05lu] %s ", millis(), thread_name);
64+
return String(msg);
65+
});
66+
Serial.suffix([]() -> String
67+
{
68+
return String("\r\n");
69+
});
70+
71+
for(;;)
72+
{
73+
/* Sleep between 5 and 500 ms */
74+
rtos::ThisThread::sleep_for(rtos::Kernel::Clock::duration_u32(random(5,500)));
75+
/* Print a unbroken log message including thread name and timestamp as a prefix. */
76+
Serial.block();
77+
Serial.print("My ");
78+
Serial.print("unbroken ");
79+
Serial.print("thread-safe ");
80+
Serial.print("message!");
81+
Serial.unblock();
82+
}
83+
}

keywords.txt

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ transfer KEYWORD2
2222
create KEYWORD2
2323
block KEYWORD2
2424
unblock KEYWORD2
25+
prefix KEYWORD2
26+
suffix KEYWORD2
2527

2628
#######################################
2729
# Constants (LITERAL1)

src/serial/SerialDispatcher.cpp

+54
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ void SerialDispatcher::begin(unsigned long baudrate, uint16_t config)
6969
ThreadCustomerData data;
7070
data.thread_id = current_thread_id;
7171
data.block_tx_buffer = false;
72+
data.prefix_func = nullptr;
73+
data.suffix_func = nullptr;
7274
_thread_customer_list.push_back(data);
7375
}
7476
}
@@ -184,6 +186,22 @@ void SerialDispatcher::unblock()
184186
_cond.notify_one();
185187
}
186188

189+
void SerialDispatcher::prefix(PrefixInjectorCallbackFunc func)
190+
{
191+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
192+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
193+
if (iter == std::end(_thread_customer_list)) return;
194+
iter->prefix_func = func;
195+
}
196+
197+
void SerialDispatcher::suffix(SuffixInjectorCallbackFunc func)
198+
{
199+
mbed::ScopedLock<rtos::Mutex> lock(_mutex);
200+
auto iter = findThreadCustomerDataById(rtos::ThisThread::get_id());
201+
if (iter == std::end(_thread_customer_list)) return;
202+
iter->suffix_func = func;
203+
}
204+
187205
/**************************************************************************************
188206
* PRIVATE MEMBER FUNCTIONS
189207
**************************************************************************************/
@@ -208,10 +226,46 @@ void SerialDispatcher::threadFunc()
208226
if (d.block_tx_buffer)
209227
return;
210228

229+
/* Return if there's no data to be written to the
230+
* serial interface. This statement is necessary
231+
* because otherwise the prefix/suffix functions
232+
* will be invoked and will be printing something,
233+
* even though no data is actually to be printed for
234+
* most threads.
235+
*/
236+
if (!d.tx_buffer.available())
237+
return;
238+
239+
/* The prefix callback function allows the
240+
* user to insert a custom message before
241+
* a new message is written to the serial
242+
* driver. This is useful e.g. for wrapping
243+
* protocol (e.g. the 'AT' protocol) or providing
244+
* a timestamp, a log level, ...
245+
*/
246+
if (d.prefix_func)
247+
{
248+
String const prefix_str = d.prefix_func();
249+
_serial.write(prefix_str.c_str());
250+
}
251+
252+
/* Now it's time to actually write the message
253+
* conveyed by the user via Serial.print/println.
254+
*/
211255
while(d.tx_buffer.available())
212256
{
213257
_serial.write(d.tx_buffer.read_char());
214258
}
259+
260+
/* Similar to the prefix function this callback
261+
* allows the user to specify a specific message
262+
* to be appended to each message, e.g. '\r\n'.
263+
*/
264+
if (d.suffix_func)
265+
{
266+
String const suffix_str = d.suffix_func();
267+
_serial.write(suffix_str.c_str());
268+
}
215269
});
216270
}
217271
}

src/serial/SerialDispatcher.h

+9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <mbed.h>
2929

3030
#include <list>
31+
#include <functional>
3132

3233
#include <SharedPtr.h>
3334

@@ -58,6 +59,12 @@ class SerialDispatcher : public arduino::HardwareSerial
5859
void block();
5960
void unblock();
6061

62+
typedef std::function<String(void)> PrefixInjectorCallbackFunc;
63+
typedef PrefixInjectorCallbackFunc SuffixInjectorCallbackFunc;
64+
void prefix(PrefixInjectorCallbackFunc func);
65+
void suffix(SuffixInjectorCallbackFunc func);
66+
67+
6168
private:
6269

6370
bool _is_initialized;
@@ -75,6 +82,8 @@ class SerialDispatcher : public arduino::HardwareSerial
7582
arduino::RingBuffer tx_buffer;
7683
bool block_tx_buffer;
7784
mbed::SharedPtr<arduino::RingBuffer> rx_buffer; /* Only when a thread has expressed interested to read from serial a receive ringbuffer is allocated. */
85+
PrefixInjectorCallbackFunc prefix_func;
86+
SuffixInjectorCallbackFunc suffix_func;
7887
} ThreadCustomerData;
7988

8089
std::list<ThreadCustomerData> _thread_customer_list;

0 commit comments

Comments
 (0)