John Bradnam
Published © GPL3+

Light On Game

A simple puzzle game built around a JK-LKM1638 display, led and button module.

IntermediateFull instructions provided8 hours105
Light On Game

Things used in this project

Hardware components

Microchip ATtiny1614 Microcontrolle
×1
1117-50 5V SMD Regulator SOT-223
×1
JY-LKM1638 8x digital tube LED module
×1
5 x 2 right-angle male pin header
5 x 2 straight female Dupont connector
×1
Passive components
1 x 0.1uF 0805 ceramic capacitor, 1 x 10uF 0805 ceramic capacitor
×1
DC panel socket
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

STL Files

Files for 3D printing

Schematics

Schematic

PCB

Eagle files

Schematic & PCB in Eagle format

Code

LightsOnV2.ino

Arduino
/* 
 *  Lights On V2
 *  Based on "Puzzle using 7 keys and 7 LEDs" by Klausj
 *  (https://www.hackster.io/Klausj/puzzle-using-7-keys-and-7-leds-e42a69)
 *  
 *  230529 John Bradnam (jbrad2089@gial.com)
 *    - Coverted project to use a ATtiny1614 processor
 *    
 *-------------------------------------------------------------------------
 Arduino IDE:
 --------------------------------------------------------------------------
  BOARD: ATtiny1614/1604/814/804/414/404/214/204
  Chip: ATtiny1614
  Clock Speed: 20MHz
  millis()/micros(): "Enabled (default timer)"
  Programmer: jtag2updi (megaTinyCore)

  ATTiny1614 Pins mapped to Ardunio Pins
 
              +--------+
          VCC + 1   14 + GND
  (SS)  0 PA4 + 2   13 + PA3 10 (SCK)
        1 PA5 + 3   12 + PA2 9  (MISO)
  (DAC) 2 PA6 + 4   11 + PA1 8  (MOSI)
        3 PA7 + 5   10 + PA0 11 (UPDI)
  (RXD) 4 PB3 + 6    9 + PB0 7  (SCL)
  (TXD) 5 PB2 + 7    8 + PB1 6  (SDA)
              +--------+
  
 **************************************************************************/

#include <TM1638plus.h>
#include <TimerFreeTone.h>  // https://bitbucket.org/teckel12/arduino-timer-free-tone/wiki/Home

#define STB0 0      //PA4
#define DIO 1       //PA5
#define CLK 2       //PA6
#define NC_PIN 3    //PA7
#define SPEAKER 6   //PB1
#define HINT 7      //PB2

#define BUZZER_PORT PORTB.OUT
#define BUZZER_MASK PIN1_bm

#define BRIGHTNESS 4
TM1638plus tm(STB0, CLK , DIO, false);

#define NUM_BITS 7
#define MAX_NUM (NUM_BITS - 1)
#define ALL_BITS_SET ((1 << NUM_BITS) - 1)

int currentBits;
int numberOfMoves;
bool allowHints;

void setup() 
{
  pinMode(SPEAKER, OUTPUT);
  pinMode(HINT, INPUT_PULLUP);
  allowHints = (digitalRead(HINT) == LOW);
  
  currentBits = getRandomBits();
  
  tm.displayBegin();
  tm.brightness(BRIGHTNESS);
  tm.displayText("Light On");
  delay(3000);
  tm.reset();
  
  showLEDs();
  playSirenUp();
}

void loop() 
{
  int buttonPressed = getHighestBit(tm.readButtons());

  // Hilfe-Taste gedrueckt?
  if (allowHints && buttonPressed == NUM_BITS)
  {
    beepDigit();
    showHint();
  }
  else if (buttonPressed >= 0)
  {
    //tm.DisplayDecNumNibble(buttonPressed,currentBits,false,TMAlignTextRight);
    beepDigit();
    toggleBits(currentBits, buttonPressed);
    numberOfMoves++;
    showLEDs();
    
    // Wait for button release:
    while (getHighestBit(tm.readButtons()) >= 0);
    
    if (currentBits == ALL_BITS_SET)
    {
      gameOver();
    }
  }
}

//-------------------------------------------------------------
// Toggle the current bit and its two neighbours
// i - byte to change
// j - bit position to swap (0 to MAX_NUM)
//-------------------------------------------------------------

