Version 3 sleeping behavior bugs, morsehandling bugs and timing bugs especially near BOD
This commit is contained in:
@ -6,13 +6,23 @@
|
|||||||
|
|
||||||
Project definitions, sources
|
Project definitions, sources
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
Version: 0.2 - ATTiny85, 1 MHz, BOD fuse disabled
|
Version: 0.3 - ATTiny85, 1 MHz, BOD fuse disabled
|
||||||
gitea : https://gitea.togo-lab.io/tgohle/0001-FireFly
|
gitea : https://gitea.togo-lab.io/tgohle/0001-FireFly
|
||||||
Date : 2026-06-13
|
Date : 2026-06-14
|
||||||
|
|
||||||
-----------------------------------------------------------------------------
|
Key changes Version 0.3:
|
||||||
|
|
||||||
Inspired by Karl Lunt's FireFly project:
|
- Removed unnecessary ADC enable/disable from loop()
|
||||||
|
- Disabled ADC and analog comparator in setup()
|
||||||
|
- Added INPUT_PULLUP for unused Arduino pins 0, 1, 2 will save power
|
||||||
|
- Added safer watchdog setup with interrupt protection to save run near brownout
|
||||||
|
- Added wdt_reset() before sleep for cleaner 8 s timing
|
||||||
|
- Added lowercase-to-uppercase handling
|
||||||
|
- Unsupported characters are now ignored without adding fake timing gaps
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Inspired by Karl Lunt's FireFly project:
|
||||||
http://www.seanet.com/~karllunt/fireflyLED.html
|
http://www.seanet.com/~karllunt/fireflyLED.html
|
||||||
|
|
||||||
Morse code reference:
|
Morse code reference:
|
||||||
@ -36,15 +46,19 @@
|
|||||||
'(', "-.--." ')', "-.--.-" '"', ".-..-."
|
'(', "-.--." ')', "-.--.-" '"', ".-..-."
|
||||||
'@', ".--.-." '&', ".-..."
|
'@', ".--.-." '&', ".-..."
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
Legal stuff / Copyright:
|
Legal stuff / Copyright:
|
||||||
License_-_CC_BY-NC_4.0
|
License_-_CC_BY-NC_4.0
|
||||||
https://creativecommons.org/licenses/by-nc/4.0/
|
https://creativecommons.org/licenses/by-nc/4.0/
|
||||||
-----------------------------------------------------------------------------
|
-----------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <avr/sleep.h>
|
#include <avr/sleep.h>
|
||||||
#include <avr/wdt.h>
|
#include <avr/wdt.h>
|
||||||
#include <avr/pgmspace.h> // FIX 3: needed for PROGMEM / pgm_read_byte
|
#include <avr/pgmspace.h>
|
||||||
|
#include <avr/interrupt.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifndef cbi
|
#ifndef cbi
|
||||||
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||||
@ -56,37 +70,52 @@
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Timing: unit length in ms.
|
// Timing: unit length in ms.
|
||||||
// At 1 MHz, delay() is accurate when F_CPU=1000000L is set in boards.txt.
|
// At 1 MHz, delay() is accurate when F_CPU=1000000L is set in boards.txt.
|
||||||
// 100 ms gives readable optical Morse; raise to 150 if readability is poor.
|
// 100 ms gives readable optical Morse; raise to 150 ms if readability is poor.
|
||||||
#define unitLength 100
|
#define unitLength 100
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// LED pin definitions (N-side = cathode, P-side = anode for sensing/driving)
|
// Sleep interval.
|
||||||
|
// Watchdog setup uses 8 s. For final use, change sleepCycles from 1 to 4
|
||||||
|
// for about 32 s between sensing/blinking activations.
|
||||||
|
const uint8_t sleepCycles = 4;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// LED pin definitions (N-side = cathode, P-side = anode for sensing/driving).
|
||||||
#define LED1_N_SIDE 3
|
#define LED1_N_SIDE 3
|
||||||
#define LED1_P_SIDE 4
|
#define LED1_P_SIDE 4
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// ATTiny85 has 512 bytes SRAM; this frees all of it for the stack.
|
// ATTiny85 Arduino pins 0, 1, 2 are unused in actual design and physically open.
|
||||||
// Only uppercase letters, digits, and the special chars in the switch below.
|
// Keep them in INPUT_PULLUP so they do not float and waste current.
|
||||||
// Test only: 20 x "0" due 0 = "-----" most energy draining
|
#define UNUSED_PIN_0 0
|
||||||
const char morseText[] PROGMEM = "0000000000000000000";
|
#define UNUSED_PIN_1 1
|
||||||
// Ruler: 0...0....1...1....2 Attention: more Text
|
#define UNUSED_PIN_2 2
|
||||||
// Ruler: 1...5....0...5....0 will cost more power!
|
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ATTiny85 has 512 bytes SRAM; this keeps the Morse text in flash.
|
||||||
|
// Only uppercase letters, digits, spaces, and the special chars in the switch
|
||||||
|
// below are emitted. Lowercase letters are converted to uppercase.
|
||||||
|
// Test setup: 20 x "0" because 0 = "-----" gives maximum LED ON time for test.
|
||||||
|
// shorter text will better, more text will cost more power.
|
||||||
|
const char morseText[] PROGMEM = "TOGO LAB";
|
||||||
|
// Ruler: 0....0....1...1....2
|
||||||
|
// Ruler: 0....5....0...5....0
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Darkness threshold.
|
// Darkness threshold.
|
||||||
// Higher value = triggers in brighter conditions.
|
// Higher value = triggers in brighter conditions.
|
||||||
// Best calibrated at actual dusk/dawn with the chosen LED type.
|
// Best calibrated at actual dusk/dawn with the chosen LED type.
|
||||||
unsigned int darknessThreshold = 17000;
|
const unsigned int darknessThreshold = 17000;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Watchdog interrupt flag — volatile because it is written in an ISR
|
// Watchdog interrupt flag — volatile because it is written in an ISR.
|
||||||
volatile boolean f_wdt = 1;
|
volatile bool f_wdt = true;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Forward declarations
|
// Forward declarations.
|
||||||
void setup_watchdog(int ii);
|
void setup_watchdog(uint8_t ii);
|
||||||
void system_sleep();
|
void system_sleep();
|
||||||
|
void configureUnusedPins();
|
||||||
|
void releaseLedPins(int LED_N, int LED_P);
|
||||||
unsigned int sensDarkness(int LED_N, int LED_P);
|
unsigned int sensDarkness(int LED_N, int LED_P);
|
||||||
void morse(int LED_N, int LED_P);
|
void morse(int LED_N, int LED_P);
|
||||||
void dit(int LED_P);
|
void dit(int LED_P);
|
||||||
@ -97,17 +126,22 @@ void dah(int LED_P);
|
|||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
// Disable unused peripherals immediately to save some µ/mA.
|
// Disable unused peripherals immediately to save power.
|
||||||
// PRTIM1, PRUSI: genuinely unused, safe to gate off
|
// PRTIM1, PRUSI: unused, safe to gate off.
|
||||||
// PRADC: controlled manually around sensDarkness()
|
// PRADC: ADC is not used; LED sensing is done with digitalRead().
|
||||||
// PRTIM0: must stay ON — delay() and millis() depend on Timer0
|
// PRTIM0: must stay ON while awake because delay() depends on Timer0.
|
||||||
PRR = (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
PRR = (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
||||||
|
|
||||||
// ADC is gated via PRR above; also clear ADEN just in case
|
// ADC off. It is not needed for digitalRead()-based LED sensing.
|
||||||
cbi(ADCSRA, ADEN);
|
cbi(ADCSRA, ADEN);
|
||||||
|
|
||||||
setup_watchdog(9); // 8-second watchdog interval
|
// Analog comparator off. Saves a little sleep current.
|
||||||
// simple repeat if more delay is need, eg 4
|
ACSR |= (1 << ACD);
|
||||||
|
|
||||||
|
configureUnusedPins();
|
||||||
|
releaseLedPins(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
|
|
||||||
|
setup_watchdog(9); // 8-second watchdog interval.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@ -115,117 +149,145 @@ void setup()
|
|||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
void loop()
|
void loop()
|
||||||
{
|
{
|
||||||
if (f_wdt == 1) {
|
if (f_wdt) {
|
||||||
f_wdt = 0;
|
f_wdt = false;
|
||||||
|
|
||||||
// Enable ADC only for the sensing window, then shut it off again.
|
|
||||||
cbi(PRR, PRADC); // un-gate ADC clock
|
|
||||||
sbi(ADCSRA, ADEN); // power ADC on
|
|
||||||
|
|
||||||
if (sensDarkness(LED1_N_SIDE, LED1_P_SIDE) > darknessThreshold) {
|
if (sensDarkness(LED1_N_SIDE, LED1_P_SIDE) > darknessThreshold) {
|
||||||
morse(LED1_N_SIDE, LED1_P_SIDE);
|
morse(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
cbi(ADCSRA, ADEN); // ADC off
|
// Return LED pins to high-Z before sleeping.
|
||||||
sbi(PRR, PRADC); // re-gate ADC clock
|
// Internal pullups stay disabled for the LED pins.
|
||||||
|
releaseLedPins(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
// Return LED pins to input (high-Z) before sleeping
|
|
||||||
pinMode(LED1_N_SIDE, INPUT);
|
|
||||||
pinMode(LED1_P_SIDE, INPUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// sleep 4 x 8 s = ~32 s between activations
|
// Test setup: 1 x 8 s sleep.
|
||||||
for (uint8_t i = 0; i < 1; i++) {
|
// Final setup: set sleepCycles = 4 for about 32 s.
|
||||||
|
for (uint8_t i = 0; i < sleepCycles; i++) {
|
||||||
system_sleep();
|
system_sleep();
|
||||||
}
|
}
|
||||||
// f_wdt is set to 1 by the WDT ISR when the 8 s expire; no manual clear needed here.
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Pin helpers
|
||||||
|
// ===========================================================================
|
||||||
|
void configureUnusedPins()
|
||||||
|
{
|
||||||
|
pinMode(UNUSED_PIN_0, INPUT_PULLUP);
|
||||||
|
pinMode(UNUSED_PIN_1, INPUT_PULLUP);
|
||||||
|
pinMode(UNUSED_PIN_2, INPUT_PULLUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
void releaseLedPins(int LED_N, int LED_P)
|
||||||
|
{
|
||||||
|
// digitalWrite LOW before INPUT disables the internal pullup on Arduino cores.
|
||||||
|
digitalWrite(LED_N, LOW);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
pinMode(LED_N, INPUT);
|
||||||
|
pinMode(LED_P, INPUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Sleep helpers
|
// Sleep helpers
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
void system_sleep()
|
void system_sleep()
|
||||||
{
|
{
|
||||||
|
// Reset watchdog counter so each sleep cycle starts with a fresh interval.
|
||||||
|
wdt_reset();
|
||||||
|
|
||||||
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
||||||
|
|
||||||
|
// Atomic sleep entry pattern.
|
||||||
|
// After sei(), AVR executes the next instruction before servicing interrupts,
|
||||||
|
// so sleep_cpu() is not skipped by a just-pending interrupt.
|
||||||
|
cli();
|
||||||
sleep_enable();
|
sleep_enable();
|
||||||
sleep_mode(); // CPU halts here until WDT fires
|
sei();
|
||||||
|
sleep_cpu();
|
||||||
|
|
||||||
|
// WDT interrupt wakes the CPU here.
|
||||||
sleep_disable();
|
sleep_disable();
|
||||||
// ADC stays off - re-enable it in loop() only when sensing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watchdog setup - ii selects timeout:
|
// Watchdog setup - ii selects timeout:
|
||||||
// 0=16ms 1=32ms 2=64ms 3=128ms 4=250ms 5=500ms
|
// 0=16ms 1=32ms 2=64ms 3=128ms 4=250ms 5=500ms
|
||||||
// 6=1s 7=2s 8=4s 9=8s
|
// 6=1s 7=2s 8=4s 9=8s
|
||||||
void setup_watchdog(int ii)
|
void setup_watchdog(uint8_t ii)
|
||||||
{
|
{
|
||||||
byte bb;
|
|
||||||
if (ii > 9) ii = 9;
|
if (ii > 9) ii = 9;
|
||||||
bb = ii & 7;
|
|
||||||
if (ii > 7) bb |= (1 << 5);
|
|
||||||
bb |= (1 << WDCE);
|
|
||||||
|
|
||||||
|
uint8_t bb = ii & 7;
|
||||||
|
if (ii > 7) bb |= (1 << WDP3);
|
||||||
|
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
|
||||||
|
wdt_reset();
|
||||||
MCUSR &= ~(1 << WDRF);
|
MCUSR &= ~(1 << WDRF);
|
||||||
WDTCR |= (1 << WDCE) | (1 << WDE); // timed sequence - must not be split
|
|
||||||
WDTCR = bb;
|
// Timed sequence: enable configuration change, then set interrupt-only WDT.
|
||||||
WDTCR |= _BV(WDIE);
|
WDTCR |= (1 << WDCE) | (1 << WDE);
|
||||||
|
WDTCR = bb | (1 << WDIE);
|
||||||
|
|
||||||
|
SREG = oldSREG;
|
||||||
}
|
}
|
||||||
|
|
||||||
ISR(WDT_vect)
|
ISR(WDT_vect)
|
||||||
{
|
{
|
||||||
f_wdt = 1;
|
f_wdt = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Light sensor
|
// Light sensor
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Returns a "darkness level": higher = darker.
|
// Returns a "darkness level": higher = darker.
|
||||||
// Charges the LED junction capacitor, then times how long it takes to bleed
|
// Charges the LED junction capacitance, then times how long it takes to bleed
|
||||||
// back through the reverse-biased diode to a logic LOW.
|
// back through the reverse-biased LED until the input reads LOW.
|
||||||
// ~30000 = pitch black, ~0 = bright light
|
// ~30000 = pitch black, ~0 = bright light
|
||||||
|
|
||||||
// return type is unsigned int (counter can reach 30000, fits in 16 bits)
|
|
||||||
unsigned int sensDarkness(int LED_N, int LED_P)
|
unsigned int sensDarkness(int LED_N, int LED_P)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
// Charge the LED (forward-bias momentarily)
|
// Charge the LED junction capacitance.
|
||||||
pinMode(LED_N, OUTPUT);
|
pinMode(LED_N, OUTPUT);
|
||||||
pinMode(LED_P, OUTPUT);
|
pinMode(LED_P, OUTPUT);
|
||||||
digitalWrite(LED_N, HIGH);
|
digitalWrite(LED_N, HIGH);
|
||||||
digitalWrite(LED_P, LOW);
|
digitalWrite(LED_P, LOW);
|
||||||
|
|
||||||
// Let the N-end float and measure bleed-down time
|
// Let the N-side float and measure bleed-down time.
|
||||||
pinMode(LED_N, INPUT);
|
pinMode(LED_N, INPUT);
|
||||||
digitalWrite(LED_N, LOW); // disable internal pull-up
|
digitalWrite(LED_N, LOW); // disable internal pullup
|
||||||
|
|
||||||
for (i = 0; i < 30000; i++) {
|
for (i = 0; i < 30000; i++) {
|
||||||
if (digitalRead(LED_N) == 0) break;
|
if (digitalRead(LED_N) == LOW) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up after sensing
|
// Clean up after sensing: both LED pins low, then caller releases them.
|
||||||
pinMode(LED_N, OUTPUT);
|
pinMode(LED_N, OUTPUT);
|
||||||
digitalWrite(LED_N, LOW);
|
digitalWrite(LED_N, LOW);
|
||||||
pinMode(LED_P, OUTPUT);
|
pinMode(LED_P, OUTPUT);
|
||||||
digitalWrite(LED_P, LOW);
|
digitalWrite(LED_P, LOW);
|
||||||
return i;
|
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Morse helpers
|
// Morse helpers
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|
||||||
void dit(int LED_P)
|
void dit(int LED_P)
|
||||||
{
|
{
|
||||||
digitalWrite(LED_P, HIGH); delay(unitLength);
|
digitalWrite(LED_P, HIGH);
|
||||||
digitalWrite(LED_P, LOW); delay(unitLength);
|
delay(unitLength);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
delay(unitLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dah(int LED_P)
|
void dah(int LED_P)
|
||||||
{
|
{
|
||||||
digitalWrite(LED_P, HIGH); delay(unitLength * 3);
|
digitalWrite(LED_P, HIGH);
|
||||||
digitalWrite(LED_P, LOW); delay(unitLength);
|
delay(unitLength * 3);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
delay(unitLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@ -236,76 +298,94 @@ void morse(int LED_N, int LED_P)
|
|||||||
pinMode(LED_N, OUTPUT);
|
pinMode(LED_N, OUTPUT);
|
||||||
pinMode(LED_P, OUTPUT);
|
pinMode(LED_P, OUTPUT);
|
||||||
digitalWrite(LED_N, LOW);
|
digitalWrite(LED_N, LOW);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
|
||||||
// read each character from flash with pgm_read_byte()
|
const size_t len = strlen_P(morseText);
|
||||||
// use < morseText length, not <= (avoids reading past the null terminator)
|
|
||||||
uint8_t len = strlen_P(morseText);
|
for (size_t i = 0; i < len; i++) {
|
||||||
for (uint8_t i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
char c = (char)pgm_read_byte(&morseText[i]);
|
char c = (char)pgm_read_byte(&morseText[i]);
|
||||||
|
|
||||||
|
// Allow lowercase source text without silently breaking timing.
|
||||||
|
if (c >= 'a' && c <= 'z') {
|
||||||
|
c = c - 'a' + 'A';
|
||||||
|
}
|
||||||
|
|
||||||
|
bool symbolSent = false;
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
// ----- Letters -----
|
// ----- Letters -----
|
||||||
case 'A': dit(LED_P); dah(LED_P); break; // .-
|
case 'A': dit(LED_P); dah(LED_P); symbolSent = true; break; // .-
|
||||||
case 'B': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // -...
|
case 'B': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // -...
|
||||||
case 'C': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // -.-.
|
case 'C': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // -.-.
|
||||||
case 'D': dah(LED_P); dit(LED_P); dit(LED_P); break; // -..
|
case 'D': dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // -..
|
||||||
case 'E': dit(LED_P); break; // .
|
case 'E': dit(LED_P); symbolSent = true; break; // .
|
||||||
case 'F': dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // ..-.
|
case 'F': dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // ..-.
|
||||||
case 'G': dah(LED_P); dah(LED_P); dit(LED_P); break; // --.
|
case 'G': dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // --.
|
||||||
case 'H': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // ....
|
case 'H': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ....
|
||||||
case 'I': dit(LED_P); dit(LED_P); break; // ..
|
case 'I': dit(LED_P); dit(LED_P); symbolSent = true; break; // ..
|
||||||
case 'J': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // .---
|
case 'J': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // .---
|
||||||
case 'K': dah(LED_P); dit(LED_P); dah(LED_P); break; // -.-
|
case 'K': dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // -.-
|
||||||
case 'L': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // .-..
|
case 'L': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // .-..
|
||||||
case 'M': dah(LED_P); dah(LED_P); break; // --
|
case 'M': dah(LED_P); dah(LED_P); symbolSent = true; break; // --
|
||||||
case 'N': dah(LED_P); dit(LED_P); break; // -.
|
case 'N': dah(LED_P); dit(LED_P); symbolSent = true; break; // -.
|
||||||
case 'O': dah(LED_P); dah(LED_P); dah(LED_P); break; // ---
|
case 'O': dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // ---
|
||||||
case 'P': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // .--.
|
case 'P': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .--.
|
||||||
case 'Q': dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // --.-
|
case 'Q': dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // --.-
|
||||||
case 'R': dit(LED_P); dah(LED_P); dit(LED_P); break; // .-.
|
case 'R': dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .-.
|
||||||
case 'S': dit(LED_P); dit(LED_P); dit(LED_P); break; // ...
|
case 'S': dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ...
|
||||||
case 'T': dah(LED_P); break; // -
|
case 'T': dah(LED_P); symbolSent = true; break; // -
|
||||||
case 'U': dit(LED_P); dit(LED_P); dah(LED_P); break; // ..-
|
case 'U': dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // ..-
|
||||||
case 'V': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // ...-
|
case 'V': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // ...-
|
||||||
case 'W': dit(LED_P); dah(LED_P); dah(LED_P); break; // .--
|
case 'W': dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // .--
|
||||||
case 'X': dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // -..-
|
case 'X': dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // -..-
|
||||||
case 'Y': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // -.--
|
case 'Y': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // -.--
|
||||||
case 'Z': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // --..
|
case 'Z': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // --..
|
||||||
|
|
||||||
// ----- Digits -----
|
// ----- Digits -----
|
||||||
case '1': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // .----
|
case '1': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // .----
|
||||||
case '2': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // ..---
|
case '2': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // ..---
|
||||||
case '3': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // ...--
|
case '3': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // ...--
|
||||||
case '4': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // ....-
|
case '4': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // ....-
|
||||||
case '5': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // .....
|
case '5': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // .....
|
||||||
case '6': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // -....
|
case '6': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // -....
|
||||||
case '7': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // --...
|
case '7': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // --...
|
||||||
case '8': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // ---.
|
case '8': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ---..
|
||||||
case '9': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // ----.
|
case '9': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // ----.
|
||||||
case '0': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // -----
|
case '0': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // -----
|
||||||
|
|
||||||
// ----- Punctuation -----
|
// ----- Punctuation -----
|
||||||
case ' ': delay(unitLength * 5); break; // word gap (5 + 3 = 8 units total)
|
case '.': dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // .-.-.-
|
||||||
case '.': dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // .-.-.-
|
case ',': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // --..--
|
||||||
case ',': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // --..--
|
case '?': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ..--..
|
||||||
case '?': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // ..--..
|
case '!': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); symbolSent = true; break; // -.-.--
|
||||||
case '!': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // -.-.--
|
case ':': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // ---...
|
||||||
case ':': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // ---...
|
case ';': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // -.-.-.
|
||||||
case ';': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // -.-.-.
|
case '(': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // -.--.
|
||||||
case '(': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // -.--.
|
case ')': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); symbolSent = true; break; // -.--.-
|
||||||
case ')': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // -.--.-
|
case '"': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .-..-.
|
||||||
case '"': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // .-..-.
|
case '@': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); symbolSent = true; break; // .--.-.
|
||||||
case '@': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // .--.-.
|
case '&': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); symbolSent = true; break; // .-...
|
||||||
case '&': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // .-...
|
|
||||||
|
// Space handling:
|
||||||
|
// Previous symbol already ended with 1 unit OFF and then received the
|
||||||
|
// normal 2 unit character gap. Add 4 units here -> total word gap 7 units.
|
||||||
|
case ' ':
|
||||||
|
delay(unitLength * 4);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Unsupported chars are ignored without adding an artificial gap.
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inter-character gap: 3 units (the dit/dah functions already trail 1 unit,
|
// Inter-character gap: 3 units total.
|
||||||
// so this adds the remaining 2 to reach the standard 3-unit gap).
|
// dit()/dah() already trail 1 unit OFF, so add the remaining 2 units.
|
||||||
// Space case already handles word gap - no extra delay needed there.
|
if (symbolSent) {
|
||||||
if (c != ' ') {
|
|
||||||
delay(unitLength * 2);
|
delay(unitLength * 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
digitalWrite(LED_N, LOW);
|
||||||
}
|
}
|
||||||
@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
=============================================================================
|
||||||
|
FireFly Morse Throwie
|
||||||
|
- a light controlled (LED as Sensor) morse blinker throwie with ATTiny85
|
||||||
|
=============================================================================
|
||||||
|
|
||||||
|
Project definitions, sources
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
Version: 0.2 - ATTiny85, 1 MHz, BOD fuse disabled
|
||||||
|
gitea : https://gitea.togo-lab.io/tgohle/0001-FireFly
|
||||||
|
Date : 2026-06-13
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Inspired by Karl Lunt's FireFly project:
|
||||||
|
http://www.seanet.com/~karllunt/fireflyLED.html
|
||||||
|
|
||||||
|
Morse code reference:
|
||||||
|
'A', ".-" 'B', "-..." 'C', "-.-."
|
||||||
|
'D', "-.." 'E', "." 'F', "..-."
|
||||||
|
'G', "--." 'H', "...." 'I', ".."
|
||||||
|
'J', ".---" 'K', "-.-" 'L', ".-.."
|
||||||
|
'M', "--" 'N', "-." 'O', "---"
|
||||||
|
'P', ".--." 'Q', "--.-" 'R', ".-."
|
||||||
|
'S', "..." 'T', "-" 'U', "..-"
|
||||||
|
'V', "...-" 'W', ".--" 'X', "-..-"
|
||||||
|
'Y', "-.--" 'Z', "--.."
|
||||||
|
|
||||||
|
'1', ".----" '2', "..---" '3', "...--"
|
||||||
|
'4', "....-" '5', "....." '6', "-...."
|
||||||
|
'7', "--..." '8', "---.." '9', "----."
|
||||||
|
'0', "-----"
|
||||||
|
|
||||||
|
'.', ".-.-.-" ',', "--..--" '?', "..--.."
|
||||||
|
'!', "-.-.--" ':', "---..." ';', "-.-.-."
|
||||||
|
'(', "-.--." ')', "-.--.-" '"', ".-..-."
|
||||||
|
'@', ".--.-." '&', ".-..."
|
||||||
|
|
||||||
|
Legal stuff / Copyright:
|
||||||
|
License_-_CC_BY-NC_4.0
|
||||||
|
https://creativecommons.org/licenses/by-nc/4.0/
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <avr/sleep.h>
|
||||||
|
#include <avr/wdt.h>
|
||||||
|
#include <avr/pgmspace.h> // FIX 3: needed for PROGMEM / pgm_read_byte
|
||||||
|
|
||||||
|
#ifndef cbi
|
||||||
|
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
|
||||||
|
#endif
|
||||||
|
#ifndef sbi
|
||||||
|
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Timing: unit length in ms.
|
||||||
|
// At 1 MHz, delay() is accurate when F_CPU=1000000L is set in boards.txt.
|
||||||
|
// 100 ms gives readable optical Morse; raise to 150 if readability is poor.
|
||||||
|
#define unitLength 100
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// LED pin definitions (N-side = cathode, P-side = anode for sensing/driving)
|
||||||
|
#define LED1_N_SIDE 3
|
||||||
|
#define LED1_P_SIDE 4
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// ATTiny85 has 512 bytes SRAM; this frees all of it for the stack.
|
||||||
|
// Only uppercase letters, digits, and the special chars in the switch below.
|
||||||
|
// Test only: 20 x "0" due 0 = "-----" most energy draining
|
||||||
|
const char morseText[] PROGMEM = "0000000000000000000";
|
||||||
|
// Ruler: 0...0....1...1....2 Attention: more Text
|
||||||
|
// Ruler: 1...5....0...5....0 will cost more power!
|
||||||
|
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Darkness threshold.
|
||||||
|
// Higher value = triggers in brighter conditions.
|
||||||
|
// Best calibrated at actual dusk/dawn with the chosen LED type.
|
||||||
|
unsigned int darknessThreshold = 17000;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Watchdog interrupt flag — volatile because it is written in an ISR
|
||||||
|
volatile boolean f_wdt = 1;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Forward declarations
|
||||||
|
void setup_watchdog(int ii);
|
||||||
|
void system_sleep();
|
||||||
|
unsigned int sensDarkness(int LED_N, int LED_P);
|
||||||
|
void morse(int LED_N, int LED_P);
|
||||||
|
void dit(int LED_P);
|
||||||
|
void dah(int LED_P);
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Setup
|
||||||
|
// ===========================================================================
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// Disable unused peripherals immediately to save some µ/mA.
|
||||||
|
// PRTIM1, PRUSI: genuinely unused, safe to gate off
|
||||||
|
// PRADC: controlled manually around sensDarkness()
|
||||||
|
// PRTIM0: must stay ON — delay() and millis() depend on Timer0
|
||||||
|
PRR = (1 << PRTIM1) | (1 << PRUSI) | (1 << PRADC);
|
||||||
|
|
||||||
|
// ADC is gated via PRR above; also clear ADEN just in case
|
||||||
|
cbi(ADCSRA, ADEN);
|
||||||
|
|
||||||
|
setup_watchdog(9); // 8-second watchdog interval
|
||||||
|
// simple repeat if more delay is need, eg 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Main loop
|
||||||
|
// ===========================================================================
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
if (f_wdt == 1) {
|
||||||
|
f_wdt = 0;
|
||||||
|
|
||||||
|
// Enable ADC only for the sensing window, then shut it off again.
|
||||||
|
cbi(PRR, PRADC); // un-gate ADC clock
|
||||||
|
sbi(ADCSRA, ADEN); // power ADC on
|
||||||
|
|
||||||
|
if (sensDarkness(LED1_N_SIDE, LED1_P_SIDE) > darknessThreshold) {
|
||||||
|
morse(LED1_N_SIDE, LED1_P_SIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
cbi(ADCSRA, ADEN); // ADC off
|
||||||
|
sbi(PRR, PRADC); // re-gate ADC clock
|
||||||
|
|
||||||
|
// Return LED pins to input (high-Z) before sleeping
|
||||||
|
pinMode(LED1_N_SIDE, INPUT);
|
||||||
|
pinMode(LED1_P_SIDE, INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sleep 4 x 8 s = ~32 s between activations
|
||||||
|
for (uint8_t i = 0; i < 1; i++) {
|
||||||
|
system_sleep();
|
||||||
|
}
|
||||||
|
// f_wdt is set to 1 by the WDT ISR when the 8 s expire; no manual clear needed here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Sleep helpers
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
void system_sleep()
|
||||||
|
{
|
||||||
|
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
|
||||||
|
sleep_enable();
|
||||||
|
sleep_mode(); // CPU halts here until WDT fires
|
||||||
|
sleep_disable();
|
||||||
|
// ADC stays off - re-enable it in loop() only when sensing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watchdog setup - ii selects timeout:
|
||||||
|
// 0=16ms 1=32ms 2=64ms 3=128ms 4=250ms 5=500ms
|
||||||
|
// 6=1s 7=2s 8=4s 9=8s
|
||||||
|
void setup_watchdog(int ii)
|
||||||
|
{
|
||||||
|
byte bb;
|
||||||
|
if (ii > 9) ii = 9;
|
||||||
|
bb = ii & 7;
|
||||||
|
if (ii > 7) bb |= (1 << 5);
|
||||||
|
bb |= (1 << WDCE);
|
||||||
|
|
||||||
|
MCUSR &= ~(1 << WDRF);
|
||||||
|
WDTCR |= (1 << WDCE) | (1 << WDE); // timed sequence - must not be split
|
||||||
|
WDTCR = bb;
|
||||||
|
WDTCR |= _BV(WDIE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ISR(WDT_vect)
|
||||||
|
{
|
||||||
|
f_wdt = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Light sensor
|
||||||
|
// ===========================================================================
|
||||||
|
// Returns a "darkness level": higher = darker.
|
||||||
|
// Charges the LED junction capacitor, then times how long it takes to bleed
|
||||||
|
// back through the reverse-biased diode to a logic LOW.
|
||||||
|
// ~30000 = pitch black, ~0 = bright light
|
||||||
|
|
||||||
|
// return type is unsigned int (counter can reach 30000, fits in 16 bits)
|
||||||
|
unsigned int sensDarkness(int LED_N, int LED_P)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
// Charge the LED (forward-bias momentarily)
|
||||||
|
pinMode(LED_N, OUTPUT);
|
||||||
|
pinMode(LED_P, OUTPUT);
|
||||||
|
digitalWrite(LED_N, HIGH);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
|
||||||
|
// Let the N-end float and measure bleed-down time
|
||||||
|
pinMode(LED_N, INPUT);
|
||||||
|
digitalWrite(LED_N, LOW); // disable internal pull-up
|
||||||
|
|
||||||
|
for (i = 0; i < 30000; i++) {
|
||||||
|
if (digitalRead(LED_N) == 0) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up after sensing
|
||||||
|
pinMode(LED_N, OUTPUT);
|
||||||
|
digitalWrite(LED_N, LOW);
|
||||||
|
pinMode(LED_P, OUTPUT);
|
||||||
|
digitalWrite(LED_P, LOW);
|
||||||
|
return i;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Morse helpers
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
void dit(int LED_P)
|
||||||
|
{
|
||||||
|
digitalWrite(LED_P, HIGH); delay(unitLength);
|
||||||
|
digitalWrite(LED_P, LOW); delay(unitLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dah(int LED_P)
|
||||||
|
{
|
||||||
|
digitalWrite(LED_P, HIGH); delay(unitLength * 3);
|
||||||
|
digitalWrite(LED_P, LOW); delay(unitLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Morse sender
|
||||||
|
// ===========================================================================
|
||||||
|
void morse(int LED_N, int LED_P)
|
||||||
|
{
|
||||||
|
pinMode(LED_N, OUTPUT);
|
||||||
|
pinMode(LED_P, OUTPUT);
|
||||||
|
digitalWrite(LED_N, LOW);
|
||||||
|
|
||||||
|
// read each character from flash with pgm_read_byte()
|
||||||
|
// use < morseText length, not <= (avoids reading past the null terminator)
|
||||||
|
uint8_t len = strlen_P(morseText);
|
||||||
|
for (uint8_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
char c = (char)pgm_read_byte(&morseText[i]);
|
||||||
|
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
// ----- Letters -----
|
||||||
|
case 'A': dit(LED_P); dah(LED_P); break; // .-
|
||||||
|
case 'B': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // -...
|
||||||
|
case 'C': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // -.-.
|
||||||
|
case 'D': dah(LED_P); dit(LED_P); dit(LED_P); break; // -..
|
||||||
|
case 'E': dit(LED_P); break; // .
|
||||||
|
case 'F': dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // ..-.
|
||||||
|
case 'G': dah(LED_P); dah(LED_P); dit(LED_P); break; // --.
|
||||||
|
case 'H': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // ....
|
||||||
|
case 'I': dit(LED_P); dit(LED_P); break; // ..
|
||||||
|
case 'J': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // .---
|
||||||
|
case 'K': dah(LED_P); dit(LED_P); dah(LED_P); break; // -.-
|
||||||
|
case 'L': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // .-..
|
||||||
|
case 'M': dah(LED_P); dah(LED_P); break; // --
|
||||||
|
case 'N': dah(LED_P); dit(LED_P); break; // -.
|
||||||
|
case 'O': dah(LED_P); dah(LED_P); dah(LED_P); break; // ---
|
||||||
|
case 'P': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // .--.
|
||||||
|
case 'Q': dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // --.-
|
||||||
|
case 'R': dit(LED_P); dah(LED_P); dit(LED_P); break; // .-.
|
||||||
|
case 'S': dit(LED_P); dit(LED_P); dit(LED_P); break; // ...
|
||||||
|
case 'T': dah(LED_P); break; // -
|
||||||
|
case 'U': dit(LED_P); dit(LED_P); dah(LED_P); break; // ..-
|
||||||
|
case 'V': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // ...-
|
||||||
|
case 'W': dit(LED_P); dah(LED_P); dah(LED_P); break; // .--
|
||||||
|
case 'X': dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // -..-
|
||||||
|
case 'Y': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // -.--
|
||||||
|
case 'Z': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // --..
|
||||||
|
|
||||||
|
// ----- Digits -----
|
||||||
|
case '1': dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // .----
|
||||||
|
case '2': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // ..---
|
||||||
|
case '3': dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // ...--
|
||||||
|
case '4': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); break; // ....-
|
||||||
|
case '5': dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // .....
|
||||||
|
case '6': dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // -....
|
||||||
|
case '7': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // --...
|
||||||
|
case '8': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // ---.
|
||||||
|
case '9': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // ----.
|
||||||
|
case '0': dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); dah(LED_P); break; // -----
|
||||||
|
|
||||||
|
// ----- Punctuation -----
|
||||||
|
case ' ': delay(unitLength * 5); break; // word gap (5 + 3 = 8 units total)
|
||||||
|
case '.': dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // .-.-.-
|
||||||
|
case ',': dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // --..--
|
||||||
|
case '?': dit(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); break; // ..--..
|
||||||
|
case '!': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); break; // -.-.--
|
||||||
|
case ':': dah(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // ---...
|
||||||
|
case ';': dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // -.-.-.
|
||||||
|
case '(': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); break; // -.--.
|
||||||
|
case ')': dah(LED_P); dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); break; // -.--.-
|
||||||
|
case '"': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // .-..-.
|
||||||
|
case '@': dit(LED_P); dah(LED_P); dah(LED_P); dit(LED_P); dah(LED_P); dit(LED_P); break; // .--.-.
|
||||||
|
case '&': dit(LED_P); dah(LED_P); dit(LED_P); dit(LED_P); dit(LED_P); break; // .-...
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inter-character gap: 3 units (the dit/dah functions already trail 1 unit,
|
||||||
|
// so this adds the remaining 2 to reach the standard 3-unit gap).
|
||||||
|
// Space case already handles word gap - no extra delay needed there.
|
||||||
|
if (c != ' ') {
|
||||||
|
delay(unitLength * 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user