#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "myGFX.h"
#include <FastLED.h>
#define NUM_LEDS 20
#define BRIGHTNESS 25
FASTLED_USING_NAMESPACE
CRGB leds[NUM_LEDS];
#define potiPin 36
#define ledPin 14
#define encA 34
#define encB 35
#define encBtn 33
/*
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin)
*/
Adafruit_SSD1306 display(128, 64, &Wire, 4);
#define MENU_TIME 0
#define MENU_TEMP 1
#define MENU_STATE 2
struct scale{ uint8_t lineLen; float linePosDeg; char t_val[3]; int8_t xOff; int8_t yOff;};
const scale scaleVals[9] = {
{4, -120.0, "0",-12,3},
{3, -90.0 , "", 0, 0},
{4, -60.0 , "10",-13,-8},
{3, -30.0 , "",0,0},
{4, 0.0 , "20",-4,-12},
{3, 30.0 , "",0,0},
{4, 60.0 , "30",8,-8},
{3, 90.0 , "",0,0},
{4, 120.0 , "40",6,2}
};
unsigned long time_stop;
char time_m[4];
char time_s[4];
//overview grapics const
// const int gauge_x = 36;
// const int gauge_y = 37;
// const int gauge_r = 26;
const int gauge_x = 36;
const int gauge_y = 40;
const int gauge_r = 16;
const int dot_r = 6;
const float arcMin = -120.0;
const float arcMax = 120.0;
// val contraining const
const float temp_min = 30;
const float temp_max = 60;
const uint16_t time_min = 60;
const uint16_t time_max = 1500;
uint16_t time_setP = 600;
float temp_setP = 50.0;
bool state = false;
int8_t menuSelect = MENU_TEMP; // selected menu index MENU_TIME=0, MENU_TEMP=1 MENU_STATE=2
void setup() {
Serial.begin(115200);
FastLED.addLeds<WS2812B, ledPin, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for(;;);}
pinMode(potiPin, INPUT);
display.clearDisplay();
drawOverview(); // draw all static elements (gauge, static icons etc...)
// Overview initial values :
drawTimer(time_setP); // int, seconds
drawTempSP(temp_setP); // float, temp setpoint --> TO DO: update PID set point
drawState(state); // bool, plate on/off state
drawTempRead(25.0); // update gauge
display.display();
setMenuSelect(0); // also changes menuSelect (menuSelect in bounds 0 to 2)
drawState(false);
showWarn(true);display.display();
delay(3000);
showWarn(false);display.display();
}
void loop() {
uint16_t potiVal = chkPotiUpdate();
if(potiVal != -1){
switch (menuSelect) {
case MENU_TIME : drawTimer(poti2time(potiVal)); break;
case MENU_TEMP : drawTimer(poti2tempSP(potiVal)); break;
case MENU_STATE: setState(potiVal); break;
default: break;
}
}
Serial.println(analogRead(potiPin));
}
// returns -1 if no significant update happend
// deadzone depends on last time & amount value changed
// run this at least every 100 ms
int16_t chkPotiUpdate(){
static unsigned long potiSample; //sampling timer to compare value deviation
static uint8_t deadzone = 15; //5 or 15
static int16_t potiVal_last = analogRead(potiPin);
static int16_t potiVal_last_s = potiVal_last;
int16_t potiVal = analogRead(potiPin);
static uint8_t count = 0;
if(millis() > potiSample){ // 100ms min sample time
potiSample = millis() + 100;
if((potiVal < potiVal_last_s + 5 && potiVal > potiVal_last_s - 5) && deadzone == 5){count++;}
else{count = 0;}
potiVal_last_s = potiVal;
}
if(count > 10){deadzone = 15;} // raise deadzone to 15 after 10x 10ms within range of 5
if(potiVal > potiVal_last + deadzone || potiVal < potiVal_last - deadzone){
count = 0; deadzone = 5;
potiVal_last = potiVal;
return potiVal;
}
else{
potiVal_last = potiVal;
return -1; // no significant change
}
}
// just as additional alias
void setMenuSelect(int8_t idx){drawSelFrame(idx);}
void drawSelFrame(int8_t idx){
static int8_t idx_last = -1;
if(idx_last == idx){return;}
if(idx < 0){idx += 3; }
menuSelect = (idx % 3);
drawSelFrame_color(idx_last,BLACK);
drawSelFrame_color(idx,WHITE);
display.display();
idx_last = idx;
}
void drawSelFrame_color(uint8_t idx, uint16_t color){
display.drawRoundRect(75, 1+idx*20, 52, 21, 3, color);
}
//---------------------------------------TIME--------------------------------------------
//returns timer val in seconds constrained by time_min & time_max
uint16_t poti2time(uint16_t potiVal){
static uint16_t time_last = 0;
uint16_t time_new;
time_new = map(potiVal,12,1000, time_min / 5,time_max / 5) * 5 ;
// poti bounds -> 12 to 1000
if(time_new > time_max){return time_max;} // when poti val is > 1000
else if(time_new < time_min){return time_min;}
return time_new;
}
void drawTimer(uint16_t time_in){
static uint16_t time_in_last = 0;
if(time_in_last == time_in )return;
printTimeColor(time_in_last,BLACK);
printTimeColor(time_in,WHITE);
time_in_last = time_in;
display.display();
}
void printTimeColor(uint16_t time_in,uint16_t color){
if(time_in >= time_max){ display.drawBitmap(92, 9, infinite_15x7 , 15, 7, color);}
else{
display.setTextSize(1);
display.setTextColor(color);
display.setCursor(79, 5);
sprintf(time_m,"%02d", (int)(time_in / 60));
sprintf(time_s,"%02d", (int)(time_in % 60));
display.print(time_m);
display.print(':');
display.print(time_s);
display.drawBitmap(82, 14, mmss_21x5 , 21, 5, color);
}
}
//----------------------------------------TEMP--------------------------------------------
float poti2tempSP(uint16_t potiVal){
float tempSP_new;
if(potiVal < 12){tempSP_new = temp_min;}
else if (potiVal > 1012){tempSP_new = temp_max;}
else {tempSP_new = ((int)map(potiVal,12,1012, (int)temp_min*10,(int)temp_max*10)/10.0) ;}
return tempSP_new;
}
void drawTempSP(float tempSP_in){
static float tempSP_last = 0;
if(tempSP_in == tempSP_last)return;
if(tempSP_last != 0 ) { printTempSP_color(tempSP_last,BLACK);}
printTempSP_color(tempSP_in,WHITE);
drawSetP(gauge_x,gauge_y,gauge_r,tempSP_in);
tempSP_last = tempSP_in;
display.display();
}
void printTempSP_color(float tempSP_in,uint16_t color){
display.setTextSize(1);
display.setTextColor(color);
display.setCursor(82, 27);
char t_disp[4];
dtostrf(tempSP_in,3,1,t_disp);
display.print(t_disp);
}
//----------------------------------------ON/OFF-------------------------------------
void setState(uint16_t pVal){
static int16_t pVal_last = -1;
if(pVal_last == -1){pVal_last = pVal; return;}
}
void drawOverview(){
display.clearDisplay();
drawArc(gauge_x,gauge_y,gauge_r,arcMin,arcMax,true,2);
for(int i = 0; i < (sizeof(scaleVals)/sizeof(scaleVals[0])); i++){
drawScale(gauge_x,gauge_y,gauge_r,scaleVals[i]);
}
drawCenter(gauge_x,gauge_y,dot_r);
display.drawBitmap(112, 4, time_13x16 , 13, 16, SSD1306_WHITE);
display.drawBitmap(112, 24, temperature_13x16 , 13, 16, SSD1306_WHITE);
display.display();
}
float DEG2RAD(float deg) { return ((deg * 71.0) / 4068.0);}
char attrBuf[8];
const char * i2char(int ival){ itoa(ival,&attrBuf[strlen(attrBuf)],10); return attrBuf;}
//----------------------------------- --------------------------------------------------------------//
//----------------------------------- static display functions -------------------------------------//
void drawArc(uint8_t xcenter, uint8_t ycenter, uint8_t r_arc, float degStart, float degEnd, bool clockwise, int width){
degStart+= clockwise ? 90.0 : 270.0; // shift start from 3 o'clock to 12 o'clock
degEnd+=clockwise ? 90.0 : 270.0;// shift end like degStart
float dir = clockwise ? -1 : 1 ;
static int xlast = -1;
static int ylast = -1;
for(int w = 0; w < width; w++){
for (float i = DEG2RAD(degStart); i < DEG2RAD(degEnd) ; i = i + 0.01){
int xnew = xcenter + ( cos(i) * (r_arc + w) * dir );
int ynew = ycenter + ( sin(i) * (r_arc + w) * dir );
if(xnew != xlast || ynew != ylast){ // skip when already drawn
display.drawPixel(xnew,ynew,WHITE);
Serial.print("{ ");Serial.print(xnew);Serial.print(" , ");Serial.print(xnew);Serial.println("}");
delay(10);
xlast = xnew; ylast = ynew;
}
}
}
}
void drawScale(uint8_t xcenter, uint8_t ycenter, uint8_t r_arc, scale thisVal){
//uint8_t len, float degPos, String value, int8_t xoff, int8_t yoff
float i = DEG2RAD(thisVal.linePosDeg+90);
uint8_t x1 = xcenter - cos(i) * r_arc ;
uint8_t y1 = ycenter - sin(i) * r_arc ;
uint8_t x2 = xcenter - cos(i) * (r_arc - thisVal.lineLen) ;
uint8_t y2 = ycenter - sin(i) * (r_arc - thisVal.lineLen) ;
display.drawLine(x1, y1, x2, y2, SSD1306_WHITE);
if(thisVal.t_val[0] != '\0'){ drawTinyNum(x2 + thisVal.xOff, y2 + thisVal.yOff ,thisVal.t_val , WHITE); }
}
void drawTinyNum(uint8_t xPos, uint8_t yPos, char * numChars, uint16_t color){
uint8_t xOff = 0;
for(uint8_t i = 0; i < strlen(numChars); i++){ // ASCII Table --> 48(dec) = '0', 57 = '9'
if(numChars[i] >= 48 && numChars[i] <= 57){ display.drawBitmap(xPos+xOff, yPos, tinyNums[numChars[i]-48] , 3, 5, color);}
xOff += 4;
}
}
void drawCenter(uint8_t xcenter, uint8_t ycenter, uint8_t r_dot){
display.setTextSize(1);
display.fillCircle(xcenter,ycenter,r_dot,WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(xcenter - 5,ycenter - 9);
display.print(".");
display.setCursor(xcenter - 1,ycenter - 2);
display.print("C");
display.drawRoundRect(xcenter-15, ycenter+11, 31, 11,4, WHITE); // current temperature box
}
//----------------------------------- --------------------------------------------------------------//
//----------------------------------- modular display functions-------------------------------------//
void showWarn(bool warnActive){
static bool warnLast = false;
if(warnActive != warnLast){
display.drawBitmap(75, 44, warning_20x16, 20, 16, warnActive ? WHITE : BLACK);
warnLast = warnActive;
display.display();
}
}
void drawState(bool newState){
//state = newState;
if(newState){
display.drawBitmap(102, 44, StateOff_22x16 , 22, 16, BLACK);
display.drawBitmap(102, 44,StateOn_22x16 , 22, 16, WHITE);
}
else{
display.drawBitmap(102, 44,StateOn_22x16 , 22, 16, BLACK);
display.drawBitmap(102, 44, StateOff_22x16 , 22, 16, SSD1306_WHITE);
}
display.display();
}
void drawTempRead(float newTemp){ drawPointerandVal(gauge_x, gauge_y, newTemp, dot_r+3,gauge_r-6,20);}
void drawPointerandVal(uint8_t xcenter, uint8_t ycenter, float temp,int r_min, int r_max, float deg_width){
static float temp_last = -1;
if(temp_last != -1){drawPointerColor(xcenter, ycenter, temp_last, r_min, r_max, deg_width, BLACK);}
drawPointerColor(xcenter, ycenter, temp, r_min, r_max, deg_width, WHITE);
temp_last = temp;
}
void drawPointerColor(uint8_t xcenter, uint8_t ycenter, float temp,int r_min, int r_max, float deg_width, uint16_t color){
float deg = map(temp,20.0,60.0,arcMin,arcMax);
float i = DEG2RAD(deg+90);
uint8_t x0 = xcenter - cos(i) * r_min ;
uint8_t y0 = ycenter - sin(i) * r_min ;
uint8_t x1 = xcenter - cos(i) * r_max ;
uint8_t y1 = ycenter - sin(i) * r_max ;
i = DEG2RAD(deg+90+deg_width);
uint8_t x2 = xcenter - cos(i) * r_min ;
uint8_t y2 = ycenter - sin(i) * r_min ;
i = DEG2RAD(deg+90-deg_width);
uint8_t x3 = xcenter - cos(i) * r_min ;
uint8_t y3 = ycenter - sin(i) * r_min ;
display.fillTriangle(x0, y0, x1, y1, x2, y2, color);
display.fillTriangle(x0, y0, x1, y1, x3, y3, color);
display.setTextSize(1);
display.setTextColor(color);
display.setCursor(xcenter-11, ycenter+13);
char t_disp[4];
dtostrf(temp,3,1,t_disp);
display.print(t_disp);
}
void drawSetP(uint8_t xcenter, uint8_t ycenter, uint8_t r_arc, float temp_set){
static float temp_set_last = -1;
if(temp_set_last != -1){ drawSetP_color(xcenter, ycenter, r_arc, temp_set_last, BLACK); }
drawSetP_color(xcenter, ycenter, r_arc, temp_set, WHITE);
temp_set_last = temp_set;
}
void drawSetP_color(uint8_t xcenter, uint8_t ycenter, uint8_t r_arc, float temp_set, uint16_t color){
uint8_t height = 4;
uint8_t halfbase = 6; //
float deg = map(temp_set,20.0,60.0,arcMin,arcMax);
//
for(int i = 0; i < (sizeof(scaleVals)/sizeof(scaleVals[0])); i++){
// if(scaleVals[i].linePosDeg > deg - (halfbase*2) && scaleVals[i].linePosDeg < deg + (halfbase*2)){
scale redraw = scaleVals[i]; // copy to tem scale entry
redraw.t_val[0] = '\0'; // clear the value chars (only redraw the scale line)
drawScale(gauge_x,gauge_y,gauge_r,redraw);
// break;
// }
}
float i = DEG2RAD(deg+90);
uint8_t x0 = xcenter - cos(i) * (r_arc-1) ;
uint8_t y0 = ycenter - sin(i) * (r_arc-1) ;
i = DEG2RAD(deg+90-halfbase);
uint8_t x1 = xcenter - cos(i) * (r_arc - height);
uint8_t y1 = ycenter - sin(i) * (r_arc - height);
i = DEG2RAD(deg+90+halfbase);
uint8_t x2 = xcenter - cos(i) * (r_arc - height);
uint8_t y2 = ycenter - sin(i) * (r_arc - height) ;
display.fillTriangle(x0, y0, x1, y1, x2, y2, color);
}
esp:VIN
esp:GND.2
esp:D13
esp:D12
esp:D14
esp:D27
esp:D26
esp:D25
esp:D33
esp:D32
esp:D35
esp:D34
esp:VN
esp:VP
esp:EN
esp:3V3
esp:GND.1
esp:D15
esp:D2
esp:D4
esp:RX2
esp:TX2
esp:D5
esp:D18
esp:D19
esp:D21
esp:RX0
esp:TX0
esp:D22
esp:D23
encoder1:CLK
encoder1:DT
encoder1:SW
encoder1:VCC
encoder1:GND
ssd1306:DATA
ssd1306:CLK
ssd1306:DC
ssd1306:RST
ssd1306:CS
ssd1306:3V3
ssd1306:VIN
ssd1306:GND
joystick1:VCC
joystick1:VERT
joystick1:HORZ
joystick1:SEL
joystick1:GND
pot1:GND
pot1:SIG
pot1:VCC