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 | |
| 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 | #ifdef _AVR_ATtiny85_ // Trinket, Gemma, etc. | |
| 16 | #include <avr/power.h> | |
| 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 FPS 30 // Animation frames/second (ish) | |
| 25 | #define COLOR_ORDER BGR | |
| 26 | ||
| 27 | CRGB leds[NUM_LEDS]; //naming our LED array | |
| 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 | // Stop incoming data & init software serial | |
| 40 | pinMode(CTS_PIN, OUTPUT); digitalWrite(CTS_PIN, HIGH); | |
| 41 | ser.begin(9600); | |
| 42 | ||
| 43 | - | FastLED.addLeds<DOTSTAR, LED_PIN, CLOCK_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); |
| 43 | + | FastLED.addLeds<DOTSTAR, LED_PIN, CLOCK_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); |
| 44 | FastLED.setBrightness( BRIGHTNESS ); | |
| 45 | - | currentBlending = BLEND; |
| 45 | + | currentBlending = LINEARBLEND; |
| 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) |
| 51 | + | |
| 52 | - | prevTime = 0L; // For animation timing |
| 52 | + | CRGB color = CRGB::Red; // Current animation color (red by default) |
| 53 | uint32_t prevTime = 0L; // For animation timing | |
| 54 | ||
| 55 | void loop(void) {
| |
| 56 | int c; | |
| 57 | uint32_t t; | |
| 58 | ||
| 59 | // Animation happens at about 30 frames/sec. Rendering frames takes less | |
| 60 | // than that, so the idle time is used to monitor incoming serial data. | |
| 61 | digitalWrite(CTS_PIN, LOW); // Signal to BLE, OK to send data! | |
| 62 | for(;;) {
| |
| 63 | t = micros(); // Current time | |
| 64 | if((t - prevTime) >= (1000000L / FPS)) { // 1/30 sec elapsed?
| |
| 65 | prevTime = t; | |
| 66 | break; // Yes, go update LEDs | |
| 67 | } // otherwise... | |
| 68 | if((c = ser.read()) == '!') { // Received UART app input?
| |
| 69 | while((c = ser.read()) < 0); // Yes, wait for command byte | |
| 70 | switch(c) {
| |
| 71 | case 'B': // Button (Control Pad) | |
| 72 | if(readAndCheckCRC(255-'!'-'B', buf, 2) & (buf[1] == '1')) {
| |
| 73 | buttonPress(buf[0]); // Handle button-press message | |
| 74 | } | |
| 75 | break; | |
| 76 | case 'C': // Color Picker | |
| 77 | if(readAndCheckCRC(255-'!'-'C', buf, 3)) {
| |
| 78 | // As mentioned earlier, setBrightness() was avoided to save space. | |
| 79 | // Instead, results from the color picker (in buf[]) are divided | |
| 80 | // by 4; essentially equivalent to setBrightness(64). This is to | |
| 81 | - | color = pixels.Color(buf[0]/4, buf[1]/4, buf[2]/4); |
| 81 | + | |
| 82 | color = CRGB(buf[0]/4, buf[1]/4, buf[2]/4); | |
| 83 | } | |
| 84 | break; | |
| 85 | case 'Q': // Quaternion | |
| 86 | skipBytes(17); // 4 floats + CRC (see note below re: parsing) | |
| 87 | break; | |
| 88 | case 'A': // Accelerometer | |
| 89 | #if 0 | |
| 90 | // The phone sensors are NOT used by this sketch, but this shows how | |
| 91 | // they might be read. First, buf[] must be delared large enough for | |
| 92 | // the expected data packet (minus header & CRC) -- that's 16 bytes | |
| 93 | // for quaternions (above), or 12 bytes for most of the others. | |
| 94 | // Second, the first arg to readAndCheckCRC() must be modified to | |
| 95 | // match the data type (e.g. 'A' here for accelerometer). Finally, | |
| 96 | // values can be directly type-converted to float by using a suitable | |
| 97 | // offset into buf[] (e.g. 0, 4, 8, 12) ... it's not used in this | |
| 98 | // example because floating-point math uses lots of RAM and code | |
| 99 | // space, not suitable for the space-constrained Trinket/Gemma, but | |
| 100 | // maybe you're using a Pro Trinket, Teensy, etc. | |
| 101 | if(readAndCheckCRC(255-'!'-'A', buf, 12)) {
| |
| 102 | float x = *(float *)(&buf[0]), | |
| 103 | y = *(float *)(&buf[4]), | |
| 104 | z = *(float *)(&buf[8]); | |
| 105 | } | |
| 106 | // In all likelihood, updates from the buttons and color picker | |
| 107 | // alone are infrequent enough that you could do without any mention | |
| 108 | // of the CTS pin in this code. It's the extra sensors that really | |
| 109 | // start the firehose of data. | |
| 110 | break; | |
| 111 | #endif | |
| 112 | case 'G': // Gyroscope | |
| 113 | case 'M': // Magnetometer | |
| 114 | case 'L': // Location | |
| 115 | skipBytes(13); // 3 floats + CRC | |
| 116 | } | |
| 117 | } | |
| 118 | } | |
| 119 | digitalWrite(CTS_PIN, HIGH); // BLE STOP! | |
| 120 | ||
| 121 | // Show pixels calculated on prior pass; this ensures more uniform timing | |
| 122 | FastLED.show(); | |
| 123 | ||
| 124 | // Then calculate pixels for next frame... | |
| 125 | switch(animMode) {
| |
| 126 | case 0: // Pinwheel mode | |
| 127 | - | uint32_t c = 0; |
| 127 | + | |
| 128 | CRGB c = CRGB::Black; | |
| 129 | - | leds[i](c); // First eye |
| 129 | + | |
| 130 | - | leds[NUM_LEDS-1-i](c); // Second eye (flipped) |
| 130 | + | leds[i] = c; // First eye |
| 131 | leds[NUM_LEDS-1-i] = c; // Second eye (flipped) | |
| 132 | } | |
| 133 | animPos++; | |
| 134 | break; | |
| 135 | - | leds[animPos](0); // Erase old dot |
| 135 | + | |
| 136 | leds[animPos] = CRGB::Black; // Erase old dot | |
| 137 | - | leds[animPos](color); // and light it |
| 137 | + | |
| 138 | leds[animPos] = color; // and light it | |
| 139 | break; | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | boolean readAndCheckCRC(uint8_t sum, uint8_t *buf, uint8_t n) {
| |
| 144 | for(int c;;) {
| |
| 145 | while((c = ser.read()) < 0); // Wait for next byte | |
| 146 | if(!n--) return (c == sum); // If CRC byte, we're done | |
| 147 | *buf++ = c; // Else store in buffer | |
| 148 | sum -= c; // and accumulate sum | |
| 149 | } | |
| 150 | } | |
| 151 | ||
| 152 | void skipBytes(uint8_t n) {
| |
| 153 | while(n--) {
| |
| 154 | while(ser.read() < 0); | |
| 155 | } | |
| 156 | } | |
| 157 | ||
| 158 | void buttonPress(char c) {
| |
| 159 | fill_solid(leds, NUM_LEDS, CHSV(0,0,0)); // Clear pixel data when switching modes (else residue) | |
| 160 | switch(c) {
| |
| 161 | case '1': | |
| 162 | animMode = 0; // Switch to pinwheel mode | |
| 163 | break; | |
| 164 | case '2': | |
| 165 | animMode = 1; // Switch to sparkle mode | |
| 166 | break; | |
| 167 | case '3': | |
| 168 | break; | |
| 169 | case '4': | |
| 170 | break; | |
| 171 | case '5': // Up | |
| 172 | break; | |
| 173 | case '6': // Down | |
| 174 | break; | |
| 175 | case '7': // Left | |
| 176 | break; | |
| 177 | case '8': // Right | |
| 178 | break; | |
| 179 | } | |
| 180 | } |