The optoisolator carrying the Bafang controller’s LIGHT signal pulls Pin 2 down to turn the LED on constantly for night riding:
if (!Morser.continueSending())
if (digitalRead(PIN_LIGHTMODE) == HIGH)
Morser.startSending();
else
digitalWrite(PIN_OUTPUT,HIGH); // constantly turn on in headlight mode
That’s the entirety of the program’s loop() function, so there’s not much to the firmware.
Imagine that: a whole computer devoted to sampling an input bit a zillion times a second and persistently setting an output bit:

The Morse output to the rear is now “s” rather than “i” for more blinkiness, but I doubt anybody will ever notice.
The next time I raise the hood on this thing, I’ll add a digital input to select FRONT or REAR mode to get me out of having to remember which hardware goes where.
The Arduino source code as a GitHub Gist:
| // Tour Easy Running Light | |
| // Ed Nisley – KE4ZNU | |
| // September 2021 | |
| // 2023-03 preprocessorize for front/rear lights | |
| // https://github.com/markfickett/arduinomorse | |
| #include <morse.h> | |
| // Bafang headlight output pulls pin low | |
| #define PIN_LIGHTMODE 2 | |
| #define PIN_OUTPUT 13 | |
| #define FRONT | |
| #if defined(FRONT) | |
| #define BLINKS "b e " | |
| #define POLARITY false | |
| #elif defined(REAR) | |
| #define BLINKS "s " | |
| #define POLARITY true | |
| #else | |
| #error "Needs FRONT or REAR" | |
| #endif | |
| // second param: true = active low output | |
| LEDMorseSender Morser(PIN_OUTPUT,POLARITY,(float)10.0); | |
| void setup() | |
| { | |
| pinMode(PIN_LIGHTMODE,INPUT_PULLUP); | |
| Morser.setup(); | |
| Morser.setMessage(String("qst de ke4znu ")); | |
| Morser.sendBlocking(); | |
| Morser.setSpeed(75); | |
| Morser.setMessage(String(BLINKS)); | |
| } | |
| void loop() | |
| { | |
| if (!Morser.continueSending()) | |
| if (digitalRead(PIN_LIGHTMODE) == HIGH) | |
| Morser.startSending(); | |
| else | |
| digitalWrite(PIN_OUTPUT,HIGH); // constantly turn on in headlight mode | |
| } |





























