Topics

Games
Sound & Music
Watches & Clocks
Wireless
GPS
Power Supplies
Computers
Graphics
Lighting
Thermometers
Educational
Wearables
Test Equipment
Tutorials
Libraries
PCB-Based Projects

By processor

AVR ATtiny

ATtiny10
ATtiny2313
ATtiny84
ATtiny841
ATtiny85
ATtiny861
ATtiny88

AVR ATmega

ATmega328
ATmega1284

AVR 0, 1, and 2-series

ATmega1608
ATmega4808
ATmega4809
ATtiny1604
ATtiny1614
ATtiny202
ATtiny3216
ATtiny3224
ATtiny3227
ATtiny402
ATtiny404
ATtiny414
ATtiny814

AVR DA/DB/DD-series

AVR128DA28
AVR128DA32
AVR128DA48
AVR128DB28
AVR128DB48
AVR64DD14
AVR32DD28

ARM

ATSAMD21
RP2040
RA4M1

RISC-V

ESP32-P4

About me

  • About me
  • Twitter
  • Mastodon

Feeds

RSS feed

Five LEDs Puzzle PCB

4th January 2021

Last month I gave the circuit and program for a puzzle consisting of five LEDs, five pushbuttons, and an ATtiny85. The aim of the puzzle was to find the sequence of button presses that will light up all five LEDs.

Since building the original Five LEDs Puzzle on a prototyping board I've now designed a PCB version of the puzzle:

FiveLEDsPCB.jpg

I've included details, together with information about getting the PCBs, in case you want to build yourself a board. I also describe an update to the program to make it go to sleep after a delay, to save power and avoid the need for an on/off switch.

The circuit

The PCB version of the Five LEDs Puzzle uses exactly the same circuit as the original prototype:

FiveLEDsPuzzle.gif

Circuit of the Five LEDs Puzzle, based on an ATtiny85.

The PCB is designed to use through-hole components to make it easy to solder.

I recommend using red LEDs as they have the lowest forward voltage. For the processor I recommend using an ATtiny85V as it works down to 1.8V, so you may get a slightly longer battery life. Program it using Spence Konde's ATTinyCore [1]. You can program it before you solder it in; alternatively you can temporarily piggy-back an 8-pin DIL socket (the sort with flat legs) on top of the chip once it has been soldered in [2], and plug leads from the programmer into the socket.

The PCB is designed to fit on top of a standard battery pack containing two AAA batteries; I fitted it in place with two double-sided self-adhesive foam pads:

FiveLEDsPCBBack.jpg

► Parts list

The program

I've updated the program so it automatically goes to sleep if you haven't pressed any button for 30 seconds. This ensures that you don't accidentally leave it switched on, and avoids the need for an on/off switch. The power consumption in sleep is less than 0.05µA, so the drain on the battery is negligible. To wake the puzzle up just press any button.

The following lines are added to setup() to set up pin change interrupts on the five I/O lines, turn off the ADC to save power, and set the power-down sleep mode:

  // Set up pin change interrupts for buttons
  PCMSK = 1<<PINB0 | 1<<PINB1 | 1<<PINB2 | 1<<PINB3 | 1<<PINB4;
  // Disable ADC to save power
  ADCSRA &= ~(1<<ADEN);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);

We need to provide an interrupt service routine that will be called on a pin-change interrupt. It doesn't actually do anything, except return immediately:

ISR (PCINT0_vect) {
}

The timeout is specified by the global variable Timeout:

const unsigned long Timeout = 30000; // 30 seconds

The main program is enclosed in a loop that counts down the time until Timeout is reached:

void loop () {
  unsigned long Start = millis();
  while (millis() - Start < Timeout) {
    for (int b=0; b<5; b++) {
      int d = DDRB;
      DDRB = d & ~(1<<b);
      PORTB |= 1<<b;
      delay(1);
      if (!(PINB & 1<<b)) {
        while (!(PINB & 1<<b));
        PORTB &= ~(1<<b);
        DDRB = d ^ ((!b || (d & ((1<<b)-1)) == 1<<(b-1))<<b);
        Start = millis();
      } else {
        PORTB &= ~(1<<b);
        DDRB = d;
      }
      delay(10);
    }
  }

Any keypress resets the start time in Start.

Finally, this code turns off anything that might consume power, and puts the processor to sleep:

  int timsk = TIMSK;
  TIMSK = 0;                          // Disable timer interrupts
  int d = DDRB;
  DDRB = 0;                           // All lights off
  PORTB = 0x1F;                       // Pullups on
  GIFR = 1<<PCIF;                     // Clear flag
  GIMSK = GIMSK | 1<<PCIE;            // Enable pin change interrupt
  sleep_enable();
  sleep_cpu();                        // Go to sleep

A pin-change interrupt will cause the processor to continue executing from the call to sleep_cpu(). We then restore the state of the program and resume executing the puzzle:

  GIMSK = GIMSK | 1<<PCIE;            // Disable pin change interrupt
  while ((PINB & 0x1F) != 0x1F);      // Wait for all buttons released
  TIMSK = timsk;                      // Re-enable timer interrupts
  PORTB = 0;                          // Pullups off
  DDRB = (d == 0b11111) ? 0 : d;      // Restore lights or reset

The final line adds the feature that if you reach the winning position with all lights on, allowing the puzzle to go to sleep will reset it back to the starting position.

Compiling the program

As with the earlier version, compile the program using Spence Konde's ATTiny Core [3]. Choose the ATtiny25/45/85 option under the ATTinyCore heading on the Board menu. Then check that the subsequent options are set as follows (ignore any other options):

Chip: "ATtiny85"
Clock: "1 MHz (internal)"

This is the default configuration for a new chip, but if you've used other settings choose Burn Bootloader to set the fuses appropriately. Then upload the program using ISP (in-system programming); I used Sparkfun's Tiny AVR Programmer Board; see ATtiny-Based Beginner's Kit.

Resources

Here's the updated version of the Five LEDs Puzzle program, with the sleep timeout: Five LEDs Puzzle Program.

Get the Five LEDs Puzzle source from GitHub, together with the Eagle files for the PCB so you can make yourself a board: https://github.com/technoblogy/five-leds-puzzle.

Or order a board from OSH Park here: Five LEDs Puzzle.

Or order a board from PCBWay here: Five LEDs Puzzle.

Updates

5th January 2021: Corrected the statement to wait for buttons to be released when coming out of sleep. Thanks to JChristensen for pointing this out.

9th January 2021: Added code to turn on the pullup resistor when reading an input for more reliable operation as the battery voltage decreases, or when using yellow LEDs, as suggested by JChristensen.

15th March 2021: Added a feature so that if you reach the winning position with all lights on, allowing the puzzle to go to sleep will reset it back to the starting position.


  1. ^ ATtinyCore on GitHub.
  2. ^ 8 Pin DIL Socket on HobbyTronics.
  3. ^ ATTinyCore on GitHub.

blog comments powered by Disqus