I have a similar problem with USBSerial
. I override the console to use the USBSerial
and printf works without issues. However with scanf and getchar, it seems to buffer the inputs and return them all at once (around 1000 chars). It works when I use the USBSerial::getc()
method instead. So it seems to be an issue with the combination of getc()
redirection and USBSerial
. Does anybody have an idea what is causing this?
Thanks for the great posts. very helpful.
One question mbed-app.json does not seem to be used when compiling a library (e.g. mbed compile --library --source=mbed-os …). Looking at the source mbed.py it is only included when building a complete application. Is the only option to manual change in the different mbed_lib.json files or is there an other possibility?
The mbed_app.json
seems to be used when building a library too.
mbed-os/tools/build_api.py
:
def build_library(src_paths, build_path, target, toolchain_name,
dependencies_paths=None, name=None, clean=False,
archive=True, notify=None, macros=None, inc_dirs=None, jobs=1,
report=None, properties=None, project_id=None,
remove_config_header_file=False, app_config=None,
build_profile=None, ignore=None, resource_filter=None, coverage_patterns=None):
...
Keyword arguments:
dependencies_paths - The location of libraries to include when linking
...
app_config - location of a chosen mbed_app.json file
...
# Pass all params to the unified prepare_toolchain()
toolchain = prepare_toolchain(
src_paths, build_path, target, toolchain_name, macros=macros,
clean=clean, jobs=jobs, notify=notify, app_config=app_config,
build_profile=build_profile, ignore=ignore, coverage_patterns=coverage_patterns)
...
I realize I should have been more specific about the Library. Mbed is having 2 “versions.” I mend that Mbed_app.json is not included when you build a static library (.a / .ar) instead of a linked executable. You then use the command: mbed compile --library. It is then using build.py to create one
The build_api.py build_library seems to be creating a source library that can be included in a future project .lib file.
Any help/advice on including the changes in a static library appreciated.
regards,
Paul
Hello Paul,
Now I see your point. However, since it doesn’t directly relate to printf
, in order to avoid kidnapping this thread I’d suggest to open a new one and continue there, if you don’t mind.
Best regards, Zoltan
Thanks.
It is related, as the changes to enable printing to serial/printf must be done now be in mbed_app.json which is NOT included in a static library. However it is a separate and much broader issue than only this serial/printf issue.
i’ll raise a new issue.
regards,
Paul
Great post! Is there a way to get an ´mbed::FileHandle´ or similar out of a ´std::FILE´, that I can e.g. use together with int ´FATFileSystem::file_sync(fs_file_t file)´ ?
You can get the FileHandle
of std
by defining your own console.
For example:
...
FileHandle* stdFileHandle; // declare a global variable
...
FileHandle* mbed::mbed_override_console(int)
{
static BufferedSerial myConsole(STDIO_UART_TX, STDIO_UART_RX, 115200);
stdFileHandle = &myConsole; // get the std file handle
return &myConsole;
}
...
int main()
{
// FileHandle of std is available as 'stdFileHandle`
How have you override the console to use USBSerial with printf ?
Any USBSerial methode doesn’t give you BufferedSerial class/object.
mbed_override_console
is the function that redirects printf and it requires a FileHandle
, not a BufferedSerial
.
USBSerial
itself derives from FileHandle
, so you can just return the USBSerial
object in that function.
As explained by @ boraozgen :
#include "mbed.h"
#include "USBSerial.h"
FileHandle* mbed::mbed_override_console(int)
{
static USBSerial myConsole(false, 0x1f00, 0x2012, 0x0001);
return &myConsole;
}
Edit: To make this work
- either create the
myConsole
asmyConsole(true, 0x1f00, 0x2012, 0x0001);
- or call
myConsole.connect();
before returning from the function.
See below @ boraozgen’s tip.
I was my problem. Thanks for your answers.
Have you solved the getc redirection ?
I am using v6.12 and have found the same effect with getc.
I don’t remember solving it, it was just a PoC and I didn’t work further on it.
Another tip on USBSerial:
Constructing USBSerial with connect_blocking = true
(first parameter) blocks in the first printf call until the COM port is opened.
mbed::FileHandle *mbed::mbed_override_console(int)
{
static USBSerial serial;
return &serial;
}
Constructing with false
and calling connect()
does not block and the messages will be printed after opening the COM port. The COM port is not available if I do not call connect
after constructing with false
. Isn’t this the case for you @hudakz?
mbed::FileHandle *mbed::mbed_override_console(int)
{
static USBSerial serial(false);
serial.connect();
return &serial;
}
Thank you Bora for pointing this out! Yes, it works the same way on my side. No /dev/ttyACMx
port is available (on Linux) when the USBSerial
object is created with connect_blocking = false
unless the connect()
method is called as you suggest.
Actually, no ITM initialization is needed for STM targets. It seems that it’s sufficient to define a dummy ‘itm_init’ function as below and redirect the Mbed CONSOLE to the SWO:
#include "mbed.h"
#include "SerialWireOutput.h"
DigitalOut led1(LED1);
extern "C" void itm_init() { }
FileHandle* mbed::mbed_override_console(int)
{
static SerialWireOutput swo;
return &swo;
}
int main()
{
printf("Hello, SWO!\r\n");
while (true) {
led1 = !led1;
printf("blink\r\n");
ThisThread::sleep_for(500);
}
}
After adding an ITM
device in the mbed_app.json
file as below, printf
should print over the SWO:
{
"target_overrides": {
"*": {
"target.device_has_add": ["ITM"]
}
}
}
A modified STM-Link V 2 probe can be used to program and debug STM custom target boards. The SWO
wire shall be connected to the target MCU’s PB_3
pin (but it’s better to check also the related datasheet).
On the NUCLEO boards the SWO wire is available at the pin #6 on the CN4 (SWD) connector. To print over the SWO connect it to the PB_3
pin with a wire.
Printf messages can be then displayed with the STM32CubeProgrammer by switching to the SWV page.
Thats correct, I don’t know why I didn’t mention this before
Thanks for the insightful elaboration on console behavior in Mbed OS 6.
How would you test the std console input for readability? Here’s the example from Mbed OS 5:
#include "mbed.h"
Serial pc(USBTX, USBRX);
int main()
{
while (true)
{
if(pc.readable()) {
pc.getchar();
......
}
}
}
Hello Hans,
You can try:
#include "mbed.h"
int main()
{
FileHandle* console_in = mbed_file_handle(STDIN_FILENO);
FileHandle* console_out = mbed_file_handle(STDOUT_FILENO);
const int buffer_size = 256;
char buffer[buffer_size];
int read_count;
while(true) {
while (console_in->readable()) {
read_count = console_in->read(buffer, buffer_size);
console_out->write(buffer, read_count); // echo
...
}
...
}
}
If you’d like to do it byte by byte:
int main()
{
FileHandle* console_in = mbed_file_handle(STDIN_FILENO);
FileHandle* console_out = mbed_file_handle(STDOUT_FILENO);
char c;
while(true) {
while (console_in->readable()) {
console_in->read(&c, 1);
console_out->write(&c, 1); // echo
...
}
...
}
}
However, the first method should be more efficient.
Came up with another hack… this is if you want to switch between console UART pins (as defined in pinnames.h) and RX/TX pins of your choosing without having to change code between the two in main.
#include "mbed.h"
#define USE_MYUART_INTERFACE 0
#if USE_MYUART_INTERFACE
BufferedSerial otherUART(P3_1,P3_0,MBED_CONF_PLATFORM_STDIO_BAUD_RATE);
FILE* myTX = fdopen(&otherUART, "r+");
#define printf(f_, ...) fprintf((f_), __VA_ARGS__);
#else
FILE* myTX = NULL;
#define printf(f_, ...) printf( __VA_ARGS__);
#endif
// main() runs in its own thread in the OS
int main()
{
printf(myTX,"Hello New World\r\n");
while (true) {
}
}