l’amico [francesco] mi ha messo in mano un attiny85 e mi ha insegnato come programmarlo e come farlo suonare. poi mi ha insegnato anche che cos’è il charlieplexing.
c’è voluto un po’ di tempo, ma alla fine ho inventato un progettino che comprendesse quanto avevo imparato.
è quasi natale, allora ho pensato di aggiungere luci e suoni a un addobbo natalizio, ne ho realizzati un po’ e li attaccherò ai pacchetti dei regali per i parenti. le forme sono diverse: una stellina, tre palline collegate, una pallina singola; il contenuto invece è sempre lo stesso.
quando si scuote la stellina parte una musichetta natalizia e si accendono i led nelle punte; ci sono 20 diverse musiche, possono essere riprodotte a caso oppure in sequenza, cambiando una riga nel codice. anche l’ordine di accensione dei led può essere impostato come fisso o casuale dal codice.
le musiche sono codificate in formato rtttl, usato per le suonerie dei vecchi cellulari; la ram dell’attiny è poca, quindi le musiche sono tenute nella memoria del programma grazie alla parola chiave PROGMEM e vengono caricate in ram appena prima di suonarle. la funzione che suona le note si occupa anche di accendere uno dei led che quindi si accendono a tempo.
i led si possono accendere casualmente o secondo una sequenza fissa attivando o disattivando la direttiva alla riga 18, #define RANDOMLED
. anche le musiche possono essere scelte casualmente o secondo una sequenza fissa anche in questo caso con la direttiva alla riga 19, #define RANDOMSONG
. ho scelto di usare le direttive del preprocessore perché in questo modo viene compilato solo il codice che serve mentre le altre parti vengono ignorate, risparmiando preziosa memoria sull’attiny
grazie al charlieplexing si possono montare fino a 6 led, ma si può anche scegliere di usare un led rgb collegando gli anodi ai piedi 5, 6 e 7 dell’integrato. in entrambi i casi, anche se nello schema sono indicate delle resistenze, io non le ho messe e i led non sono bruciati; il circuito è alimentato a 3 V e la corrente in uscita dall’attiny85 è limitata a 40 mA.
ho trovato qui le funzioni per leggere e riprodurre musica in formato rtttl, e qui un modo comodo per gestire i led in charlieplexing
[francesco] con lo stesso sistema ha costruito un alberello di natale da appendere alla porta dell’ufficio
il circuito comprende:
- U1: ATtiny85
- S1: sensore di vibrazioni
- J1: speaker piezoelettrico
- R1-3: resistenze 150 Ω, 250 mW
- LED1-6: led colorati
- BAT1: portabatteria per CR2016/CR2032
lo schema:
e la breadboard:
il codice per arduino:
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 |
// inizio implementazione assert con output in compilazione #define CONCAT_TOKENS( TokenA, TokenB ) TokenA ## TokenB #define EXPAND_THEN_CONCAT( TokenA, TokenB ) CONCAT_TOKENS( TokenA, TokenB ) #define ASSERT( Expression ) enum{ EXPAND_THEN_CONCAT( ASSERT_line_, __LINE__ ) = 1 / !!( Expression ) } #define ASSERTM( Expression, Message ) enum{ EXPAND_THEN_CONCAT( Message ## _ASSERT_line_, __LINE__ ) = 1 / !!( Expression ) } // fine implementazione assert con output in compilazione #define ISDIGIT(n) (n >= '0' && n <= '9') #define NUM_OF_PINS (sizeof(pins) / sizeof(char)) #define DEBOUNCETIME 125 #define SPEAKER_PIN 4 #define BUTTON_PIN 3 #define NUM_OF_SONGS 21 #define NUM_OF_LEDS 6 #define RANDOMLED #define RANDOMSONG const int notes[] = { 0, 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, // o=4 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988, // o=5 1047, 1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760, 1865, 1976, // o=6 2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951, // o=7 }; const char song_1[] PROGMEM = "Silent:d=4,o=5,b=150:f#.,8g#,f#,d#.,p,f#.,8g#,f#,d#.,p,2c#6,c#6,a#.,p,2b,b,f#.,p,2g#,g#,b.,8a#,g#,f#.,8g#,f#,d#.,p,2g#,g#,b.,8a#,g#,f#.,8g#,f#,d#.,p,2c#6,c#6,e.6,8c#6,a#,2b.,2d#6,p,b,f#,d#,f#.,8e,c#,b.4"; const char song_2[] PROGMEM = "DeckTheHalls:d=4,o=5,b=140:g.6,8f6,e6,d6,c6,d6,e6,c6,8d6,8e6,8f6,8d6,e.6,8d6,c6,b,2c6,g.6,8f6,e6,d6,c6,d6,8e6,8b,8c6,8g,8d6,8e6,8f6,8d6,e6,8c6,8d6,8c6,8a,8b,8f,2c6"; const char song_3[] PROGMEM = "JingleBells:d=4,o=5,b=180:8e.,8e.,e.,8e.,8e.,e.,8e.,8g.,c,16d.,2e.,8f.,8f.,f,16f.,8f.,8e.,8e.,16e.,16e.,8e.,8d.,8d.,8e.,d.,g.,8e.,8e.,e.,8e.,8e.,e.,8e.,8g.,c,16d.,2e.,8f.,8f.,f,16f.,8f.,8e.,8e.,16e.,16e.,8g.,8g.,8f.,8d.,c."; const char song_4[] PROGMEM = "LetItSnow:d=8,o=5,b=120:4c,c6,c6,4a#,4a,4g,4f,2c,c.,16c,4g.,f,4g.,f,4e,2c,4d,d6,d6,4c6,4a#,4a,2g.,e.6,16d6,4c6,c.6,16a#,4a,a.,16g,2f."; const char song_5[] PROGMEM = "WeWish:d=8,o=5,b=140:4d,4g,g,a,g,f#,4e,4c,4e,4a,a,b,a,g,4f#,4d,4f#,4b,b,c6,b,a,4g,4e,4d,4e,4a,4f#,2g"; const char song_6[] PROGMEM = "SantaClausIsComingTonight:d=4,o=5,b=170:g,8e,8f,g,g.,8g,8a,8b,c6,2c6,8e,8f,g,g,g,8a,8g,f,2f,e,g,c,e,d,2f,b4,1c,p,g,8e,8f,g,g.,8g,8a,8b,c6,2c6,8e,8f,g,g,g,8a,8g,f,f,e,g,c,e,d,2f,b4,1c"; const char song_7[] PROGMEM = "OhTannenbaum:d=4,o=5,b=85:d,8g,16g,g,a,8b,32b,b,16b.,8a,8b,c6,f#,a,g,16d.6,16d.6,8b,e.6,16d.6,8d6,16c.6,c.6,16c.6,8c6,16a.,d.6,8c6,8c6,16b.,b.,d,8g,16g,g,a,8b,32b,b,16b.,8a,8b,c6,f#,a,g."; const char song_8[] PROGMEM = "Rudolph:d=4,o=5,b=125:8g.,16a,8p,16g.,e,c6,a,1g,8g.,16a,8g.,16a,g,c6,1b,8f.,16g,8p,16f.,d,b,a,1g,8g.,16a,8g.,16a,g,d6,2c.6"; const char song_9[] PROGMEM = "TuScendi:d=4,o=5,b=77:8g,g,8a,g,8f,8f,e,8d,8e,8f,8g,8g,8f,8e,d.,8e,d,8e,f,8e,d.,a.,8g,8a,8g,8f,8e,8d,e.,8d,d,8e,f,8e,d.,a.,8g,8a,8g,8f,8e,8d,2c"; const char song_10[] PROGMEM = "FirstDayOfChristmas:d=4,o=5,b=160:8c,8c,f,8f,8f,f,8f,8f,8g,8a,8a#,8g,a.,8p,c,8g,8a,8a#,8g,8c,8c,8g,8a,8a#,8g,c,d,c.,8p,8c,8a#,8a,8g,f,8g,8a,a#,d,d,c,8f,8g,8a,8a#,a,g,2f"; const char song_11[] PROGMEM = "MorgenKommtDerWeihnachtsmann:d=4,o=5,b=70:8g,8g,8d6,8d6,8e6,8e6,d6,8c6,8c6,8b,8b,a,g,8d6,8d6,8c6,8c6,8b,8b,a,8d6,8d6,8c6,8c6,8b,8b,a,8g,8g,8d6,8d6,8e6,8e6,d6,8c6,8c6,8b,8b,a,g"; const char song_12[] PROGMEM = "DingDongMerrilyOnHigh:d=4,o=5,b=230:g6,g6,8a6,8g6,8f#6,8e6,2d.6,d6,e6,g6,g6,f#6,2g6,2g6,g6,g6,8a6,8g6,8f#6,8e6,2d.6,d6,e6,g6,g6,f#6,2g6,2g6,d.7,8c7,8b6,8c7,8d7,8b6,c.7,8b6,8a6,8b6,8c7,8a6,b.6,8a6,8g6,8a6,8b6,8g6,a.6,8g6,f#6,d6,e6,g6,g6,f#6,2g6,2g6"; const char song_13[] PROGMEM = "FelizNavidad:d=4,o=5,b=140:8a,d6,8c#6,8d6,2b.,p,8b,e6,8d6,8b,2a.,p,8a,d6,8c#6,8d6,b.,8g,b,b,8a,8a,8b,8a,g,8g,2f#."; const char song_14[] PROGMEM = "JingleBellRock:d=4,o=5,b=100:8a.,8c.6,16d.6,16d6,8c.6,8g#,16g#,16c6,16p,d.6,8p,8c6,16a,8b,16a,8g.,2c.6,8p,8c6,16c6,8c.6,16p,8b,16b,8b.,8a,16b,8a,e.,8p,8a,16b,8a,8e.,8g,16p,8a,16b,8a,f.,8p,8d,8e.,16f,16g,8p,8a,16g,8d,16e,16f,16p,2g"; const char song_15[] PROGMEM = "AuldLangSign:d=4,o=5,b=125:a.4,d.,8d,d,f#,e.,8d,e,8f#,8e,d.,8d,f#,a,2b.,b,a.,8f#,f#,d,e.,8d,e,8f#,8e,d.,8b4,b4,a4,2d"; const char song_16[] PROGMEM = "JoyToTheWorld:d=4,o=5,b=112:d6,8c#.6,16b,a.,8g,f#,e,d,8p,8a,b,8p,8b,c#6,8p,8c#6,2d.6,8p,8d6,8d6,8c#6,8b,8a,8a.,16g,8f#,8d6,8d6,8c#6,8b,8a,8a.,16g,8f#,8f#,8f#,8f#,8f#,16f#,16g,a.,16g,16f#,8e,8e,8e,16e,16f#,g,8p,16f#,16e,8d,8d6,8p,8b,8a.,16g,8f#,8g,f#,e,2d"; const char song_17[] PROGMEM = "SoThisIsChristmas:d=8,o=5,b=76:a,b,c#6,a,e.,8p.,e,a,b,c#6,4b,5p,16f#,b,c#6,d6,c#6,4b,16e,16e,c#6,e6,16c#6,16b,4a"; const char song_18[] PROGMEM = "Fr:d=4,o=5,b=140:2g,e,8p,8f,g,2c6,8b,8c6,d6,c6,b,a,2g,p,8b,8c6,d6,c6,b,8a,8a,8g,c6,e.,8f,8a,g,f,e,f,2g,2p,2g,e,8p,8f,g,2c6,8b,8c6,d6,c6,b,a,2g,p,8b,8c6,d6,c6,b,8a,8a,8g,c6,e.,8g,8a,g,f,e,d,2c,p,c,a,a,c6,c6,b,a,g,e,f,a,g,f,2e,p,e,d,d,g,g,b,b,d6,8d6,8b,d6,c6,b,a,g,p,2g"; const char song_19[] PROGMEM = "LastChristmas:d=4,o=5,b=112:e.6,e6,8d6,8p,8a,8e6,8e6,8f#6,d.6,8b,8b,8e6,8e6,f#6,d.6,8b,8c#6,8d6,8c#6,2b.,16e6,f#6,8p,e.6,8p,8b,8f#6,8g6,8f#6,2e6,8d6,8c#6,8d6,8c#6,c#6,8d6,8p,8c#6,8p,a"; const char song_20[] PROGMEM = "White:d=8,o=5,b=56:4f#,g,f#,f,f#,4g,g#,a,p,b,c#6,d6,e6,d6,c#6,b,a,p,d,e,4f#,4f#,b,4a,a,4d6,p,d,e,4f#,4f#,b,b,c#,16c#.,4d"; const char song_21[] PROGMEM = "12DaysofChristmas:d=4,o=5,b=140:8d,8d,d,8g,8g,g,8f#,8g,8a,8b,8c6,8a,b,8p,8c6,d6,8e6,8c6,8b,8g,a,2g,8d6,8d6,d6,8g6,8g6,g6,8f#6,8g6,8a6,8b6,8c7,8a6,2b6,d7,8a,8b,c6,8b6,8c7,d7,8e7,8c7,8b6,8g6,a6,2g.6";//,8f#,8f#,f#,8b,8b,b"; const char* const mySongs[NUM_OF_SONGS] PROGMEM = { song_1, // ok song_2, // ok song_3, // ok song_4, // ok song_5, // ok song_6, // ok song_7, // ok song_8, // ok song_9, // ok song_10, // uguale a 21 song_11, // ok song_12, // ok song_13, // ok song_14, // JingleBellRock:d=4,o=5,b=112:8a.,8c,16d,16d6,8c,8g#,16g#,8c6,2d6,16a,8b,16a,8g.,1c6, 8c6,16c6,c6,8b,16b,8b.,8a,16b,8a,2e,8a,16b,8a,8e,8g.,8a,16b,8a,2f,8d,8e.,16f,8g,8a, 16g,8d,16e,8f,2g song_15, // ok song_16, // ok song_17, // ok song_18, // ok song_19, // ok song_20, // ok song_21, // uguale a 10 }; const char pins[] = {0, 1, 2}; const char ledPins[NUM_OF_LEDS][2] = { {0, 2}, {0, 1}, {1, 2}, {2, 1}, {1, 0}, {2, 0}, }; #ifndef RANDOMSONG byte giro = 0; #endif #ifndef RANDOMLED byte j = 0; #endif ASSERT(NUM_OF_LEDS <= (NUM_OF_PINS * (NUM_OF_PINS-1))); void setup(void) { pinMode(SPEAKER_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); } void loop(void) { alloff(); if ( digitalRead(BUTTON_PIN) == HIGH ) { delay(DEBOUNCETIME); if ( digitalRead(BUTTON_PIN) == HIGH ) { char songs[270]; #ifdef RANDOMSONG strcpy_P(songs, (char*)pgm_read_word(&(mySongs[random(0, NUM_OF_SONGS)]))); #else strcpy_P(songs, (char*)pgm_read_word(&(mySongs[giro]))); giro = (giro < (NUM_OF_SONGS - 1)) ? giro + 1 : 0; #endif play_rtttl(songs); } } } void play_rtttl(char *p) { // http://www.interactiondesign.se/wiki/attiny // 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) // legge il titolo 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 } // get default octave if(*p == 'o') { p++; p++; // skip "o=" num = *p++ - '0'; if(num >= 3 && num <=7) default_oct = num; p++; // skip comma } // get BPM if(*p == 'b') { p++; p++; // skip "b=" num = 0; while(ISDIGIT(*p)) { num = (num * 10) + (*p++ - '0'); } bpm = num; p++; // skip colon } // bpm = quante note da un quarto stanno in un minuto wholenote = (60 * 1000L / bpm) * 4 ; // this is the time for whole note (in milliseconds) // 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; } if(*p == ',') p++; // skip comma for next note (or we may be at the end) // now play the note if(note) { freqout(notes[(scale - 4) * 12 + note], duration); } else { delay(duration); } } } void freqout(int freq, int t) { freqout(freq, t, SPEAKER_PIN); } void freqout(int freq, int t, byte speaker) { // http://www.arduino.cc/playground/Main/Freqout #ifdef RANDOMLED turnon(random(0, NUM_OF_LEDS - 1)); #else turnon(j); #endif const int hperiod = 500000 / freq - 7; // subtract 7 us to make up for digitalWrite overhead const long cycles = ((long)freq * (long)t) / 1000; // calculate cycles pinMode(SPEAKER_PIN, OUTPUT); // turn on output pin for (long i = 0; i <= cycles; i++) { // play note for t ms - SOFTWARE PWM? digitalWrite(SPEAKER_PIN, HIGH); delayMicroseconds(hperiod); digitalWrite(SPEAKER_PIN, LOW); delayMicroseconds(hperiod - 1); // - 1 to make up for digitaWrite overhead } pinMode(SPEAKER_PIN, INPUT); // shut off pin to avoid noise from other operations #ifndef RANDOMLED alloff(); j = (j < (NUM_OF_LEDS - 1)) ? j + 1 : 0; #endif } void turnon(int led) { // http://www.instructables.com/id/CharliePlexed-LED-string-for-the-Arduino/ // This turns on a certian led from the list of leds int pospin = ledPins[led][0]; int negpin = ledPins[led][1]; pinMode (pospin, OUTPUT); digitalWrite (pospin, HIGH); pinMode (negpin, OUTPUT); digitalWrite (negpin, LOW); } void alloff() { // http://www.instructables.com/id/CharliePlexed-LED-string-for-the-Arduino/ // This turns all the LED's off for(int i = 0; i < NUM_OF_PINS; i++) pinMode (pins[i], INPUT); } |
Che bell’articolino, come hai fatto a formattare il codice così bene?
grazie! per il codice c’è un plugin per wordpress, si chiama Crayon Syntax Highlighter