#include <TeensyDMX.h> // DMX library
#include <Audio.h>
#include <SD.h>
#include <SPI.h>
#include <Wire.h>
#include <SerialFlash.h>
#include <Encoder.h> // Rotary encoder library
#include <Bounce2.h> // Button bouce library
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library for ST7735
#include <EEPROM.h>
namespace teensydmx = ::qindesign::teensydmx;
Bounce startbutton = Bounce();
Bounce stopbutton = Bounce();
Bounce encoderbutton = Bounce();
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioPlaySdWav playSdWav1; //xy=116,304
AudioEffectFade fade1; //xy=286,273
AudioEffectFade fade2; //xy=287,338
AudioMixer4 mixer1; //xy=471,292
AudioMixer4 mixer2; //xy=472,358
AudioOutputI2S i2s1; //xy=663,318
AudioConnection patchCord1(playSdWav1, 0, fade1, 0);
AudioConnection patchCord2(playSdWav1, 1, fade2, 0);
AudioConnection patchCord3(fade1, 0, mixer1, 0);
AudioConnection patchCord4(fade2, 0, mixer2, 0);
AudioConnection patchCord5(mixer1, 0, i2s1, 0);
AudioConnection patchCord6(mixer2, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=372,166
// GUItool: end automatically generated code
#define SDCARD_CS_PIN 10 // SD on the audio card pins
#define SDCARD_MOSI_PIN 11
#define SDCARD_MISO_PIN 12
#define SDCARD_SCK_PIN 13
#define statusLED 27
#define fan 26
#define backlightpower 28
#define TFT_CS 36 // 1.8" screen connections
#define TFT_RST 39
#define TFT_DC 38
#define SD_CS 37
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
Encoder encoderknob(34, 35); // Encoder pins
long encoderposition = -999;
long newencoderposition;
byte cursorX; // Text position value
byte cursorY;
byte DMXaddress;
boolean triggered; // True when the start button has been pressed
unsigned long startMillis; // Timer values
unsigned long currentMillis;
boolean flashtext; // Toggle for flashing text
File root;
teensydmx::Sender dmxTx{Serial7}; // Create the DMX sender on Serial7.
//----------------------------------------------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
Wire.begin();
dmxTx.begin();
pinMode(statusLED, OUTPUT);
pinMode(fan, OUTPUT);
pinMode(backlightpower, OUTPUT);
startbutton.attach(31, INPUT_PULLUP ); // USE INTERNAL PULL-UP
stopbutton.attach(32, INPUT_PULLUP ); // USE INTERNAL PULL-UP
encoderbutton.attach(33, INPUT_PULLUP ); // USE INTERNAL PULL-UP
startbutton.interval(5); // interval in ms
stopbutton.interval(5); // interval in ms
encoderbutton.interval(5); // interval in ms
tft.initR(INITR_BLACKTAB);
//tft.initR(INITR_REDTAB); // initialize a ST7735R chip, red tab
//tft.initR(INITR_GREENTAB); // initialize a ST7735R chip, green tab
tft.setRotation(1); // Set screen to Landscape
tft.fillScreen(ST77XX_BLACK);
tft.setTextColor(ST77XX_WHITE);
tft.setTextSize(0);
delay(500);
digitalWrite(backlightpower, LOW); // Backlight on
//---------- Load EEPROM settings --------
//---------------- Audio -----------------
AudioMemory(8); // Audio setup
sgtl5000_1.enable();
sgtl5000_1.volume(0.6);
//AudioNoInterrupts();
//fade1.fadeOut(5);
//fade2.fadeIn(5);
//sgtl5000_1.dacVolume(1, 1); // dacVolume(left,right)
//mixer1.gain(0, 0); // Left mixer 1 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
//mixer2.gain(0, 1); // Right mixer 2 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
//AudioInterrupts();
delay(500);
//---------------- SD CARD ---------------
SPI.setMOSI(SDCARD_MOSI_PIN); // SD card setup
SPI.setSCK(SDCARD_SCK_PIN);
Serial.print(F("Initializing SD card..."));
if (!(SD.begin(SDCARD_CS_PIN)))
{
while (1)
{
Serial.println(F("Unable to access the SD card"));
delay(1000);
}
}
Serial.println(F("SD card OK!"));
delay(500);
root = SD.open("/"); // open SD card main root
printDirectory(root, 0); // print all files names and sizes
root.close(); // close the opened root
Serial.println(F("ONLINE"));
bmpDraw("LOGO1.BMP", 0, 0); // Load logo 1
delay(3000);
//-------------- Test DMX lighting --------------
dmxTx.begin();
Serial.println(F("Testing DMX channels..."));
clearscreen();
tft.setCursor(8, 10);
tft.println("Testing DMX channels...");
delay(500);
tft.setTextColor(ST77XX_GREEN);
tft.setCursor(8, 25);
tft.println("Top light: ");
cursorX = 110; cursorY = 25; DMXaddress = 1; testDMX();
delay(500);
tft.setCursor(8, 37);
tft.println("Bottom light: ");
cursorX = 110; cursorY = 37; DMXaddress = 2; testDMX();
delay(500);
tft.setCursor(8, 49);
tft.println("Strobe 1: ");
cursorX = 110; cursorY = 49; DMXaddress = 3; testDMX();
delay(500);
tft.setCursor(8, 61);
tft.println("Strobe 2: ");
cursorX = 110; cursorY = 61; DMXaddress = 4; testDMX();
delay(500);
tft.setCursor(8, 73);
tft.println("Strobe 3: ");
cursorX = 110; cursorY = 73; DMXaddress = 5; testDMX();
delay(500);
tft.setCursor(8, 85);
tft.println("Strobe 4: ");
cursorX = 110; cursorY = 85; DMXaddress = 6; testDMX();
delay(500);
tft.setCursor(8, 97);
tft.println("Strobe 5: ");
cursorX = 110; cursorY = 97; DMXaddress = 7; testDMX();
delay(500);
tft.setTextColor(ST77XX_RED);
tft.setCursor(8, 112);
tft.println("DMX Testing complete");
//---------------- Flash status LED ---------------
for (int i = 0; i <= 5; i++) {
digitalWrite(statusLED, HIGH);
delay(100);
digitalWrite(statusLED, LOW);
delay(100);
}
//---------------- Test sound system ---------------
Serial.println(F("Testing sound channels..."));
clearscreen();
tft.setCursor(8, 10);
tft.println("Testing sound channels..");
delay(500);
mixer1.gain(0, 1); // Left mixer 1 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
mixer2.gain(0, 0); // Right mixer 2 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
tft.setTextColor(ST77XX_GREEN);
tft.setCursor(8, 25);
tft.println("Left channel: ");
delay(300);
playSdWav1.play("Lefttest.WAV");
delay(3000);
tft.setTextColor(ST77XX_YELLOW);
tft.setCursor(120, 25);
tft.println("OK");
mixer1.gain(0, 0); // Left mixer 1 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
mixer2.gain(0, 1); // Right mixer 2 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
delay(500);
tft.setTextColor(ST77XX_GREEN);
tft.setCursor(8, 37);
tft.println("Right channel: ");
delay(300);
playSdWav1.play("Rightest.WAV");
delay(3000);
tft.setTextColor(ST77XX_YELLOW);
tft.setCursor(120, 37);
tft.println("OK");
delay(500);
tft.setTextColor(ST77XX_RED);
tft.setCursor(8, 112);
tft.println("Sound Testing complete");
mixer1.gain(0, 1); // Left mixer 1 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
mixer2.gain(0, 1); // Right mixer 2 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
delay(2000);
mainlogo(); // Main screen logo
delay(2000);
Serial.println("ONLINE");
startMillis = millis();
playSdWav1.play("PlaneM1.WAV"); // Non-panning plane sound effect - 12s long
} //-------------- End of setup--------------------
//------------- Send BMP to screen ------------------ // Display BMP file
#define BUFFPIXEL 20
void bmpDraw(char *filename, uint8_t x, uint16_t y) {
File bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24)
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3 * BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
uint8_t buffidx = sizeof(sdbuffer); // Current position in sdbuffer
boolean goodBmp = false; // Set to true on valid header parse
boolean flip = true; // BMP is stored bottom-to-top
int w, h, row, col;
uint8_t r, g, b;
uint32_t pos = 0, startTime = millis();
if ((x >= tft.width()) || (y >= tft.height())) return;
Serial.println();
Serial.print(F("Loading image '"));
Serial.print(filename);
Serial.println('\'');
// Open requested file on SD card
if ((bmpFile = SD.open(filename)) == 0) {
Serial.print(F("File not found"));
return;
}
// Parse BMP header
if (read16(bmpFile) == 0x4D42) { // BMP signature
Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
(void)read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
// Read DIB header
Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
if (read16(bmpFile) == 1) { // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
if ((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed
goodBmp = true; // Supported BMP format -- proceed!
Serial.print(F("Image size: "));
Serial.print(bmpWidth);
Serial.print('x');
Serial.println(bmpHeight);
rowSize = (bmpWidth * 3 + 3) & ~3;
if (bmpHeight < 0) {
bmpHeight = -bmpHeight;
flip = false;
}
// Crop area to be loaded
w = bmpWidth;
h = bmpHeight;
if ((x + w - 1) >= tft.width()) w = tft.width() - x;
if ((y + h - 1) >= tft.height()) h = tft.height() - y;
// Set TFT address window to clipped image bounds
tft.startWrite();
tft.setAddrWindow(x, y, w, h);
for (row = 0; row < h; row++) { // For each scanline...
if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
else // Bitmap is stored top-to-bottom
pos = bmpImageoffset + row * rowSize;
if (bmpFile.position() != pos) { // Need seek?
tft.endWrite();
bmpFile.seek(pos);
buffidx = sizeof(sdbuffer); // Force buffer reload
}
for (col = 0; col < w; col++) { // For each pixel...
// Time to read more pixel data?
if (buffidx >= sizeof(sdbuffer)) { // Indeed
bmpFile.read(sdbuffer, sizeof(sdbuffer));
buffidx = 0; // Set index to beginning
tft.startWrite();
}
// Convert pixel from BMP to TFT format, push to display
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
tft.pushColor(tft.color565(r, g, b));
} // end pixel
} // end scanline
tft.endWrite();
Serial.print(F("Loaded in "));
Serial.print(millis() - startTime);
Serial.println(" ms");
} // end goodBmp
}
}
bmpFile.close();
if (!goodBmp) Serial.println(F("BMP format not recognized."));
}
uint16_t read16(File f) {
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(File f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
//------------- Print SD card directory ------------- // Obtain the contents of the SD card
void printDirectory(File dir, int numTabs) {
while (true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i = 0; i < numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs + 1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}
// ----------- Test DMX ------------ // Test the DMX channel requested
void testDMX() {
delay(200);
for (int i = 0; i < 255; i = i + 10) {
tft.fillRect(108, cursorY - 2, 25, 12, ST77XX_BLACK);
tft.setCursor(cursorX, cursorY);
tft.println(i);
dmxTx.set(DMXaddress, i);
delay(30);
}
delay(500);
for (int i = 255; i > 0; i = i - 10) {
tft.fillRect(108, cursorY - 2, 25, 12, ST77XX_BLACK);
tft.setCursor(cursorX, cursorY);
tft.println(i);
dmxTx.set(DMXaddress, i);
delay(30);
}
tft.setTextColor(ST77XX_YELLOW);
tft.fillRect(108, cursorY - 2, 25, 12, ST77XX_BLACK);
tft.setCursor(120, cursorY);
tft.println("OK");
tft.setTextColor(ST77XX_GREEN);
}
// ----------- Clear screen ------------ // Clear screen and draw standard border
void clearscreen() {
tft.fillScreen(ST77XX_BLACK);
tft.drawRect(1, 2 , 159, 126, ST77XX_WHITE);
tft.drawRect(3, 4 , 155, 122, ST77XX_RED);
}
//----------- Flash waiting -------------
void waiting() {
if (flashtext == true) {
flashtext = false;
tft.fillRect(10, 70, 140, 40, ST77XX_BLACK); // Awaiting button press text message
tft.drawRoundRect(10, 70, 140, 40, 3, ST77XX_RED);
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(2);
tft.setCursor(33, 74);
tft.println("AWAITING");
tft.setCursor(39, 92);
tft.println("TRIGGER");
}
else
{
tft.fillRect(12, 72, 136, 36, ST77XX_BLACK); // Awaiting button press text message
flashtext = true;
}
}
//----------- Flash plane approaching -------------
void approaching() {
if (flashtext == true) {
flashtext = false;
tft.fillRect(10, 70, 140, 40, ST77XX_BLACK); // System running
tft.drawRoundRect(10, 70, 140, 40, 3, ST77XX_RED);
tft.setTextColor(ST77XX_YELLOW);
tft.setTextSize(2);
tft.setCursor(52, 74);
tft.println("PLANE");
tft.setCursor(15, 92);
tft.println("APPROACHING");
}
else
{
tft.fillRect(12, 72, 136, 36, ST77XX_BLACK); // Awaiting button press text message
flashtext = true;
}
}
//--------- Main logo -----------
void mainlogo() {
tft.fillScreen(ST77XX_BLACK);
//root = SD.open("/"); // open SD card main root
bmpDraw("MAINLOGO.BMP", 0, 0); // Load main Logo
//root.close(); // close the file
delay(500);
}
//################################################################################################################
void loop() {
currentMillis = millis();
startbutton.update();
stopbutton.update();
encoderbutton.update();
if (triggered == false) { //------------------------------------------------------------------------------------- Waiting for start trigger, so setup menu available
if (currentMillis > (startMillis + 1000)) {
startMillis = currentMillis;
waiting();
}
//----- Check Address rotary encoder-----
newencoderposition = encoderknob.read();
if (newencoderposition != encoderposition) {
Serial.print("Encoder = ");
Serial.println(newencoderposition);
encoderposition = newencoderposition;
}
//-------------- Buttons ----------------
if (startbutton.changed() ) { // Start button
int deboucedInput = startbutton.read();
if (deboucedInput == LOW ) {
Serial.println("SYSTEM TRIGGERED");
triggered = true;
mainlogo();
approaching();
//mixer1.gain(0, 0); // Left mixer 1 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
//mixer2.gain(0, 1); // Right mixer 2 channel. gain(channel,level). Level 0 = off. 1 = direct passthrough. >1 to 32767 amplifies the signal
}
}
if (encoderbutton.changed() ) { // Encoder button (setup menu)
int deboucedInput = encoderbutton.read();
if (deboucedInput == LOW ) {
Serial.println("encoder button");
setupmenu();
}
}
}
else
{ // Start button has been pressed---------------------------------------------------------------------------
if (currentMillis > (startMillis + 1000)) { // Flash plane approaching
startMillis = currentMillis;
approaching();
}
if (stopbutton.changed() ) { // Stop button
int deboucedInput = stopbutton.read();
if (deboucedInput == LOW ) {
Serial.println("Stop button operated");
triggered = false;
mainlogo();
}
}
}
}
//##############################################################################################################
// -------------- Setup ---------------
void setupmenu() {
clearscreen();
tft.setTextColor(ST77XX_GREEN);
tft.setTextSize(0);
tft.setCursor(5, 5);
tft.println("SETUP MENU");
}