// Last Used and modified 07-19-21 // ESP32 4 Channel Generator Ver 1.32 07-19-21 gsk // Board Manager = ESP32 Arduino, selection = Node32 or Nano32 // NOTE 14: Added frequency adjust multiplier to componsent for lost interrupt time // NOTE 13: Added Suspend Button feature // NOTE 12: Added "wifidata.txt" file to allow updating or changing the WiFi SSID and // Password for WiFi connectivity. (See WiFi documentation for details) // NOTE 11: Changed 'VERSION' from a define to const char* & changed NOTE numbering scheme // NOTE 10: Fixed inversion of frequency in setFrequency() routine. Now idles at low vs high // NOTE 9: Version number now a #define value. // NOTE 8: Opened up Flash Memory Storage Size to 10800 bytes. Allows 275 protocol lines // NOTE 7: Now displays a Failed SD RAM if missing or corrupted. Displays file failure and // the file number that failed to read if it was a missing or corrupted file // NOTE 6: Updated file read capability. Now can select 1 of 10 possible generator files // stored on the micro SD ram card. (see Gen. File documentation for details) // NOTE 5: In "calcSweepParam()" changed "sweepFreqInc<0.0" to sweepFreqInc<=0.0 // NOTE 4: Added a debug option to turn on and off the Serial Printer 05-01-21 // This does not effect the setup and initialization functions/routines // NOTE 3: Inverted the channel outputs to compensate for the 2n7000 mosfet inversions // of the output signals. // NOTE 2: Created an UPDATE only function when "Setup" is not zero. In other words the // unit will only run in RUN mode when "Setup=0" else it is in UPDATE mode. // NOTE 1: The "#define wifiEnable" parameter enables the wireless feature added to this // software. When active the OLED will show the IP address generated - to link // to your computer or cell phone via the internet. Presently only offers a // Start/Stop generator feature. However, this addition is expandable. #include #include "FS.h" #include "SD.h" #include "SPI.h" #include #include //#include //#include #define BAUD 9600 // IDE Serial Monitor baud rate #define DEBUG 0 // debug option where 0 = Serial.print OFF and 1 = ON #define debounceTime 350 // interruupt debouce time in milliseconds #define FLASH_SIZE 10800 // ESP32 Arduino EEPROM equivalent memory area byte size //#define FLASH_SIZE 8192 // ESP32 Arduino EEPROM equivalent memory area byte size #define FLASH_START 0 // ESP32 Arduino EEPROM equivalent memory area byte size //************************** #define PWM1_Pin 32 // define what GPIO pin to use for PWM #1 #define PWM2_Pin 33 // define what GPIO pin to use for PWM #2 #define PWM3_Pin 27 // define what GPIO pin to use for PWM #3 #define PWM4_Pin 14 // define what GPIO pin to use for PWM #4 //************************** #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels #define BLINK_LED 2 // Blinking LED for various status and error states #define line0X 4 // Proto (Frequency set Number) X position #define line00X 64 // #define line1X 0 // F1 starting curX postion #define line2X 1 // F2 starting curX postion #define line3X 2 // F2 starting curX postion #define line4X 3 // F2 starting curX postion #define line0XC 40 // X cursor Coordinate for setupOpt value #define line0Y 0 // Proto (frequency set number) Y position #define line0YC 2 // Proto Y Cursor position 2 #define line1YP 9 // 1st Y address for Protocol numbers #define line1Y 13 // F1 starting curY postion #define line2Y 27 // F2 starting curY postion 18 #define line3Y 41 // F2 starting curY postion 18 #define line4Y 55 // F2 starting curY postion 18 #define line1YC 15 // cursor for F1 starting curY postion #define line2YC 29 // cursor for F2 starting curY postion 20 #define line3YC 43 // cursor for F1 starting curY postion #define line4YC 57 // cursor for F2 starting curY postion 20 #define linePX 0 // Screen 5 Protocol File selection line X #define linePY 41 // Screen 5 Protocol File selection line Y #define linePXC 57 // Screen 5 Protocol File selection line y #define linePYC 43 // Screen 5 Protocol File selection #define temp 720 // temperature limit for the MOSFET // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); byte againOnce; // a single shot to allow reading the SD 1 extra time byte change = 0; // byte change2 = 0; // Set if the cursor has moved byte change3 = 0; // used to designate "protoID" has changed byte curX; // byte curX1; // byte curX2; // byte curY; // byte curY1; // byte curY2; // byte fileChange; // file Change flag byte fileNo = 0; // initialize to zero "0" byte fileNoHold; // backup of "fileNo" byte fileRtnCode; // byte highMem; // byte intFlag; // byte lowFreqSw; // byte modeID; // byte noRead = 0; // readFile() routine inhibit flag byte presentState; // byte runFlag = 0; // byte runFlagHold; // byte runFmMemory = 0; // initialize "run From Memory" to 0 to read SD card byte setupOpt = 0; // 0=Read, 1=Set, 2=modes, 3=Protocol, 4=Sweep, 5=Time byte singleShot = 0; // byte timeOut = 0; // initialize to zero, used in Wi-fi and rotary routines byte wifiEnable = 1; // define if wifi is enabled = 1 and disabled = 0 const byte cursorPin = 4; // GPio 4 polling pin to monitor Cursor Button const byte interruptPin = 15; // GPio 15 as interrupt for RUNNING start and stop const byte rotaryA = 16; // GPio 16 polling pin for rotary switch const byte rotaryB = 17; // GPio 17 polling pin for rotary switch const char* ssid = "123456789012345678901234567890"; // ssid is a pointer to a 30 byte field const char* password = "0123456789012345678901234567890"; // password is a pointer to the field const char* VERSION = "1.32"; // present version number of this software volatile int rotaryCounter = 0; // volatile long rotaryMicrosA; // volatile long rotaryMicrosB; // char asciiChar; // int counter; // unsigned int eeAddress; // unsigned int eeAddress2; // int intCnt; // int lastDirection; // int pValue; // used for restoring to previous "value" int rotaryDirection; // int value; // int channel1 = 1; // assign a channel for each GPIO used int channel2 = 2; // assign a different channel #2 works well int channel3 = 4; // assign a different channel #3 doesn't work but #4 does int channel4 = 6; // #5 doesn't work but #6 does; unknown quirk ??? int memGrp; int numberOfInterrupts = 0; // int prescaler = 10; // Prescaler divide by counter designator, 10 works well int dutyConst = 1024; // Duty Constant 1024 = 100% with a prescaler of 10 int protoID = 1; // Protocal ID int protoIDhold; // used to check if protoID changed int protoIDlast; // obviously the last protoID available int rotaryT1 = 150; // Rotary Debounce Time in milliseconds int dutyValue; // work field resulting from dutycycle calculation int startFreqGrpID; // starting Frequency Group ID int startSweepGrpID; // starting Sweep Group ID int stopFreqGrpID; // stopping Frequency Group ID int stopSweepGrpID; // stopping Sweep Group ID int temper = temp; // temper contains the present MOSFET temperature float FreqAdjust = 1.00065; // A percentage value to add to frequency to compensate for interrupts float F1 = 0.00; // used in Sweep function float F2 = 0.00; // frequency (in Hz) float F3 = 0.00; // frequency (in Hz) float F4 = 0.00; // frequency (in Hz) float D1 = 0.00; // Duty cycle float D2 = 0.00; // Duty cycle float D3 = 0.00; // Duty cycle float D4 = 0.00; // Duty cycle float d1 = 0.00; // used to compensate for harware inversion float d2 = 0.00; // see 'setFrequency' in spport_Routines float d3 = 0.00; // used to compensate for harware inversion float d4 = 0.00; // see 'setFrequency' in spport_Routines float sweepFreqInc = 0.00; // sweep Frequency Increment value float sweepAccum; // sweep Accummulator variable float sweepF1; // sweep function work frequency 1 float sweepF2; // sweep function work frequency 2 float sweepWork1; // a floating work field for 'calcSweepParams' function float sweepWork2; // a floating work field for 'calcSweepParams' function unsigned long endTime1; // 180 seconds, freq set time, default = 180000 = 3 minutes unsigned long endTime2; // 1 minute, protocol time, default = 1800000 = 30 minutes unsigned long FLASH_LAST; // Not presently used unsigned long interruptDebounce; // used with millis() to debounce interrupt unsigned long runTime; // work run Time field unsigned long runTime1; // working field for running time of a frequency set unsigned long runTime2; // working field for running time of a protocol unsigned long savePgmTime; // this is for restoring time left for program time unsigned long saveRunTime; // to restore time left for frequency runtime unsigned int sweepNumCnt; // Sweep Number Count unsigned long timeOutVal; // used in Rotary function call unsigned long workLong; // used to translate FLASH data to intergers or floats String ssidStr = "0123456789012345678901234567890"; String passwordStr = "0123456789012345678901234567890"; String workStr = "1234567890123456"; // Collection string for for 'protoID' reserve 16 charachter char fileName[20] = "/protocols-0.txt"; struct EPromObject1 { // This structure contains the two channel basics int field0; // protoID = Memory Group number float field1; // F1 = Frequency 1 float field2; // D1 = Duty Cycle 1 float field3; // F2 = Frequnecy 2 float field4; // D2 = Duty Cycle 2 float field5; // F3 = Frequency 3 float field6; // D3 = Duty Cycle 3 float field7; // F4 = Frequnecy 4 float field8; // D4 = Duty Cycle 4 }; struct EPromObject2 { // structure contains startup and range defaults int field0; // Last Memory Group used, always identified with a zero '0' int field1; // protoID to start with if in Simple Mode byte field2; // mode of operation SIMPLE, PROTOCOL, or SWEEP unsigned long field3; // Total Program runtime in seconds eg. 600000 = 10 minutes unsigned long field4; // Total frequency runtime in seconds eg 60000 = 1 minute int field5; // start group for Frequency Range int field6; // stop group for Frequency Range int field7; // sweep start group for Sweep Range int field8; // sweep stop group for Sweep Range float field9; // sweep frequency increment value byte field10; // file ID = presently up to 9 different file allowed }; portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; // This function prevents concurrent access of // the 'interrupt' used in two places void IRAM_ATTR handleInterrupt() { // IRAM_ATTR puts this code in special interrupt memory area portENTER_CRITICAL_ISR(&mux); // prevent concurrent access of 'interrupt' if (interruptDebounce < millis()) { // runFlag ^= true; // Exclusive OR will flip back forth between 1 and 0 interruptDebounce = millis() + debounceTime; } portEXIT_CRITICAL_ISR(&mux); // allow access to 'interrupt' } // ************* See WiFi TAB for available Routines **************** WiFiServer server(80); // ********************INITIAL Part of SETUP ***************************** void setup() { curX = line0XC; // Changes depending what line position is displayed curY = line0YC; // Changes depending what line is displayed pinMode(BLINK_LED, OUTPUT); // do first so you can use it pinMode(interruptPin, INPUT_PULLUP); // setup interrupt pin with pullup interruptDebounce = millis(); // initialize interruptDebounce variable attachInterrupt(digitalPinToInterrupt(interruptPin), handleInterrupt, FALLING); pinMode(cursorPin, INPUT_PULLUP); pinMode(rotaryA, INPUT_PULLUP); pinMode(rotaryB, INPUT_PULLUP); Serial.begin(BAUD); // Initialize the internal IDE serial port for (int i = 1; i > 0; --i) { blinker(5, 125); // waste some time before starting interfact to monitor Serial.println("Starting the Program"); // this little loop compensates for delay(200); } // an initialization bug; works OK // initialize EEPROM with predefined size EEPROM.begin(FLASH_SIZE); // ESP32 Flash Ram EEPROM look alike area begins FLASH_LAST = FLASH_SIZE - 36; // setup Previous run information // ************************ PWM Parameters ****************************** ledcAttachPin(PWM1_Pin, channel1); // PWM_PIN, channel 1 assignment ledcAttachPin(PWM2_Pin, channel2); // PWM_PIN, channel 2 assignment ledcAttachPin(PWM3_Pin, channel3); // PWM_PIN, channel 3 assignment ledcAttachPin(PWM4_Pin, channel4); // PWM_PIN, channel 4 assignment // ************************** PWM FINISHED ******************************* // ***************** OLED initialization part of 'setup' ***************** if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { delay(3000); Serial.println("SSD1306 allocation failed"); while (1) { // if here SSD1306 failed to start blinker(50, 25); // Do 50 blink loops with 25 millisecond blink delay } } value = 0; pValue = value; display.setTextSize(1); display.setTextColor(WHITE); display.clearDisplay(); display.display(); // ******************* micro SD part of SETUP *************************** fileFetch(); // unconditionally load file "protocols-0.txt" // ***************** Wifi portion of the "setup" function ********************* // We start by connecting to a WiFi network if (wifiEnable == 1) { Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { timeOut+=1; delay(500); Serial.print("."); if(timeOut > 9) break; } display.clearDisplay(); // Clear the OLED buffer display.setCursor(0, 0); // Set the OLED cursor position display.setTextSize(2); // Set the OLED text size if(timeOut > 9){ wifiEnable = 0; // Disable WiFi display.print("WiFi Fail-"); // Display Not Connected display.setCursor(0, 16); // Set the OLED cursor position display.print("ed, Moving"); // Display verbiage display.setCursor(0, 32); // Set the OLED cursor position display.print("On in 10"); // Display verbiage display.setCursor(0, 48); // Set the OLED cursor position display.print("Seconds"); // Display verbiage } else { Serial.println(""); Serial.println("WiFi connected."); Serial.println("IP address: "); Serial.println(WiFi.localIP()); wifiEnable = 1; // Set Connected display.print("Connected"); // Display Connected display.setCursor(0, 20); // Set the OLED cursor position display.print("IP Address"); // Display verbiage display.setCursor(0, 48); // Set the OLED cursor position display.setTextSize(1); // Set the OLED text size display.print(WiFi.localIP()); // display connection IP address server.begin(); // connnect to the server } display.display(); // display the message delay(10000); // give it some time to read display.clearDisplay(); // Clear the OLED buffer display.setTextSize(1); // Set the OLED text size } } // ***************** MAIN LOOP of this PROGRAM *********************** void loop() { if (wifiEnable == 1) // check if wifi Enabled wifiRoutine(); // on condition check for wifi on/off request if (runFlag == 1) { // on condition START RUNNING if (setupOpt == 0) { // you can actually RUN the program is "Setupt=0" if (singleShot == 1) { if (modeID != 1) { // check if protoID and parameters need updating if (modeID == 2) // check if protoID needs updating protoID = startFreqGrpID; // update on condition if (modeID == 3) { // calcSweepParams(); // calculate the Sweep Parameters protoID = startSweepGrpID; // } fetchProtoID(protoID); // update to selected protoID parameters } setFrequency(1); // set the four channel frequencies displayRunning(); // display screen in running mode saveCurrent(); // Save any changes to Flash Memory ?? singleShot = 0; // only run this part of the loop once setClocks(1); // set the runtime clocks to active mode } checkTime(); // if here check time } else { checkSaving(); // check if updating the SDram desired displayUpdate(); // display screen in running mode saveCurrent(); // Save any changes to Flash Memory ?? if (fileChange == 1) { fileFetch(); // on condition go load a new file } singleShot = 0; // only run this part of the loop once runFlag = 0; // insure RUN mode is cancelled } } else { // on condition STOP RUNNING if (singleShot == 0) { setFrequency(0); // turn off the four channel frequencies setClocks(0); // set the runtime clocks to INactive mode Display(); // display screen in dial mode singleShot = 1; // only run this part of the loop once } protoIDhold = protoID; // save modeID 1 protoID value rotary(); // NOTE: rotary could change things like protoID cursorMove(); // check for cursor move request if (protoID != protoIDhold) // check if protoID got changed fetchProtoID(protoID); // if here fetch the new protoID parameters if (change == 1 || change2 == 1) { //change=1 a number has changed, change2=1 the cursor has changed Display(); } } }