Undefined reference to Serial1 (generic STM32F103RB)

I am having a problem building code for the “genericSTM32F103RB” board, in particular, referencing the serial ports. There is an inconsistency between the bluepill board, which uses a very similar uC.

SSCCE:
src/main.c:

#include "Arduino.h"

#define SERIAL_PORT Serial1

void setup() {
	SERIAL_PORT.begin(115200);
}

void loop() {
	SERIAL_PORT.println("Hello world");
}

platformio.ini:

[env]
debug_tool = blackmagic
framework = arduino
platform = platformio/ststm32@14.0.0
upload_protocol = blackmagic

[env:genericSTM32F103RB]
board = genericSTM32F103RB

[env:bluepill]
board = bluepill_f103c8_128k

Both of these parts are classified as medium-density in the datasheet (stm32f103c8.pdf), and therefore, I would expect them to have the same peripherals defined.

However, while both boards have Serial defined, BluePill has Serial1 defined, while the generic does not, and generic has Serial2 defined, while bluepill does not.

Is this purely based on which pins are physically available on the PCB? Or is there something else underlying this? And if so, where can I find where this is defined?

Every variant has a different set of serials activated. The documentation tells you how to active the other non-activated ones. Use this info together with build_flags.

Thank you for responding!
Can you possibly point me to how I enable the alternate function? I.e. so I can use USART3 on pins PC_11 (rx) and PC_10(tx)?

I did try to simply create the HardwareSerial port on those pins, and that did not work.
I have been trawling through the source, but so far have been unable to figure it out.

I have found the pin_function() function, but cannot figure out the argument it needs. And there are very few hits on the internet of any relevance.

Unfortunately, I was an idiot. A typo in the src led to the wrong serial port being used. The following works perfectly to enable the third serial port on the alternate pins:

#include "Arduino.h"

#define SERIAL_PORT Serial3

HardwareSerial Serial3(PC_11, PC_10);

void setup() {
    SERIAL_PORT.begin(115200);
}

void loop() {
    SERIAL_PORT.println("Hello world");
}

Defining your own Serial3 object will conflict for those specific board variants which have their Serial object set to be Serial3 (aka UART3 is the standard one). I do not know whether any of the board variants in the STM32Duino core actually have their standard serial on UART3. The way I would have done it is to follow the documentation and use the ENABLE_HWSERIALx with PIN_SERIALn_RX and PIN_SERIALn_TX macros as build flags, so e.g.

[env]
debug_tool = blackmagic
framework = arduino
platform = platformio/ststm32@14.0.0
upload_protocol = blackmagic
build_flags = 
   -DENABLE_HWSERIAL3
   -DPIN_SERIAL3_RX=PC11
   -DPIN_SERIAL3_TX=PC10

[env:genericSTM32F103RB]
board = genericSTM32F103RB

[env:bluepill]
board = bluepill_f103c8_128k

together with omitting HardwareSerial Serial3(PC_11, PC_10); in the code.

For all intents and purposes, for the boards you’re compiling for, your config and code are perfectly valid and work.

This is a very low-level function. If you want to change the pins during run-time, you should call the

functions on your serial object (which are instances of the HardwareSerial class).

So if you e.g. have a Serial3 object (because you’ve previously enabled it or created it yourself), then you you can use

Serial3.setRx(PC11);
Serial3.setTx(PC10);

in the code.

The arguments are either the pin numbers (uint32_t) or a value from the PinName enum. The pun numbers are the PXY macros (e.g. PC3) where as the enum is the e.g. PC_3 value.

and here.

Most APIs accept either.

Hm, interesting. On another note, I see for the F103C8 and F103CB, those pins are not available for UART3 (TX)

Wherase for the F103RB, they are listed as UART3 with partial remap.

Not sure why the entry is missing on the C8/CB boards. Does Serial3 on those boards work correctly or does it crash on run- or boot time?

Thanks for looking into this in such detail. I guess you are correct, in terms of predefining the UART in the build flags, rather than in code. My rationale is that I am porting ESPHome, code designed on run on the ESP8266 and ESP32, the latter of which is almost entirely flexible in terms of pin allocations to peripherals.
Of course, since ESPHome constructs the platformio.ini file as well, adding the UART pin assignments in the build file is also an option.

Unfortunately, adding the STM32 into a mix of “only ESP8266 and ESP32” adds an exponential amount of complexity to the project.