The justification was empirical. It dropped maybe 2% of characters at 56K. Maybe a new bootloader would fix it, but … I’m lazy.
Why set up the ‘scope when it can tell me itself at multiple speeds? I also use raw chips with lower clock speeds.
how does that help you with your existing code?
why just resport the speed at one speed, 300, and then change to that speed
??? All my ‘existing code’ over many years has the same basic framework. The various ‘chunks’ of framework are timestamped, so I can tell at a glance how ‘old’ the various chunks are.
I keep the chunks in a file called ArduinoUsefulCodeFrags_txt.cpp and when I revisit projects, update chunks if needed.
An example: my framework for running Unit Tests … it can even tell me the result without using Serial, but using three brightnesses (off, dim, bright) of BUILTIN_LED …
void loop(){// aunit 2025-02-01
aunit::TestRunner::setTimeout(60); // 60 seconds
// following requires cough local mods
aunit::TestRunner::run(); // AUnit will add colour option for EpoxyDuino
if(aunit::TestRunner::gAllDone){
if(aunit::TestRunner::gFailedOrExpiredTestCount == 0){
pinMode(LED_BUILTIN, INPUT_PULLUP); //digitalWrite(LED_BUILTIN, LOW);
delay(1000);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
delay(1000);
pinMode(LED_BUILTIN, INPUT_PULLUP); //digitalWrite(LED_BUILTIN, LOW);
delay(1000);
pinMode(LED_BUILTIN, INPUT); //digitalWrite(LED_BUILTIN, LOW);
delay(1000);
} else {
for(int ix = 0; ix < aunit::TestRunner::gFailedOrExpiredTestCount; ++ix){
pinMode(LED_BUILTIN, INPUT_PULLUP); //digitalWrite(LED_BUILTIN, LOW);
delay(150);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
delay(200);
pinMode(LED_BUILTIN, INPUT_PULLUP); //digitalWrite(LED_BUILTIN, LOW);
delay(150);
pinMode(LED_BUILTIN, INPUT); //digitalWrite(LED_BUILTIN, LOW);
delay(150);
}
delay(750);
}
}
}
Here’s the first 20 lines of one …
#include <TimestampNow.h>
#line 3 SKETCH_MAIN_FILE_HANDLE // update FILE with shorter name NB this confused avr-objdump finding source code
//#define BREW_MEAN_AND_LEAN // to reduce code size
#define NO_STRING // optional - to check that no strings are used! (which use heap space) Try AceCommon.h, using namespace ace_common; , PrintStr instead
#include <BrewmanzUtes.h>
using namespace BrewmanzUtes;
#include <BrewmanzAvr.h>
using namespace BrewmanzAvr;
//#pragma message("\n#BTW SOURCE_HANDLE = <" NUM2STR(SOURCE_HANDLE) ">.")
F_SOURCE_HANDLE_STRING( OptoCouplerTransferTester,ino); // define Flash string with name & extension
#define USE_F_SOURCE_HANDLE_STRING_NAME_NQ F_OptoCouplerTransferTester_ino
namespace { // 2025-06-03 this allows collapsing environmental stuff in IDE
#pragma message("\n#BTW __cplusplus (nb >= 201103L is special) = <" NUM2STR(__cplusplus) ">.")
#pragma message("\n#BTW __GXX_RTTI = <" NUM2STR(__GXX_RTTI) ">") // gcc RTTI stuff
#pragma message("\n#BTW BUILD_PATH = <" BUILD_PATH ">") // handy to find elf files etc
#pragma message("\n#BTW LED_BUILTIN = <" NUM2STR(LED_BUILTIN) ">")
probably too many macros to be useful for others. BrewmanzUtes.h alone has over 4K lines in it.
I’ll show you TimestampNow.h - which is created by an automated script inside the IDE on compile …
#pragma once
//# Generated via TimestampNow.sh in ~/git/XXXX/BrewmanzUtes
//# For Arduino IDE, in ~/.arduino15/packages/arduino/hardware/avr/1.8.3/platform.txt,
//# (maybe other .../platform.txt (different builds like MiniCore maybe) as well)
//# (# AVR compile patterns), add line ...
// recipe.hooks.sketch.prebuild.1.pattern=/home/XXXX/git/XXXX/libraries/BrewmanzUtes/TimestampNow.sh {build.source.path}
//# (## Compile c files) + (## Compile c++ files), add fragment ...
// -DPROJECT_NAME="{build.project_name}" # e.g. "MyProject.ino"
//# For EpoxyDuino, in Makefile add ...
// TIMESTAMP_FORCE != ~/git/XXXX/BrewmanzUtes/TimestampNow.sh
//# Inside .ino, maybe use SKETCH_HANDLE or PROJECT_HANDLE instead of FILE
//# Inside .ino, for compile time, consider ...
// Serial.println(F(TIMESTAMP_NOW_STR_CY_M_D_H_M_S_F)); // CCYY-mm-DD@HH:MM:SS.FFF ; ideal if space not an issue
//# or
// Serial.println(F(TIMESTAMP_NOW_STR_YMD "@" TIMESTAMP_NOW_STR_HM)); // YYmmDD@HHMM ; compromise if space limited
#if defined(EPOXY_DUINO)
#include "pgmspace.h" // for PROGMEM def'n
#elif defined(ARDUINO_ARCH_AVR)
#include "avr/pgmspace.h" // for PROGMEM def'n
#else
#pragma message("#BTW unsure of where to get pgmspace.h from")
#include "pgmspace.h" // for PROGMEM def'n
#endif
#define GIT_BRANCH_ "develop"
#define GIT_LAST_HASH "bf1f44f-dirty"
#define GIT_LAST_TIME "2026-01-10 17:46:09 +1300"
#define GIT_LAST_TIME_NO_SPACES "2026-01-10T17:46:09+13:00"
//#define GIT_LAST_SUBJECT "more tests for OptoCouplerTransferTester.ino"
#define SKETCH_HANDLE_FULL "/home/XXXX/git/XXXX/TestEquipment_Folder/OptoCouplerTransferTester"
#define SKETCH_HANDLE "OptoCouplerTransferTester.ino" // Arduino IDE; last bit of {build.source.path} (quoted)
#define SKETCH_HANDLE_NQ OptoCouplerTransferTester.ino // Arduino IDE; last bit of {build.source.path}
#define SKETCH_MAIN_FILE_HANDLE "OptoCouplerTransferTester.ino" // Arduino IDE; last bit of {build.source.path} (+ '.ino', quoted)
#define SKETCH_FOLDER1 "OptoCouplerTransferTester"
#define SKETCH_FOLDER2 "TestEquipment_Folder"
#define SKETCH_FOLDER3 "XXXX"
#define SKETCH_FOLDER4 "git"
#define SKTCH_FLDR2 "TestEquipment_Folder//[A-Z]*/"
#define BUILD_PATH "/tmp/arduino_build_625872"
#ifdef xxPROJECT_NAME // defined for ARDUINO IDE, not for EpoxyDuino
#define PROJECT_HANDLE PROJECT_NAME // e.g. "MyProject.ino"
#define PROJECT_HANDLE_NQ SKETCH_HANDLE_NQ // e.g. MyProject
#else
#define PROJECT_HANDLE SKETCH_HANDLE // e.g. "MyProject"
#define PROJECT_HANDLE_NQ SKETCH_HANDLE_NQ // e.g. MyProject
#endif
class __FlashStringHelper;
extern const char ProjectHandleName_actual[] PROGMEM; // may expand to 'attribute((progmem))' // will be filled with PROJECT_HANDLE in TimestampNow.cpp
const __FlashStringHelper* const ProjectHandleName = reinterpret_cast<const __FlashStringHelper*>(ProjectHandleName_actual); // print this version
#define TIMESTAMP_NOW_STR_CY_M_D_H_M_S_F "2026-01-10@23:39:46.764"
#define TIMESTAMP_NOW_STR_CY_M_D_H_M_S "2026-01-10@23:39:46"
#define TIMESTAMP_NOW_STR_Y_M_D_H_M_S "26-01-10@23:39:46"
#define TIMESTAMP_NOW_STR_CY_M_D "2026-01-10"
#define TIMESTAMP_NOW_STR_Y_M_D "26-01-10"
#define TIMESTAMP_NOW_STR_H_M_S_F "23:39:46.764"
#define TIMESTAMP_NOW_STR_H_M_S "23:39:46"
#define TIMESTAMP_NOW_STR_YMD "260110"
#define TIMESTAMP_NOW_STR_HMS "233946"
#define TIMESTAMP_NOW_STR_HM "2339"
#define TIMESTAMP_NOW_NUM_CYMD_HMSF 20260110.233946764
#define TIMESTAMP_NOW_NUM_CYMD_HMS 20260110.233946
#define TIMESTAMP_NOW_NUM_YMD_HMS 260110.233946
#define TIMESTAMP_NOW_NUM_YMD_HM 260110.2339
#define TIMESTAMP_NOW_NUM_CYMD 20260110
#define TIMESTAMP_NOW_NUM_YMD 260110
#define TIMESTAMP_NOW_NUM_HMSF 23394676
#define TIMESTAMP_NOW_NUM_HMS 233946
#define TIMESTAMP_NOW_NUM_HM 2339
#pragma message("Compiling " PROJECT_HANDLE " @ " TIMESTAMP_NOW_STR_CY_M_D_H_M_S ": GIT " GIT_BRANCH_ " @" GIT_LAST_TIME " #" GIT_LAST_HASH)
I was an Assembler programmer for several decades - it probably shows …
printSwitchBaudrateMessage(pSerial, 9600); printSwitchBaudrateMessage(pSerial, 19200); printSwitchBaudrateMessage(pSerial, 38400); printSwitchBaudrateMessage(pSerial, 115200);
why not
void setup ()
{
const unsigned bps = 38400;
Serial.begin (300);
Serial.print ("baudrate ");
Serial.println (bps);
Serial.flush ();
Serial.begin (bps);
}
why not
(state project bps @ 300 bps, then use project bps)
Because software switching of baudrate is quicker and easier (and yes, lazier [a trait I have spent years developing]) than humanware switching.
When I plug your project into my Serial Monitor running at 9600 bps from my last project, I get garbled messages, so I need to switch to … “was it 1200 bps? No; still garbled … try 300 bps? Ah! Yes!” to get bps, then switch AGAIN to project bps.
If I decided that 300 bps was the best option for a new project, I’d add printSwitchBaudrateMessage(pSerial, 300);
(i.e. making 5 broadcasts) to my framework code.
40+ years ago, when nationwide networks used RS232 async modems at various bps, I could tell just by looking at the garbled characters and became pretty good at recognising what the bps was. That skill has long faded away.
Because software switching of baudrate is quicker
you only spcified 4 speeds. Your approach woudln't work if you weren't set to one of those speeds.
one reason for using the characters 'A' and 'T' for those Hayes modems was that from those 2 characters the modem can determine the speed and parity the serial interface is set to
The bootloader only runs during reset and during sketch upload, so its limitations mainly affect code uploading and any serial traffic that occurs before your sketch takes control. Once your program is running, the bootloader is no longer involved, and serial reliability depends entirely on your sketch, the Arduino core, and how you handle interrupts and buffering at run time.
I still don’t get it. Seems to be a solution looking for a problem or something of another age when serial coms were less robust …
But if that makes you happy , that’s what matters.
Your approach woudln't work if you weren't set to one of those speeds.
The moment I start using a different speed, I add it to my framework.
Note that ‘start using’ a speed is different from ‘preparing to use’ a speed.
Iirc, 57600 is a particularly “fraught” bitrate. It has a relatively high error from ideal, and that made worse because the usb serial chip on some boards also has a high error in the opposite direction..
The arduino serial code has some hack at 57600 to “help”, but it might be worse with other usb/serial chips.
Errors at 57600 do not imply that there will also be errors at 115200, or 250000.
And the baud rate used by the bootloader has nothing to do with speeds used in sketches…
And the baud rate used by the bootloader has nothing to do with speeds used in sketches…
yes, that's my point and why I don't get it.
Also, I onderstaand "lazy" engineering, but when there is a much better boot loader available and it's a one off operation I don't get why you would stick to the old crappy boot loader from the last millenium and danse around its issues. What OP explains (and I don't get) seems actually more work every time he wants to use the Nano, so not so lazy...
serial reliability depends entirely on your sketch, the Arduino core, and how you handle interrupts and buffering at run time.
I reckon that the USB-to-serial converter chip is also involved with serial reliability - or lack thereof. Have you heard about the fake CH340G chips?
Have you heard about the fake CH340G chips?
Sure, but I would not use such if identified as crappy. If I had bought such a module, I would recycle it and purchase better quality parts for my gigs rather than trying to danse around the issue.
Counterfeit CH340G USB‑to‑serial chips can behave differently from genuine ones and cause problems, especially with drivers - have you heard about FakeCH340DriverFixer?
Anyway - I still don't get how your code snippet does you any good.
I guess you didn’t buy a batch of Chinese Nano clones with an old bootloader that drop Serial bits even at 57K6 baud.
We established your code has nothing to do with the boot loader's speed.
So you now say it's to address the issues with fake CH340G chips and not Nanos with the old boot loader?
Just to be clear, I'm not trying to be annoying here, I really don't get how your code snippet is helping. If there are statistical errors in communicating with fake CH340G.
Printing a few words successfully at several baud rates only demonstrates that the device can transmit short bursts of UART data under light load. It does not exercise sustained throughput, buffer handling, interrupt timing, ... which are areas where counterfeit CH340‑compatible chips typically fail.