#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include "AiEsp32RotaryEncoder.h"
#include <Adafruit_NeoPixel.h>
#include "FastAccelStepper.h"
#define BUZZER_PIN 25
#define ROTARY_ENCODER_A_PIN 33
#define ROTARY_ENCODER_B_PIN 32
#define ROTARY_ENCODER_BUTTON_PIN 27
#define ROTARY_ENCODER_STEPS 4
// Default heater control values
#define HEATER_MIN 0
#define HEATER_MAX 90
#define HEATER_DEFAULT 60
#define HEATER_STEP 1
// Default mixing control values
#define MIXING_MIN 0
#define MIXING_MAX 100
#define MIXING_DEFAULT 25
#define MIXING_STEP 1
#define BEEP_ENABLE true // enable/disable buzzer
#define PIN_NEO_PIXEL 26 // The Arduino Nano ESP32 pin connected to NeoPixel
#define NUM_PIXELS 1 // The number of LEDs (pixels) on NeoPixel LED strip
#define TermPinT 12
#define TermPinB 14
#define DIR 18
#define STEP 19
#define ENABLE 5
#define MOSFET 13
float currtempb;
float currtempt;
bool beepEnable = BEEP_ENABLE;
const uint8_t *font = u8g2_font_5x7_tr;
uint16_t DefaultVolt = HEATER_DEFAULT;
uint16_t MixingSpeed = MIXING_DEFAULT;
uint16_t SetTemperature = HEATER_DEFAULT;
uint16_t SetCurrent = MIXING_DEFAULT;
uint16_t heater_status = 0;
uint16_t mixing_status = 0;
bool show_menu;
int termNom = 100000; // Thermistor reference resistance
int refTemp = 25; // Temperature for reference resistance
int beta = 3950; // Beta factor of thermistor
int resistance = 100000; // value of resistance in series in the circuit
float current;
float temperature;
float LogicTemp;
int StepperSpeed;
// U8G2_SSD1306_128X64_ALT0_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
U8G2_SH1106_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE);
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(ROTARY_ENCODER_A_PIN, ROTARY_ENCODER_B_PIN, ROTARY_ENCODER_BUTTON_PIN, -1, ROTARY_ENCODER_STEPS);
Adafruit_NeoPixel NeoPixel(NUM_PIXELS, PIN_NEO_PIXEL, NEO_GRB + NEO_KHZ800);
FastAccelStepperEngine engine = FastAccelStepperEngine();
FastAccelStepper *stepper = NULL;
String menu_items[] =
{
"Set Temperature",
"Heater Status",
"Mixing Speed",
"Mixing Status",
"Back..."};
int menu_item = 0;
int currentMenu_item = 0;
void setForRotary(int value)
{
currentMenu_item = value;
switch (value)
{
case 0:
rotaryEncoder.setBoundaries(0, ROTARY_ENCODER_STEPS, true);
rotaryEncoder.setEncoderValue(0);
break;
case 1: //"Set Voltage online"
rotaryEncoder.setBoundaries(HEATER_MIN, HEATER_MAX, false);
rotaryEncoder.setEncoderValue(SetTemperature);
break;
case 2: //"Set Current online"
rotaryEncoder.setBoundaries(0, 2, false);
rotaryEncoder.setEncoderValue(heater_status);
break;
case 3: //"Set Default Voltage"
rotaryEncoder.setBoundaries(MIXING_MIN, MIXING_MAX, false);
rotaryEncoder.setEncoderValue(MixingSpeed);
break;
case 4: //"Set Default Current"
rotaryEncoder.setBoundaries(0, 1, false);
rotaryEncoder.setEncoderValue(mixing_status);
break;
default:
break;
}
showMenu();
}
void rotary_onButtonClick()
{
static unsigned long lastTimePressed = 0;
if (millis() - lastTimePressed < 200)
return;
lastTimePressed = millis();
int selectedValue = rotaryEncoder.readEncoder();
switch (currentMenu_item)
{
case 0: //"Setup Menu"
menu_item = selectedValue + 1;
setForRotary(menu_item);
break;
case 1: //"Set Voltage online"
SetVoltageOnline();
setForRotary(0);
break;
case 2: //"Set Current online"
SetVoltageOnline();
setForRotary(0);
break;
case 3: //"Set Default Voltage"
SetVoltageOnline();
setForRotary(0);
break;
case 4: //"Set Default Current"
SetVoltageOnline();
setForRotary(0);
break;
default:
break;
}
}
void IRAM_ATTR readEncoderISR()
{
rotaryEncoder.readEncoder_ISR();
}
void setup()
{
Serial.begin(115200);
pinMode(MOSFET, OUTPUT);
NeoPixel.begin(); // initialize NeoPixel strip object (REQUIRED)
u8g2.begin();
u8g2.setFont(font);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
u8g2.clearBuffer();
rotaryEncoder.begin(); // Core encoder
rotaryEncoder.setup(readEncoderISR);
rotaryEncoder.setAcceleration(150);
rotaryEncoder.correctionOffset = 2; // try with zero or ROTARY_ENCODER_STEPS/2
rotaryEncoder.isButtonPulldown = false;
rotaryEncoder.areEncoderPinsPulldownforEsp32 = true;
engine.init();
stepper = engine.stepperConnectToPin(STEP);
stepper->setDirectionPin(DIR);
stepper->setEnablePin(ENABLE);
stepper->setAutoEnable(true);
}
void loop()
{
currtempb = MeasureTemp(TermPinB); // Thermistor GPIO 18
currtempt = MeasureTemp(TermPinT); // Thermistor GPIO 19
if (heater_status == 1)
{
if (currtempt < 0)
{
LogicTemp = currtempb;
}
else
{
LogicTemp = currtempt;
}
float RED = abs(((LogicTemp / SetTemperature) - 0.05) * 255); // If temp is lower show blue, if hot show red
float BLUE = 255 - RED;
for (int pixel = 0; pixel < NUM_PIXELS; pixel++)
{
NeoPixel.setPixelColor(pixel, NeoPixel.Color(constrain(RED + random(-10, 10), 0, 255), 0, constrain(BLUE + random(-10, 10), 0, 255)));
NeoPixel.show();
}
if (LogicTemp < SetTemperature)
{
digitalWrite(MOSFET, 1);
}
else
{
digitalWrite(MOSFET, 0);
}
}
else
{
NeoPixel.clear();
NeoPixel.show();
}
if (mixing_status == 1)
{
StepperSpeed = map(MixingSpeed, 0, 100, 0, 1000);
stepper->setSpeedInHz(StepperSpeed);
stepper->setAcceleration(100);
stepper->runForward();
}
else
{
stepper->stopMove();
}
// Menu
if (!show_menu)
{
MainScreen();
if (rotaryEncoder.isEncoderButtonClicked())
{
show_menu = true;
setForRotary(0);
}
}
else
{
if (rotaryEncoder.encoderChanged())
{
showMenu();
}
if (rotaryEncoder.isEncoderButtonClicked())
{
rotary_onButtonClick();
}
}
}
void MainScreen()
{
u8g2.clearBuffer();
u8g2.setCursor(0, 10);
u8g2.print("Heater: ");
if (heater_status == 0)
{
u8g2.print("OFF ");
}
else
{
u8g2.print("ON ");
}
u8g2.print("Mixing: ");
if (mixing_status == 0)
{
u8g2.print("OFF");
}
else
{
u8g2.print("ON");
}
u8g2.setCursor(2, 36);
u8g2.print("0");
u8g2.setCursor(48, 25);
u8g2.print(currtempb, 1); // current bottom
u8g2.setCursor(110, 36);
u8g2.print("100"); // HEAT SET + 20
u8g2.setCursor(2, 61);
u8g2.print("0");
u8g2.setCursor(48, 50);
u8g2.print(currtempt, 1); // current top
u8g2.setCursor(110, 61);
u8g2.print("100"); // HEAT SET + 20
drawProgressbar(12, 30, 92, 6, int(currtempb));
drawProgressbar(12, 55, 92, 6, int(currtempt));
u8g2.sendBuffer();
}
void showMenu()
{
beep();
int selectedValue = rotaryEncoder.readEncoder();
u8g2.clearBuffer();
u8g2.setCursor(40, 10);
uint8_t arrow = 1;
switch (currentMenu_item)
{
case 0: //"Setup menu"
uint8_t selected;
uint8_t lastselected;
u8g2.print("Setup Menu");
selected = selectedValue;
arrow = constrain(arrow + selected - lastselected, 0, 2);
lastselected = selected;
u8g2.setCursor(0, (16 * arrow) + 5);
u8g2.print(">");
for (uint8_t i = 1; i < 3; i++)
{
uint8_t drawnumber = selected + i - arrow;
if (drawnumber < sizeof(menu_items))
u8g2.setCursor(12, (16 * i) + 5);
u8g2.print(menu_items[selected + i - arrow]);
}
break;
case 1: // Heater temp
u8g2.setCursor(10, 15);
u8g2.print("Set Heater Temperature");
u8g2.setCursor(0, 25);
u8g2.print(">");
u8g2.setCursor(20, 25);
u8g2.print(selectedValue);
u8g2.print(" °C");
SetTemperature = selectedValue;
break;
case 2: // Heater ON/OFF
u8g2.setCursor(10, 15);
u8g2.print("Set Heater Status");
u8g2.setCursor(0, 25);
u8g2.print(">");
u8g2.setCursor(20, 25);
u8g2.print(selectedValue);
heater_status = selectedValue;
break;
case 3: // Mixing speed
u8g2.setCursor(10, 15);
u8g2.print("Set Mixing Speed");
u8g2.setCursor(0, 25);
u8g2.print(">");
u8g2.setCursor(20, 25);
u8g2.print(selectedValue);
u8g2.print(" %");
MixingSpeed = selectedValue;
break;
case 4: // Mixing ON/OFF
u8g2.setCursor(10, 15);
u8g2.print("Set Mixing Status");
u8g2.setCursor(0, 25);
u8g2.print(">");
u8g2.setCursor(20, 25);
u8g2.print(selectedValue);
mixing_status = selectedValue;
break;
case 5:
currentMenu_item = 0;
show_menu = false;
break;
default:
break;
}
u8g2.sendBuffer();
}
void SetVoltageOnline()
{
}
// creates a short beep on the buzzer
void beep()
{
if (beepEnable)
{
for (uint8_t i = 0; i < 255; i++)
{
digitalWrite(BUZZER_PIN, HIGH);
delayMicroseconds(125);
digitalWrite(BUZZER_PIN, LOW);
delayMicroseconds(125);
}
}
}
void drawProgressbar(int x, int y, int width, int height, int progress)
{
progress = progress > 100 ? 100 : progress; // set the progress value to 100
progress = progress < 0 ? 0 : progress; // start the counting to 0-100
float bar = ((float)(width - 1) / 100) * progress;
u8g2.drawFrame(x, y, width, height);
u8g2.drawBox(x + 2, y + 2, bar, height - 4); // initailize the graphics fillRect(int x, int y, int width, int height)
}
float MeasureTemp(int pin)
{
current = analogRead(pin);
current = 4095 / current - 1;
current = resistance / current;
temperature = current / termNom; // (R/Ro)
temperature = log(temperature); // ln(R/Ro)
temperature /= beta; // 1/B * ln(R/Ro)
temperature += 1.0 / (refTemp + 273.15); // + (1/To)
temperature = 1.0 / temperature; // The inverted value
temperature -= 273.15; // Convert from Kelvin to degrees Celsius
if (temperature < 0)
{
return 0;
}
else
{
return temperature;
}
}