//DMX to Serial Interface //LIBRARIES #include //LCD via I2c (also loads wire.h for I2C comms) #include //EEPROM I/O #include //SPI Bus for SD Card #include //Receive DMX on hardware serial port via RS485 adaptor #include //Software serial for comms on any digital pins #include //SD Card (can't use SD.h as requires Serial object which clashes with DMXSerial library) //DEFINITIONS (Constants) #define SdCS 10 //Set SD Card ChipSelect (CS) Pin - use 10 as SD.h reserves this pin anyhow #define SerTxPin 4 //Serial port transmit pin #define SerRxPin 3 //Serial port transmit pin //EEPROM Registers (of MSB for multi byte values) #define EEepromCheck 0 //Check value to confirm config has been written to EEPROM (1 byte unsigned integer, should be EepromCheckVal defined below) #define EBaud 1 //Baud Rate (4 bytes - long unsigned integer) #define ETimeout 5 //Serial transmit timeout (ms) (2 bytes - unsigned integer) #define EDMXDebounce 7 //DMX change detect debounce (ms) (2 bytes - unsigned integer) #define EBacklightEn 9 //Backlight enable (1 byte unsigned integer (0/1)) #define EBacklightTimeout 10 //Backlight timeout (ms) (2 bytes - unsigned integer) #define ENCommands 12 //Number of defined commands (1 byte unsigned integer) #define ECommandData 13 //Command data, in frames of (MaxCmdLength + MaxRespLength + 23) bytes for each defined command: //Name Length (1 byte unsigned integer) //Name (16 bytes) //command length (1 byte unsigned integer) //command (MaxCmdLength bytes) //acknowledgment length (1 byte unsigned integer) //acknowledgement (MaxRespLength bytes) //DMX Channel (2 bytes - unsigned integer) //DMX Low threshold (1 byte unsigned integer) //DMX High threshold (1 byte unsigned integer) //Will be followed by //Number of LEDs (1 byte unsigned integer) //LED data (On Command, Off Command and pin number for each LED, all 1 byte unsigned integer //Number of buttons (1 byte unsigned integer) //Button Data (command and pin number for each button, all 1 byte unsigned integer #define EepromCheckVal 230 //Check value used to test if setup available on Eeprom #define MaxCommands 4 //Maximum permissible commands that can be defined (data structures are sized to this) #define MaxCmdLength 8 //Maximum Command length in bytes #define MaxButtons 2 //Maximum permissible number of hardware buttons #define MaxRespLength 4 //Maximum Command Response length in bytes #define ButInt 500 //Maximum button press interval (ms), stops multiple triggering of button command actions due to fast running of code. 500ms is suitable, short enough not to be noticeable by user, but long enough to debounce #define TypeDMX 1 //Command type is DMX trigger #define TypeButton 0 //Command type is button trigger #define LongIntMax 4294967295 //Maximum value of an unsigned long integer, used in timer check in case value has reset to zero in the interval #define MaxMsgLength 16 //Maximum LCD message length for a command, 16 characters = full row #define RedLEDPin 5 //Pin for Red LED, used for error messages on setup, may be shared with indicator LEDs defined in config file #define GreenLEDPin 6 //Pin for Green LED, used for messages on setup, may be shared with indicator LEDs defined in config file //GLOBAL VARIABLES byte SetupError; //1 if error occurs in setup that prevents loop execution byte CommandFrameLength; //Bytes of 1 set of commands (MaxCmdLength + MaxRespLength + MaxMsgLength + 7) byte ELEDData; //Start of LED data on EEPROM byte EButtonData; //Start of button data on EEPROM byte OldDMXVal[MaxCommands - 1]; //Old Values of DMX channels that need to be checked byte CommandSent[MaxCommands - 1]; //Whether command has been sent for current period where criteria met unsigned long DMXChangeTime[MaxCommands - 1]; //Time in ms since values last changed across threshold byte DMXVal; //Current value of DMX channel being checked unsigned long DebounceDatum[MaxCommands - 1]; //Clock time at which the value of each DMX channel last changed byte RS232RespCode; //Response code for RS232 transmission 1 OK, 0 error unsigned long RS232Datum; //Time datum for RS232 timeout byte ButtonState; //State of the button being checked, 1 = pressed, 0 = open unsigned long OldButTime[MaxButtons]; //Previous button press time unsigned long BacklightDatum; //Backlight timeout datum char TempStr10[11]; //Temporary string read from file //INITIALISATION LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //LCD address and pin map to I2C - addr, en,rw,rs,d4,d5,d6,d7,bl,blpol SoftwareSerial mySerial(SerRxPin,SerTxPin); //pins connected to Rx and Tx pins of TTL to serial board respectively SdFat SD; //SD Card File DataFile; //Reference to file //___________ void setup() { //INITIALISE DEVICES //LCD //Wire.begin(); not needed as called by LCD initialisation lcd.begin(16,2); //LCD Characters columns, rows lcd.noBacklight(); //Backlight off //LEDs for setup indications, may be defined a second time if shared with indicators defined in config file pinMode(RedLEDPin, OUTPUT); pinMode(GreenLEDPin, OUTPUT); digitalWrite(RedLEDPin, LOW); digitalWrite(GreenLEDPin, LOW); //SD Card pinMode(SdCS, OUTPUT); //SD Card CS Pin digitalWrite(SdCS, HIGH); //Set pin high to enable device //DMX Port (RS485) DMXSerial.init(DMXReceiver); DMXSerial.resetUpdated(); //Reset update flag for checking for new DMX data //RETRIEVE SETTINGS CommandFrameLength = MaxCmdLength + MaxRespLength + MaxMsgLength + 7; SetupError = 0; //Default to no setup error //If SD card present and exists, read values to variables and EEPROM if (SD.begin(SdCS)) { if (SD.exists("dmx_ser.cfg")) { DataFile = SD.open(F("dmx_ser.cfg"), FILE_READ); if (DataFile) { //Baud Rate ReadNumberFromFile(6); EEPROMWriteUint32(EBaud, atol(TempStr10)); //Serial Timeout ReadNumberFromFile(5); EEPROMWriteUint16(ETimeout, atoi(TempStr10)); //DMX Debounce ReadNumberFromFile(5); EEPROMWriteUint16(EDMXDebounce, atoi(TempStr10)); //Backlight Enable ReadNumberFromFile(1); EEPROMWriteUint8(EBacklightEn, atoi(TempStr10)); //Backlight Timeout ReadNumberFromFile(5); EEPROMWriteUint16(EBacklightTimeout, atoi(TempStr10)); //Number of Commands ReadNumberFromFile(3); EEPROMWriteUint8(ENCommands, atoi(TempStr10)); //Skip header line - search for next line feed character (twice as one at end of current line) DataFile.seek(FindNext(DataFile,10)); DataFile.seek(FindNext(DataFile,10)); //Commands for (byte CmdNo = 0; CmdNo < EEPROM.read(ENCommands); CmdNo++) { //Read parameters for each command - comma delimited //Name for LCD - length ReadCSVFromFile(3); EEPROMWriteUint8(ENCommands + (CmdNo * CommandFrameLength) + 1, atoi(TempStr10)); //Name FileToEEPROM(ENCommands + (CmdNo * CommandFrameLength) + 2, EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + 1)); //Wipe out unused bytes as they are still written to the LCD for(byte CharNo = EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + 1) + 1; CharNo <= MaxMsgLength ; CharNo++) { EEPROMWriteUint8(ENCommands + (CmdNo * CommandFrameLength) + 1 + CharNo, 32); } //Skip comma DataFile.read(); //skip comma delimiter //Command Length and following comma ReadCSVFromFile(3); EEPROMWriteUint8(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + 2, atoi(TempStr10)); //Command FileToEEPROM(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + 3, EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + 2)); DataFile.read(); //skip comma delimiter //Acknowledgment Length and following comma ReadCSVFromFile(3); EEPROMWriteUint8(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3, atoi(TempStr10)); //Acknowledgment FileToEEPROM(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 4, EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3)); DataFile.read(); //skip comma delimiter //DMX Channel and following comma ReadCSVFromFile(3); EEPROMWriteUint16(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 4, atoi(TempStr10)); //DMX Low Threshold and following comma ReadCSVFromFile(3); EEPROM.write(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 6, atoi(TempStr10)); //DMX High Threshold and carriage return ReadCSVFromFile(3); EEPROM.write(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 7, atoi(TempStr10)); //Line feed DataFile.read(); } //Store start of EEPROM LED data ELEDData = ENCommands + (EEPROM.read(ENCommands) * CommandFrameLength) + 1; //Number of LEDs ReadNumberFromFile(3); //This skips to number after next colon EEPROM.write(ELEDData, atoi(TempStr10)); //Skip header line - search for next line feed character DataFile.seek(FindNext(DataFile,10)); DataFile.seek(FindNext(DataFile,10)); //LED Data for (byte LEDNo = 0; LEDNo < EEPROM.read(ELEDData); LEDNo++) { //On Command for LED ReadCSVFromFile(3); EEPROM.write(ELEDData + (LEDNo * 3) + 1, atoi(TempStr10)); //Off command for LED ReadCSVFromFile(3); EEPROM.write(ELEDData + (LEDNo * 3) + 2, atoi(TempStr10)); //Pin for LED ReadCSVFromFile(3); EEPROM.write(ELEDData + (LEDNo * 3) + 3, atoi(TempStr10)); //Line feed DataFile.read(); } //Store start of EEPROM Button data EButtonData = ELEDData + 1 + (EEPROM.read(ELEDData) * 3); //Number of Buttons ReadNumberFromFile(3); //This skips to number after next colon EEPROM.write(EButtonData, atoi(TempStr10)); //Skip header line - search for next line feed character (twice as one at end of current line) DataFile.seek(FindNext(DataFile,10)); DataFile.seek(FindNext(DataFile,10)); //Button Data for (byte ButtonNo = 0; ButtonNo < EEPROM.read(EButtonData); ButtonNo++) { //Command for button ReadCSVFromFile(3); EEPROM.write(EButtonData + (ButtonNo * 2) + 1, atoi(TempStr10)); //Pin for button ReadCSVFromFile(3); EEPROM.write(EButtonData + (ButtonNo * 2) + 2, atoi(TempStr10)); //Line Feed in case extra parameters added to file DataFile.read(); //NB file must include CR+LF before EOF } DataFile.close(); //Write value to EEPROM to denote values are available on EEPROM EEPROM.write(EEepromCheck, EepromCheckVal); } else { //File wouldn't open, fatal error lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: Cannot ")); lcd.setCursor(0,1); lcd.print(F("open dmx_ser.cfg")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } } else { //File not found, write example file if possible then fatal error if(EEPROM.read(EEepromCheck) == EepromCheckVal) { //Write sample file DataFile = SD.open(F("dmx_ser.cfg"), FILE_WRITE); //create example file if (DataFile) { //Write file from EEPROM values //Baud Rate DataFile.print(F("RS232 Baud rate (bps): ")); DataFile.println(EEPROMReadUint32(EBaud)); //Serial Timeout DataFile.print(F("RS232 Timeout (ms): ")); DataFile.println(EEPROMReadUint16(ETimeout)); //DMX Debounce DataFile.print(F("DMX Debounce (ms): ")); DataFile.println(EEPROMReadUint16(EDMXDebounce)); //Backlight Enable DataFile.print(F("Backlight Enable (0/1): ")); DataFile.println(EEPROM.read(EBacklightEn)); //Backlight Timeout DataFile.print(F("Backlight Timeout (ms): ")); DataFile.println(EEPROMReadUint16(EBacklightTimeout)); //Number of Commands DataFile.print(F("Number of Commands: ")); DataFile.println(EEPROM.read(ENCommands)); //Command Header DataFile.println(F("Name Length,Name,Command length,Command,Acknowledgment Length,Acknowledgment,DMX Channel,DMX Low Threshold,DMX High Threshold")); //Commands for(byte CmdNo = 0; CmdNo < EEPROM.read(ENCommands); CmdNo++) { //Parameters for each command //Name Length DataFile.print(EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + 1)); DataFile.print(F(",")); //Name EEPROMToFile(ENCommands + (CmdNo * CommandFrameLength) + 2, EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + 1)); DataFile.print(F(",")); //Command Length DataFile.print(EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + 2)); DataFile.print(F(",")); //Command EEPROMToFile(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + 3, EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + 2)); DataFile.print(F(",")); //Acknowledgment Length DataFile.print(EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3)); DataFile.print(F(",")); //Acknowledgment EEPROMToFile(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 4, EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3)); DataFile.print(F(",")); //DMX Channel DataFile.print(EEPROMReadUint16(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 4)); DataFile.print(F(",")); //DMX Low Threshold DataFile.print(EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 6)); DataFile.print(F(",")); //DMX High Threshold DataFile.print(EEPROM.read(ENCommands + (CmdNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 7)); DataFile.println(); } //Store start of EEPROM LED data ELEDData = ENCommands + (EEPROM.read(ENCommands) * CommandFrameLength) + 1; //Number of LEDs DataFile.print(F("Number of LEDs: ")); DataFile.println(EEPROM.read(ELEDData)); //LED Header DataFile.println(F("On Command Number,Off Command Number,Pin Number")); //LED Data for (byte LEDNo = 0; LEDNo < EEPROM.read(ELEDData); LEDNo++) { DataFile.print(EEPROM.read(ELEDData + (LEDNo * 3) + 1)); DataFile.print(F(",")); DataFile.print(EEPROM.read(ELEDData + (LEDNo * 3) + 2)); DataFile.print(F(",")); DataFile.print(EEPROM.read(ELEDData + (LEDNo * 3) + 3)); DataFile.println(); } //Store start of EEPROM Button data EButtonData = ELEDData + 1 + (EEPROM.read(ELEDData) * 3); //Number of Buttons DataFile.print(F("Number of Buttons: ")); DataFile.println(EEPROM.read(EButtonData)); //Button Header DataFile.println(F("Command Number,Pin Number")); //Button Data for (byte ButtonNo = 0; ButtonNo < EEPROM.read(EButtonData); ButtonNo++) { DataFile.print(EEPROM.read(EButtonData + (ButtonNo * 2) + 1)); DataFile.print(F(",")); DataFile.print(EEPROM.read(EButtonData + (ButtonNo * 2) + 2)); DataFile.println(); } DataFile.print(F("End of File")); //Ensures CR+LF after final value read in //Close File DataFile.close(); //User Message lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F("dmx_ser.cfg not ")); lcd.setCursor(0,1); lcd.print(F(" found, created ")); digitalWrite(RedLEDPin, HIGH); digitalWrite(GreenLEDPin, HIGH); SetupError = 1; return; } else { //Couldn't create example file lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: Config ")); lcd.setCursor(0,1); lcd.print(F("file not created")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } } else { //Error condition lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: No setup")); lcd.setCursor(0,1); lcd.print(F("on card / memory")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } } } else { //No card present, use values from EEPROM if possible if(EEPROM.read(EEepromCheck) == EepromCheckVal) { //EEPROM values available //User message if (EEPROM.read(EBacklightEn)) { BacklightDatum = millis(); lcd.backlight(); } lcd.setCursor(0,0); lcd.print(F(" No card, setup ")); lcd.setCursor(0,1); lcd.print(F("read from memory")); digitalWrite(GreenLEDPin, HIGH); delay(3000); //Store start of EEPROM LED data ELEDData = ENCommands + (EEPROM.read(ENCommands) * CommandFrameLength) + 1; //Store start of EEPROM Button data EButtonData = ELEDData + 1 + (EEPROM.read(ELEDData) * 3); } else { //EEPROM values not available lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: No setup")); lcd.setCursor(0,1); lcd.print(F("on card / memory")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } } //Initialise LED pins for (byte LEDNo = 0; LEDNo < EEPROM.read(ELEDData); LEDNo++) { pinMode(EEPROM.read(ELEDData + (LEDNo * 3) + 3), OUTPUT); // Configure LED pin as output digitalWrite(EEPROM.read(ELEDData + (LEDNo * 3) + 3), LOW); //Turn off } //Initialise button pins for (byte ButtonNo = 0; ButtonNo < EEPROM.read(EButtonData); ButtonNo++) { pinMode(EEPROM.read(EButtonData + (ButtonNo * 2) + 2), INPUT_PULLUP); //Button pins } //INITIALISE RS232 with defined baud rate mySerial.begin(EEPROMReadUint32(EBaud)); //Only supports 8N1 which is what we want anyway //Print baud rate and comms timeout to screen and pause lcd.clear(); lcd.setCursor(0,0); lcd.print(F("Baud: ")); lcd.print(EEPROMReadUint32(EBaud)); lcd.setCursor(0,1); lcd.print(F("Timeout: ")); lcd.print(EEPROMReadUint16(ETimeout)); lcd.print(F("s")); if (EEPROM.read(EBacklightEn)) { BacklightDatum = millis(); lcd.backlight(); } delay(3000); lcd.clear(); //READ INITIAL STATES OF DMX CHANNELS used into variables for (byte CommandNo = 0; CommandNo < EEPROM.read(ENCommands); CommandNo++) { OldDMXVal[CommandNo] = DMXSerial.read(EEPROMReadUint16(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 4)); CommandSent[CommandNo] = 0; DebounceDatum[CommandNo] = millis(); } //Default to no RS232 error RS232RespCode = 1; } //___________ void loop() { if(!SetupError) { //Backlight timeout if (EEPROM.read(EBacklightEn)) { if(IntervalCheck(BacklightDatum, EEPROMReadUint16(EBacklightTimeout))) { lcd.noBacklight(); } } //Loop over commands for (byte CommandNo = 0; CommandNo < EEPROM.read(ENCommands); CommandNo++) { //Read current DMX value and compare to OldValue DMXVal = DMXSerial.read(EEPROMReadUint16(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 4)); //compare to thresholds if ((DMXVal >= EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 6)) && (DMXVal <= EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 7))) { //If old DMXvalue not between thresholds, then reset debounce timer for the command if ((OldDMXVal[CommandNo] < EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 6)) || (OldDMXVal[CommandNo] > EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 7))) { DebounceDatum[CommandNo] = millis(); } else { //If old DMX value between thresholds and debounce timer exceeds debounce threshold, and command not already sent, action the command if((IntervalCheck(DebounceDatum[CommandNo], EEPROMReadUint16(EDMXDebounce))) && (CommandSent[CommandNo] == 0)) { RS232RespCode = 0; RS232Datum = millis(); //Repeat command until success or timeout do { SendRS232(CommandNo, TypeDMX, 0); //Final parameter not used } while((RS232RespCode == 0) && (IntervalCheck(RS232Datum, EEPROMReadUint16(ETimeout)) == 0)); CommandSent[CommandNo] = 1; //Don't send again until DMX value has been outside the range for the debounce interval and returns to within range fot the debounce interval if (RS232RespCode == 0) { lcd.setCursor(0,0); //column 0 row 0 lcd.print(F("RS232 Timeout ")); if (EEPROM.read(EBacklightEn)) { BacklightDatum = millis(); lcd.backlight(); } } } } } else { //DMX value not within thresholds //If old DMXvalue between thresholds, then reset debounce timer for the command if ((OldDMXVal[CommandNo] >= EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 6)) && (OldDMXVal[CommandNo] <= EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 7))) { DebounceDatum[CommandNo] = millis(); } //If value outside threshold for at least the debounce interval, mark command as not sent if(IntervalCheck(DebounceDatum[CommandNo], EEPROMReadUint16(EDMXDebounce))) { CommandSent[CommandNo] = 0; //So next command is actioned once debounce is met } } //Update Old DMX value OldDMXVal[CommandNo] = DMXVal; } //Read buttons for (byte ButtonNo = 0; ButtonNo < EEPROM.read(EButtonData); ButtonNo++) { //Read Button State ButtonState = ReadButton(ButtonNo); if (ButtonState) { //If the last transmission resulted in an error, clear the error by pressing any button if(RS232RespCode == 0) { RS232RespCode = 1; lcd.setCursor(0,0); //column 0 row 0 lcd.print(F(" ")); } else //No error to clear, action the command { //Button Pressed RS232RespCode = 0; //Repeat command until success or timeout RS232Datum = millis(); do { SendRS232(EEPROM.read(EButtonData + (ButtonNo * 2) + 1) - 1, TypeButton, ButtonNo); //command number is zero based for the function but stored as 1-based } while((RS232RespCode == 0) && (IntervalCheck(RS232Datum, EEPROMReadUint16(ETimeout)) == 0)); if (RS232RespCode == 0) { lcd.setCursor(0,0); //column 0 row 0 lcd.print(F("RS232 Timeout ")); if (EEPROM.read(EBacklightEn)) { BacklightDatum = millis(); lcd.backlight(); } } } } } } } //___________ //Copy data from EEPROM to file void EEPROMToFile(unsigned int StartAddress,unsigned int NBytes) { for (unsigned int j = 0; j < NBytes; j++) { DataFile.write(EEPROM.read(StartAddress + j)); //Write converts to ASCII characters } } //__________ //Print EEPROM values to LCD as ASCII characters void EEPROMToLCD(unsigned int StartAddress,unsigned int NBytes) { for (unsigned int j = 0; j < NBytes; j++) { lcd.write(EEPROM.read(StartAddress + j)); //Write writes the character not the ascii value } } //__________ //Read an unsigned 16 bit integer from 2 EEPROM addresses unsigned int EEPROMReadUint16(unsigned int StartAddress) { byte TempBytes[2]; //Temporary store for the upper and lower byte for (byte j = 0; j < 2; j++) { TempBytes[j] = EEPROM.read(StartAddress + j); //First argument is address, second is value } return ((int)(TempBytes[0]) << 8) + ((int)(TempBytes[1])); } //__________ //Read an unsigned 32 bit integer from 4 EEPROM addresses unsigned long EEPROMReadUint32(unsigned int StartAddress) { byte TempBytes[4]; //Temporary store for the bytes, unsigned long is four bytes for (byte j = 0; j < 4; j++) { TempBytes[j] = EEPROM.read(StartAddress + j); //Argument is address } //Convert to unsigned long return ((long)(TempBytes[0]) << 24) + ((long)(TempBytes[1]) << 16) + ((long)(TempBytes[2]) << 8) + ((long)(TempBytes[3])); } //__________ //Write an unsigned 8 bit integer to EEPROM address with EEPROM overrun check void EEPROMWriteUint8(unsigned int StartAddress, byte ValToWrite) { //Check the data will fit in EEPROM, E2END returns maximum address which varies by Arduino model if(StartAddress > E2END) { lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: Config ")); lcd.setCursor(0,1); lcd.print(F(" file too large ")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } EEPROM.write(StartAddress, ValToWrite); //First argument is address, second is value } //__________ //Write an unsigned 16 bit integer to 2 EEPROM addresses void EEPROMWriteUint16(unsigned int StartAddress, unsigned int ValToWrite) { //Check the data will fit in EEPROM, E2END returns maximum address which varies by Arduino model if(StartAddress + 1 > E2END) { lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: Config ")); lcd.setCursor(0,1); lcd.print(F(" file too large ")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } byte TempBytes[2]; TempBytes[0] = (byte) ((ValToWrite & 0x0000FF00) >> 8 ); TempBytes[1] = (byte) ((ValToWrite & 0X000000FF) ); for (byte j = 0; j < 2; j++) { EEPROM.write(StartAddress + j, TempBytes[j]); //First argument is address, second is value } } //__________ //Write an unsigned 32 bit integer to 4 EEPROM addresses void EEPROMWriteUint32(unsigned int StartAddress, unsigned long ValToWrite) { //Check the data will fit in EEPROM, E2END returns maximum address which varies by Arduino model if(StartAddress + 3 > E2END) { lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: Config ")); lcd.setCursor(0,1); lcd.print(F(" file too large ")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } byte TempBytes[4]; //Temporary store for the bytes, unsigned long is four bytes TempBytes[0] = (byte) ((ValToWrite & 0xFF000000) >> 24 ); TempBytes[1] = (byte) ((ValToWrite & 0x00FF0000) >> 16 ); TempBytes[2] = (byte) ((ValToWrite & 0x0000FF00) >> 8 ); TempBytes[3] = (byte) ((ValToWrite & 0X000000FF) ); for (byte j = 0; j < 4; j++) { EEPROM.write(StartAddress + j, TempBytes[j]); //First argument is address } } //__________ //Copy data from file to EEPROM void FileToEEPROM(unsigned int StartAddress,unsigned int NBytes) { //Check the data will fit in EEPROM, E2END returns maximum address which varies by Arduino model if((StartAddress + NBytes - 1) > E2END) { lcd.backlight(); //Backlight on lcd.setCursor(0,0); lcd.print(F(" ERROR: Config ")); lcd.setCursor(0,1); lcd.print(F(" file too large ")); digitalWrite(RedLEDPin, HIGH); SetupError = 1; return; } //Write Data for (unsigned int j = 0; j < NBytes; j++) { EEPROM.write(StartAddress + j, DataFile.read()); } } //__________ //Function to find the next instance of a character in the sd card file //Returns the position of the next byte after the character sought unsigned int FindNext(File DF,byte CharToFind) { byte DataRead; do { DataRead = DF.read(); } while ((DataRead != 255) && (DataRead != CharToFind)); //end of file or character position return DF.position(); } //__________ void FlushRS232Input() //Flush input buffer of RS232 by reading all available data { char RespChar = 255; while (mySerial.available()) { RespChar = mySerial.read(); } } //__________ byte IntervalCheck(unsigned long Datum, unsigned long Interval) //return 1 if interval has elapsed { unsigned long CurrentTime = millis(); if ((CurrentTime < Datum) && (((LongIntMax - Datum) + (CurrentTime)) > Interval)) { return 1; } else if ((CurrentTime < Datum) && (((LongIntMax - Datum) + (CurrentTime)) <= Interval)) { return 0; } else if ((CurrentTime - Datum) > Interval) { return 1; } else { return 0; } } //___________ //Function to read value of button input, return 1 if button pressed (allowing for debounce) byte ReadButton(byte ButtonNo) { //Minimum interval between keypresses to avoid multiple triggers if(IntervalCheck(OldButTime[ButtonNo], ButInt)) { // read the button value if (!digitalRead(EEPROM.read(EButtonData + (ButtonNo * 2) + 2))) //Button is LOW when pressed { OldButTime[ButtonNo] = millis(); //Store time of this button press for debounce return 1; //button pressed } else { return 0; //button not pressed } } else //Insufficient time since previous button action: ignore button state { return 0; } } //___________ //Function to read a number until the next comma or carriage return void ReadCSVFromFile(byte MaxDigits) { byte DataRead; //Raw data from file byte CharNo = 1; memset(TempStr10, 0, sizeof(TempStr10)); //Clear the temporary string do { DataRead = DataFile.read(); TempStr10[CharNo - 1] = DataRead; CharNo++; } while ((DataRead != 13) && (DataRead != 44) && (CharNo < MaxDigits + 1)); if(CharNo == MaxDigits + 1) { //Need to skip the comma or CR as it wasn't read DataFile.read(); } } //___________ //Function to read a number from after the next : to the end of the line void ReadNumberFromFile(byte MaxDigits) { byte DataRead; //Raw data from file DataFile.seek(FindNext(DataFile,':') + 1); //Expects a space after the colon byte CharNo = 1; memset(TempStr10, 0, sizeof(TempStr10)); //Clear the temporary string do { DataRead = DataFile.read(); TempStr10[CharNo - 1] = DataRead; CharNo++; } while ((DataRead != 13) && (CharNo < MaxDigits + 1)); } //__________ void SendRS232(byte CommandNo, byte TriggerType, byte ButtonNo) { unsigned long RS232RespDatum; //Flush Receive buffer FlushRS232Input(); //Send command byte CharNo; for (CharNo = 0; CharNo < EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + 2); CharNo++) { mySerial.write(EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + 3 + CharNo)); //Send a command } mySerial.write(13); //Send CR (ASCII 13) at end of message //Read response char RetVal[MaxRespLength]; //Response Code RS232RespDatum = millis(); //Wait for response or timeout condition while ((mySerial.available() < EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3)) && (IntervalCheck(RS232RespDatum, EEPROMReadUint16(ETimeout)) == 0)) { } if(mySerial.available() >= EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3)) { RS232RespCode = 1; //Default to OK for (byte CharNo = 0; CharNo < EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 3); CharNo++) { RetVal[CharNo] = mySerial.read(); if(RetVal[CharNo] != EEPROM.read(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + 4 + CharNo)) { RS232RespCode = 0; return; } } } else { RS232RespCode = 0; return; } //Success, set LCD message line 1 (name) lcd.setCursor(0,0); //column 0 row 0 EEPROMToLCD(ENCommands + (CommandNo * CommandFrameLength) + 2, 16); //Always send whole line even if all not used //LCD message Line 2 if (TriggerType == TypeDMX) { //Show DMX Address and value lcd.setCursor(0,1); lcd.print(F("DMX Ch Val ")); lcd.setCursor(6,1); lcd.print(EEPROMReadUint16(ENCommands + (CommandNo * CommandFrameLength) + MaxMsgLength + MaxCmdLength + MaxRespLength + 4)); lcd.setCursor(13,1); lcd.print(DMXVal); if (EEPROM.read(EBacklightEn)) { BacklightDatum = millis(); lcd.backlight(); } } else { //Show Button Number lcd.setCursor(0,1); lcd.print(F("Button ")); lcd.setCursor(7,1); lcd.print(ButtonNo + 1); if (EEPROM.read(EBacklightEn)) { BacklightDatum = millis(); lcd.backlight(); } } //Set LEDs for (byte LEDNo = 0; LEDNo < EEPROM.read(ELEDData); LEDNo++) { if(EEPROM.read(ELEDData + (LEDNo * 3) + 1) == (CommandNo + 1)) { digitalWrite(EEPROM.read(ELEDData + (LEDNo * 3) + 3),HIGH); } if(EEPROM.read(ELEDData + (LEDNo * 3) + 2) == (CommandNo + 1)) { digitalWrite(EEPROM.read(ELEDData + (LEDNo * 3) + 3),LOW); } } } //___________