Description
On this section we are going to configure a system in which a buzz is going to be activated by a remote button. For achieve this purpose we are going to establish communication between two Xbee modules connected to different Arduino.
We are also showing how we implemented a system in which a melody is displayed in one side according to the number of times a button is pressed on the other side.
Our invented project (explained in more detail in Part 2) consists in a melody player using two Arduinos and two XBee's (along with XBee Explorer to connect to the breadboard). In one of the nodes we will select the melody to be played and in the second one the order will be received and the melody will be played; moreover some information will be return from the player to the order node.
The items we are going to need for this build are [1]:
- Hookup wires.
- Two Arduino boards.
- USB A-to-B cable for the Arduinos.
- Two 10K resistors.
- Two momentary switch or push button for input.
- One buzzer for output.
- One XBee radio configured as ZigBee Coordinator AT.
- One XBee radio configured as ZigBee Router AT.
- Two breakout boards.
- USB cable for the XBee breakout board.
- Two LEDs (one green and one yellow).
- 1 display.
The software used to complete the task is the same as in the previous practice exercise:
Remember you can download the code from the code section.
Part 1: Wireless Doorbell
First of
all we must build the circuits shown on the WSN Course Guide (Section
8) in order to configure the Switch and the Buzz of the system, i.e.,
the button and the buzz. Here are the images of how the cuircuits
must be done:
Figure 1: Button circuit building [2] |
Figure 2: Buzz circuit building [3] |
Besides the
building of the circuit, we must configure the Xbee modules as we saw
on the previous practice, obtaining a Coordinator AT and a Router AT.
On the
figures shown below we can see pictures of both circuits. Note that
we have added two LEDs in the button circuit: one is used for checking that current passes
through the button and the other is used to check that buzz is
buzzing.
Now we show the code that we have used in order to implement the button and the buzz functionalities. This modified code is attached to the code section of this blog.
Button Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | int BUTTON = 2; int ACK_led = 12; void setup() { pinMode(BUTTON, INPUT); // initialize the digital pin as an output. pinMode(ACK_led, OUTPUT); Serial.begin(9600); } void loop(){ // SEND // send a capital D over the serial port if the button is pressed if(digitalRead(BUTTON)==HIGH) { Serial.print("D"); delay(300); // prevents overwhelming the serial port } // RECEIVE if (Serial.available()>0) { if (Serial.read() == 'A') { digitalWrite(ACK_led, HIGH); delay(500); } digitalWrite(ACK_led, LOW); } } |
Buzzer Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // Pin where the buzzer is connected const int BELL = 5; void setup() { pinMode(BELL, OUTPUT); Serial.begin(9600); } void loop() { if (Serial.available() > 0) { if (Serial.read() == 'D') { // Send 'A' to tell the other node that // it is playing a beep in the buzzer Serial.print("A"); analogWrite(BELL, 240); delay(10); analogWrite(BELL, 0); } } } |
Part 2: Melody Player
In this extra part, as explained before, we have two nodes:
- One node has an array of melodies that it can play thanks to a buzzer. It receives the melody identifier to play through an XBee connected to the Arduino. Moreover, it sends back the title of the song (it includes an end identifier which in our case is '\n') being played through the XBee too (in case the song does not exist, an error message is sent). When the song has been totally played, an end character is sent.
- One node forms sums one to a variable each time a button is pressed; once we have pushed as many times as we wanted, we push another button to send the number through the XBee to the first node described. Then, it receives the title of the song played or an error message if the song did not exist. This title (or error) will be displayed through an LCD screen along with the previously sent number until the character indicating the end of the song is received; in that case, a message that tells the songs has ended is displayed.
Some difficulties appeared during the elaboration of the lab. One of the main problems we faced was the fact that the character that tells the song has ended was never being displayed individually; by doing some experiments we observed that this character was only sent if some other characters were sent previously through the serial connection. Therefore, as we did not find a good solution, we modified the code of Brett Hagman such that each time the value of the pointer *p is different from '\0' we send a blank through the serial and when it is '\0' we send the desired character indicating the end of song. It is a quite dirty way to do it but any more way to do it was found.
What we also notice is that we tried to incorporate all songs Brett Hagman had on his code in the array of songs we introduced. However, some problems were experimented: songs were reproduced in kind of random way (even some songs seemed to have two id's). But after reducing the number of songs to 7 from 15, everything worked fine. Therefore, we think it was something related to memory issues, which is very important to take into account.
Another problem we faced was the following: from the buzzer we send a string with the song name. Therefore, we thought that in the button part we could read a string as well using readString() but it did not work well since there was a lot of delay (or even it was not printed). Consequently, we tried to read char by char and putting them into a string; as a result, the performance was much better this time.
Another problem we faced was the following: from the buzzer we send a string with the song name. Therefore, we thought that in the button part we could read a string as well using readString() but it did not work well since there was a lot of delay (or even it was not printed). Consequently, we tried to read char by char and putting them into a string; as a result, the performance was much better this time.
Circuit schemas
Figure 5: Button circuit |
Figure 6: Buzzer circuit |
Images
Figure 7: Global setup |
Figure 8: Button part setup |
Figure 9: Buzzer part setup |
Video
Button Part Code
1 2 3 4 5 6 7 8 9 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | /** * Button Part * While a song is not being played, the user can * introduce a song number using a button and sending * it to the buzzer part. Once it receives the song * name from the buzzer part, it prints it in the lcd * screen along with its songId until it has been * completely playedd. */ #include <LiquidCrystal_I2C.h> #include <Wire.h> // Set the LCD address to 0x27 for a 20 chars 4 line display // Set the pins on the I2C chip used for LCD connections: // addr, en,rw,rs,d4,d5,d6,d7,bl,blpol // Set the LCD I2C address LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); /** * Constants */ // Button that increases the songIndex const int BUTTON = 2; // Button that sends the song to the other XBee const int ENTER = 3; // Char used to indicate the end of the song const char END_CHAR = 'E'; // String used to indicate the song id is not in the allowed range const String SONG_NOT_FOUND = "Song not found"; // Char used to indicate that the song title has been completely sent const char END_SONG_TITLE_CHAR = '\n'; /** * Global variables */ int songIndex = 0; String songName = ""; boolean isPlayingSong = false; /** * Helper functions */ // Prints information of a song in LCD screen // First line: songIndex - Second line: songName void printCurrentInformation() { lcd.clear(); lcd.setCursor(0, 0); lcd.print(songIndex); lcd.setCursor(0, 1); lcd.print(songName); } /** * Main functions */ void setup() { pinMode(BUTTON, INPUT); pinMode(ENTER, INPUT); lcd.begin(16,2); lcd.backlight(); printCurrentInformation(); Serial.begin(9600); } void loop() { // If a song is being played, not allow to // modify or send songIndex if (!isPlayingSong) { // Increments the songIndex very time is pressed if (digitalRead(BUTTON) == HIGH) { ++songIndex; printCurrentInformation(); delay(300); } // Sends the current selected song if (digitalRead(ENTER) == HIGH) { Serial.print(songIndex); delay(300); } } // If we are receiving sth through the serial connection... if (Serial.available() > 0) { // It would be easier if we did 'readString()' but // some delay problems appeared. It is much faster // it we receive the song name char by char char c = Serial.read(); if (isPlayingSong) { // Reset the lcd screen showing that the song has ended if (c == END_CHAR) { songIndex = 0; songName = "End of the song"; isPlayingSong = false; printCurrentInformation(); songName = ""; // restore song name } } else { // Is the symbol that tells that the songName has been // completely introduced if (c == END_SONG_TITLE_CHAR) { // If the songIndex did not exist, if (songName == SONG_NOT_FOUND) { songIndex = 0; printCurrentInformation(); songName = ""; } else { isPlayingSong = true; printCurrentInformation(); } } else { // Keep forming the name of the song songName += c; } } } } |
Buzzer Part Code
1 2 3 4 5 6 7 8 9 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 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | /** * Buzzer Part * Plays the specified melody by the sender and * sends a text to the other node specyfing which * song is being played. * * Based on Brett Hagman code (www.roguerobotics.com) * http://code.google.com/p/rogue-code/wiki/ToneLibraryDocumentation */ /** * Macros defining the notes features (from Brett Hagman code) */ #define OCTAVE_OFFSET 0 #define TONE_PIN 5 #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 /** * Constants */ // Notes array (from Brett Hagman code) const int notes[] = { 0, NOTE_C4, NOTE_CS4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_GS4, NOTE_A4, NOTE_AS4, NOTE_B4, NOTE_C5, NOTE_CS5, NOTE_D5, NOTE_DS5, NOTE_E5, NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_AS5, NOTE_B5, NOTE_C6, NOTE_CS6, NOTE_D6, NOTE_DS6, NOTE_E6, NOTE_F6, NOTE_FS6, NOTE_G6, NOTE_GS6, NOTE_A6, NOTE_AS6, NOTE_B6, NOTE_C7, NOTE_CS7, NOTE_D7, NOTE_DS7, NOTE_E7, NOTE_F7, NOTE_FS7, NOTE_G7, NOTE_GS7, NOTE_A7, NOTE_AS7, NOTE_B7 }; // Number of songs that can be played const int N_SONGS = 7; // Char used to indicate the end of the song const char END_CHAR = 'E'; // String used to indicate the song id is not in the allowed range const String SONG_NOT_FOUND = "Song not found"; // Char used to indicate that the song title has been completely sent const char END_SONG_TITLE_CHAR = '\n'; /** * Global variables */ // Array of songs to select them easily char* songArray[N_SONGS]; /** * Helper functions */ // Tells if a char is representing a digit or not boolean isDigit(char c) { return (c >= '0' && c <= '9'); } // Play the specified song (from Brett Hagman code) void play_rtttl(char *p) { // Absolutely no error checking in here byte default_dur = 4; byte default_oct = 6; int bpm = 63; int num; long wholenote; long duration; byte note; byte scale; // format: d=N,o=N,b=NNN: // find the start (skip name, etc) while(*p != ':') p++; // ignore name p++; // skip ':' // get default duration if(*p == 'd') { p++; p++; // skip "d=" num = 0; while(isDigit(*p)) { num = (num * 10) + (*p++ - '0'); } if(num > 0) default_dur = num; p++; // skip comma } //Serial.print("ddur: "); Serial.println(default_dur, 10); // get default octave if(*p == 'o') { p++; p++; // skip "o=" num = *p++ - '0'; if(num >= 3 && num <=7) default_oct = num; p++; // skip comma } //Serial.print("doct: "); Serial.println(default_oct, 10); // get BPM if(*p == 'b') { p++; p++; // skip "b=" num = 0; while(isDigit(*p)) { num = (num * 10) + (*p++ - '0'); } bpm = num; p++; // skip colon } //Serial.print("bpm: "); Serial.println(bpm, 10); // BPM usually expresses the number of quarter notes per minute wholenote = (60 * 1000L / bpm) * 4; // this is the time for whole note (in milliseconds) //Serial.print("wn: "); Serial.println(wholenote, 10); // now begin note loop while(*p) { // first, get note duration, if available num = 0; while(isDigit(*p)) { num = (num * 10) + (*p++ - '0'); } if(num) duration = wholenote / num; else duration = wholenote / default_dur; // we will need to check if we are a dotted note after // now get the note note = 0; switch(*p) { case 'c': note = 1; break; case 'd': note = 3; break; case 'e': note = 5; break; case 'f': note = 6; break; case 'g': note = 8; break; case 'a': note = 10; break; case 'b': note = 12; break; case 'p': default: note = 0; } p++; // now, get optional '#' sharp if(*p == '#') { note++; p++; } // now, get optional '.' dotted note if(*p == '.') { duration += duration/2; p++; } // now, get scale if(isDigit(*p)) { scale = *p - '0'; p++; } else { scale = default_oct; } scale += OCTAVE_OFFSET; if (*p == ',') p++; // skip comma for next note (or we may be at the end) // From line 248 to 256 // Added to code so end of the song signal is transmitted // It is a very dirty way to send the END_CHAR but it was // impossible to make it work by sending just the END_CHAR char songPlaying = *p; if (songPlaying) { Serial.print(" "); } else { Serial.print(END_CHAR); } // now play the note if(note) { //Serial.print("Playing: "); //Serial.print(scale, 10); Serial.print(' '); //Serial.print(note, 10); Serial.print(" ("); //Serial.print(notes[(scale - 4) * 12 + note], 10); //Serial.print(") "); //Serial.println(duration, 10); tone(TONE_PIN, notes[(scale - 4) * 12 + note]); delay(duration); noTone(TONE_PIN); } else { //Serial.print("Pausing: "); //Serial.println(duration, 10); delay(duration); } } } // Returns the name of a song given an input of the type // "Name Song : Notes Features" String getSongName(char* p) { String songName = ""; while (*p != ':') { songName += *p; ++p; } return songName; } /** * Main functions */ void setup(void) { Serial.begin(9600); // Put all desired songs in the array of songs songArray[0] = "The Simpsons:d=4,o=5,b=160:c.6,e6,f#6,8a6,g.6,e6,c6,8a,8f#,8f#,8f#,2g,8p,8p,8f#,8f#,8f#,8g,a#.,8c6,8c6,8c6,c6"; songArray[1] = "Indiana:d=4,o=5,b=250:e,8p,8f,8g,8p,1c6,8p.,d,8p,8e,1f,p.,g,8p,8a,8b,8p,1f6,p,a,8p,8b,2c6,2d6,2e6,e,8p,8f,8g,8p,1c6,p,d6,8p,8e6,1f.6,g,8p,8g,e.6,8p,d6,8p,8g,e.6,8p,d6,8p,8g,f.6,8p,e6,8p,8d6,2c6"; songArray[2] = "Entertainer:d=4,o=5,b=140:8d,8d#,8e,c6,8e,c6,8e,2c.6,8c6,8d6,8d#6,8e6,8c6,8d6,e6,8b,d6,2c6,p,8d,8d#,8e,c6,8e,c6,8e,2c.6,8p,8a,8g,8f#,8a,8c6,e6,8d6,8c6,8a,2d6"; songArray[3] = "Looney:d=4,o=5,b=140:32p,c6,8f6,8e6,8d6,8c6,a.,8c6,8f6,8e6,8d6,8d#6,e.6,8e6,8e6,8c6,8d6,8c6,8e6,8c6,8d6,8a,8c6,8g,8a#,8a,8f"; songArray[4] = "StarWars:d=4,o=5,b=45:32p,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#.6,32f#,32f#,32f#,8b.,8f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32c#6,8b.6,16f#.6,32e6,32d#6,32e6,8c#6"; songArray[5] = "GoodBad:d=4,o=5,b=56:32p,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,d#,32a#,32d#6,32a#,32d#6,8a#.,16f#.,16g#.,c#6,32a#,32d#6,32a#,32d#6,8a#.,16f#.,32f.,32d#.,c#,32a#,32d#6,32a#,32d#6,8a#.,16g#.,d#"; songArray[6] = "MissionImp:d=16,o=6,b=95:32d,32d#,32d,32d#,32d,32d#,32d,32d#,32d,32d,32d#,32e,32f,32f#,32g,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,g,8p,g,8p,a#,p,c7,p,g,8p,g,8p,f,p,f#,p,a#,g,2d,32p,a#,g,2c#,32p,a#,g,2c,a#5,8c,2p,32p,a#5,g5,2f#,32p,a#5,g5,2f,32p,a#5,g5,2e,d#,8d"; } void loop(void) { if (Serial.available() > 0) { // Read the songNumber sent by the other node int songNumber = Serial.parseInt(); // Check that the songNumber is inside the range of allowed values if (songNumber >= 0 && songNumber < N_SONGS) { // Print song title Serial.print(getSongName(songArray[songNumber]) + END_SONG_TITLE_CHAR); delay(300); // Play the song play_rtttl(songArray[songNumber]); } else { // Send error message to the other node Serial.print(SONG_NOT_FOUND + END_SONG_TITLE_CHAR); delay(300); } } } |
References
[1] Elements list for the system build. Retrieved from WSN course guide on 25/04/2014.
[2] Image "8.1 Wireless doorbell: switch layout". Retrieved from WSN Course Guide on 25/04/2014.
[3] Image "8.2 Wireless doorbell: buzzer layout". Retrieved from WSN Course Guide on 25/04/2014.
[4] Arduino Forums - Need help with a melody (link). Retrieved on 25/04/2014.
[5] Arduino Star Wars Song for Piezo (link). Retrieved on 25/04/2014.
[2] Image "8.1 Wireless doorbell: switch layout". Retrieved from WSN Course Guide on 25/04/2014.
[3] Image "8.2 Wireless doorbell: buzzer layout". Retrieved from WSN Course Guide on 25/04/2014.
[4] Arduino Forums - Need help with a melody (link). Retrieved on 25/04/2014.
[5] Arduino Star Wars Song for Piezo (link). Retrieved on 25/04/2014.
This comment has been removed by the author.
ReplyDelete