/* This code has the UI.
logic:
it can detect short/long/very longs clicks. both while beeing pressed and while released.
there are some normal displays and some setup.
The normal diplays show information.
short click would go to the next window.
long click have no effect
the setup displays allow the user to select the alarm levels.
short click go to next window.
if you wail for a specifc time, the click would go to window1
waiting without any click will go to window1
long hold/click change to edit mode.
in edit click would increese the limit value.
waiting without a click ends the edit mode.
there are values with "off" setting. it change the value to 0
*/
#define DEBUG 1 // 1 to get the Serial.print
#include <Wire.h> //I2C for display
#include <OneWire.h> // for the DS18B20
#include <DallasTemperature.h> // for the DS18B20
#include <U8g2lib.h> //display
//display:
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0, /* reset=*/U8X8_PIN_NONE, /* clock=*/16, /* data=*/17); // ESP32 Thing, HW I2C with pin remapping
byte xo = 0;
byte yo = 0; //used for Oled anti burn-in
byte x1, x2, x3;
// u8g2.setContrast(n);n=1 low light, 254=high
uint8_t value=90;
long last_display_update = millis();
#define display_Update_time 2000 // update the display after this delay
#define pinButton 3 // hardware pin for display button (only pins 2+3 have interrupts support)
uint8_t Motor_alarm = 110;
uint8_t Gear_alarm = 105;
uint8_t Speed_alarm= 130;
typedef struct { // used to define the setup values min/max.
uint8_t minVal;
uint8_t maxVal;
boolean AllowOff;
} SetupLimitsStruct ;
const SetupLimitsStruct SetupLimits [] = {
{90, 125, false}, //motor temp limits
{90, 125, false}, //gear temp limits
{100,140, true} //speed limits
};
//declare all the display fuctions in order to get them into a pointer array
void DisplayMain();
void DisplayInternalExternal ();
void DisplayMaximun ();
void DisplayMotorSetPoint ();
void DisplayGearSetPoint ();
void DisplaySpeedSetPoint ();
typedef void (*DisplayFunction) ();
DisplayFunction DF[]={DisplayMain, DisplayInternalExternal, DisplayMaximun, DisplayMotorSetPoint, DisplayGearSetPoint, DisplaySpeedSetPoint};
uint8_t *pEditValue[]={NULL, NULL, NULL, &Motor_alarm, &Gear_alarm, &Speed_alarm}; // pointers to the value changed in the edit mode. (NULL for the none edit displays)
uint8_t ActiveDisplay=1; // current display
#define DisplyLastNormal 3 // there are 3 normal info displays
#define DisplyLast 6 // there are 6 displays
#define DisplySetupSkipTimeout 5000 // a click in a setup display longer than this from the last click will go to display 1 (and not next setup)
#define DisplySetupTimeout 20000 // after this time in a setup display with no input, go to display 1
boolean Item_in_edit = false; // the current item is in edit mode
boolean Item_in_edit_show=true; // does the item currently shown?
//uint8_t *pEditValue; // pointer to the current edit item
const uint16_t Item_flash_length = 1000 ; // time in ms for full flash cycle of selected item
const uint16_t Item_flash_hidden_time = 300 ; // time in which the item is hidden
unsigned long Item_Millis_edit_start; // time when the edit started.
unsigned long currentMillis;
unsigned long lastMillisPressed;
unsigned long lastMillisReleased;
unsigned long previousMillisReleased;
const unsigned long shortDelay = 500;
const unsigned long longDelay = 1500;
#define bts 1 // still pressed short
#define btl 2 // still pressed long
#define btv 3 // still pressed very long
#define btS 11 // presed short
#define btL 12 // presed long
#define btV 13 // presed very long
byte lastButtonState = HIGH; // set default to not active
bool sequenceActive = false; // true when a button sequence is active
char input;
uint8_t Bstate;
void setup() {
Serial.begin(9600);
Serial.println(F("Hi"));
Wire.begin();
pinMode(pinButton, INPUT_PULLUP); // display button
// Wire.setWireTimeout(3000,true); // agaist getting stuck in I2C transmission to the display.
u8g2.begin();
}
void loop() {
// if (Wire.getWireTimeoutFlag()) { // detect time out in I2C (OLED) communication.
// Wire.clearWireTimeoutFlag();
// Serial.println(F("I2C timeout\n"));
// }
currentMillis = millis();
Bstate = ReadButton();
if (Bstate > 0) {analyzeButton (); } // button pressed
if (ActiveDisplay > DisplyLastNormal) { // long time in the setup displays with no input
if (Item_in_edit && (currentMillis-previousMillisReleased > DisplySetupTimeout /2 )) { // in edit and 3/4 of the allowed time with no click passed --> edit edit
//??? need to update this to close the edit....
Serial.println("long delay in edit, exit edit");
Item_in_edit=false;
Item_in_edit_show=true;
last_display_update=0;
}
if (currentMillis-previousMillisReleased > DisplySetupTimeout) { // long time with no click in setup, go to display 1
Serial.println("long delay in setup, back to 1");
ActiveDisplay=1; // back to display 1
last_display_update=0;
}
}
// normal display update once every 2 sec
if (!Item_in_edit && (currentMillis-last_display_update >= display_Update_time)) { // update display once every x sec
last_display_update = millis();
DisplayData();
}
if (Item_in_edit) {
// Serial.print("-"); Serial.print (Item_in_edit_show);
// Serial.print(" -"); Serial.println((currentMillis-Item_Millis_edit_start) % Item_flash_length);
if ((currentMillis-Item_Millis_edit_start) % Item_flash_length < Item_flash_hidden_time) {
if (Item_in_edit_show) {
Item_in_edit_show=false;
DisplayData();
}
} else {
if (!Item_in_edit_show) {
Item_in_edit_show=true;
DisplayData();
}
}
}
delay(5);
}
// do this in a function to allow return to exit out
void analyzeButton () {
Serial.print("d="); Serial.print(ActiveDisplay); Serial.print(" b=");Serial.println(Bstate);
if (ActiveDisplay < DisplyLastNormal) { // in normal display (1/2) go to next
if (Bstate > 10) {
ActiveDisplay++; // on any lenght of press (no edit in these displays)
last_display_update=0; // make sure the display will be updated
return(0);
}
}
if (ActiveDisplay == DisplyLastNormal) { // in last normal display go to next. in long delay from last click jump to 0
if (Bstate > 10) {
if (currentMillis-previousMillisReleased < DisplySetupSkipTimeout) { // no long delay from previus click
ActiveDisplay++; // go to next display
} else {ActiveDisplay=1;} // after long delay go to display 1
}
last_display_update=0; // make sure the display will be updated
return(0);
}
if (ActiveDisplay > DisplyLastNormal) { // in setup display
if (!Item_in_edit) { // not in edit mode
Serial.print(">>");
if (Bstate==btS) { // short click, in the setup displays go to next or 1 display
Serial.print("Time=");
Serial.println(currentMillis-previousMillisReleased );
if (currentMillis-previousMillisReleased < DisplySetupSkipTimeout) { // no long delay from previus click
ActiveDisplay++; // go to next display
if (ActiveDisplay > DisplyLast) {ActiveDisplay=1;}
} else {ActiveDisplay=1;} // after long delay go to display 1
last_display_update=0; // make sure the display will be updated
return(0);
}
if (Bstate==btl) { // Long continus click, in the setup displays go to edit
Serial.println("$$$$$$$$$$$$$$");
Serial.println("going to edit mode");
// if (ActiveDisplay==4) pEditValue=&Motor_alarm; // set the pointer to the needed varibale to edit
// if (ActiveDisplay==5) pEditValue=&Gear_alarm;
// if (ActiveDisplay==6) pEditValue=&Speed_alarm;
Serial.print (" V="); Serial.println(*pEditValue[ActiveDisplay-1]);
Item_in_edit=true;
Item_in_edit_show=true;
Item_Millis_edit_start=currentMillis; // store current time for the blinking
last_display_update=0;
return(0);
}
}
if (Item_in_edit) { // in edit mode
if (Bstate> 10) { // any click , add 1
Serial.print("## ");
Serial.println(Bstate);
*pEditValue[ActiveDisplay-1]=*pEditValue[ActiveDisplay-1]+1;
//*pEditValue++;
uint8_t n=ActiveDisplay-DisplyLastNormal-1;
if (*pEditValue[ActiveDisplay-1] == 1) *pEditValue[ActiveDisplay-1]=SetupLimits[n].minVal; // getting out of OFF mode
if (*pEditValue[ActiveDisplay-1]> SetupLimits[n].maxVal) { // if over the max
if (SetupLimits[n].AllowOff) *pEditValue[ActiveDisplay-1]=0; // if allwed off then set to 0
else *pEditValue[ActiveDisplay-1]= SetupLimits[n].minVal; // else set to min
}
last_display_update=0;
Serial.println(" click in edit mode");
Item_Millis_edit_start= currentMillis + Item_flash_length-Item_flash_hidden_time; //make sure the update value will be shown for shot time
Item_in_edit_show=false;
return(0);
}
// in edit mode
}
}
}
/* read the state of the button and return:
" " = nothing pressed
"s" = short press, still pressing
"l" = long press, still pressing
"S" = Short press, no longer pressing
"L" = long press, no longer pressing
"V" = very long press, no longer pressing
*/
uint8_t ReadButton () {
byte buttonState = (byte) digitalRead(pinButton);
if (buttonState != lastButtonState) { // change in buttonState
lastButtonState = buttonState; // store new button state
if (buttonState == LOW) { // LOW when the button is pressed
lastMillisPressed = currentMillis; //remember the when the button was pressed.
Serial.println("press");
return (0); // still don't send this as pressed, to be sure it's not transient effect
} else { // Button event: the button has been released just now.
unsigned long t = currentMillis - lastMillisPressed;
if (t < 10) {Serial.println("too short click detected"); return(0);}
previousMillisReleased = lastMillisReleased; // release time of the click before
lastMillisReleased = currentMillis; //remember the when the button was released.
// Calculate for how long the button was pressed.
Serial.print("unpress ");
Serial.println(t);
if( t < shortDelay) { return (btS); } // button was pressed for a short time
else if( (t >= shortDelay) && (t < longDelay)) { return (btL);} // button was pressed for a long time
else {return (btV);} // button was pressed for a very long time
}
} else if (buttonState== LOW) { // still pressed, return for now long
unsigned long t = currentMillis - lastMillisPressed;
if( t < shortDelay) { return (bts); } // button pressed for a short time
else if( (t >= shortDelay) && (t < longDelay)) { return (btl);} // button pressed for a long time
else {return (btv);} // button pressed for a very long time
}
return(0); // not pressed, same as last test
}
void DisplayMain () {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_logisoso24_tr); //t= ?? n=only numbers
u8g2.setCursor(0, 41);
u8g2.print(F("D1: Main"));
} while (u8g2.nextPage());
}
void DisplayInternalExternal () {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_logisoso24_tr); //t= ?? n=only numbers
u8g2.setCursor(0, 41);
u8g2.print(F("D2: int/ext"));
} while (u8g2.nextPage());
}
void DisplayMaximun () {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_logisoso24_tr); //t= ?? n=only numbers
u8g2.setCursor(0, 41);
u8g2.print(F("D3: Max"));
} while (u8g2.nextPage());
}
void DisplayMotorSetPoint () {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_logisoso16_tr); //t= ?? n=only numbers
u8g2.setCursor(0, 20);
u8g2.print(F("Motor alarm"));
u8g2.setCursor(30, 50);
if (Item_in_edit_show) u8g2.print(Motor_alarm);
} while (u8g2.nextPage());
}
void DisplayGearSetPoint () {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_logisoso16_tr); //t= ?? n=only numbers
u8g2.setCursor(0, 20);
u8g2.print(F("Gear alarm"));
u8g2.setCursor(30, 50);
if (Item_in_edit_show) u8g2.print(Gear_alarm);
} while (u8g2.nextPage());
}
void DisplaySpeedSetPoint () {
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_logisoso16_tr); //t= ?? n=only numbers
u8g2.setCursor(0, 20);
u8g2.print(F("Spead alarm"));
u8g2.setCursor(30, 50);
if (Item_in_edit_show) {
if (Speed_alarm==0) u8g2.print("Off");
else u8g2.print(Speed_alarm);
}
} while (u8g2.nextPage());
}
void DisplayData() {
DF[ActiveDisplay-1](); // call for the function that connects to the active display
}
void u8g2_WriteText(char text2[10], boolean DrawBorder) { // display a text with/without a border
u8g2.setPowerSave(0); // turn on screen
u8g2.firstPage(); //double buffer displaying
do {
u8g2.setFont(u8g2_font_logisoso24_tr); //t= ?? n=only numbers
int width = u8g2.getStrWidth(text2);
u8g2.setCursor((128 - width) / 2, 40);
u8g2.drawStr((128 - width) / 2, 40, text2);
if (DrawBorder) { u8g2.drawFrame(0, 0, 128, 64); }
} while (u8g2.nextPage());
}
void u8g2_WriteText(char text2[10]) {
u8g2_WriteText(text2, false);
}