//**************************************************
// for ATtiny402 Practice Keyer with Sleep + Buffer
//
// Pin1 Vcc Pin2 Dit Pin3 Dah Pin4 Led
// Pin5 Piezo Pin7 Button Pin8 Gnd
// XRQTechLab 2025.8.8
// Speed 10~30 WPM up by Button
//**************************************************
#include <EEPROM.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#define DIT_PIN 0
#define DAH_PIN 1
#define LED_PIN 2
#define PIEZO_PIN 3
#define BUTTON_PIN 4
int wpm = 10;
int ditLength;
int dahLength;
bool ditPressed = false;
bool dahPressed = false;
bool squeezeMode = false;
bool lastWasDit = false;
volatile bool wakeUpFlag = false;
unsigned long lastInputTime = 0;
// バッファ機能追加
char paddleBuffer = 0;
bool sending = false;
void setup() {
pinMode(DIT_PIN, INPUT_PULLUP);
pinMode(DAH_PIN, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
pinMode(PIEZO_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(DIT_PIN), wakeISR, FALLING);
attachInterrupt(digitalPinToInterrupt(DAH_PIN), wakeISR, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), wakeISR, FALLING);
wpm = EEPROM.read(0);
if (wpm < 10 || wpm > 30) {
wpm = 10;
}
updateSpeed();
playOpeningMessage();
lastInputTime = millis();
}
void loop() {
if (millis() - lastInputTime > 60000) {
goToSleep();
}
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50);
if (digitalRead(BUTTON_PIN) == LOW) {
wpm++;
if (wpm > 30) {
wpm = 10;
}
EEPROM.write(0, wpm);
updateSpeed();
playSpeedSignal();
lastInputTime = millis();
}
}
// 入力処理(送出中ならバッファに記録)
if (!sending) {
if (paddleBuffer) {
if (paddleBuffer == '.') sendDit();
else if (paddleBuffer == '-') sendDah();
paddleBuffer = 0;
} else {
if (digitalRead(DIT_PIN) == LOW) {
ditPressed = true;
lastInputTime = millis();
}
if (digitalRead(DAH_PIN) == LOW) {
dahPressed = true;
lastInputTime = millis();
}
if (ditPressed && dahPressed) {
squeezeMode = true;
}
if (squeezeMode) {
sendSqueeze();
squeezeMode = false;
} else {
if (ditPressed) sendDit();
if (dahPressed) sendDah();
}
}
} else {
// 送出中の入力を記録
if (digitalRead(DIT_PIN) == LOW) {
paddleBuffer = '.';
} else if (digitalRead(DAH_PIN) == LOW) {
paddleBuffer = '-';
}
}
}
void goToSleep() {
digitalWrite(LED_PIN, LOW);
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_cpu();
sleep_disable();
wakeUpFlag = false;
lastInputTime = millis();
}
void wakeISR() {
wakeUpFlag = true;
}
void updateSpeed() {
ditLength = 1200 / wpm;
dahLength = ditLength * 3;
}
void sendDit() {
sending = true;
digitalWrite(LED_PIN, HIGH);
tone(PIEZO_PIN, 800);
delay(ditLength);
digitalWrite(LED_PIN, LOW);
noTone(PIEZO_PIN);
delay(ditLength);
ditPressed = false;
lastWasDit = true;
sending = false;
}
void sendDah() {
sending = true;
digitalWrite(LED_PIN, HIGH);
tone(PIEZO_PIN, 800);
delay(dahLength);
digitalWrite(LED_PIN, LOW);
noTone(PIEZO_PIN);
delay(ditLength);
dahPressed = false;
lastWasDit = false;
sending = false;
}
void sendSqueeze() {
while (digitalRead(DIT_PIN) == LOW && digitalRead(DAH_PIN) == LOW) {
if (lastWasDit) {
sendDah();
} else {
sendDit();
}
}
}
void playSpeedSignal() {
for (int i = 0; i < 3; i++) {
tone(PIEZO_PIN, 800);
delay(ditLength);
noTone(PIEZO_PIN);
delay(ditLength);
}
}
void playOpeningMessage() {
tone(PIEZO_PIN, 800);
delay(ditLength);
noTone(PIEZO_PIN);
delay(ditLength);
tone(PIEZO_PIN, 800);
delay(dahLength);
noTone(PIEZO_PIN);
delay(ditLength);
tone(PIEZO_PIN, 800);
delay(ditLength);
noTone(PIEZO_PIN);
delay(ditLength);
}