/ Ada_remoteFXTrigger_NeoTrellis_FastLED_TX / Ada_remoteFXTrigger_NeoTrellis_FastLED_TX.ino
Ada_remoteFXTrigger_NeoTrellis_FastLED_TX.ino
1 // SPDX-FileCopyrightText: 2019 Erin St. Blaine for Adafruit Industries 2 // SPDX-FileCopyrightText: 2019 John Edgar Park for Adafruit Industries 3 // 4 // SPDX-License-Identifier: MIT 5 // 6 //Ada_remoteFXTrigger_NeoTrellis_TX 7 //Remote Effects Trigger Box Transmitter 8 //by John Park 9 // & Erin St. Blaine 10 //for Adafruit Industries 11 12 // General purpose button box 13 // for triggering remote effects 14 // using packet radio Feather boards 15 // 16 // 17 //MIT License 18 19 #include <SPI.h> 20 #include <RH_RF69.h> 21 #include <Wire.h> 22 #include <Adafruit_GFX.h> 23 #include <Adafruit_SSD1306.h> 24 #include <Adafruit_NeoTrellis.h> 25 26 #include <Encoder.h> 27 int m = 0; //variable to increment through menu list 28 int x = 17; //variable for referencing buttons -- setting X to a non-existent button 29 /********* Encoder Setup ***************/ 30 #define PIN_ENCODER_SWITCH 11 31 Encoder knob(10, 12); 32 uint8_t activeRow = 0; 33 long pos = -999; 34 long newpos; 35 int prevButtonState = HIGH; 36 bool needsRefresh = true; 37 bool advanced = false; 38 unsigned long startTime; 39 40 41 /********* NeoTrellis Setup ***************/ 42 Adafruit_NeoTrellis trellis; 43 44 #define MOMENTARY 0 45 #define LATCHING 1 46 #define MODE LATCHING //all Trellis buttons in latching mode 47 #define NUMTRELLIS 1 48 #define numKeys (NUMTRELLIS * 16) 49 #define INTPIN A2 50 int NUM_SCREENS = 3; // change this number to add more screens 51 52 53 //define a callback for key presses 54 TrellisCallback blink(keyEvent evt){ 55 // Check is the pad pressed? 56 if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_RISING) { 57 //trellis.pixels.setPixelColor(evt.bit.NUM, 0xFFFFFF); //on rising 58 } else if (evt.bit.EDGE == SEESAW_KEYPAD_EDGE_FALLING) { 59 // or is the pad released? 60 //trellis.pixels.setPixelColor(m, Wheel(map(m*2, 0, trellis.pixels.numPixels(), 0, 255))); //off falling 61 x = evt.bit.NUM; 62 } 63 64 // Turn on/off the neopixels! 65 trellis.pixels.show(); 66 67 return 0; 68 } 69 70 71 72 /************ OLED Setup ***************/ 73 Adafruit_SSD1306 oled = Adafruit_SSD1306(); 74 #if defined(ESP8266) 75 #define BUTTON_A 0 76 #define BUTTON_B 16 77 #define BUTTON_C 2 78 #define LED 0 79 #elif defined(ESP32) 80 #define BUTTON_A 15 81 #define BUTTON_B 32 82 #define BUTTON_C 14 83 #define LED 13 84 #elif defined(ARDUINO_STM32F2_FEATHER) 85 #define BUTTON_A PA15 86 #define BUTTON_B PC7 87 #define BUTTON_C PC5 88 #define LED PB5 89 #elif defined(TEENSYDUINO) 90 #define BUTTON_A 4 91 #define BUTTON_B 3 92 #define BUTTON_C 8 93 #define LED 13 94 #elif defined(ARDUINO_FEATHER52) 95 #define BUTTON_A 31 96 #define BUTTON_B 30 97 #define BUTTON_C 27 98 #define LED 17 99 #else // 32u4, M0, and 328p 100 #define BUTTON_A 9 101 #define BUTTON_B 6 102 #define BUTTON_C 5 103 #define LED 13 104 #endif 105 106 107 /************ Radio Setup ***************/ 108 // Can be changed to 434.0 or other frequency, must match RX's freq! 109 #define RF69_FREQ 915.0 110 111 #if defined (__AVR_ATmega32U4__) // Feather 32u4 w/Radio 112 #define RFM69_CS 8 113 #define RFM69_INT 7 114 #define RFM69_RST 4 115 #endif 116 117 #if defined(ARDUINO_SAMD_FEATHER_M0) // Feather M0 w/Radio 118 #define RFM69_CS 8 119 #define RFM69_INT 3 120 #define RFM69_RST 4 121 #endif 122 123 #if defined (__AVR_ATmega328P__) // Feather 328P w/wing 124 #define RFM69_INT 3 // 125 #define RFM69_CS 4 // 126 #define RFM69_RST 2 // "A" 127 #endif 128 129 #if defined(ESP32) // ESP32 feather w/wing 130 #define RFM69_RST 13 // same as LED 131 #define RFM69_CS 33 // "B" 132 #define RFM69_INT 27 // "A" 133 #endif 134 135 136 // Singleton instance of the radio driver 137 RH_RF69 rf69(RFM69_CS, RFM69_INT); 138 139 int lastButton=17; //last button pressed for Trellis logic 140 141 int menuList[8]={1,2,3,4,5,6,7,8}; //for rotary encoder choices 142 //int m = 0; //variable to increment through menu list 143 int lastTB[8] = {16, 16, 16, 16, 16, 16, 16, 16}; //array to store per-menu Trellis button 144 145 146 /*******************SETUP************/ 147 void setup() { 148 delay(500); 149 Serial.begin(115200); 150 //while (!Serial) { delay(1); } // wait until serial console is open, 151 //remove if not tethered to computer 152 153 pinMode(PIN_ENCODER_SWITCH, INPUT_PULLUP);//set encoder push switch pin to input pullup 154 pinMode(INTPIN, INPUT); 155 156 // digitalPinToInterrupt(10); //on M0, Encoder library doesn't auto set these as interrupts 157 // digitalPinToInterrupt(12); 158 159 // Initialize OLED display 160 oled.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32) 161 oled.setTextWrap(false); 162 oled.display(); 163 delay(500); 164 oled.clearDisplay(); 165 oled.display(); 166 oled.setTextSize(2); 167 oled.setTextColor(WHITE); 168 pinMode(BUTTON_A, INPUT_PULLUP); 169 pinMode(BUTTON_B, INPUT_PULLUP); 170 pinMode(BUTTON_C, INPUT_PULLUP); 171 pinMode(LED, OUTPUT); 172 pinMode(RFM69_RST, OUTPUT); 173 digitalWrite(RFM69_RST, LOW); 174 175 // manual reset 176 digitalWrite(RFM69_RST, HIGH); 177 delay(10); 178 digitalWrite(RFM69_RST, LOW); 179 delay(10); 180 181 if (!rf69.init()) { 182 Serial.println("RFM69 radio init failed"); 183 while (1); 184 } 185 Serial.println("RFM69 radio init OK!"); 186 187 // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) 188 // No encryption 189 if (!rf69.setFrequency(RF69_FREQ)) { 190 Serial.println("setFrequency failed"); 191 } 192 193 // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the 194 // ishighpowermodule flag set like this: 195 rf69.setTxPower(14, true); 196 197 // The encryption key has to be the same as the one in the server 198 uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 199 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; 200 rf69.setEncryptionKey(key); 201 202 pinMode(LED, OUTPUT); 203 204 Serial.print("RFM69 radio @"); Serial.print((int)RF69_FREQ); Serial.println(" MHz"); 205 206 oled.setCursor(0,0); 207 oled.println("RFM69 @ "); 208 oled.print((int)RF69_FREQ); 209 oled.println(" MHz"); 210 oled.display(); 211 delay(1200); //pause to let freq message be read by a human 212 213 oled.clearDisplay(); 214 oled.setCursor(0,0); 215 oled.println("REMOTE FX"); 216 oled.setCursor(0,16); 217 oled.println("TRIGGER"); 218 oled.display(); 219 220 if (!trellis.begin()) { 221 Serial.println("Could not start trellis, check wiring?"); 222 while(1); 223 } else { 224 Serial.println("NeoPixel Trellis started"); 225 } 226 227 //activate all NeoTrellis keys and set callbacks 228 for(int i=0; i<NEO_TRELLIS_NUM_KEYS; i++){ 229 trellis.activateKey(i, SEESAW_KEYPAD_EDGE_RISING); 230 trellis.activateKey(i, SEESAW_KEYPAD_EDGE_FALLING); 231 trellis.registerCallback(i, blink); 232 } 233 234 //do a little animation to show we're on 235 for (uint16_t i=0; i<trellis.pixels.numPixels(); i++) { 236 trellis.pixels.setPixelColor(i, Wheel(map(i, 0, trellis.pixels.numPixels(), 0, 255))); 237 trellis.pixels.show(); 238 delay(50); 239 } 240 for (uint16_t i=0; i<trellis.pixels.numPixels(); i++) { 241 trellis.pixels.setPixelColor(i, 0x000000); 242 trellis.pixels.show(); 243 delay(50); 244 } 245 246 } 247 248 ////////////////////////////////////////////////////////////////// 249 ////////////////////////////////////////////////////////////////// 250 251 void loop() { 252 253 254 /*************Rotary Encoder Menu***********/ 255 256 //check the encoder knob, set the current position as origin 257 long newpos = knob.read() / 4;//divide for encoder detents 258 259 /* // for debugging 260 Serial.print("pos="); 261 Serial.print(pos); 262 Serial.print(", newpos="); 263 Serial.println(newpos); 264 */ 265 266 if(newpos != pos){ 267 int diff = newpos - pos;//check the different between old and new position 268 if(diff>=1){ 269 m++; 270 m = (m+NUM_SCREENS) % NUM_SCREENS;//modulo to roll over the m variable through the list size 271 } 272 273 if(diff==-1){ //rotating backwards 274 m--; 275 m = (m+NUM_SCREENS) % NUM_SCREENS; 276 } 277 /* //uncomment for debugging or general curiosity 278 Serial.print("Diff = "); 279 Serial.print(diff); 280 Serial.print(" pos= "); 281 Serial.print(pos); 282 Serial.print(", newpos="); 283 Serial.println(newpos); 284 Serial.println(menuList[m]); 285 */ 286 287 pos = newpos; 288 289 // Serial.print("m is: "); 290 //Serial.println(m); 291 //write to the display 292 oled.setCursor(0,3); 293 oled.clearDisplay(); 294 295 int p; //for drawing bullet point menu location pixels 296 int q; 297 298 if (m==0){ 299 for(p=4;p<8;p++){ 300 for(q=0;q<4;q++){ 301 oled.drawPixel(q,p,WHITE); 302 } 303 } 304 oled.print(" Solids"); 305 clearPixels(); 306 trellis.pixels.setPixelColor(0, 50,0,0); 307 trellis.pixels.setPixelColor(1, 50,50,0); 308 trellis.pixels.setPixelColor(2, 0,50,0); 309 trellis.pixels.setPixelColor(3, 0,0,50); 310 trellis.pixels.setPixelColor(4, 20,0,50); 311 trellis.pixels.setPixelColor(5, 50,0,20); 312 trellis.pixels.setPixelColor(6, 100,100,100); 313 trellis.pixels.setPixelColor(7, 10,10,10); 314 trellis.pixels.show(); 315 } 316 if (m==1){ 317 for(p=8;p<12;p++){ 318 for(q=0;q<4;q++){ 319 oled.drawPixel(q,p,WHITE); 320 } 321 } 322 oled.print(" Gradients"); 323 clearPixels(); 324 trellis.pixels.setPixelColor(0, 50,0,0); 325 trellis.pixels.setPixelColor(1, 50,50,0); 326 trellis.pixels.setPixelColor(2, 0,50,0); 327 trellis.pixels.setPixelColor(3, 0,0,50); 328 trellis.pixels.setPixelColor(4, 20,0,50); 329 trellis.pixels.setPixelColor(5, 50,0,20); 330 trellis.pixels.setPixelColor(6, 100,100,100); 331 trellis.pixels.setPixelColor(7, 10,10,10); 332 trellis.pixels.show(); 333 } 334 if (m==2){ 335 for(p=12;p<16;p++){ 336 for(q=0;q<4;q++){ 337 oled.drawPixel(q,p,WHITE); 338 } 339 } 340 oled.print(" Rainbows"); 341 clearPixels(); 342 trellis.pixels.setPixelColor(0, 50,0,0); 343 trellis.pixels.setPixelColor(1, 50,50,0); 344 trellis.pixels.setPixelColor(2, 0,50,0); 345 trellis.pixels.setPixelColor(7, 10,10,10); 346 trellis.pixels.show(); 347 } 348 349 350 351 oled.display(); 352 } 353 354 char radiopacket[20]; 355 356 357 /**************Solids**************/ 358 359 if(m==0){ //next menu item 360 361 if (x==0){ //button 1 sends button A command 362 radiopacket[0] = 'A'; 363 oled.clearDisplay(); 364 oled.setCursor(0,0); 365 oled.print("Solid"); 366 oled.setCursor(50,16); 367 oled.print("RED"); 368 oled.display(); 369 } 370 if (x==1){ //button 2 sends button B command 371 radiopacket[0] = 'B'; 372 oled.clearDisplay(); 373 oled.setCursor(0,0); 374 oled.print("Solid"); 375 oled.setCursor(50,16); 376 oled.print("GOLD"); 377 oled.display(); 378 } 379 if (x==2){ //button 3 sends button C command 380 radiopacket[0] = 'C'; 381 oled.clearDisplay(); 382 oled.setCursor(0,0); 383 oled.print("Solid"); 384 oled.setCursor(50,16); 385 oled.print("GREEN"); 386 oled.display(); 387 } 388 389 if (x==3){ //button 4 sends button D command 390 radiopacket[0] = 'D'; 391 oled.clearDisplay(); 392 oled.setCursor(0,0); 393 oled.print("Solid"); 394 oled.setCursor(50,16); 395 oled.print("BLUE"); 396 oled.display(); 397 } 398 399 if (x==4){ //button 5 sends button E command 400 radiopacket[0] = 'E'; 401 oled.clearDisplay(); 402 oled.setCursor(0,0); 403 oled.print("Solid"); 404 oled.setCursor(50,16); 405 oled.print("PURPLE"); 406 oled.display(); 407 } 408 if (x==5){ //button 6 sends button F command 409 radiopacket[0] = 'F'; 410 oled.clearDisplay(); 411 oled.setCursor(0,0); 412 oled.print("Solid"); 413 oled.setCursor(50,16); 414 oled.print("PINK"); 415 oled.display(); 416 } 417 if (x==6){ //button 7 sends button G command 418 radiopacket[0] = 'G'; 419 oled.clearDisplay(); 420 oled.setCursor(0,0); 421 oled.print("Solid"); 422 oled.setCursor(50,16); 423 oled.print("WHITE"); 424 oled.display(); 425 } 426 427 if (x==7){ //button 8 sends button H command 428 radiopacket[0] = 'H'; 429 oled.clearDisplay(); 430 oled.setCursor(0,0); 431 oled.print("Solid"); 432 oled.setCursor(50,16); 433 oled.print("OFF"); 434 oled.display(); 435 } 436 437 438 trellis.pixels.show(); // tell the trellis to set the LEDs we requested 439 } 440 441 /**************Gradients**************/ 442 443 if(m==1){ //next menu item 444 445 if (x==0){ //button 1 sends button A command 446 radiopacket[0] = 'I'; 447 oled.clearDisplay(); 448 oled.setCursor(0,0); 449 oled.print("Gradient"); 450 oled.setCursor(50,16); 451 oled.print("RED"); 452 oled.display(); 453 } 454 if (x==1){ //button 2 sends button B command 455 radiopacket[0] = 'J'; 456 oled.clearDisplay(); 457 oled.setCursor(0,0); 458 oled.print("Gradient"); 459 oled.setCursor(50,16); 460 oled.print("GOLD"); 461 oled.display(); 462 } 463 if (x==2){ //button 3 sends button C command 464 radiopacket[0] = 'K'; 465 oled.clearDisplay(); 466 oled.setCursor(0,0); 467 oled.print("Gradient"); 468 oled.setCursor(50,16); 469 oled.print("GREEN"); 470 oled.display(); 471 } 472 473 if (x==3){ //button 4 sends button D command 474 radiopacket[0] = 'L'; 475 oled.clearDisplay(); 476 oled.setCursor(0,0); 477 oled.print("Gradient"); 478 oled.setCursor(50,16); 479 oled.print("BLUE"); 480 oled.display(); 481 } 482 483 if (x==4){ //button 5 sends button E command 484 radiopacket[0] = 'M'; 485 oled.clearDisplay(); 486 oled.setCursor(0,0); 487 oled.print("Gradient"); 488 oled.setCursor(50,16); 489 oled.print("PURPLE"); 490 oled.display(); 491 } 492 if (x==5){ //button 6 sends button F command 493 radiopacket[0] = 'N'; 494 oled.clearDisplay(); 495 oled.setCursor(0,0); 496 oled.print("Gradient"); 497 oled.setCursor(50,16); 498 oled.print("PINK"); 499 oled.display(); 500 } 501 if (x==6){ //button 7 sends button G command 502 radiopacket[0] = 'O'; 503 oled.clearDisplay(); 504 oled.setCursor(0,0); 505 oled.print("Gradient"); 506 oled.setCursor(50,16); 507 oled.print("WHITE"); 508 oled.display(); 509 } 510 511 if (x==7){ //button 8 sends button H command 512 radiopacket[0] = 'H'; 513 oled.clearDisplay(); 514 oled.setCursor(0,0); 515 oled.print("Gradient"); 516 oled.setCursor(50,16); 517 oled.print("OFF"); 518 oled.display(); 519 } 520 trellis.pixels.show(); // tell the trellis to set the LEDs we requested 521 } 522 523 524 /**************Rainbows**************/ 525 526 if(m==2){ //next menu item 527 528 if (x==0){ //button 1 sends button A command 529 radiopacket[0] = 'P'; 530 oled.clearDisplay(); 531 oled.setCursor(0,0); 532 oled.print("Rainbow"); 533 oled.setCursor(50,16); 534 oled.print("FADE"); 535 oled.display(); 536 } 537 if (x==1){ //button 2 sends button B command 538 radiopacket[0] = 'Q'; 539 oled.clearDisplay(); 540 oled.setCursor(0,0); 541 oled.print("Rainbow"); 542 oled.setCursor(50,16); 543 oled.print("SOFT"); 544 oled.display(); 545 } 546 if (x==2){ //button 3 sends button C command 547 radiopacket[0] = 'R'; 548 oled.clearDisplay(); 549 oled.setCursor(0,0); 550 oled.print("Rainbow"); 551 oled.setCursor(50,16); 552 oled.print("TWINKLE"); 553 oled.display(); 554 } 555 556 if (x==7){ //button 8 sends button H command 557 radiopacket[0] = 'H'; 558 oled.clearDisplay(); 559 oled.setCursor(0,0); 560 oled.print("Rainbow"); 561 oled.setCursor(50,16); 562 oled.print("OFF"); 563 oled.display(); 564 } 565 trellis.pixels.show(); // tell the trellis to set the LEDs we requested 566 } 567 568 569 570 Serial.print("Sending "); 571 Serial.println(radiopacket[0]); 572 573 rf69.send((uint8_t *)radiopacket, strlen(radiopacket)); 574 rf69.waitPacketSent(); 575 //reset packet so unassigned buttons don't send last command 576 radiopacket[0]='Z'; //also being used to turn off NeoPixels 577 //from any unused button 578 579 if (rf69.waitAvailableTimeout(1000)) { 580 // Should be a message for us now 581 uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; 582 uint8_t len = sizeof(buf); 583 584 if (! rf69.recv(buf, &len)) { 585 Serial.println("Receive failed"); 586 return; 587 } 588 digitalWrite(LED, HIGH); 589 rf69.printBuffer("Received: ", buf, len); 590 buf[len] = 0; 591 592 //Serial.print("TX Got: "); 593 //Serial.println((char*)buf); 594 Serial.print("RSSI: "); 595 Serial.println(rf69.lastRssi(), DEC); 596 597 //delay(1000);//chill for a moment before returning the message to RX unit 598 599 /*************Reply message from RX unit***********/ 600 //oled.clearDisplay(); 601 //oled.print((char*)buf[0]); 602 //oled.print("RSSI: "); oled.print(rf69.lastRssi()); 603 //oled.display(); 604 605 606 digitalWrite(LED, LOW); 607 } 608 609 //lastButton=i;//set for next pass through to turn this one off 610 611 612 trellis.read(); 613 delay(100); //the NeoTrellis has a resolution of around 60hz 614 615 } 616 617 618 /******************************************/ 619 620 // Input a value 0 to 255 to get a color value. 621 // The colors are a transition r - g - b - back to r. 622 uint32_t Wheel(byte WheelPos) { 623 if(WheelPos < 85) { 624 return trellis.pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0); 625 } else if(WheelPos < 170) { 626 WheelPos -= 85; 627 return trellis.pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3); 628 } else { 629 WheelPos -= 170; 630 return trellis.pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3); 631 } 632 return 0; 633 } 634 635 void clearPixels() { 636 for (uint16_t i=0; i<trellis.pixels.numPixels(); i++) { 637 trellis.pixels.setPixelColor(i, 0x000000); 638 trellis.pixels.show(); 639 delay(2); 640 } 641 } 642 643