SHOW:
|
|
- or go back to the newest paste.
| 1 | // Bluetooth Goggles Sketch -- shows the Adafruit Bluefruit LE UART Friend | |
| 2 | // can be used even with Trinket or Gemma! | |
| 3 | ||
| 4 | - | // https://www.adafruit.com/products/2479 |
| 4 | + | // https://www.adafruit.com/products/2479 |
| 5 | // Works in conjunction with Bluefruit LE Connect app on iOS or Android -- | |
| 6 | // pick colors or use '1' and '2' buttons to select pinwheel or sparkle modes. | |
| 7 | // You can try adding more, but space is VERY tight...helps to use Arduino IDE | |
| 8 | // 1.6.4 or later; produces slightly smaller code than the 1.0.X releases. | |
| 9 | ||
| 10 | // BLUEFRUIT LE UART FRIEND MUST BE SWITCHED TO 'UART' MODE | |
| 11 | ||
| 12 | #include <SPI.h> | |
| 13 | #include <SoftwareSerial.h> | |
| 14 | #include <FastLED.h> | |
| 15 | - | #include <Adafruit_DotStar.h> |
| 15 | + | #ifdef _AVR_ATtiny85_ // Trinket, Gemma, etc. |
| 16 | - | #ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc. |
| 16 | + | |
| 17 | #endif | |
| 18 | ||
| 19 | #define RX_PIN 8 // Connect this Trinket pin to BLE 'TXO' pin | |
| 20 | #define CTS_PIN 6 // Connect this Trinket pin to BLE 'CTS' pin | |
| 21 | #define LED_PIN 4 // Connect NeoPixels to this Trinket pin | |
| 22 | #define CLOCK_PIN 3 | |
| 23 | #define NUM_LEDS 72 // Two 16-LED NeoPixel rings | |
| 24 | - | #define NUM_LEDS 72 // DotStar Strip |
| 24 | + | |
| 25 | #define COLOR_ORDER BGR | |
| 26 | ||
| 27 | CRGB leds[NUM_LEDS]; //naming our LED array | |
| 28 | - | Adafruit_DotStar pixels(NUM_LEDS, LED_PIN, CLOCK_PIN, DOTSTAR_BGR ); |
| 28 | + | |
| 29 | SoftwareSerial ser(RX_PIN, -1); | |
| 30 | ||
| 31 | int BRIGHTNESS = 255; //0-255. Lower number saves battery life, higher number is screamingly bright | |
| 32 | TBlendType currentBlending; | |
| 33 | ||
| 34 | void setup() {
| |
| 35 | #if defined(__AVR_ATtiny85__) && (F_CPU == 16000000L) | |
| 36 | // MUST do this on 16 MHz Trinket for serial & NeoPixels! | |
| 37 | clock_prescale_set(clock_div_1); | |
| 38 | #endif | |
| 39 | - | pixels.begin(); // NeoPixel init |
| 39 | + | |
| 40 | - | // Flash space is tight on Trinket/Gemma, so setBrightness() is avoided -- |
| 40 | + | |
| 41 | - | // it adds ~200 bytes. Instead the color picker input is 'manually' scaled. |
| 41 | + | |
| 42 | ||
| 43 | FastLED.addLeds<DOTSTAR, LED_PIN, CLOCK_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); | |
| 44 | FastLED.setBrightness( BRIGHTNESS ); | |
| 45 | currentBlending = BLEND; | |
| 46 | } | |
| 47 | ||
| 48 | uint8_t buf[3], // Enough for RGB parse; expand if using sensors | |
| 49 | animMode = 0, // Current animation mode | |
| 50 | animPos = 0; // Current animation position | |
| 51 | uint32_t color = 0x400000, // Current animation color (red by default) | |
| 52 | prevTime = 0L; // For animation timing | |
| 53 | ||
| 54 | void loop(void) {
| |
| 55 | int c; | |
| 56 | uint32_t t; | |
| 57 | ||
| 58 | // Animation happens at about 30 frames/sec. Rendering frames takes less | |
| 59 | // than that, so the idle time is used to monitor incoming serial data. | |
| 60 | digitalWrite(CTS_PIN, LOW); // Signal to BLE, OK to send data! | |
| 61 | for(;;) {
| |
| 62 | t = micros(); // Current time | |
| 63 | if((t - prevTime) >= (1000000L / FPS)) { // 1/30 sec elapsed?
| |
| 64 | prevTime = t; | |
| 65 | break; // Yes, go update LEDs | |
| 66 | } // otherwise... | |
| 67 | if((c = ser.read()) == '!') { // Received UART app input?
| |
| 68 | while((c = ser.read()) < 0); // Yes, wait for command byte | |
| 69 | switch(c) {
| |
| 70 | case 'B': // Button (Control Pad) | |
| 71 | if(readAndCheckCRC(255-'!'-'B', buf, 2) & (buf[1] == '1')) {
| |
| 72 | buttonPress(buf[0]); // Handle button-press message | |
| 73 | } | |
| 74 | break; | |
| 75 | case 'C': // Color Picker | |
| 76 | if(readAndCheckCRC(255-'!'-'C', buf, 3)) {
| |
| 77 | // As mentioned earlier, setBrightness() was avoided to save space. | |
| 78 | // Instead, results from the color picker (in buf[]) are divided | |
| 79 | // by 4; essentially equivalent to setBrightness(64). This is to | |
| 80 | // improve battery run time (NeoPixels are still plenty bright). | |
| 81 | color = pixels.Color(buf[0]/4, buf[1]/4, buf[2]/4); | |
| 82 | } | |
| 83 | break; | |
| 84 | case 'Q': // Quaternion | |
| 85 | skipBytes(17); // 4 floats + CRC (see note below re: parsing) | |
| 86 | break; | |
| 87 | case 'A': // Accelerometer | |
| 88 | #if 0 | |
| 89 | // The phone sensors are NOT used by this sketch, but this shows how | |
| 90 | // they might be read. First, buf[] must be delared large enough for | |
| 91 | // the expected data packet (minus header & CRC) -- that's 16 bytes | |
| 92 | // for quaternions (above), or 12 bytes for most of the others. | |
| 93 | // Second, the first arg to readAndCheckCRC() must be modified to | |
| 94 | // match the data type (e.g. 'A' here for accelerometer). Finally, | |
| 95 | // values can be directly type-converted to float by using a suitable | |
| 96 | // offset into buf[] (e.g. 0, 4, 8, 12) ... it's not used in this | |
| 97 | // example because floating-point math uses lots of RAM and code | |
| 98 | // space, not suitable for the space-constrained Trinket/Gemma, but | |
| 99 | // maybe you're using a Pro Trinket, Teensy, etc. | |
| 100 | if(readAndCheckCRC(255-'!'-'A', buf, 12)) {
| |
| 101 | float x = *(float *)(&buf[0]), | |
| 102 | y = *(float *)(&buf[4]), | |
| 103 | z = *(float *)(&buf[8]); | |
| 104 | } | |
| 105 | // In all likelihood, updates from the buttons and color picker | |
| 106 | // alone are infrequent enough that you could do without any mention | |
| 107 | // of the CTS pin in this code. It's the extra sensors that really | |
| 108 | // start the firehose of data. | |
| 109 | break; | |
| 110 | #endif | |
| 111 | case 'G': // Gyroscope | |
| 112 | case 'M': // Magnetometer | |
| 113 | case 'L': // Location | |
| 114 | skipBytes(13); // 3 floats + CRC | |
| 115 | } | |
| 116 | - | // Show pixels calculated on *prior* pass; this ensures more uniform timing |
| 116 | + | |
| 117 | - | pixels.show(); |
| 117 | + | |
| 118 | digitalWrite(CTS_PIN, HIGH); // BLE STOP! | |
| 119 | - | // Then calculate pixels for *next* frame... |
| 119 | + | |
| 120 | // Show pixels calculated on prior pass; this ensures more uniform timing | |
| 121 | FastLED.show(); | |
| 122 | ||
| 123 | // Then calculate pixels for next frame... | |
| 124 | switch(animMode) {
| |
| 125 | - | pixels.setPixelColor( i, c); // First eye |
| 125 | + | |
| 126 | - | pixels.setPixelColor(NUM_LEDS-1-i, c); // Second eye (flipped) |
| 126 | + | |
| 127 | uint32_t c = 0; | |
| 128 | if(((animPos + i) & 7) < 2) c = color; // 4 pixels on... | |
| 129 | leds[i](c); // First eye | |
| 130 | leds[NUM_LEDS-1-i](c); // Second eye (flipped) | |
| 131 | - | pixels.setPixelColor(animPos, 0); // Erase old dot |
| 131 | + | |
| 132 | animPos++; | |
| 133 | - | pixels.setPixelColor(animPos, color); // and light it |
| 133 | + | |
| 134 | case 1: // Sparkle mode | |
| 135 | leds[animPos](0); // Erase old dot | |
| 136 | animPos = random(NUM_LEDS); // Pick a new one | |
| 137 | leds[animPos](color); // and light it | |
| 138 | break; | |
| 139 | } | |
| 140 | } | |
| 141 | ||
| 142 | boolean readAndCheckCRC(uint8_t sum, uint8_t *buf, uint8_t n) {
| |
| 143 | for(int c;;) {
| |
| 144 | while((c = ser.read()) < 0); // Wait for next byte | |
| 145 | if(!n--) return (c == sum); // If CRC byte, we're done | |
| 146 | *buf++ = c; // Else store in buffer | |
| 147 | sum -= c; // and accumulate sum | |
| 148 | } | |
| 149 | } | |
| 150 | ||
| 151 | void skipBytes(uint8_t n) {
| |
| 152 | while(n--) {
| |
| 153 | while(ser.read() < 0); | |
| 154 | - | pixels.clear(); // Clear pixel data when switching modes (else residue) |
| 154 | + | |
| 155 | } | |
| 156 | ||
| 157 | void buttonPress(char c) {
| |
| 158 | fill_solid(leds, NUM_LEDS, CHSV(0,0,0)); // Clear pixel data when switching modes (else residue) | |
| 159 | switch(c) {
| |
| 160 | case '1': | |
| 161 | animMode = 0; // Switch to pinwheel mode | |
| 162 | break; | |
| 163 | case '2': | |
| 164 | animMode = 1; // Switch to sparkle mode | |
| 165 | break; | |
| 166 | case '3': | |
| 167 | - | animMode = animMode+1; |
| 167 | + | |
| 168 | case '4': | |
| 169 | break; | |
| 170 | - | animMode = animMode-1; |
| 170 | + | |
| 171 | break; | |
| 172 | case '6': // Down | |
| 173 | break; | |
| 174 | case '7': // Left | |
| 175 | break; | |
| 176 | case '8': // Right | |
| 177 | break; | |
| 178 | } | |
| 179 | } |