////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // // Analog Input // // // // An ESP32 analog input demonstration. // // // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Analog Input Parts List. // // 1) ESP32 Capacitive Touch demonstrator https://www.instructables.com/id/ESP32-Capacitive-Touch-Buttons/ // 2) Five, 4" pieces of 28awg wire (one red, one black, one yellow, two green). // 3) One, Maverick "ET-72 Temperature Probe" probe (https://www.maverickthermometers.com/product/pr-003/). // 4) One, 2.5mm "phone" connector, panel mount (https://www.mouser.com/ProductDetail/502-TR-2A). // 5) One, 33k ohm 1% 1/8 watt resistor. // 6) One, piezo buzzer https://www.adafruit.com/product/160 // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Include files. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// #include // for graphics (see https://github.com/olikraus/u8g2/wiki/u8g2reference) ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Global constants. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Analog. #define THERMISTOR_COUNTS 100 // size of thermistor input averaging array // Oled display. // Dimensions. #define DISPLAY_HEIGHT 64 // display height #define DISPLAY_WIDTH 128 // display width #define FONT_ONE_HEIGHT 8 // font one height in pixels #define FONT_TWO_HEIGHT 22 // font two height in pixels // Temperatures. #define TEMPERATURE_MAX 300 // maximum displayed temperature (degrees F) #define TEMPERATURE_MIN 40 // minimum displayed temperature (degrees F) ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Global variables. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Analog. // Analog input channel. int nAdTemperature = A0; // use A0 for for analog input // Audio. int nPiezo = A4; // use A4 for piezo alarm buzzer // Degrees Kelvin. double dKelvin = 273.15; // degrees kelvin // Display control parameters. bool bAlarmTemperatureEdit = false; // temperature alarm edit flag int nAlarmTemperature = 165; // temperature alarm setting int nAlarmTemperatureDelay = 0; // temperature alarm setting edit control int nAlarmTemperatureDelayReset = 0; // temperature alarm setting edit control int nAlarmTemperatureIncrement = 0; // temperature alarm setting edit control char chBuffer[81]; // general purpose character buffer bool bDisplayThermistorCount = false; // thermistor count display control // Voltage Divider resistor. double dResistor = 32550; // in ohms // Steinhart-Hart coeffecients from the spreadsheet. double dProbeA = -1.8331074420E-03; // value A from spreadsheet double dProbeB = 5.0172879701E-04; // value B from spreadsheet double dProbeC = -6.1708711853E-07; // value C from spreadsheet // Temperature. double dDegreesC = 0.0; // calculated degrees C double dDegreesF = 0.0; // calculated degrees F // Thermistor. double dThermistorAverage = 0.0; // average of all thermistor counts in nThermistorCounts[] int nThermistorCounts[THERMISTOR_COUNTS]; // input counts array used for averaging int nThermistorPointer = 0; // location of next input in nThermistor array // Buttons. int nButton1 = 0; // button 1 int nButton2 = 0; // button 2 int nButton3 = 0; // button 3 int nTouch1 = 0; // touch 1 value int nTouch2 = 0; // touch 2 value int nTouch3 = 0; // touch 3 value // Interrupts. hw_timer_t * timerInterruptService = NULL; // interrupt service timer hook volatile SemaphoreHandle_t timerLoopSemaphore; // main loop semaphore from interrupt service // Oled. U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, 16, 15, 4); // OLED graphics // Timer. int nMilliseconds = 0; // milliseconds timer ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Interrupts. // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // InterruptService // Called by timer interrupt services for updating of nMilliseconds, loop() and buttons. // Entry : nothing // Returns : nothing // Notes : // // 1) Designed for a 1000hz (1ms) interrupt rate. // void IRAM_ATTR InterruptService() { // Local constants. #define BUTTON_DELAY 20 // at 1ms interrupt service rate, 20 results in 20ms (50hz) #define BUTTON_FILTER 4 // "filter" constant #define BUTTON_THRESHOLD 80 // capacitive touch threshold #define LOOP_DELAY 30 // at 1ms interrupt service rate, 30 results in 30ms (33.333hz) // Local variables. static int nAlarmCount = 0; // alarm count static int nButton1Count = 0; // button 1 count static int nButton2Count = 0; // button 2 count static int nButton3Count = 0; // button 3 count static int nButtonDelay = BUTTON_DELAY - 1;// button update delay counter static int nLoopDelay = LOOP_DELAY - 1; // loop update delay counter // Update milliseconds counter. nMilliseconds += 1; // Update alarm. if((int)(dDegreesF) >= nAlarmTemperature) { // Alarm on, pulse piezo on and off to give a "beep..beep...................beep..beep..................." effect. switch(nAlarmCount) { case 0: case 200: { // At 0 and 200ms, turn the piezo on. ledcWrite(0, 50); } break; case 100: case 300: { // At 100 and 300ms, turn the piezo off. ledcWrite(0, 0); } break; } // Update the alarm count, limiting it to 1hz (1000ms). nAlarmCount ++; nAlarmCount %= 1000; } else { // Alarm off. ledcWrite(0, 0); nAlarmCount = 0; } // Update loop(). if(nLoopDelay) { // Not yet time to update. nLoopDelay --; } else { // Time to update, reset delay. nLoopDelay = LOOP_DELAY - 1; // Execute a main loop pass. xSemaphoreGiveFromISR(timerLoopSemaphore, NULL); } // Update buttons. if(nButtonDelay) { // Not yet time to update. nButtonDelay --; } else { // Time to update, reset delay. nButtonDelay = BUTTON_DELAY - 1; // Update button 1. if((nTouch1 = touchRead(T4)) < BUTTON_THRESHOLD) { // Below threshold, filter. if(nButton1Count < BUTTON_FILTER) { // Keep filtering. nButton1Count += 1; nButton1 = 0; } else { // Filtered enough, button 1 is pressed. nButton1 = 1; } } else { // Above threshold, reset button and filter. nButton1 = nButton1Count = 0; } // Update button 2. if((nTouch2 = touchRead(T5)) < BUTTON_THRESHOLD) { // Below threshold, filter. if(nButton2Count < BUTTON_FILTER) { // Keep filtering. nButton2Count += 1; nButton2 = 0; } else { // Filtered enough, button 2 is pressed. nButton2 = 1; } } else { // Above threshold, reset button and filter. nButton2 = nButton2Count = 0; } // Update button 3. if((nTouch3 = touchRead(T6)) < BUTTON_THRESHOLD) { // Below threshold, filter. if(nButton3Count < BUTTON_FILTER) { // Keep filtering. nButton3Count += 1; nButton3 = 0; } else { // Filtered enough, button 3 is pressed. nButton3 = 1; } } else { // Above threshold, reset button and filter. nButton3 = nButton3Count = 0; } } // End. } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // setup() // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// void setup() { // Serial. Serial.begin(115200); while(!Serial){}; // OLED graphics. u8g2.setBusClock(800000); u8g2.begin(); u8g2.setFont(u8g2_font_6x10_tr); u8g2.setFontPosTop(); u8g2.setDrawColor(1); // Analog input. // Prepare the analog input circuitry. analogReadResolution(12); analogSetWidth(12); analogSetCycles(8); analogSetSamples(1); analogSetClockDiv(1); analogSetAttenuation(ADC_11db); // Initialize the thermistor counts array. for(int nCount = 0; nCount < THERMISTOR_COUNTS; nCount ++) { nThermistorCounts[nCount] = analogRead(nAdTemperature); delay(20); } // Piezo pwm output. // Use pwm 0, 2000 hertz, 8 bit resolution. ledcSetup(0, 2000, 8); // Attach pwm 0 to ESP32 pin nPiezo. ledcAttachPin(nPiezo, 0); // Start interrupt service. // Create timerInterruptService to main loop semaphore. timerLoopSemaphore = xSemaphoreCreateBinary(); // Use timer 0 (4 available from 0 through 3), with a prescale of 80. timerInterruptService = timerBegin(0, 80, true); // Attach timerInterruptService() to timer. timerAttachInterrupt(timerInterruptService, & InterruptService, true); // Set alarm to call onTimer function every millisecond (value in microseconds) // with repeat (third parameter). timerAlarmWrite(timerInterruptService, 1000, true); // Start the alarm. timerAlarmEnable(timerInterruptService); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // loop() // ////////////////////////////////////////////////////////////////////////////////////////////////////////////// void loop() { // Wait forever for semaphore from timerInterruptService(). if(xSemaphoreTake(timerLoopSemaphore, portMAX_DELAY) == pdTRUE) { // Received the semaphore, update analog. Analog(); // Process button presses. Buttons(); // Update the OLED display. Display(); } else { Serial.printf("loop(): using portMAX_DELAY as the xSemaphoreTake timout value, this should never print.\n"); } } // // Analog() // Perform analog input of the thermistor and convert to degrees C and F // Entry : nothing // Returns : nothing // void Analog() { // Read a new thermistor count into the thermistor counts array. nThermistorCounts[nThermistorPointer++] = analogRead(nAdTemperature); nThermistorPointer %= THERMISTOR_COUNTS; // Calculate the average of all thermistor counts in the array. dThermistorAverage = 0.0; for(int nCount = 0; nCount < THERMISTOR_COUNTS; nCount++) dThermistorAverage += (double)nThermistorCounts[nCount]; dThermistorAverage /= THERMISTOR_COUNTS; dThermistorAverage = ((dThermistorAverage >= 1.0) ? dThermistorAverage : 1.0); // Calculate the thermistor value from the thermistor counts average. double dThermistor = dResistor * ((4096.0 / dThermistorAverage) - 1.0); // Calculate degrees C. // The Steinhart-Hart equation uses log(dRThermistor) four times, so calculate it once. double dLogdRThermistor = log(dThermistor); // Then calculate degrees C. dDegreesC = 1.0 / (dProbeA + (dProbeB * dLogdRThermistor) + (dProbeC * dLogdRThermistor * dLogdRThermistor * dLogdRThermistor)) - dKelvin; // Calculate and limit degrees F. dDegreesF = (dDegreesC * 9.0 / 5.0) + 32.0; dDegreesF = (dDegreesF > TEMPERATURE_MAX) ? TEMPERATURE_MAX : (dDegreesF < TEMPERATURE_MIN) ? TEMPERATURE_MIN : dDegreesF; } // // Buttons() // Process button presses. // Entry : nothing // Returns: nothing // void Buttons() { // Local variables. static int nButton1N1 = 0; static int nButton2N1 = 0; static int nButton3N1 = 0; static int nDisplayThermistorCountDelay = 0; // Process button 1. if(nButton1N1 != nButton1) { // State changed. if(nButton1N1) { // Released. nButton1N1 = nButton1; nAlarmTemperatureIncrement = 0; } else { // Pressed. nButton1N1 = nButton1; // Check for return to temperature display from thermistor counts display. if(bDisplayThermistorCount) { // Yes, return to temperature display. bDisplayThermistorCount = 0; } else { // No, update alarm temperature. if(bAlarmTemperatureEdit) { nAlarmTemperatureIncrement = -1; nAlarmTemperatureDelay = nAlarmTemperatureDelayReset = 8; nAlarmTemperature += nAlarmTemperatureIncrement; if(nAlarmTemperature < TEMPERATURE_MIN) nAlarmTemperature = TEMPERATURE_MIN; } } } } // Process button 2. if(nButton2N1 != nButton2) { // State changed. if(nButton2N1) { // Released. nButton2N1 = nButton2; } else { // Pressed. nButton2N1 = nButton2; // Check for return to temperature display from thermistor counts display. if(bDisplayThermistorCount) { // Yes, return to temperature display. bDisplayThermistorCount = 0; } else { // No, toggle editing of alarm temperature on and off. bAlarmTemperatureEdit = (bAlarmTemperatureEdit) ? false : true; } } } // Process button 3. if(nButton3N1 != nButton3) { // State changed. if(nButton3N1) { // Released. nButton3N1 = nButton3; nAlarmTemperatureIncrement = 0; } else { // Pressed. nButton3N1 = nButton3; // Check for return to temperature display from thermistor counts display. if(bDisplayThermistorCount) { // Yes, return to temperature display. bDisplayThermistorCount = 0; } else { // No, update alarm temperature. if(bAlarmTemperatureEdit) { nAlarmTemperatureIncrement = 1; nAlarmTemperatureDelay = nAlarmTemperatureDelayReset = 8; nAlarmTemperature += nAlarmTemperatureIncrement; if(nAlarmTemperature > TEMPERATURE_MAX) nAlarmTemperature = TEMPERATURE_MAX; } } } } // Test for thermistor count display enable. if((nButton1) && (nButton3) && (bAlarmTemperatureEdit == false)) { if(nDisplayThermistorCountDelay < 50) { nDisplayThermistorCountDelay += 1; } else { bDisplayThermistorCount = true; } } } // // Display() // Update the OLED display. // Entry : nothing // Returns: nothing // void Display() { // Update nTemperatureAlarm. if(nAlarmTemperatureIncrement) { if(nAlarmTemperatureDelay) { // Non-zero, delay. nAlarmTemperatureDelay --; } else { // Zero, decrement nTemperatureAlarmDelay until 1. if(nAlarmTemperatureDelayReset > 1) nAlarmTemperatureDelayReset --; // Then update nTemperatureAlarmDelay with the result. nAlarmTemperatureDelay = nAlarmTemperatureDelayReset; // Finally, update nAlarmTemperature. nAlarmTemperature += nAlarmTemperatureIncrement; if(nAlarmTemperature < TEMPERATURE_MIN) nAlarmTemperature = TEMPERATURE_MIN; else if(nAlarmTemperature > TEMPERATURE_MAX) nAlarmTemperature = TEMPERATURE_MAX; } } // Clear the display buffer. u8g2.clearBuffer(); // Display the title. if(bAlarmTemperatureEdit) { // Display alarm temperature title. sprintf(chBuffer, "%s", "Set Alarm Temperature"); } else { // Not editing alarm temperature. if(bDisplayThermistorCount) { // Display thermistor counts title. sprintf(chBuffer, "%s", "Thermistor Counts"); } else { // Display temperature title. sprintf(chBuffer, "%s", "Temperature"); } } u8g2.drawStr((DISPLAY_WIDTH / 2) - (u8g2.getStrWidth(chBuffer) / 2), 0, chBuffer); // Display temperature. // Use a larger font. u8g2.setFont(u8g2_font_fur20_tr); // Display the thermistor count or temperature. if(bDisplayThermistorCount) { // Thermistor count. sprintf(chBuffer, "%d", (int)(dThermistorAverage)); } else { // Temperature. if(bAlarmTemperatureEdit) { // Alarm temperature. sprintf(chBuffer, "%d", nAlarmTemperature); } else { // Temperature. sprintf(chBuffer, "%d", (int)(dDegreesF)); } } u8g2.drawStr((DISPLAY_WIDTH / 2) - (u8g2.getStrWidth(chBuffer) / 2), (DISPLAY_HEIGHT / 2) - (FONT_TWO_HEIGHT / 2), chBuffer); // Restore the smaller font. u8g2.setFont(u8g2_font_6x10_tr); // Send the display buffer to the OLED. u8g2.sendBuffer(); }