|
| 1 | +// Smartphone-activated audio gizmo (e.g. talking dog collar). |
| 2 | +// Uses the following Adafruit parts: |
| 3 | +// |
| 4 | +// - Bluefruit LE Micro (adafruit.com/product/2661) |
| 5 | +// - 500 mAh LiPoly battery (1578) |
| 6 | +// - LiPoly backpack (2124) |
| 7 | +// - Audio FX Mini sound board (2342 or 2341) |
| 8 | +// - 2.5W class D mono amp (2130) |
| 9 | +// - Speaker (TBD) |
| 10 | +// |
| 11 | +// Needs Adafruit_BluefruitLE_nRF51 and Adafruit_Soundboard libs: |
| 12 | +// github.com/adafruit |
| 13 | + |
| 14 | +#include <SPI.h> |
| 15 | +#include <SoftwareSerial.h> |
| 16 | +#include <Adafruit_BluefruitLE_SPI.h> |
| 17 | +#include <Adafruit_Soundboard.h> |
| 18 | + |
| 19 | +#define LED A0 // LED on while "talking" |
| 20 | +#define AUDIO_ACT 5 // "Act" on Audio FX |
| 21 | +#define AUDIO_RESET 6 // "Rst" on Audio FX |
| 22 | + |
| 23 | +Adafruit_Soundboard sfx(&Serial1, NULL, AUDIO_RESET); |
| 24 | +Adafruit_BluefruitLE_SPI ble(8, 7, 4); // CS, IRQ, RST pins |
| 25 | + |
| 26 | +char filename[12] = " OGG"; // Tail end of filename NEVER changes |
| 27 | + |
| 28 | +// PROGMEM string arrays are wretched, and sfx.playTrack() expects a |
| 29 | +// goofy fixed-length space-padded filename...we take care of both by |
| 30 | +// declaring all the filenames inside one big contiguous PROGMEM string |
| 31 | +// (notice there are no commas here, it's all concatenated), and copying |
| 32 | +// an 8-byte section as needed into filename[]. Some waste, but we're |
| 33 | +// not hurting for space. If you change or add any filenames, they MUST |
| 34 | +// be padded with spaces to 8 characters, else there will be...trouble. |
| 35 | +static const char PROGMEM bigStringTable[] = // play() index |
| 36 | + "1 " "2 " "3 " "4 " // 0-3 |
| 37 | + "5 " "6 " "7 " "8 " // 4-7 |
| 38 | + "BOOT "; // 8- |
| 39 | + |
| 40 | +void fail(uint16_t ms) { // If startup error, LED flash indicates status |
| 41 | + for(uint8_t x=0;;) { |
| 42 | + digitalWrite(LED, ++x & 1); |
| 43 | + delay(ms); |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +void setup(void) { |
| 48 | + pinMode(LED, OUTPUT); |
| 49 | + digitalWrite(LED, HIGH); // LED steady on during init |
| 50 | + Serial1.begin(9600); // Audio FX serial link |
| 51 | + if(!sfx.reset()) fail(250); // Audio FX init error? Slow blink |
| 52 | + if(!ble.begin(false)) fail(100); // BLE init error? Fast blink |
| 53 | + ble.echo(false); |
| 54 | + digitalWrite(LED, LOW); // LED off = successful init |
| 55 | + play(8); // Play startup sound |
| 56 | +} |
| 57 | + |
| 58 | +void loop(void) { |
| 59 | + if(ble.isConnected()) { |
| 60 | + ble.println(F("AT+BLEUARTRX")); // Request string from BLE module |
| 61 | + ble.readline(); // Read outcome |
| 62 | + if(!strncmp(ble.buffer, "!B", 2) && // Controller button command |
| 63 | + checkCRC(255-'!'-'B', 4) && // Verify checksum |
| 64 | + (ble.buffer[3] == '1')) { // Button press? 1=press 0=release |
| 65 | + play(ble.buffer[2] - '1'); |
| 66 | + } |
| 67 | + ble.waitForOK(); |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +boolean checkCRC(uint8_t sum, uint8_t CRCindex) { |
| 72 | + for(uint8_t i=2; i<CRCindex; i++) sum -= (uint8_t)ble.buffer[i]; |
| 73 | + return ((uint8_t)ble.buffer[CRCindex] == sum); |
| 74 | +} |
| 75 | + |
| 76 | +void play(uint16_t i) { |
| 77 | + digitalWrite(LED, HIGH); |
| 78 | + memcpy_P(filename, &bigStringTable[i * 8], 8); // PROGMEM -> RAM |
| 79 | + sfx.playTrack(filename); |
| 80 | + delay(250); // Need this -- some delay before ACT LED is valid |
| 81 | + while(digitalRead(AUDIO_ACT) == LOW); // Wait for sound to finish |
| 82 | + digitalWrite(LED, LOW); |
| 83 | +} |
0 commit comments