/ 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