Every lighthouse has its own pattern of light, called its character, so sailors can tell at night which lighthouse they are looking at. Some flash once every five seconds, some flash in groups, and old ones had a turning lens that swept a beam across the sea. In this lesson you print a small lighthouse, light it with a white LED and program four real lighthouse characters. A button switches between them, and a red harbour light blinks next to it.
What you'll learn
- How to fade an LED smoothly with
analogWrite()(PWM) - How to make repeating patterns with the remainder operator
% - How to keep several things going at once with
millis() - How to print a part that fits an electronic component
What you need
- UNO R3 board and USB cable (UNO R3 Starter Kit)
- White LED and red LED (UNO R3 Starter Kit)
- 2 resistors, 220 Ω (UNO R3 Starter Kit)
- 1 push button (UNO R3 Starter Kit)
- 2 female-to-female and 2 male-to-male jumper wires, to reach the LED in the lantern (UNO R3 Starter Kit)
- The printed parts from the 3D printing box below (lighthouse tower), or the cardboard version described there
- Breadboard and jumper wires (UNO R3 Starter Kit)
Wiring

| From | To |
|---|---|
| UNO pin 9 | 220 Ω resistor, then the white LED's long leg (through the jumper wires up the tower) |
| White LED short leg | GND |
| UNO pin 6 | 220 Ω resistor, then the red LED's long leg |
| Red LED short leg | GND |
| Push button, one leg | UNO pin 2 |
| Push button, the diagonally opposite leg | GND |
The beacon must be on pin 9, because only the pins marked with a ~ on the UNO can fade an LED.
The sketch
const int BEAM_PIN = 9; // white LED in the lantern (a PWM pin, marked ~)
const int HARBOUR_PIN = 6; // red LED: the harbour light
const int BUTTON_PIN = 2;
const byte MODES = 4;
const char* const NAMES[MODES] = {
"Rotating beam: sweeps past every 6 s",
"Single flash every 5 s",
"Group of 3 flashes every 10 s",
"Occulting: on, with a short dark gap every 4 s"
};
byte mode = 0;
unsigned long modeStart = 0;
bool lastButton = HIGH;
unsigned long lastPress = 0;
// Brightness of the beam (0-255), t milliseconds after the mode started
int beamLevel(unsigned long t) {
if (mode == 0) {
long phase = t % 6000;
long fromPeak = abs(phase - 3000); // 0 when the beam points straight at you
if (fromPeak > 1200) return 6; // a faint glow while it points away
long x = 1200 - fromPeak; // 0 ... 1200
return 6 + (int)(x * x / 5800); // a soft curve up to full brightness
}
if (mode == 1) {
return (t % 5000 < 400) ? 255 : 0;
}
if (mode == 2) {
unsigned long p = t % 10000;
for (int i = 0; i < 3; i++) {
if (p >= i * 1000UL && p < i * 1000UL + 350) return 255;
}
return 0;
}
return (t % 4000 < 3000) ? 255 : 0; // occulting: mostly on
}
void setup() {
Serial.begin(9600);
pinMode(BEAM_PIN, OUTPUT);
pinMode(HARBOUR_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.println(NAMES[mode]);
}
void loop() {
bool button = digitalRead(BUTTON_PIN);
if (button == LOW && lastButton == HIGH && millis() - lastPress > 200) {
lastPress = millis();
mode = (mode + 1) % MODES;
modeStart = millis();
Serial.println(NAMES[mode]);
}
lastButton = button;
analogWrite(BEAM_PIN, beamLevel(millis() - modeStart));
digitalWrite(HARBOUR_PIN, millis() % 2000 < 1000); // red: 1 s on, 1 s off
}
How it works
analogWrite() switches pin 9 on and off about 500 times a second. The number from 0 to 255 sets how much of each cycle the pin is on, so 255 is full brightness and 6 is a faint glow. Your eye sees a steady, dimmer light. This trick is called pulse-width modulation (PWM).
Every pattern repeats, so the sketch uses the remainder operator %. t % 5000 counts from 0 to 4999 and starts again, so t % 5000 < 400 is true for the first 400 ms of every 5 seconds: one flash. The rotating beam measures how far it is from the middle of its 6-second cycle and turns that distance into a brightness that rises and falls smoothly.
Because nothing in loop() waits with delay(), the button, the beacon and the harbour light all keep working at the same time.
Try this next
- Look up a lighthouse near you (or a famous one, such as Fastnet or Kõpu) and program its real character.
- Add a "storm" mode that flashes faster and makes the harbour light shine all the time.
- Paint or print the lantern room in a different colour so the windows stand out.
Troubleshooting
- The beacon is only on or off, never in between: it must be on pin 9 (or another pin marked ~).
- The LED doesn't light in the tower: the long leg must connect to the resistor. Swap the two jumper wires at the breadboard.
-
The button changes two modes at once: make the 200 ms in
millis() - lastPress > 200bigger. - A printed part is too tight or too loose: printers differ by a few tenths of a millimetre. Sand or file a tight hole, and fix a loose part with a drop of hot glue or a strip of tape.