/* * KeyerBuddy - morse code practice partner, ATtiny4313 main code * Copyright (C) 2025 David Montag * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Created: 7/22/2025 1:37:14 PM * Author: david@montag.se */ #ifndef F_CPU #define F_CPU 8000000UL #endif #include #include //#include //#include #include #include #include #include // 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 0 1 2 3 4 5 6 7 8 9 /* Format: XXXXXYYY * XXXXX = dit/dah (1 = dit, 0 = dah) * YYY = length (1-5 = 1-5, 6-7 = 6) * When length is 6, the first sound is a dit. When length is 7 the first sound is dah. In both cases the actual length is 6. */ const uint8_t char_to_dit_dah[] PROGMEM = { 0b10010, // A 0b0111100, // B 0b0101100, // C 0b011011, // D 0b1001, // E 0b1101100, // F 0b001011, // G 0b1111100, // H 0b11010, // I 0b1000100, // J 0b010011, // K 0b1011100, // L 0b00010, // M 0b01010, // N 0b000011, // O 0b1001100, // P 0b0010100, // Q 0b101011, // R 0b111011, // S 0b0001, // T 0b110011, // U 0b1110100, // V 0b100011, // W 0b0110100, // X 0b0100100, // Y 0b0011100, // Z 0b00000101, // 0 0b10000101, // 1 0b11000101, // 2 0b11100101, // 3 0b11110101, // 4 0b11111101, // 5 0b01111101, // 6 0b00111101, // 7 0b00011101, // 8 0b00001101, // 9 0b10011110, // ? question mark 0b01010110, // . period 0b01100111, // , comma 0b00000000, // _ space 0b01110101, // = BT 0b01001101, // < KN 0b11010110, // ~ SK 0b01101101 // / stroke }; #define NUM_WORDS 100 const uint8_t words[] PROGMEM = "CQ FB UR_RST_599 TNX ES " "VY QTH ANT RIG WX " "GE GM OM OP RST " "579 PWR_5W QSO TNX_FER_CALL PSE_QRS " "CQ_DX CU TU 73 AGN " "PSE MY_NAME_IS QSB HR SOTA " "UR DR_OM HW? DIPOLE POTA " "QRL? CQ_DE GA CU_AGN_SN HPE " "WX_CLOUDY WX_SUNNY WX_21C OK QTH_NR " "R_R_R = AGN_PSE QRZ? GUD " "QRP UR_449_449 R_R_FB_UR_589 TNX_FER_NICE_QSO TU_ES_CU_E_E " "TNX_QSO_ES_HPE_CU_SN OP_DAVID GUD_CONDX RIG_ICOM_7300 RIG_QRP_4W " "QTH_NR_STOCKHOLM OK_GE_OM ANT_DIPOLE ANT_6EL_YAGI HR_PWR_IS_500_W " "OK_FB_DR_JONATHAN UR_FB_55N BEST_DX TU_~_E_E QSL_VIA_BURO " "HPE_CU_AGN_MY_73 TNX_FER_INFO CIAO TNX_FER_RPRT VY_VY_73 " "R_R_OK RR_FB_OM HI_UR_RST_5NN HR_TEMP_18C TKS_RPRT " "ANT_3_BAND_QUAD SA0DMA/P SA0DMA/MM OP_DAVE_=_=_QTH_NR GE_DR_OM " "SRI_AGN_QSB UR_5NN_=_HW? R_OK_VY_FB GE_TNX_RST_57N_OP_MAL RIG_TRUSDX " "MY_RIG_IS_YAESU_710 TNX_FER_NICE_QSO UR_RST_44N_QSB ANT_EFHW ANT_VERTICAL " "RIG_300W_ANT_DIPOLE FB_CPY NAME_IS_WALTER GM_OM_ES_TNX_FER_CALL TEST"; void led_on(void) { PORTB |= (1<> 3; if (len == 0) { var_delay(delay*7); } if (len == 6) { play_dit(delay, flash); len = 5; } if (len == 7) { play_dah(delay, flash); len = 5; } for(uint8_t i = 0; i < len; i++) { uint8_t ditdah = bits & (1 << (len-i-1)); if (ditdah) { play_dit(delay, flash); } else { play_dah(delay, flash); } } } // c is ascii void play_char(uint8_t c, uint32_t delay, uint8_t flash) { if (c >= 65 && c <= 90) c = c-65; else if (c >= 48 && c <= 57) c = c-48+26; else if (c == '?') c = 36; else if (c == '.') c = 37; else if (c == ',') c = 38; else if (c == '_') c = 39; else if (c == '=') c = 40; else if (c == '<') c = 41; else if (c == '~') c = 42; else if (c == '/') c = 43; else return; play_lut_char(c, delay, flash); } void play_word(uint8_t word_num, uint32_t delay, uint8_t flash) { uint8_t current_word = 0; uint16_t char_index = 0; while (current_word < word_num) { while (pgm_read_byte(&words[char_index]) > 0x20) { char_index += 1; } char_index += 1; current_word += 1; } while (pgm_read_byte(&words[char_index]) > 0x20) { play_char(pgm_read_byte(&words[char_index]), delay, flash); var_delay(delay*2); // last character includes one delay already char_index += 1; } } void play_random_callsign(uint32_t delay, uint8_t flash) { play_lut_char(random() % 26, delay, flash); var_delay(delay*3); play_lut_char(random() % 26, delay, flash); var_delay(delay*3); play_lut_char(26+(random() % 10), delay, flash); var_delay(delay*3); play_lut_char(random() % 26, delay, flash); var_delay(delay*3); play_lut_char(random() % 26, delay, flash); var_delay(delay*3); play_lut_char(random() % 26, delay, flash); if((random() % 10) == 0) { var_delay(delay*3); play_char('/', delay, flash); var_delay(delay*3); play_char('P', delay, flash); } } void play_random_word(uint32_t delay, uint8_t flash) { uint8_t word_num = random() % NUM_WORDS; play_word(word_num, delay, flash); } #define MIN_VALUE 10 #define MAX_VALUE 800 #define SLEEP_DELAY_SECS 10 int main(void) { hardware_init(); sound_off(); GIMSK |= (1< (1000/POT_CHARGE_DELAY_MS)*SLEEP_DELAY_SECS) { // pin change interrupt for wakeup GIMSK |= (1< straight key // keytype=straight socket=plugged --> callsign generator // keytype=paddle socket=empty --> callsign generator // keytype=paddle socket=plugged --> callsign generator if (KEY_SOCKET_EMPTY && KEY_TYPE_STRAIGHT) { if (IS_ONBOARD_KEY_DOWN) { idle_cycle_counter = 0; sound_on(); led_on(); } else { sound_off(); led_off(); } } else if (KEY_TYPE_STRAIGHT) { if (DIT_IS_PRESSED || DAH_IS_PRESSED) { idle_cycle_counter = 0; sound_on(); led_on(); } else { sound_off(); led_off(); } } if (!KEY_TYPE_STRAIGHT || KEY_SOCKET_PLUGGED) { if (IS_ONBOARD_KEY_DOWN) { idle_cycle_counter = 0; _delay_ms(100); if (!IS_ONBOARD_KEY_DOWN) { play_random_word(delay, flash_sounds); } else { _delay_ms(100); if (!IS_ONBOARD_KEY_DOWN) { play_random_word(delay, flash_sounds); } else { _delay_ms(100); if (!IS_ONBOARD_KEY_DOWN) { play_random_word(delay, flash_sounds); } else { _delay_ms(100); if (!IS_ONBOARD_KEY_DOWN) { play_random_word(delay, flash_sounds); } else { _delay_ms(100); if (IS_ONBOARD_KEY_DOWN) { play_random_callsign(delay, flash_sounds); } } } } } } } if (!KEY_TYPE_STRAIGHT) { // PADDLE KEY if ((DIT_IS_PRESSED || key_triggered & 1) && (DAH_IS_PRESSED || key_triggered & 2)) { idle_cycle_counter = 0; if (latest_triggered == 1) { play_dah(delay, flash_sounds); latest_triggered = 2; } else if (latest_triggered == 2) { play_dit(delay, flash_sounds); latest_triggered = 1; } } else if (DIT_IS_PRESSED || key_triggered & 1) { idle_cycle_counter = 0; play_dit(delay, flash_sounds); latest_triggered = 1; } else if (DAH_IS_PRESSED || key_triggered & 2) { idle_cycle_counter = 0; play_dah(delay, flash_sounds); latest_triggered = 2; } } } }