Components and supplies
Arduino UNO
Piezo sounder element
0.1 Inch Jumper
0.1 Inch pins
Deek Robot data logging shield v1.0
LCD Keypad shield
Project description
Code
txt to Morse stack version
c_cpp
1 2//.txt file to morse code 3//uses standard morse binary tree for translation of letters to dots and dashes, this requires the use of a stack 4//stack library source - https://playground.arduino.cc/Code/StackArray/ 5 6 7//CHECK IF LCD SHIELD USES DIGITAL PIN 10 AS THIS IS USED AS CS FOR DATA LOGGING SHIELD 8//CHECK TO SEE IF LCD IS ONE OF THE FAULTY ONES 9//MINE HAD BOTH PROBLEMS SO I DESOLDERED AND REMOVED THE FAULTY PIN 10 10//REFERENCE - https://forum.arduino.cc/index.php?topic=96747.0 (Retrieved 7/June/2019) 11 12/* 13 The circuit: 14 * SD card attached to SPI bus as follows: 15 ** MOSI - pin 11 16 ** MISO - pin 12 17 ** CLK - pin 13 18 ** CS - pin 10 19 * 20 * RTC connections (I2C) - Clock not used in this project: 21 * gnd to gnd 22 * + to +5V 23 * SDA to A4 24 * SCL to A5 25 * 26 * LCD shield: 27 * Backlight - pin 10 removed 28 * B4 to D4 29 * B5 to D5 30 * B6 to D6 31 * B7 to D7 32 * RS to D8 33 * E to D9 34 * 35 * LCD shield buttons 36 * Analog 0 37 * 38 * Buzzer 39 * GND to GND 40 * + to 3 41 * 42 * To change morse speed ground one of the following analog pins 43 * A1 = 5 wpm 44 * A2 = 13 wpm 45 * A2 = 20wpm 46 * default with no analog pins grounded 5 wpm 47*/ 48#include <SPI.h> 49#include <SD.h> 50 51const int chipSelect=10; 52 53//my_aliases 54//const int move_switch=2; 55//const int enter_switch=3; 56const int initialization_screen=1; 57const int file_menu_screen=2; 58const int file_wont_open_screen=3; 59const int file_selected_screen=4; 60const int display_character_screen=5; 61const int print_filename_screen=6; 62 63File root; 64File myFile; 65 66//for the LCD shield 67#include <LiquidCrystal.h> 68 69// select the pins used on the LCD panel 70LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 71// http://www.robotshop.com/content/PDF/dfrobot-lcd-keypad-shield-schematic.pdf 72 73//required to debounce switch 74#define MAX_CHECKS 10 75volatile uint8_t Debounced_State=0;//accessed by isr and main loop code 76uint8_t State[MAX_CHECKS]={0}; 77uint8_t Index=0; 78 79// include stack library header. 80#include <StackArray.h> 81 82// create a stack of characters. 83StackArray <int> stack; 84 85//my_global_variables 86int screen_number=initialization_screen; 87bool fail=false; 88bool file_exists=false; 89static String my_filename=""; 90char my_character=' '; 91 92 93const int sounder_pin=3;// the pin we attach our sounder to 94const int duty_cycle=128;//50% duty cycle for analogWrite() 95 96void setup() { 97 98//initial lcd setup 99 lcd.begin(16,2); // initialize the lcd for 16 chars 2 lines, turn on backlight 100 lcd.clear(); 101 102 103//try to initialize sd card 104 if (!SD.begin(chipSelect)) { 105 fail=true; 106 update_display(); 107 return; 108 } 109 fail=false; 110 update_display(); 111 delay(5000); 112 113 //set up sounder frequency 114 TCCR2B = (TCCR2B & 0b11111000) | 0x02; 115 116 // initialize digital pin as an output. 117 pinMode(sounder_pin, OUTPUT); 118 119 //for morse code speed selection 120 pinMode(A1,INPUT_PULLUP); 121 pinMode(A2,INPUT_PULLUP); 122 pinMode(A3,INPUT_PULLUP); 123 124 // initialize timer1 125 noInterrupts(); // disable all interrupts 126 TCCR1A = 0; 127 TCCR1B = 0; 128 TCNT1 = 0; 129 130 OCR1A = 625; // compare match register 16MHX/256/100HZ 131 TCCR1B |= (1 << WGM12); // CTC mode 132 TCCR1B |= (1 << CS12); // 256 prescaler 133 TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt 134 interrupts(); 135 136 137}/*end of setup*/ 138 139void loop() { 140 141 root = SD.open("/"); 142 File dir; 143 dir=root; 144 145//variables 146bool count_updated=true; 147bool enter_pressed=false; 148 149static bool button_up=false; 150static bool chord_ready=false; 151static int Stored_State=0; 152static int Action_Me=0; 153 154//all timing is based around the length of the dot 155//source for timings -> http://www.kent-engineers.com/codespeed.htm (retreived 5 March 2017) 156 157static unsigned long unit_length=240; 158if(!digitalRead(A1)){unit_length=240;}//dot time calculated for 5 words per minute 159if(!digitalRead(A2)){unit_length=92;}//dot time calculated for 13 words per minute 160if(!digitalRead(A3)){unit_length=60;}//dot time calculated for 20 words per minute 161 162 163while(1){ 164 165 noInterrupts(); // disable all interrupts 166if (Debounced_State>Stored_State){Stored_State=Debounced_State;chord_ready=true;}//store chord and flag as ready 167if (Debounced_State<Stored_State){if(chord_ready){button_up=true;Action_Me=Stored_State;}Stored_State=Debounced_State;} 168 interrupts(); // enable all interrupts 169 170if(button_up && chord_ready) 171{ 172chord_ready=false; 173button_up=false; 174 175if (Action_Me&1<<1){enter_pressed=true;}//select button 176 177if (Action_Me&1<<0){count_updated=true;}//down button 178 179} 180 181 if (count_updated){ 182 183 count_updated=false; 184 185 File entry = dir.openNextFile(); 186 if (! entry) { 187 // no more files -> so go back to beginning 188 entry.close(); 189 dir.rewindDirectory(); 190 my_filename="No more"; 191 update_display(); 192 }else 193 { 194 my_filename=entry.name(); 195 screen_number=file_menu_screen; 196 update_display(); 197 entry.close();} 198 } 199 200if (enter_pressed){ 201 202 enter_pressed=false; 203 204 screen_number=file_selected_screen; 205 206 if (SD.exists(my_filename)) {file_exists=true;}else{file_exists=false;} 207 208 update_display(); 209 210//read data from file 211 // re-open the file for reading: 212 myFile = SD.open(my_filename, FILE_READ); 213 if (myFile) { 214 215 // read from the file until there's nothing else in it: 216 217 while (myFile.available()) 218 { 219 my_character=myFile.read(); 220 screen_number=display_character_screen; 221 update_display(); 222 if (my_character>=65 && my_character<=90){my_character+=32;};//change letters to lower case if necessary 223 beep_character(my_character,unit_length); 224 } 225 226 // close the file: 227 myFile.close(); 228 } else { 229 // if the file didn't open, print an error: 230 screen_number=file_wont_open_screen; 231 update_display(); 232 } 233 234} 235 236} 237 238}/*end of loop*/ 239 240//my_functions 241 242ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine 243{ 244 245//read buttons 246 uint8_t temp=0x00; 247 248 if (analogRead(0) > 200 && analogRead(0) < 400){temp|=1<<0;} //btnDOWN 249 if (analogRead(0) > 600 && analogRead(0) < 800){temp|=1<<1;} //btnSELECT 250 251//debounce 252 uint8_t i,j; 253 State[Index]= temp; 254 ++Index; 255 j=0xFF; 256 for (i=0;i<MAX_CHECKS;i++){j=j&State[i];} 257 Debounced_State=j; 258 if(Index>=MAX_CHECKS){Index=0;} 259} 260 261 262//my_screen_functions 263void update_display(void) 264{ 265 switch (screen_number) 266 { 267 case initialization_screen:lcd.clear(); 268 lcd.setCursor(0,0); 269 lcd.print("Init SD..."); 270 if (!fail){lcd.print("done.");}else{lcd.print("fail!");} 271 break; 272 case file_menu_screen:lcd.clear(); 273 lcd.setCursor(2,0); 274 lcd.print("Select file"); 275 lcd.setCursor(2,1); 276 lcd.print(" ");//clear any previous filename (8.3 character format, so 12 spaces) 277 lcd.setCursor(2,1); 278 lcd.print(my_filename); 279 break; 280 281 case file_wont_open_screen:lcd.clear(); 282 lcd.print("Can't open file"); 283 break; 284 case file_selected_screen:lcd.clear(); 285 lcd.setCursor(0,0); 286 lcd.print("Opt->"); 287 lcd.print(my_filename); 288 lcd.setCursor(0,1); 289 if(!file_exists){lcd.print("No file.");} 290 break; 291 case display_character_screen:lcd.setCursor(7,1); 292 lcd.write(my_character); 293 break; 294 } 295 } 296 297 298//beep_character function to sound out morse 299//uses functions delay() and analogWrite() 300//all timing is based around the length of the dot 301//source for timings -> http://www.kent-engineers.com/codespeed.htm 302//examples 303//unit_length = 240, dot time calculated for 5 words per minute 304//unit_length = 92, dot time calculated for 13 words per minute 305//unit_length = 60, dot time calculated for 20 words per minute 306//uses stack library and hardware pwm 307 308void beep_character(char incoming_byte, unsigned long unit_length) 309{ 310 311//Morse tree, intended to be processed forward, that is + from index 0 312//we are going in reverse so need to use a stack to correctly sound out the dots & dashes 313const unsigned char characters_array[]={ 3140x00,' ',//positions 0 not used position 1 is used to return the space character 315'e','t',//2 316'i','a','n','m',//4 317's','u','r','w','d','k','g','o',//8 318'h','v','f',252,'l',228,'p','j','b','x','c','y','z','q',246,154,//16 319'5','4','S','3',233,0x00,208,'2',0x00,232,'+',0x00,254,224,'J','1','6','=','/',0x00,231,0x00,'(',0x00,'7',0x00,'G',241,'8',0x00,'9','0',//32 3200x00,0x00,0x00,0x00,//spaces used for alt, ctrl, fn, shift (index positions 64, 65, 66, 67)(we just check the index not what's placed there) 321'$',0x00,0x00,0x00,0x00,0x00,0x00,0x00,'?','_',0x00,0x00,0x00,0x00,'"',0x00,0x00,'.',0x00,0x00,0x00,0x00,'@',0x00,0x00,0x00,39,0x00,//64 part 1 3220x00,'-',0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,';','!',0x00,')',0x00,0x00,0x00,0x00,0x00,',',0x00,0x00,0x00,0x00,':',0x00,0x00,0x00,0x00,0x00,0x00,0x00,//64 part 2 323}; 324const int characters_array_limit=127;//128 characters so 0-127 325 326const unsigned long dot=unit_length; 327const unsigned long dash=unit_length*3; 328const unsigned long intra_letter_space=unit_length;//space between dot's and dashe's within letter 329const unsigned long inter_letter_space=(unit_length*3)-intra_letter_space;// space between letters, subtracts used to deal with spaces already there 330const unsigned long word_space=(unit_length*7)-intra_letter_space-inter_letter_space;// space between words, subtracts used to deal with spaces already there 331 332int i=0; 333 334if (incoming_byte==' '){delay(word_space);return;} 335 336 337//find character in array 338 339for(i=2;i<=characters_array_limit;i++) 340{ 341 if (incoming_byte==characters_array[i]){break;}//locate letter 342 } 343 344if (i<=characters_array_limit){ 345 346 //work backwards -> stopping at arrray position 1 347 //odd or even -> with binary number first bit always set if odd 348 //so bitwise & index with 1, if result = 1 then index is odd 349 //in both cases push index to stack 350 //then: 351 //if odd prior position = (current position -1)/2 352 //if even prior position = current position/2 353 //repeat until you reach the root of binary tree, that is position 1 354 355 while((i!=1)) 356 { 357 if (i&1){ //odd so dash 358 stack.push(i);i=(i-1)/2; } 359 else{ //even so dot 360 stack.push(i);i=i/2;} 361 } 362 } 363//pop each element from the stack and play dot or dash as appropriate 364 while(!stack.isEmpty ()) 365 { 366 if (stack.pop()&1){ //odd so dash 367 analogWrite(sounder_pin, duty_cycle); 368 delay(dash); // wait for a while 369 analogWrite(sounder_pin,0); 370 delay(intra_letter_space); // space between letter parts 371 i=(i-1)/2; } 372 else{ //even so dot 373 analogWrite(sounder_pin, duty_cycle); 374 delay(dot); // wait for a while 375 analogWrite(sounder_pin,0); 376 delay(intra_letter_space); // space between letter parts 377 i=i/2;} 378 } delay(inter_letter_space);//space between letters 379 } 380
txt to Morse direct translation version
c_cpp
1 2//.txt file to morse code 3//uses modified morse binary tree to allow direct translation of letters to dots and dashes 4 5//CHECK IF LCD SHIELD USES DIGITAL PIN 10 AS THIS IS USED AS CS FOR DATA LOGGING SHIELD 6//CHECK TO SEE IF LCD IS ONE OF THE FAULTY ONES 7//MINE HAD BOTH PROBLEMS SO I DESOLDERED AND REMOVED THE FAULTY PIN 10 8//REFERENCE - https://forum.arduino.cc/index.php?topic=96747.0 (Retrieved 7/June/2019) 9 10/* 11 The circuit: 12 * SD card attached to SPI bus as follows: 13 ** MOSI - pin 11 14 ** MISO - pin 12 15 ** CLK - pin 13 16 ** CS - pin 10 17 * 18 * RTC connections (I2C) - Clock not used in this project: 19 * gnd to gnd 20 * + to +5V 21 * SDA to A4 22 * SCL to A5 23 * 24 * LCD shield: 25 * Backlight - pin 10 removed 26 * B4 to D4 27 * B5 to D5 28 * B6 to D6 29 * B7 to D7 30 * RS to D8 31 * E to D9 32 * 33 * LCD shield buttons 34 * Analog 0 35 * 36 * Buzzer 37 * GND to GND 38 * + to 3 39 * 40 * To change morse speed ground one of the following analog pins 41 * A1 = 5 wpm 42 * A2 = 13 wpm 43 * A2 = 20wpm 44 * default with no analog pins grounded 5 wpm 45*/ 46#include <SPI.h> 47#include <SD.h> 48 49const int chipSelect=10; 50 51//my_aliases 52//const int move_switch=2; 53//const int enter_switch=3; 54const int initialization_screen=1; 55const int file_menu_screen=2; 56const int file_wont_open_screen=3; 57const int file_selected_screen=4; 58const int display_character_screen=5; 59const int print_filename_screen=6; 60 61File root; 62File myFile; 63 64//for the LCD shield 65#include <LiquidCrystal.h> 66 67// select the pins used on the LCD panel 68LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 69// http://www.robotshop.com/content/PDF/dfrobot-lcd-keypad-shield-schematic.pdf 70 71//required to debounce switch 72#define MAX_CHECKS 10 73volatile uint8_t Debounced_State=0;//accessed by isr and main loop code 74uint8_t State[MAX_CHECKS]={0}; 75uint8_t Index=0; 76 77 78 79//my_global_variables 80int screen_number=initialization_screen; 81bool fail=false; 82bool file_exists=false; 83static String my_filename=""; 84char my_character=' '; 85 86 87const int sounder_pin=3;// the pin we attach our sounder to 88const int duty_cycle=128;//50% duty cycle for analogWrite() 89 90void setup() { 91 92//initial lcd setup 93 lcd.begin(16,2); // initialize the lcd for 16 chars 2 lines, turn on backlight 94 lcd.clear(); 95 96 97//try to initialize sd card 98 if (!SD.begin(chipSelect)) { 99 fail=true; 100 update_display(); 101 return; 102 } 103 fail=false; 104 update_display(); 105 delay(5000); 106 107 //set up sounder frequency 108 TCCR2B = (TCCR2B & 0b11111000) | 0x02; 109 110 // initialize digital pin as an output. 111 pinMode(sounder_pin, OUTPUT); 112 113 //for morse code speed selection 114 pinMode(A1,INPUT_PULLUP); 115 pinMode(A2,INPUT_PULLUP); 116 pinMode(A3,INPUT_PULLUP); 117 118 // initialize timer1 119 noInterrupts(); // disable all interrupts 120 TCCR1A = 0; 121 TCCR1B = 0; 122 TCNT1 = 0; 123 124 OCR1A = 625; // compare match register 16MHX/256/100HZ 125 TCCR1B |= (1 << WGM12); // CTC mode 126 TCCR1B |= (1 << CS12); // 256 prescaler 127 TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt 128 interrupts(); 129 130 131}/*end of setup*/ 132 133void loop() { 134 135 root = SD.open("/"); 136 File dir; 137 dir=root; 138 139//variables 140bool count_updated=true; 141bool enter_pressed=false; 142 143static bool button_up=false; 144static bool chord_ready=false; 145static int Stored_State=0; 146static int Action_Me=0; 147 148//all timing is based around the length of the dot 149//source for timings -> http://www.kent-engineers.com/codespeed.htm (retreived 5 March 2017) 150 151static unsigned long unit_length=240; 152if(!digitalRead(A1)){unit_length=240;}//dot time calculated for 5 words per minute 153if(!digitalRead(A2)){unit_length=92;}//dot time calculated for 13 words per minute 154if(!digitalRead(A3)){unit_length=60;}//dot time calculated for 20 words per minute 155 156 157while(1){ 158 159 noInterrupts(); // disable all interrupts 160if (Debounced_State>Stored_State){Stored_State=Debounced_State;chord_ready=true;}//store chord and flag as ready 161if (Debounced_State<Stored_State){if(chord_ready){button_up=true;Action_Me=Stored_State;}Stored_State=Debounced_State;} 162 interrupts(); // enable all interrupts 163 164if(button_up && chord_ready) 165{ 166chord_ready=false; 167button_up=false; 168 169if (Action_Me&1<<1){enter_pressed=true;}//select button 170 171if (Action_Me&1<<0){count_updated=true;}//down button 172 173} 174 175 if (count_updated){ 176 177 count_updated=false; 178 179 File entry = dir.openNextFile(); 180 if (! entry) { 181 // no more files -> so go back to beginning 182 entry.close(); 183 dir.rewindDirectory(); 184 my_filename="No more"; 185 update_display(); 186 }else 187 { 188 my_filename=entry.name(); 189 screen_number=file_menu_screen; 190 update_display(); 191 entry.close();} 192 } 193 194if (enter_pressed){ 195 196 enter_pressed=false; 197 198 screen_number=file_selected_screen; 199 200 if (SD.exists(my_filename)) {file_exists=true;}else{file_exists=false;} 201 202 update_display(); 203 204//read data from file 205 // re-open the file for reading: 206 myFile = SD.open(my_filename, FILE_READ); 207 if (myFile) { 208 209 // read from the file until there's nothing else in it: 210 211 while (myFile.available()) 212 { 213 my_character=myFile.read(); 214 screen_number=display_character_screen; 215 update_display(); 216 if (my_character>=65 && my_character<=90){my_character+=32;};//change letters to lower case if necessary 217 beep_character(my_character,unit_length); 218 } 219 220 // close the file: 221 myFile.close(); 222 } else { 223 // if the file didn't open, print an error: 224 screen_number=file_wont_open_screen; 225 update_display(); 226 } 227 228} 229 230} 231 232}/*end of loop*/ 233 234//my_functions 235 236ISR(TIMER1_COMPA_vect) // timer compare interrupt service routine 237{ 238 239//read buttons 240 uint8_t temp=0x00; 241 242 if (analogRead(0) > 200 && analogRead(0) < 400){temp|=1<<0;} //btnDOWN 243 if (analogRead(0) > 600 && analogRead(0) < 800){temp|=1<<1;} //btnSELECT 244 245//debounce 246 uint8_t i,j; 247 State[Index]= temp; 248 ++Index; 249 j=0xFF; 250 for (i=0;i<MAX_CHECKS;i++){j=j&State[i];} 251 Debounced_State=j; 252 if(Index>=MAX_CHECKS){Index=0;} 253} 254 255 256//my_screen_functions 257void update_display(void) 258{ 259 switch (screen_number) 260 { 261 case initialization_screen:lcd.clear(); 262 lcd.setCursor(0,0); 263 lcd.print("Init SD..."); 264 if (!fail){lcd.print("done.");}else{lcd.print("fail!");} 265 break; 266 case file_menu_screen:lcd.clear(); 267 lcd.setCursor(2,0); 268 lcd.print("Select file"); 269 lcd.setCursor(2,1); 270 lcd.print(" ");//clear any previous filename (8.3 character format, so 12 spaces) 271 lcd.setCursor(2,1); 272 lcd.print(my_filename); 273 break; 274 275 case file_wont_open_screen:lcd.clear(); 276 lcd.print("Can't open file"); 277 break; 278 case file_selected_screen:lcd.clear(); 279 lcd.setCursor(0,0); 280 lcd.print("Opt->"); 281 lcd.print(my_filename); 282 lcd.setCursor(0,1); 283 if(!file_exists){lcd.print("No file.");} 284 break; 285 case display_character_screen:lcd.setCursor(7,1); 286 lcd.write(my_character); 287 break; 288 } 289 } 290 291//beep_character function to sound out morse 292//uses functions delay() and analogWrite() 293//all timing is based around the length of the dot 294//source for timings -> http://www.kent-engineers.com/codespeed.htm 295//examples 296//unit_length = 240, dot time calculated for 5 words per minute 297//unit_length = 92, dot time calculated for 13 words per minute 298//unit_length = 60, dot time calculated for 20 words per minute 299 300void beep_character(char incoming_byte, unsigned long unit_length) 301{ 302 303//here the standard morse dichotic tree has been modified to allow direct translation from character look up to dots and dashes 304//by rearranging the tree we eliminate the need to use a stack 305const char reverse_morse_array[]={ 3060x00,0x00,//positions 0 and 1 not used 307101,116,//2 308105,110,97,109,//4 309115,100,114,103,117,107,119,111,//8 310104,98,108,122,102,99,112,246,118,120,228,113,252,121,106,154,//16 31153,54,0x00,55,233,231,254,56,83,47,43,71,208,72,74,57,52,61,232,0x00,13,0x00,224,0x00,51,0x00,71,241,50,0x00,49,48,//32 3120x00,0x00,0x00,0x00,0x00,0x00,0x00,58,0x00,0x00,0x00,0x00,63,95,0x00,0x00,0x00,0x00,34,0x00,0x00,59,64,0x00,0x00,0x00,64,0x00,0x00,40,39, 3130x00,0x00,45,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,46,33,95,0x00,40,0x00,0x00,0x00,0x00,44,0x00,33,0x00,0x00,58,0x00,0x00,0x00,0x00,0x00,0x00,0x00 314}; 315const int reverse_morse_array_limit=127;//128 elements total so 0 to 127 316 317const unsigned long dot=unit_length; 318const unsigned long dash=unit_length*3; 319const unsigned long intra_letter_space=unit_length;//space between dot's and dashe's within letter 320const unsigned long inter_letter_space=(unit_length*3)-intra_letter_space;// space between letters, subtracts used to deal with spaces already there 321const unsigned long word_space=(unit_length*7)-intra_letter_space-inter_letter_space;// space between words, subtracts used to deal with spaces already there 322 323int i=0; 324 325if (incoming_byte==' '){delay(word_space);return;} 326 327 328//find character in array 329 330for(i=2;i<=reverse_morse_array_limit;i++) 331{ 332 if (incoming_byte==reverse_morse_array[i]){break;}//locate letter 333 } 334 335if (i<=reverse_morse_array_limit){ 336 337 //work backwards -> stopping at arrray position 1 338 //odd or even -> with binary number first bit always set if odd 339 //so bitwise & index with 1, if result = 1 then index is odd 340 //if odd prior position = (current position -1)/2 341 //if even prior position = current position/2 342 //sound dash for odd, dot for even 343 //repeat until you reach the root of binary tree, that is position 1 344 345 while((i!=1)) 346 { 347 if (i&1){ //odd so dash 348 analogWrite(sounder_pin, duty_cycle); 349 delay(dash); // wait for a while 350 analogWrite(sounder_pin,0); 351 delay(intra_letter_space); // space between letter parts 352 i=(i-1)/2; } 353 else{ //even so dot 354 analogWrite(sounder_pin, duty_cycle); 355 delay(dot); // wait for a while 356 analogWrite(sounder_pin,0); 357 delay(intra_letter_space); // space between letter parts 358 i=i/2;} 359 } delay(inter_letter_space);//space between letters 360 } 361 362 } 363
txt to Morse stack version
c_cpp
1 2//.txt file to morse code 3//uses standard morse binary tree for 4 translation of letters to dots and dashes, this requires the use of a stack 5//stack 6 library source - https://playground.arduino.cc/Code/StackArray/ 7 8 9//CHECK 10 IF LCD SHIELD USES DIGITAL PIN 10 AS THIS IS USED AS CS FOR DATA LOGGING SHIELD 11//CHECK 12 TO SEE IF LCD IS ONE OF THE FAULTY ONES 13//MINE HAD BOTH PROBLEMS SO I DESOLDERED 14 AND REMOVED THE FAULTY PIN 10 15//REFERENCE - https://forum.arduino.cc/index.php?topic=96747.0 16 (Retrieved 7/June/2019) 17 18/* 19 The circuit: 20 * SD card attached to SPI 21 bus as follows: 22 ** MOSI - pin 11 23 ** MISO - pin 12 24 ** CLK - pin 13 25 26 ** CS - pin 10 27 * 28 * RTC connections (I2C) - Clock not used in this project: 29 30 * gnd to gnd 31 * + to +5V 32 * SDA to A4 33 * SCL to A5 34 * 35 * LCD shield: 36 37 * Backlight - pin 10 removed 38 * B4 to D4 39 * B5 to D5 40 * B6 to D6 41 * 42 B7 to D7 43 * RS to D8 44 * E to D9 45 * 46 * LCD shield buttons 47 * Analog 48 0 49 * 50 * Buzzer 51 * GND to GND 52 * + to 3 53 * 54 * To change morse 55 speed ground one of the following analog pins 56 * A1 = 5 wpm 57 * A2 = 13 wpm 58 59 * A2 = 20wpm 60 * default with no analog pins grounded 5 wpm 61*/ 62#include 63 <SPI.h> 64#include <SD.h> 65 66const int chipSelect=10; 67 68//my_aliases 69//const 70 int move_switch=2; 71//const int enter_switch=3; 72const int initialization_screen=1; 73const 74 int file_menu_screen=2; 75const int file_wont_open_screen=3; 76const int file_selected_screen=4; 77const 78 int display_character_screen=5; 79const int print_filename_screen=6; 80 81File 82 root; 83File myFile; 84 85//for the LCD shield 86#include <LiquidCrystal.h> 87 88// 89 select the pins used on the LCD panel 90LiquidCrystal lcd(8, 9, 4, 5, 6, 7); 91// 92 http://www.robotshop.com/content/PDF/dfrobot-lcd-keypad-shield-schematic.pdf 93 94//required 95 to debounce switch 96#define MAX_CHECKS 10 97volatile uint8_t Debounced_State=0;//accessed 98 by isr and main loop code 99uint8_t State[MAX_CHECKS]={0}; 100uint8_t Index=0; 101 102// 103 include stack library header. 104#include <StackArray.h> 105 106// create a stack 107 of characters. 108StackArray <int> stack; 109 110//my_global_variables 111int screen_number=initialization_screen; 112bool 113 fail=false; 114bool file_exists=false; 115static String my_filename=""; 116char 117 my_character=' '; 118 119 120const int sounder_pin=3;// the pin we attach our 121 sounder to 122const int duty_cycle=128;//50% duty cycle for analogWrite() 123 124void 125 setup() { 126 127//initial lcd setup 128 lcd.begin(16,2); // initialize the lcd 129 for 16 chars 2 lines, turn on backlight 130 lcd.clear(); 131 132 133//try to 134 initialize sd card 135 if (!SD.begin(chipSelect)) { 136 fail=true; 137 update_display(); 138 139 return; 140 } 141 fail=false; 142 update_display(); 143 delay(5000); 144 145 146 //set up sounder frequency 147 TCCR2B = (TCCR2B & 0b11111000) | 0x02; 148 149 150 // initialize digital pin as an output. 151 pinMode(sounder_pin, OUTPUT); 152 153 154 //for morse code speed selection 155 pinMode(A1,INPUT_PULLUP); 156 pinMode(A2,INPUT_PULLUP); 157 158 pinMode(A3,INPUT_PULLUP); 159 160 // initialize timer1 161 noInterrupts(); 162 // disable all interrupts 163 TCCR1A = 0; 164 TCCR1B = 0; 165 TCNT1 166 = 0; 167 168 OCR1A = 625; // compare match register 16MHX/256/100HZ 169 170 TCCR1B |= (1 << WGM12); // CTC mode 171 TCCR1B |= (1 << CS12); // 256 172 prescaler 173 TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt 174 175 interrupts(); 176 177 178}/*end of setup*/ 179 180void loop() { 181 182 root 183 = SD.open("/"); 184 File dir; 185 dir=root; 186 187//variables 188bool count_updated=true; 189bool 190 enter_pressed=false; 191 192static bool button_up=false; 193static bool chord_ready=false; 194static 195 int Stored_State=0; 196static int Action_Me=0; 197 198//all timing is based around 199 the length of the dot 200//source for timings -> http://www.kent-engineers.com/codespeed.htm 201 (retreived 5 March 2017) 202 203static unsigned long unit_length=240; 204if(!digitalRead(A1)){unit_length=240;}//dot 205 time calculated for 5 words per minute 206if(!digitalRead(A2)){unit_length=92;}//dot 207 time calculated for 13 words per minute 208if(!digitalRead(A3)){unit_length=60;}//dot 209 time calculated for 20 words per minute 210 211 212while(1){ 213 214 noInterrupts(); 215 // disable all interrupts 216if (Debounced_State>Stored_State){Stored_State=Debounced_State;chord_ready=true;}//store 217 chord and flag as ready 218if (Debounced_State<Stored_State){if(chord_ready){button_up=true;Action_Me=Stored_State;}Stored_State=Debounced_State;} 219 220 interrupts(); // enable all interrupts 221 222if(button_up && 223 chord_ready) 224{ 225chord_ready=false; 226button_up=false; 227 228if (Action_Me&1<<1){enter_pressed=true;}//select 229 button 230 231if (Action_Me&1<<0){count_updated=true;}//down 232 button 233 234} 235 236 if (count_updated){ 237 238 count_updated=false; 239 240 241 File entry = dir.openNextFile(); 242 if (! entry) { 243 // no more files 244 -> so go back to beginning 245 entry.close(); 246 dir.rewindDirectory(); 247 248 my_filename="No more"; 249 update_display(); 250 }else 251 { 252 253 my_filename=entry.name(); 254 screen_number=file_menu_screen; 255 update_display(); 256 257 entry.close();} 258 } 259 260if (enter_pressed){ 261 262 263 enter_pressed=false; 264 265 screen_number=file_selected_screen; 266 267 268 if (SD.exists(my_filename)) {file_exists=true;}else{file_exists=false;} 269 270 271 update_display(); 272 273//read data from file 274 // re-open the file for reading: 275 276 myFile = SD.open(my_filename, FILE_READ); 277 if (myFile) { 278 279 // read 280 from the file until there's nothing else in it: 281 282 while (myFile.available()) 283 284 { 285 my_character=myFile.read(); 286 screen_number=display_character_screen; 287 288 update_display(); 289 if (my_character>=65 && my_character<=90){my_character+=32;};//change 290 letters to lower case if necessary 291 beep_character(my_character,unit_length); 292 293 } 294 295 // close the file: 296 myFile.close(); 297 } else { 298 // 299 if the file didn't open, print an error: 300 screen_number=file_wont_open_screen; 301 302 update_display(); 303 } 304 305} 306 307} 308 309}/*end 310 of loop*/ 311 312//my_functions 313 314ISR(TIMER1_COMPA_vect) // timer 315 compare interrupt service routine 316{ 317 318//read buttons 319 uint8_t temp=0x00; 320 321 322 if (analogRead(0) > 200 && analogRead(0) < 400){temp|=1<<0;} //btnDOWN 323 324 if (analogRead(0) > 600 && analogRead(0) < 800){temp|=1<<1;} //btnSELECT 325 326//debounce 327 328 uint8_t i,j; 329 State[Index]= temp; 330 ++Index; 331 j=0xFF; 332 for (i=0;i<MAX_CHECKS;i++){j=j&State[i];} 333 334 Debounced_State=j; 335 if(Index>=MAX_CHECKS){Index=0;} 336} 337 338 339//my_screen_functions 340void 341 update_display(void) 342{ 343 switch (screen_number) 344 { 345 case initialization_screen:lcd.clear(); 346 347 lcd.setCursor(0,0); 348 lcd.print("Init 349 SD..."); 350 if (!fail){lcd.print("done.");}else{lcd.print("fail!");} 351 352 break; 353 case file_menu_screen:lcd.clear(); 354 355 lcd.setCursor(2,0); 356 lcd.print("Select 357 file"); 358 lcd.setCursor(2,1); 359 lcd.print(" 360 ");//clear any previous filename (8.3 character format, so 12 spaces) 361 362 lcd.setCursor(2,1); 363 lcd.print(my_filename); 364 365 break; 366 367 case file_wont_open_screen:lcd.clear(); 368 369 lcd.print("Can't open file"); 370 break; 371 372 case file_selected_screen:lcd.clear(); 373 lcd.setCursor(0,0); 374 375 lcd.print("Opt->"); 376 lcd.print(my_filename); 377 378 lcd.setCursor(0,1); 379 if(!file_exists){lcd.print("No 380 file.");} 381 break; 382 case display_character_screen:lcd.setCursor(7,1); 383 384 lcd.write(my_character); 385 break; 386 387 } 388 } 389 390 391//beep_character function to sound 392 out morse 393//uses functions delay() and analogWrite() 394//all timing is based 395 around the length of the dot 396//source for timings -> http://www.kent-engineers.com/codespeed.htm 397 398//examples 399//unit_length = 240, dot time calculated for 5 words per minute 400//unit_length 401 = 92, dot time calculated for 13 words per minute 402//unit_length = 60, dot time 403 calculated for 20 words per minute 404//uses stack library and hardware pwm 405 406void 407 beep_character(char incoming_byte, unsigned long unit_length) 408{ 409 410//Morse 411 tree, intended to be processed forward, that is + from index 0 412//we are going 413 in reverse so need to use a stack to correctly sound out the dots & dashes 414const 415 unsigned char characters_array[]={ 4160x00,' ',//positions 0 not used position 1 417 is used to return the space character 418'e','t',//2 419'i','a','n','m',//4 420's','u','r','w','d','k','g','o',//8 421'h','v','f',252,'l',228,'p','j','b','x','c','y','z','q',246,154,//16 422'5','4','S','3',233,0x00,208,'2',0x00,232,'+',0x00,254,224,'J','1','6','=','/',0x00,231,0x00,'(',0x00,'7',0x00,'G',241,'8',0x00,'9','0',//32 4230x00,0x00,0x00,0x00,//spaces 424 used for alt, ctrl, fn, shift (index positions 64, 65, 66, 67)(we just check the 425 index not what's placed there) 426'$',0x00,0x00,0x00,0x00,0x00,0x00,0x00,'?','_',0x00,0x00,0x00,0x00,'"',0x00,0x00,'.',0x00,0x00,0x00,0x00,'@',0x00,0x00,0x00,39,0x00,//64 427 part 1 4280x00,'-',0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,';','!',0x00,')',0x00,0x00,0x00,0x00,0x00,',',0x00,0x00,0x00,0x00,':',0x00,0x00,0x00,0x00,0x00,0x00,0x00,//64 429 part 2 430}; 431const int characters_array_limit=127;//128 characters so 0-127 432 433const 434 unsigned long dot=unit_length; 435const unsigned long dash=unit_length*3; 436const 437 unsigned long intra_letter_space=unit_length;//space between dot's and dashe's within 438 letter 439const unsigned long inter_letter_space=(unit_length*3)-intra_letter_space;// 440 space between letters, subtracts used to deal with spaces already there 441const 442 unsigned long word_space=(unit_length*7)-intra_letter_space-inter_letter_space;// 443 space between words, subtracts used to deal with spaces already there 444 445int 446 i=0; 447 448if (incoming_byte==' '){delay(word_space);return;} 449 450 451//find 452 character in array 453 454for(i=2;i<=characters_array_limit;i++) 455{ 456 if 457 (incoming_byte==characters_array[i]){break;}//locate letter 458 } 459 460if (i<=characters_array_limit){ 461 462 463 //work backwards -> stopping at arrray position 1 464 //odd or even -> 465 with binary number first bit always set if odd 466 //so bitwise & index with 1, 467 if result = 1 then index is odd 468 //in both cases push index to stack 469 //then: 470 471 //if odd prior position = (current position -1)/2 472 //if even prior position 473 = current position/2 474 //repeat until you reach the root of binary tree, 475 that is position 1 476 477 while((i!=1)) 478 { 479 if (i&1){ 480 //odd so dash 481 stack.push(i);i=(i-1)/2; } 482 else{ 483 //even so dot 484 stack.push(i);i=i/2;} 485 } 486 487 } 488//pop 489 each element from the stack and play dot or dash as appropriate 490 while(!stack.isEmpty 491 ()) 492 { 493 if (stack.pop()&1){ //odd so dash 494 analogWrite(sounder_pin, 495 duty_cycle); 496 delay(dash); // wait 497 for a while 498 analogWrite(sounder_pin,0); 499 delay(intra_letter_space); 500 // space between letter parts 501 i=(i-1)/2; } 502 503 else{ //even so dot 504 analogWrite(sounder_pin, 505 duty_cycle); 506 delay(dot); // wait 507 for a while 508 analogWrite(sounder_pin,0); 509 delay(intra_letter_space); 510 // space between letter parts 511 i=i/2;} 512 } 513 delay(inter_letter_space);//space between letters 514 515 } 516
Downloadable files
How everything stacks together
How everything stacks together
Buzzer underside view with wiring annotations
Buzzer underside view with wiring annotations
Piezo sounder connections
Piezo sounder connections
Buzzer top view
Buzzer top view
Buzzer top view
Buzzer top view
How everything stacks together
How everything stacks together
Buzzer underside view with wiring annotations
Buzzer underside view with wiring annotations
Piezo sounder connections
Piezo sounder connections
Comments
Only logged in users can leave comments
glennedi
0 Followers
•0 Projects
Table of contents
Intro
3
0