Hardware components | ||||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 2 | |||
| × | 2 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 5 | |||
![]() |
| × | 5 | |||
![]() |
| × | 2 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 2 | ||||
| × | 1 | ||||
| × | 2 | ||||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
![]() |
| |||||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
![]() |
|
A while ago I brought one of those $3 Hourglass Shape Flashing LED DIY Kits from eBay. While it is a nice kit to build, it really has no practical purpose other than to show off its animation of an hour glass. It is not designed as a timer. I wanted to make mine into something useful yet keep the animation. I created a custom board that contains an Arduino, two mercury switches to detect the hourglass orientation, a rotary encoder to set the time from 1 to 99 minutes and a piezo electric speaker. I used the PCB that came in the kit to hold the LEDs. The STC15W201S microcontroller is replaced with a 16 pin ribbon cable that connects to my custom Arduino board.
VideoUsing the STL files provided, print the case on your 3D printer or get them printed at a commerical 3D print shop. Use supports for the "Case - Bottom.stl". I used a 0.2mm layer height and a 20% in-fill.
Drill out the mounting holes with a 2.5mm drill and create a thread with a 3mm tap.
Building the Hourglass Timer1a. First you will need to drill the 3mm mounting holes in the PCB that comes with the kit. (0.95" from the bottom, 0.225" from the left and right edge).
2a. Using a 6mm drill counter sink the back off each LED hole. This makes it easier to center the LEDs. Do this by hand in case you press too hard with the drill.
2b. Place all the LEDs into their respective holes ensuring the are orientated correctly.
2c. Screw the board onto the mounts using 6mm M3 screws.
2d. Push each LED hard again their respective holes on the front.
3a. Solder and trim the LEDs
4a. Create a 16 pin cable using two 16 pin DIL plugs.
4b. Test the cable.
4c. Solder one side of the cable onto the PCB.
5a. Using a Dremel or Hacksaw, cut the shaft of the rotary encoder leaving about 4mm of shaft.
5b. Pressure fit the knob to rotary encoder.
6a. The Eagle files for the custom PCB board have been included should you wish to get your PCB made commercially or you can make it yourself. I used the toner method to make mine.
6b. Assemble the board. Use IC sockets for both the ATMega328 and the 16 pin connector.
6c. Remove the 6mm M3 screws you used in 2c and replace them with M3x5+6mm Male-Female spacers.
6d. Screw the new board to the spacers using the 6mm M3 screws. I made the holes on the board 4mm and used washers. This allows the board to be adjusted. This maybe needed to align the rotary encoder knob correctly.
7a. Insert the 3.7V 400mAh LIPO battery and mini rocker switch and wire up.
The ATMega328 runs a 8Mhz crystal on a 3.7V supply. I used my home-made Arduno UNO board to program the ATMega328 chip. This board has a ZIF socket for the ATMega328 and allows me to change the crystal, supply voltage and program the chip via a USBTinyISP board or a FTDI board.
Cut the text from the attached Boards.txt file and add it into the Boards.txt file in your Arduino IDE. You will find this file in (%USERPROFILE%\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.1\boards.txt).
In the Arduino IDE
- Select "Tools - Board - Arduino Uno (8Mhz)"
- Select "Tools - Programmer - USBtinyISP"
- Plug in your USBtinyISP to the ATMega328 (should be running a 8Mhz crystal)
- Select "Tools - Burn bootloader"
- Load up the sketch in the IDE
- Select "Sketch - Upload using programmer"
- Remove the ATMega328 from your development environment and place it on the PCB
I found that the EEPROM on the ATMega328 didn't read or write when running on 3V3 but was fine on 5V. If anyone finds a solution, please let me know.
##############################################################
uno8.name=Arduino Uno (8Mhz)
uno8.vid.0=0x2341
uno8.pid.0=0x0043
uno8.vid.1=0x2341
uno8.pid.1=0x0001
uno8.vid.2=0x2A03
uno8.pid.2=0x0043
uno8.vid.3=0x2341
uno8.pid.3=0x0243
uno8.upload.tool=avrdude
uno8.upload.protocol=arduino
uno8.upload.maximum_size=32256
uno8.upload.maximum_data_size=2048
uno8.upload.speed=57600
uno8.bootloader.tool=avrdude
uno8.bootloader.low_fuses=0xFF
uno8.bootloader.high_fuses=0xDA
uno8.bootloader.extended_fuses=0xFE
uno8.bootloader.unlock_bits=0x3F
uno8.bootloader.lock_bits=0x0F
uno8.bootloader.file=optiboot/optiboot_atmega328.hex
uno8.build.mcu=atmega328p
uno8.build.f_cpu=8000000L
uno8.build.board=AVR_UNO_8
uno8.build.core=arduino
uno8.build.variant=standard
##############################################################
/*
Enhancing the Hourglass Shape Flashing LED DIY Kit
This timer extends the Hourglass Shape Flashing LED DIY Kit by adding an Arduino, two mercury switches to detect the hourglass orientation,
a rotatary encoder to set the time from 1 to 99 minutes and a piezo electric speaker. The PCB that came in the kit is still used to hold the
57 LEDs but the STC15W201S microcontroller is replaced with a 16 pin ribbon cable that connects to my custom Arduino board.
LED Layout
----------
01, 02, 03, 04, 05, 06, 07
08, 09, 10, 11, 12, 13
14, 15, 16, 17, 18
19, 20, 21, 22
23, 24, 25
26, 27
28
29
30
31, 32
33, 34, 35
36, 37, 38, 39
40, 41, 42, 43, 44
45, 46, 47, 48, 49, 50
51, 52, 53, 54, 55, 56, 57
*/
#include <EEPROM.h>
#include "hourglass.h"
#include "notes.h"
#define ENC_A A1
#define ENC_B A2
#define SPEAKER A3
#define SW_UP A4
#define SW_DN A5
#define ENCODER_MIN 1
#define ENCODER_MAX 99
uint8_t encoderLastEncoded = 0;
int encoderLastValue = 1;
int encoderValue = 1;
//define modes to operate in
enum ModeEnum { OFF, SET_TIME, TIMER, TIME_LEFT, COMPLETE };
ModeEnum currentMode;
int minutesTimer;
int currentTimer;
int displayTimer;
long secondsCounter = 0;
#define COUNTS_PER_MINUTE 684000 //11400 * 60
// HOME ON THE RANGE
const uint16_t melody_range[] PROGMEM = {
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_4|NOTE_E4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_A3, DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_F4,
DUR_8|NOTE_E4, DUR_8|NOTE_F4, DUR_4|NOTE_G4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_1|NOTE_D4,
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_4|NOTE_E4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_A3, DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_F4,
DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_6|NOTE_E4, DUR_8|NOTE_D4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_2|NOTE_C4, DUR_2|REST,
DUR_2|NOTE_G4, DUR_8|NOTE_F4, DUR_6|NOTE_E4, DUR_8|NOTE_D4, DUR_1|NOTE_E4,
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_4|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_2|NOTE_D4,
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_4|NOTE_E4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_A3, DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_F4,
DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_E4, DUR_8|NOTE_D4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_1|NOTE_C4,
DUR_4|REST, END_OF_TUNE
};
//Bb, F, G, E
const uint16_t melody_shine[] PROGMEM = {
DUR_3|NOTE_AS3, DUR_3|NOTE_F4, DUR_3|NOTE_G3, DUR_2|NOTE_E4,
DUR_4|REST, END_OF_TUNE
};
//*******************************************************************************************
// A0 .. A5 Pin Change Interrupt
//
ISR (PCINT1_vect)
{
//Handle any changes to mercury switches
bool up = (digitalRead(SW_UP) == HIGH);
bool dn = (digitalRead(SW_DN) == HIGH);
if (up != dn)
{
if (currentMode == SET_TIME && dn != hourGlassGetRotation())
{
currentMode = TIMER;
currentTimer = minutesTimer;
secondsCounter = 0;
}
hourGlassSetRotation(dn);
}
//Handle change in rotary encoder
uint8_t enc = (digitalRead(ENC_B) == HIGH) ? 2 : 0 | (digitalRead(ENC_A) == HIGH) ? 1 : 0;
uint8_t sum = (encoderLastEncoded << 2) | enc;
int lastEncoderValue = encoderValue;
if ((encoderValue < ENCODER_MAX) && (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011))
{
encoderLastValue++;
encoderValue = encoderLastValue >> 1;
}
if ((encoderValue > ENCODER_MIN) && (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000))
{
encoderLastValue--;
encoderValue = encoderLastValue >> 1;
}
encoderLastEncoded = enc; //store this value for next time
if (encoderValue != lastEncoderValue)
{
currentMode = SET_TIME;
}
}
//*******************************************************************************************
// Called by refresh timer interrupt when refreshing the display
//
void timerInterruptHandler()
{
if (currentMode == TIMER || currentMode == TIME_LEFT)
{
if (secondsCounter > 0)
{
secondsCounter--;
}
else if (currentTimer == 0)
{
currentMode = COMPLETE;
Serial.println("COMPLETE");
}
else
{
secondsCounter = COUNTS_PER_MINUTE;
currentTimer--;
Serial.println(currentTimer);
}
}
}
//*******************************************************************************************
// Setup Arduino environment
//
void setup()
{
Serial.begin(9600);
pinMode(ENC_A, INPUT);
pinMode(ENC_B, INPUT);
pciSetup(ENC_A);
pciSetup(ENC_B);
pinMode(SW_UP, INPUT);
pinMode(SW_DN, INPUT);
pciSetup(SW_UP);
pciSetup(SW_DN);
pinMode(SPEAKER, OUTPUT);
hourGlassSetup(timerInterruptHandler);
hourGlassSetRotation(digitalRead(SW_DN) == HIGH);
currentMode = SET_TIME;
//Test if last minutesTimer is available otherwise default to 3 minutes
minutesTimer = readFromEEPROM();
Serial.println("EEPROM = " + String(minutesTimer));
if (minutesTimer < 1 || minutesTimer > 99)
{
minutesTimer = 3;
writeToEEPROM(minutesTimer);
}
hourGlassDisplayNumber(minutesTimer);
encoderLastValue = minutesTimer << 1;
encoderValue = minutesTimer;
delay(200);
}
//*******************************************************************************************
// Install Pin change interrupt for a pin
void pciSetup(byte pin)
{
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // enable pin
PCIFR |= bit (digitalPinToPCICRbit(pin)); // clear any outstanding interrupt
PCICR |= bit (digitalPinToPCICRbit(pin)); // enable interrupt for the group
}
//*******************************************************************************************
// Main loop
//
void loop()
{
switch (currentMode)
{
case SET_TIME:
if (encoderValue != minutesTimer)
{
Serial.println("SET_TIME = " + String(encoderValue));
minutesTimer = encoderValue;
writeToEEPROM(minutesTimer);
}
hourGlassDisplayNumber(minutesTimer);
break;
case TIMER:
hourGlassPlayAnimation(200);
displayTimer = 10;
if (currentMode == TIMER)
{
currentMode = TIME_LEFT;
}
break;
case TIME_LEFT:
if (displayTimer == 0)
{
currentMode = TIMER;
}
else
{
hourGlassDisplayNumber(currentTimer + 1);
displayTimer--;
}
break;
case COMPLETE:
hourGlassPlayFrame(TOTAL_FRAMES - 1, 2); //Display last frame
delay(500);
hourGlassClearAllLEDs();
playSong(SPEAKER, melody_range);
currentMode = SET_TIME;
hourGlassDisplayNumber(minutesTimer);
break;
}
delay(100);
}
//*******************************************************************************************
//Play animation
//delta - time in mS between each frame
void hourGlassPlayAnimation(int delta)
{
for (int i = 0; i < TOTAL_FRAMES; i++)
{
for (int j = 0; j < 3; j++)
{
hourGlassPlayFrame(i, j);
if (currentMode != TIMER)
{
break;
}
hourGlassDelay(delta);
}
if (currentMode != TIMER)
{
break;
}
}
}
//*********************************************************************************************
// EEPROM routines to store last value of timer (0 to 99)
//
void writeToEEPROM(int v)
{
EEPROM.write(0, (byte)v);
}
int readFromEEPROM()
{
return (int)EEPROM.read(0);
}
#pragma once
/*
Support functions for Hour Glass Display
Author: jbrad2089@gmail.com
LED Layout
----------
01, 02, 03, 04, 05, 06, 07
08, 09, 10, 11, 12, 13
14, 15, 16, 17, 18
19, 20, 21, 22
23, 24, 25
26, 27
28
29
30
31, 32
33, 34, 35
36, 37, 38, 39
40, 41, 42, 43, 44
45, 46, 47, 48, 49, 50
51, 52, 53, 54, 55, 56, 57
*/
#include "hourglassfont.h"
#include "hourglassanim.h"
#include "notes.h"
#define TOTAL_LEDS 57
//Define direction AK = 0 => P3x = Anode, P1x = Cathode, AK = 1 => P3x = Cathode, P1x = Anode
#define AK 0
#define KA 0x08
//Definitions for connections to D8 - D13 pins
#define P10 (0<<4)
#define P11 (1<<4)
#define P12 (2<<4)
#define P13 (3<<4)
#define P14 (4<<4)
#define P15 (5<<4)
//Definitions for connections to D3 - D7 pins
#define P30 3
#define P31 4
#define P33 5
#define P36 6
#define P37 7
//Coordinates for each LED in Hourglass
//Bit 7, 6, 5, 4 = P10 .. P15 (PB0 .. PB5)
//Bit 3 = AK or KA
//Bit 2, 1, 0 = P30, P31, P33, P36, P37 (PD3 .. PD7)
const uint8_t ledpos[TOTAL_LEDS] PROGMEM = {
AK|P10|P30, AK|P10|P31, AK|P10|P33, AK|P10|P36, AK|P10|P37,
KA|P10|P30, KA|P10|P31, KA|P10|P33, KA|P10|P36, KA|P10|P37,
AK|P11|P30, AK|P11|P31, AK|P11|P33, AK|P11|P36, AK|P11|P37,
KA|P11|P30, KA|P11|P31, KA|P11|P33, KA|P11|P36, KA|P11|P37,
AK|P12|P30, AK|P12|P31, AK|P12|P33, AK|P12|P36, AK|P12|P37,
KA|P12|P30, KA|P12|P31, KA|P12|P33, KA|P12|P36, KA|P12|P37,
AK|P13|P30, AK|P13|P31, AK|P13|P33, AK|P13|P36, AK|P13|P37,
KA|P13|P30, KA|P13|P31, KA|P13|P33, KA|P13|P36, KA|P13|P37,
AK|P14|P30, AK|P14|P31, AK|P14|P33, AK|P14|P36, AK|P14|P37,
KA|P14|P30, KA|P14|P31, KA|P14|P33, KA|P14|P36, KA|P14|P37,
AK|P15|P30, AK|P15|P31, AK|P15|P33, AK|P15|P36, AK|P15|P37,
KA|P15|P30, KA|P15|P31
};
//Row definiton table from top left to bottom right
const uint8_t ledrows[TOTAL_LEDS] PROGMEM = {
0, 1, 2, 3, 4, 5, 6 | 0x80,
7, 8, 9, 10, 11, 12 | 0x80,
13, 14, 15, 16, 17 | 0x80,
18, 19, 20, 21 | 0x80,
22, 23, 24 | 0x80,
25, 26 | 0x80,
27 | 0x80,
28 | 0x80,
29 | 0x80,
30, 31 | 0x80,
32, 33, 34 | 0x80,
35, 36, 37, 38 | 0x80,
39, 40, 41, 42, 43 | 0x80,
44, 45, 46, 47, 48, 49 | 0x80,
50, 51, 52, 53, 54, 55, 56 | 0x80
};
//Store the current state of the LEDs. Each bit represents each LED on/off state (0 = off, 1 = on)
//=> ledsLow bit 0 is LED 01, bit 1 is LED 02 ... bit 31 is LED 32,
//=> ledsHigh bit 0 is LED 33, bit 1 is LED 34 ... bit 24 is LED 57,
uint32_t ledsLow = 0;
uint32_t ledsHigh = 0;
//Used by ISR handler to determine which LED to turn on. Only one LED can be on at anyone time.
int nextIsrLED = 0;
int lastIsrLED = 0;
//Used to determine which way is up
bool upsideDownIsr = true;
#define COUNTS_PER_SECOND 11400
long hourGlassDelayCounter = 0;
//forward references
bool hourGlassGetLED(int led);
void hourGlassClearAllLEDs();
void (*hourGlassTimerCallback)(void);
//*******************************************************************************************
// Initialse the timer interrupt to handle refreshing of the LEDs
// void (*pTimerFunction)() - callback function to allow hourglass timer to hook into this timer
void hourGlassSetup(void (*pTimerFunction)())
{
cli();//stop interrupts
hourGlassTimerCallback = pTimerFunction;
hourGlassClearAllLEDs();
//set timer2 interrupt at 8kHz
TCCR2A = 0;// set entire TCCR2A register to 0
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
#if (F_CPU == 8000000)
// set compare match register for 8Mhz Crystal
OCR2A = 88;// = (8*10^6) / (11400*8) - 1 (must be <256)
#elif (F_CPU == 16000000)
// set compare match register for 16Mhz Crystal
OCR2A = 175;// = (16*10^6) / (11400*8) - 1 (must be <256)
#else
#error "F_CPU not 16000000 or 8000000 or undefined"
#endif
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// 8 prescaler
TCCR2B |= (1 << CS21);
// Set CS21 & CS20 bit for 32 prescaler
//TCCR2B |= (1 << CS21) | (1 << CS20);
// 64 prescaler
//TCCR2B |= (1 << CS22);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A);
sei();//allow interrupts
}
//*******************************************************************************************
// Timer ISR to display current state of LEDS
// Each interrupt will only light a single LED when its on. Since there are 57 LEDS, and we
// want to paint all LEDs 200 times per second so the human eye will see it as flicker free, the
// frequency is 57 X 200 = 11400 Hz or an interrupt every 88uS.
ISR(TIMER2_COMPA_vect)
{
//Don't refresh while note is playing
if (!playingNote)
{
uint8_t oldSREG = SREG;
cli();
// Alter LED number if display upside down
int next = (upsideDownIsr) ? (TOTAL_LEDS - 1) - nextIsrLED : nextIsrLED;
int last = (upsideDownIsr) ? (TOTAL_LEDS - 1) - lastIsrLED : lastIsrLED;
// Physically force last LED off if it was on
// This stops Ghosting
if (hourGlassGetLED(last))
{
uint8_t _led = pgm_read_byte(&ledpos[lastIsrLED]);
int _bBit = (_led >> 4) & 0x0F; //P1x pins Port B
int _dBit = _led & 0x07; //P3x pins Port D
// Ensure ports with LED to display are configured as outputs and rest are inputs
DDRB = (DDRB & B11000000) | (1 << _bBit); //Assign D8..13 as outputs
DDRD = (DDRD & B00000111) | (1 << _dBit); //Assign D3..D7 as outputs
// Clear all output pins except the pins we aren't using
PORTD = PORTD & 0x07; //Current state of PD0 to PD2
PORTB = PORTB & 0xC0; //Current state of PB6 to PB7
}
//Now switch on correct LED
uint8_t led = pgm_read_byte(&ledpos[nextIsrLED]);
int bBit = (led >> 4) & 0x0F; //P1x pins Port B
int dBit = led & 0x07; //P3x pins Port D
//Ensure ports with LED to display are configured as outputs and rest are inputs
DDRB = (DDRB & B11000000) | (1 << bBit); //Assign D8..13 as outputs
DDRD = (DDRD & B00000111) | (1 << dBit); //Assign D3..D7 as outputs
// Clear all output pins except the pins we aren't using
uint8_t dState = PORTD & 0x07; //Current state of PD0 to PD2
uint8_t bState = PORTB & 0xC0; //Current state of PB6 to PB7
//Set or clear pins for the next LED
if (!hourGlassGetLED(next))
{
PORTB = bState; //d8_d13 LOW
PORTD = dState; //d3_d7 LOW
}
else if (led & 0x08)
{
//P3x = Cathode, P1x = Anode
PORTB = bState | (1 << bBit); //d8_d13 HIGH
PORTD = (dState | B11111000) & ~(1 << dBit); //d3_d7 LOW
}
else
{
//P3x = Anode, P1x = Cathode
PORTB = (bState | B00111111) & ~(1 << bBit); //d8_d13 LOW
PORTD = dState | (1 << dBit); //d3_d7 HIGH
}
//Set up for next interrupt
lastIsrLED = nextIsrLED;
nextIsrLED++;
if (nextIsrLED >= TOTAL_LEDS)
{
nextIsrLED = 0;
}
//Handle any delay requests
if (hourGlassDelayCounter > 0)
{
hourGlassDelayCounter--;
}
if (hourGlassTimerCallback != NULL)
{
hourGlassTimerCallback();
}
// Toggle D2 for scope
//DDRD = DDRD | 0x00000100; //Configure D2 as an output alter its state so we can measure the frequency
//PORTD = PORTD ^ 0x00000100;
//PIND = 0x00000100;
SREG = oldSREG;
}
}
//******************************************************************************************
// Replace standard Arduino delay routine with ours since we will be compiling a 8MHz chip
// on a 16Mhz Arduino board
void hourGlassDelay(long mS)
{
hourGlassDelayCounter = (mS * COUNTS_PER_SECOND) / 1000;
while (hourGlassDelayCounter > 0)
{
sei();
}
}
//*******************************************************************************************
//Set the rotation of the display
// upsideDown - true if display is upside down
void hourGlassSetRotation(bool upsideDown)
{
upsideDownIsr = upsideDown;
}
//*******************************************************************************************
//Set the rotation of the display
// return true if display is upside down
bool hourGlassGetRotation()
{
return upsideDownIsr;
}
//*******************************************************************************************
//Clear all
void hourGlassClearAllLEDs()
{
ledsLow = 0;
ledsHigh = 0;
}
//*******************************************************************************************
//Get LED state
//led - Led to switch on or off (0..56)
//on - true to switch on, false to switch off
bool hourGlassGetLED(int led)
{
if (led < 32) {
return (ledsLow & ((uint32_t)1 << led)) > 0;
}
else {
return (ledsHigh & ((uint32_t)1 << (led - 32))) > 0;
}
}
//*******************************************************************************************
//Switch on or off a LED
//led - Led to switch on or off (0..56)
//on - true to switch on, false to switch off
void hourGlassSetLED(int led, bool on)
{
if (led < 32) {
if (on) {
ledsLow |= ((uint32_t)1 << led);
} else {
ledsLow &= ~((uint32_t)1 << led);
}
}
else {
if (on) {
ledsHigh |= ((uint32_t)1 << (led - 32));
} else {
ledsHigh &= ~((uint32_t)1 << (led - 32));
}
}
}
//*******************************************************************************************
//Switch on or off a row of LEDS
//row - Led to switch on or off (0..14) (0 = top row, 1 is wnd row from top, etc)
//on - true to switch on, false to switch off
void hourGlassSetRowLED(int row, bool on)
{
int r = 0;
for (int i = 0; i < TOTAL_LEDS; i++)
{
uint8_t led = pgm_read_byte(&ledrows[i]);
if (r == row)
{
hourGlassSetLED(led & 0x7F, on);
}
if (led & 0x80)
{
if (r == row)
{
break;
}
r++;
}
}
}
//*******************************************************************************************
//Display upper digit
//digit - 0 .. 9
void hourGlassSetUpperDigit(int digit)
{
uint32_t bits = pgm_read_dword(&led_upper[digit % 10]);
for (int i = 0; i < 28; i++)
{
hourGlassSetLED(i, (bits & (uint32_t)1 != 0));
bits = bits >> 1;
}
}
//*******************************************************************************************
//Display lower digit
//digit - 0 .. 9
void hourGlassSetLowerDigit(int digit)
{
uint32_t bits = pgm_read_dword(&led_lower[digit % 10]);
for (int i = 0; i < 28; i++)
{
hourGlassSetLED(i + 29, (bits & (uint32_t)1 != 0));
bits = bits >> 1;
}
}
//*******************************************************************************************
//Display number
//digit - 0 .. 99
void hourGlassDisplayNumber(int number)
{
hourGlassClearAllLEDs();
if (number > 9)
{
hourGlassSetUpperDigit(floor(number / 10));
}
hourGlassSetLowerDigit(number % 10);
}
//*******************************************************************************************
//Play a frame from the animation
//frame - 0 .. TOTAL_FRAMES - 1 => primary frame
//subframe - 0, 1, 2 => central light animation
void hourGlassPlayFrame(int frame, int subframe)
{
frame = frame % TOTAL_FRAMES;
uint64 bits;
uint32_t* p = (uint32_t*)&led_anim[frame];
bits.h = pgm_read_dword(p++);
bits.l = pgm_read_dword(p++) & 0xC7FFFFFF; //Mask LEDs 28 to 30
//bits.l = pgm_read_dword(p++); // & 0xC7FF; //Mask LEDs 28 to 30
//LEDS 28 to 30 make up the sub animation
bits.l |= (uint32_t)1 << (27 + subframe % 3);
for (int i = 0; i < TOTAL_LEDS; i++)
{
if (i < 32)
{
hourGlassSetLED(i, (bits.l & (uint32_t)1 != 0));
bits.l = bits.l >> 1;
}
else
{
hourGlassSetLED(i, (bits.h & (uint32_t)1 != 0));
bits.h = bits.h >> 1;
}
}
}
#pragma once
/*
Animation sequence for hour glass
Author: jbrad2089@gmail.com
LED Layout
----------
01, 02, 03, 04, 05, 06, 07
08, 09, 10, 11, 12, 13
14, 15, 16, 17, 18
19, 20, 21, 22
23, 24, 25
26, 27
28
29
30
31, 32
33, 34, 35
36, 37, 38, 39
40, 41, 42, 43, 44
45, 46, 47, 48, 49, 50
51, 52, 53, 54, 55, 56, 57
*/
struct uint64
{
uint32_t h;
uint32_t l;
};
#define TOTAL_FRAMES 18
//Table is 64 bit - Bit 0 = LED 1, Bit 1 = LED 2 ... Bit 56 = LED 57
const uint64 led_anim[TOTAL_FRAMES] PROGMEM = {
//1 1 1 1 1 1 1
// 1 1 1 1 1 1
// 1 1 1 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 0 0
// 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0 0 0
//0 0 0 0 0 0 0
//B1111111 111111 11111 1111 111 11 0 0 0 00 000 0000 00000 000000 0000000
//B1111 1111 1111 1111 1111 1111 1110 0000 _ 0000 0000 0000 0000 0000 0000 0_000 0000
{ 0x00000000, 0x07FFFFFF },
//1 1 1 0 1 1 1
// 1 1 1 1 1 1
// 1 1 1 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0 0 0
//0 0 0 0 0 0 0
//B1110111 111111 11111 1111 111 11 0 0 0 00 010 0000 00000 000000 0000000
//B1110 1111 1111 1111 1111 1111 1110 0000 - 0100 0000 0000 0000 0000 0000 0_000 0000
{ 0x00000002, 0x07FFFFF7 },
//1 1 1 0 1 1 1
// 1 1 1 1 1 1
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 0 0 0 0 0
//0 0 0 0 0 0 0
//B1110111 111111 11011 1111 111 11 0 0 0 00 010 0000 00100 000000 0000000
//B1110 1111 1111 1110 1111 1111 1110 0000 _ 0100 0000 0100 0000 0000 0000 0_000 0000
{ 0x00000202, 0x07FF7FF7 },
//1 1 0 0 0 1 1
// 1 1 1 1 1 1
// 1 1 1 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 0 0 0 0 0
//0 0 0 1 0 0 0
//B1100011 111111 11111 1111 111 11 0 0 0 00 010 0000 00100 000000 0001000
//B1100 0111 1111 1111 1111 1111 1110 0000 _ 0100 0000 0100 0000 0000 0100 0_000 0000
{ 0x00200202, 0x07FFFFE3 },
//1 1 0 0 0 1 1
// 1 1 1 1 1 1
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 0 0 0 0 0
//0 0 1 1 1 0 0
//B1100011 111111 11011 1111 111 11 0 0 0 00 010 0000 00100 000000 0011100
//B1100 0111 1111 1110 1111 1111 1110 0000 _ 0100 0000 0100 0000 0000 1110 0_000 0000
{ 0x00700202, 0x07FF7FE3 },
//1 1 0 0 0 1 1
// 1 1 0 0 1 1
// 1 1 1 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 0 1 1 0 0
//0 0 1 1 1 0 0
//B1100011 110011 11111 1111 111 11 0 0 0 00 010 0000 00100 001100 0011100
//B1100 0111 1001 1111 1111 1111 1110 0000 _ 0100 0000 0100 0011 0000 1110 0_000 0000
{ 0x0070C202, 0x07FFF9E3 },
//1 1 0 0 0 1 1
// 1 1 0 0 1 1
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 0 1 1 0 0
//0 1 1 1 1 1 0
//B1100011 110011 11011 1111 111 11 0 0 0 00 010 0000 00100 001100 0111110
//B1100 0111 1001 1110 1111 1111 1110 0000 _ 0100 0000 0100 0011 0001 1111 0_000 0000
{ 0x00F8C202, 0x07FF79E3 },
//1 0 0 0 0 0 1
// 1 1 0 0 1 1
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 1 1 1 1 0
//0 1 1 1 1 1 0
//B1000001 110011 11011 1111 111 11 0 0 0 00 010 0000 00100 011110 0111110
//B1000 0011 1001 1110 1111 1111 1110 0000 _ 0100 0000 0100 0111 1001 1111 0_000 0000
{ 0x00F9E202, 0x07FF79C1 },
//0 0 0 0 0 0 0
// 1 1 0 0 1 1
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 1 1 1 0
// 0 1 1 1 1 0
//0 1 1 1 1 1 0
//B0000000 110011 11011 1111 111 11 0 0 0 00 010 0000 01110 011110 0111110
//B0000 0001 1001 1110 1111 1111 1110 0000 _ 0100 0000 1110 0111 1001 1111 0_000 0000
{ 0x00F9E702, 0x07FF7980 },
//0 0 0 0 0 0 0
// 1 0 0 0 0 1
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 1 1 1 1 0
//1 1 1 1 1 1 1
//B0000000 100001 11011 1111 111 11 0 0 0 00 010 0000 00100 011110 1111111
//B0000 0001 0000 1110 1111 1111 1110 0000 0100 0000 0100 0111 1011 1111 1_000 0000
{ 0x01FDE202, 0x07FF7080 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 1 1 0 1 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 1 1 1 0
// 0 1 1 1 1 0
//1 1 1 1 1 1 1
//B0000000 000000 11011 1111 111 11 0 0 0 00 010 0000 01110 011110 1111111
//B0000 0000 0000 0110 1111 1111 1110 0000 _ 0100 0000 1110 0111 1011 1111 1_000 0000
{ 0x01FDE702, 0x07FF6000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 1 0 0 0 1
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 1 1 0
// 0 1 1 1 0
// 0 1 1 1 1 0
//1 1 1 1 1 1 1
//B0000000 000000 10001 1111 111 11 0 0 0 00 010 0110 01110 011110 1111111
//B0000 0000 0000 0100 0111 1111 1110 0000 _ 0100 1100 1110 0111 1011 1111 1_000 0000
{ 0x01FDE732, 0x07FE2000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 0 0 0 0 0
// 1 1 1 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 1 1 1 0
// 1 1 1 1 1 1
//1 1 1 1 1 1 1
//B0000000 000000 00000 1111 111 11 0 0 0 00 010 0000 01110 111111 1111111
//B0000 0000 0000 0000 0011 1111 1110 0000 _ 0100 0000 1110 1111 1111 1111 1_000 0000
{ 0x01FFF702, 0x07FC0000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 0 0 0 0 0
// 1 0 0 1
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 1 1 0
// 0 1 1 1 0
// 1 1 1 1 1 1
//1 1 1 1 1 1 1
//B0000000 000000 00000 1001 111 11 0 0 0 00 010 0110 01110 111111 1111111
//B0000 0000 0000 0000 0010 0111 1110 0000 _ 0100 1100 1110 1111 1111 1111 1_000 0000
{ 0x01FFF732, 0x07E40000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0
// 1 1 1
// 1 1
// 0
// 0
// 0
// 0 0
// 0 1 0
// 0 1 1 0
// 1 1 1 1 1
// 1 1 1 1 1 1
//1 1 1 1 1 1 1
//B0000000 000000 00000 0000 111 11 0 0 0 00 010 0110 11111 111111 1111111
//B0000 0000 0000 0000 0000 0011 1110 0000 _ 0100 1101 1111 1111 1111 1111 1_000 0000
{ 0x01FFFFB2, 0x07C00000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0
// 1 0 1
// 1 1
// 1
// 1
// 1
// 0 0
// 0 1 0
// 1 1 1 1
// 1 1 1 1 1
// 1 1 1 1 1 1
//1 1 1 1 1 1 1
//B0000000 000000 00000 0000 101 11 1 1 1 00 010 1111 11111 111111 1111111
//B0000 0000 0000 0000 0000 0010 1111 1100 _ 0101 1111 1111 1111 1111 1111 1_000 0000
{ 0x01FFFFFA, 0x3F400000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0
// 0 0 0
// 1 1
// 0
// 0
// 0
// 0 0
// 1 1 1
// 1 1 1 1
// 1 1 1 1 1
// 1 1 1 1 1 1
//1 1 1 1 1 1 1
//B0000000 000000 00000 0000 000 11 0 0 0 00 111 1111 11111 111111 1111111
//B0000 0000 0000 0000 0000 0000 0110 0000 _ 1111 1111 1111 1111 1111 1111 1_000 0000
{ 0x01FFFFFF, 0x06000000 },
//0 0 0 0 0 0 0
// 0 0 0 0 0 0
// 0 0 0 0 0
// 0 0 0 0
// 0 0 0
// 0 0
// 0
// 0
// 0
// 1 1
// 1 1 1
// 1 1 1 1
// 1 1 1 1 1
// 1 1 1 1 1 1
//1 1 1 1 1 1 1
//B0000000 000000 00000 0000 000 00 0 0 0 11 111 1111 11111 111111 1111111
//B0000 0000 0000 0000 0000 0000 0000 0011 _ 1111 1111 1111 1111 1111 1111 1_000 0000
{ 0x01FFFFFF, 0xC0000000 }
};
#pragma once
/*
Digits 0 to 9 for upper and lower sections
Author: jbrad2089@gmail.com
LED Layout
----------
01, 02, 03, 04, 05, 06, 07
08, 09, 10, 11, 12, 13
14, 15, 16, 17, 18
19, 20, 21, 22
23, 24, 25
26, 27
28
29
30
31, 32
33, 34, 35
36, 37, 38, 39
40, 41, 42, 43, 44
45, 46, 47, 48, 49, 50
51, 52, 53, 54, 55, 56, 57
*/
//Digit definitions for upper triangle of LEDs
//Bit 0 = LED 1, Bit 1 = LED 2 ... Bit 26 = LED 27
const uint32_t led_upper[10] PROGMEM = {
//0 0 1 1 1 0 0
// 0 1 0 0 1 0
// 1 0 0 0 1
// 1 0 0 1
// 1 1 1
// 0 0
// 0
//B0001 1110 0110 0010 1001 0001 1100,
0x1E6291C,
//0 0 0 1 0 0 0
// 0 0 0 0 0 0
// 0 0 1 0 0
// 0 0 0 0
// 0 1 0
// 0 0
// 0
//B0000 1000 0000 1000 0000 0000 1000,
0x0808008,
//0 0 1 1 1 0 0
// 0 0 0 0 1 0
// 0 1 1 1 0
// 1 0 0 0
// 1 1 1
// 0 0
// 0
//B0001 1100 0101 1100 1000 0001 1100,
0x1C5C81C,
//0 0 1 1 1 0 0
// 0 0 0 0 1 0
// 0 1 1 1 0
// 0 0 0 1
// 1 1 1
// 0 0
// 0
//B0001 1110 0001 1100 1000 0001 1100,
0x1E1C81C,
//0 0 1 0 1 0 0
// 0 1 0 0 0 0
// 0 1 1 1 0
// 0 0 0 0
// 0 0 1
// 0 0
// 0
//B0001 0000 0001 1100 0001 0001 0100,
0x101C114,
//0 0 1 1 1 0 0
// 0 1 0 0 0 0
// 0 1 1 1 0
// 0 0 0 1
// 1 1 1
// 0 0
// 0
//B0001 1110 0001 1100 0001 0001 1100,
0x1E1C11C,
//0 0 1 1 1 0 0
// 0 1 0 0 0 0
// 0 1 1 1 0
// 1 0 0 1
// 1 1 1
// 0 0
// 0
//B0001 1110 0101 1100 0001 0001 1100,
0x1E5C11C,
//0 0 1 1 1 0 0
// 0 0 0 0 1 0
// 0 0 0 1 0
// 0 0 1 0
// 0 1 0
// 0 0
// 0
//B0000 1001 0001 0000 1000 0001 1100,
0x091081C,
//0 0 1 1 1 0 0
// 0 1 0 0 1 0
// 0 1 1 1 0
// 1 0 0 1
// 1 1 1
// 0 0
// 0
//B0001 1110 0101 1100 1001 0001 1100,
0x1E5C91C,
//0 0 1 1 1 0 0
// 0 1 0 0 1 0
// 0 1 1 1 0
// 0 0 0 1
// 1 1 1
// 0 0
// 0
//B0001 1110 0001 1100 1001 0001 1100
0x1E1C91C
};
//Digit definitions for lower triangle of LEDs
//Bit 0 = LED 29, Bit 30 = LED 2 ... Bit 26 = LED 56
const uint32_t led_lower[10] PROGMEM = {
// 0
// 0 0
// 1 1 1
// 1 0 0 1
// 1 0 0 0 1
// 0 1 0 0 1 0
//0 0 1 1 1 0 0
//B0011 1000 1001 0100 0110 0111 1000
0x3894678,
// 0
// 0 0
// 0 1 0
// 0 0 0 0
// 0 0 1 0 0
// 0 0 0 0 0 0
//0 0 0 1 0 0 0
//B0001 0000 0000 0001 0000 0001 0000
0x1001010,
// 0
// 0 0
// 1 1 1
// 0 0 0 1
// 0 1 1 1 0
// 0 1 0 0 0 0
//0 0 1 1 1 0 0
//B0011 1000 0001 0011 1010 0011 1000
0x3813A38,
// 0
// 0 0
// 1 1 1
// 0 0 0 1
// 0 1 1 1 0
// 0 0 0 0 1 0
//0 0 1 1 1 0 0
//B0011 1000 1000 0011 1010 0011 1000,
0x3883A38,
// 0
// 0 0
// 1 0 1
// 1 0 0 0
// 0 1 1 1 0
// 0 0 0 0 0 0
//0 0 0 0 1 0 0
//B0010 0000 0000 0011 1000 0110 1000,
0x2003868,
// 0
// 0 0
// 1 1 1
// 1 0 0 0
// 0 1 1 1 0
// 0 0 0 0 1 0
//0 0 1 1 1 0 0
//B0011 1000 1000 0011 1000 0111 1000,
0x3883878,
// 0
// 0 0
// 1 1 1
// 1 0 0 0
// 0 1 1 1 0
// 0 1 0 0 1 0
//0 0 1 1 1 0 0
//B0011 1000 1001 0011 1000 0111 1000,
0x3893878,
// 0
// 0 0
// 1 1 1
// 0 0 0 1
// 0 0 0 1 0
// 0 0 0 1 0 0
//0 0 0 1 0 0 0
//B0001 0000 0100 0010 0010 0011 1000
0x1042238,
// 0
// 0 0
// 1 1 1
// 1 0 0 1
// 0 1 1 1 0
// 0 1 0 0 1 0
//0 0 1 1 1 0 0
//B0011 1000 1001 0011 1010 0111 1000,
0x3893A78,
// 0
// 0 0
// 1 1 1
// 1 0 0 1
// 0 1 1 1 0
// 0 0 0 0 1 0
//0 0 1 1 1 0 0
//B0011 1000 1000 0011 1010 0111 1000
0x3883A78
};
#pragma once
#include <NewTone.h>
// Constants for notes
#define REST 0
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
#define END_OF_TUNE 0xFFFF
#define DUR_8 0xE000
#define DUR_6 0xC000
#define DUR_4 0x8000
#define DUR_3 0x6000
#define DUR_2 0x4000
#define DUR_1 0x2000
bool playingNote = false;
uint16_t playNote(int pin, uint16_t noteRaw)
{
// to calculate the note duration, take one second divided by the note type.
// e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
uint16_t frequency = noteRaw & 0x1FFF;
uint8_t duration = (noteRaw & 0xE000) >> 13;
if (duration == 7)
{
duration = 8;
}
uint16_t noteDuration = 1800 / duration;
int led = 0;
if (frequency != REST)
{
NewTone(pin, frequency, noteDuration);
}
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
uint16_t pauseBetweenNotes = (noteDuration * 13) / 10;
delay(pauseBetweenNotes);
/*
while (pauseBetweenNotes > 0)
{
_delay_ms(1);
pauseBetweenNotes--;
}
*/
if (frequency != REST)
{
// stop the tone playing:
noNewTone(pin);
}
return frequency;
}
int playSong(int pin, uint16_t* melody)
{
//Play each note in the melody until the END_OF_TUNE note is encountered
playingNote = true; //Used to stop display refresh while song is playing
int thisNote = 0;
uint16_t noteRaw = pgm_read_word(&melody[thisNote++]);
while (noteRaw != END_OF_TUNE)
{
playNote(pin, noteRaw);
noteRaw = pgm_read_word(&melody[thisNote++]);
} //while
playingNote = false;
}
Comments
Please log in or sign up to comment.