void toggleBits(int &i, byte j) 
{
  i = i ^ (1 << j);
  i = i ^ (1 << ((j > 0) ? (j - 1) : MAX_NUM));
  i = i ^ (1 << ((j < MAX_NUM) ? (j + 1) : 0));
}

//-------------------------------------------------------------
// Show the current bits and the count
//  - Returns random integer
//-------------------------------------------------------------

void showLEDs() 
{
  tm.displayIntNum(numberOfMoves, false, TMAlignTextRight );
  tm.setLEDs(currentBits);
}

//-------------------------------------------------------------
// All lights on - show winning sequence
//-------------------------------------------------------------

void gameOver() 
{
  playWinSoundShort();
  delay(1000);
  for (int i = 0; i < 20; i++) 
  {
    tm.displayText((i & 0x01) ? "        " : " SOLVED ");
    delay(250);
  }
  tm.reset();
  tm.brightness(BRIGHTNESS);
  currentBits = getRandomBits();
  numberOfMoves = 0;
  showLEDs();
  playSirenUp();
}

//-------------------------------------------------------------
// Returns the number of the highest set bit in x
//  x - value to determine hight bit from
//  Returns highest bit set eg if x = 8 it returns = 3
//-------------------------------------------------------------

int8_t getHighestBit(long x) 
{
  int8_t retVal = -1;
  while (x) 
  {
    x = x >> 1;
    retVal++;
  }
  return retVal;
}

//-------------------------------------------------------------
// Generate a random set of bits
//  - Returns random integer
//-------------------------------------------------------------

int getRandomBits() 
{
  int result = 0;
  for (int i = -100; i < analogRead(NC_PIN); i++)
  {
    result = random(ALL_BITS_SET - 2) + 1;
  }
  return result;
}

//-------------------------------------------------------------
// Show a hint
//-------------------------------------------------------------

void showHint() 
{
  // test all possible keystrokes
  for (byte i = 1; i <= ALL_BITS_SET; i++) 
  {
    int temp = currentBits;
    // process the associated keystrokes
    for (byte pos = 0; pos < NUM_BITS; pos++)
    {
      if (bitRead(i, pos)) 
      {
        toggleBits(temp, pos);
      }
    }
    // when goal achieved, print a note:
    if (temp == ALL_BITS_SET) 
    {
      byte key = getHighestBit(i) + 1;
      char msg[] = "PrESS   ";
      msg[7] = '0' + key;
      tm.displayText(msg);
      delay(1000);
      return;
    }
  }
}

//-----------------------------------------------------------------------------------
// Turn on and off buzzer quickly
//------------------------------------------------------------------

void beepDigit() 
{
  BUZZER_PORT |= BUZZER_MASK;   // turn on buzzer
  delay(5);
  BUZZER_PORT &= ~BUZZER_MASK;  // turn off the buzzer
}

//------------------------------------------------------------------
// Play the winning sound
//------------------------------------------------------------------

void playWinSoundShort()
{
  //TimerFreeTone(SPEAKER,880,300);
  TimerFreeTone(SPEAKER,880,100); //A5
  TimerFreeTone(SPEAKER,988,100); //B5
  TimerFreeTone(SPEAKER,523,100); //C5
  TimerFreeTone(SPEAKER,988,100); //B5
  TimerFreeTone(SPEAKER,523,100); //C5
  TimerFreeTone(SPEAKER,587,100); //D5
  TimerFreeTone(SPEAKER,523,100); //C5
  TimerFreeTone(SPEAKER,587,100); //D5
  TimerFreeTone(SPEAKER,659,100); //E5
  TimerFreeTone(SPEAKER,587,100); //D5
  TimerFreeTone(SPEAKER,659,100); //E5
  TimerFreeTone(SPEAKER,659,100); //E5
  delay(250);
}

//-----------------------------------------------------------------------------------
//Play the up sound to start the game
//-----------------------------------------------------------------------------------

void playSirenUp() 
{
  #define MAX_NOTE 4978               // Maximum high tone in hertz. Used for siren.
  #define MIN_NOTE 31                 // Minimum low tone in hertz. Used for siren.
  
  for (int note = MIN_NOTE; note <= MAX_NOTE; note += 5)
  {                       
    TimerFreeTone(SPEAKER, note, 1);
  }
}

TM1638plus-2.0.0.zip

Arduino
Library for Arduino IDE
No preview (download only).

Credits

John Bradnam

John Bradnam

141 projects • 170 followers
Thanks to Klausj and rodmod.

Comments