(edits: new video URL and text additions)
My first attempt of a piece of code to input wifi SSID and password via a menu option with use of a rotary encoder.
I had so much fun coding this although, as most of you can tell, it's a not-so-experienced-programming-level of coding. BUT I learned so much! I almost never ask help because doing it yourself is much better for learning. It took me 2 weeks... I also lost a lot of hair due to scratching my head so much when getting stuck so often...
(uhm... Yes I can hard-code the wifi credentials but I just wanted a new challenge...)
I used a Arduino Nano 33 IoT and VScode/PlatformIO.
Here a link to the somewhat long demo video: CLICK
The sketch of which the only part working is the password input menu.
I love to learn... I must be possible to do this better (with an excisting library)
Oh one thing: The 1,44" TFT screen I used has a terribly narrow viewing angle... Any suggestions for a better one? has to be 1" x 1.5" max.
Thanks.
BTW1: This menu option will be part of the v2 of my Bathroom Fan Controller of which version 1 performs so very well.
#include <Arduino.h>
// Wifi
#include <WiFiNINA.h>
#include <WiFiUdp.h>
#include "arduino_secrets.h"
// Includes for Time
#include <NTPClient.h>
#include <TimeLib.h>
#include <Timezone.h>
#include "timeRules.h"
// Buttons
#include <AceButton.h>
// TFT screen
#include <TFT_eSPI.h> // Hardware-specific library
//
#include <SPI.h>
#include <Wire.h>
// Encoder
#include <SimpleRotary.h>
// Pin A, Pin B, Button Pin
SimpleRotary enc(A0, A1, A2);
// TFT 1,44" ST7735 Green tab3
TFT_eSPI tft = TFT_eSPI();
// Additional font colors beside the ones of the TFT_eSPI library
#define TFT_DARKGREY1 0x5ACB // Box Backgound
#define TFT_DEEPGREY 0x2945
#define TFT_DARKBLUE 0x0006
#define TFT_TURQUOISE 0x471A
#define TFT_GOLDENROD 0xDD24
#define TFT_SLATEBLUE 0x6AD9
#define TFT_DARKGREEN1 0x0140
#define TFT_DARKRED 0x5000
#define TFT_DEEPRED 0x3000
#define TFT_NICEGREEN 0x5C4C
#define TFT_ORANGE1 0xFBE0
#define TFT_LOWBRIGHTBLUE 0x3C33
// Do not include "" around the array name!
#define AA_FONT_SMALL NotoSansBold15
#define AA_FONT_LARGE NotoSansBold36
// Custom Graphics
// Ventilation Fan icon:
#define fan_width 16
#define fan_height 16
const unsigned char fan_icon[] PROGMEM = {
// fan, 16x16px
0x0f, 0x00, 0x1f, 0x80, 0x1f, 0xc0, 0x0f, 0xc6, 0x07, 0xcf, 0x03, 0x9f, 0x3b, 0xff, 0x7e, 0x7f,
0xfe, 0x7e, 0xff, 0xdc, 0xf9, 0xc0, 0xf3, 0xe0, 0x63, 0xf0, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0xf0};
//Wifi
int status = WL_AP_CONNECTED;
//Button
using namespace ace_button;
// The pin numbers attached to the buttons.
const int MODE_BUTTON_PIN = 2;
const int UP_BUTTON_PIN = 3;
const int DOWN_BUTTON_PIN = 4;
// Two buttons, automatically sharing the default System ButtonConfig.
AceButton modeButton(MODE_BUTTON_PIN);
AceButton upButton(UP_BUTTON_PIN);
AceButton downButton(DOWN_BUTTON_PIN);
//please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = WIFI_SSID; // your network SSID (name)
char pass[] = WIFI_PASSWORD; // your network password (use for WPA, or use as key for WEP)
// A UDP instance to let us send and receive packets over UDP
// Wifi Objects
WiFiUDP ntpUDP;
// Time Objects
NTPClient timeClient(ntpUDP);
// Function prototypes
time_t syncNTPTime();
// d8b 888 888
// Y8P 888 888
// 888 888
// 888 888 8888b. 888d888 888 8888b. 88888b. 888 .d88b. .d8888b
// 888 888 "88b 888P" 888 "88b 888 "88b 888 d8P Y8b 88K
// Y88 88P .d888888 888 888 .d888888 888 888 888 88888888 "Y8888b.
// Y8bd8P 888 888 888 888 888 888 888 d88P 888 Y8b. X88
// Y88P "Y888888 888 888 "Y888888 88888P" 888 "Y8888 88888P'
//
//
//
// custom ASCII (lookup) table. If entering characters it's easier to know
// roughly where to find the character you need.
// You can change the it to your own liking... I used a spreadsheet to quickly
// put this together and then copied it here with some editing afterwards.
const int asciiVal[][2] = {
{0, 65}, // ASCII: A
{1, 66}, // ASCII: B
{2, 67}, // ASCII: C
{3, 68}, // ASCII: D
{4, 69}, // ASCII: E
{5, 70}, // ASCII: F
{6, 71}, // ASCII: G
{7, 72}, // ASCII: H
{8, 73}, // ASCII: I
{9, 74}, // ASCII: J
{10, 75}, // ASCII: K
{11, 76}, // ASCII: L
{12, 77}, // ASCII: M
{13, 78}, // ASCII: N
{14, 79}, // ASCII: O
{15, 80}, // ASCII: P
{16, 81}, // ASCII: Q
{17, 82}, // ASCII: R
{18, 83}, // ASCII: S
{19, 84}, // ASCII: T
{20, 85}, // ASCII: U
{21, 86}, // ASCII: V
{22, 87}, // ASCII: W
{23, 88}, // ASCII: X
{24, 89}, // ASCII: Y
{25, 90}, // ASCII: Z
{26, 97}, // ASCII: a
{27, 98}, // ASCII: b
{28, 99}, // ASCII: c
{29, 100}, // ASCII: d
{30, 101}, // ASCII: e
{31, 102}, // ASCII: f
{32, 103}, // ASCII: g
{33, 104}, // ASCII: h
{34, 105}, // ASCII: i
{35, 106}, // ASCII: j
{36, 107}, // ASCII: k
{37, 108}, // ASCII: l
{38, 109}, // ASCII: m
{39, 110}, // ASCII: n
{40, 111}, // ASCII: o
{41, 112}, // ASCII: p
{42, 113}, // ASCII: q
{43, 114}, // ASCII: r
{44, 115}, // ASCII: s
{45, 116}, // ASCII: t
{46, 117}, // ASCII: u
{47, 118}, // ASCII: v
{48, 119}, // ASCII: w
{49, 120}, // ASCII: x
{50, 121}, // ASCII: y
{51, 122}, // ASCII: z
{52, 49}, // ASCII: 1
{53, 50}, // ASCII: 2
{54, 51}, // ASCII: 3
{55, 52}, // ASCII: 4
{56, 53}, // ASCII: 5
{57, 54}, // ASCII: 6
{58, 55}, // ASCII: 7
{59, 56}, // ASCII: 8
{60, 57}, // ASCII: 9
{61, 48}, // ASCII: 0
{62, 33}, // ASCII: !
{63, 63}, // ASCII: ?
{64, 64}, // ASCII: @
{65, 35}, // ASCII: #
{66, 36}, // ASCII: $
{67, 37}, // ASCII: %
{68, 94}, // ASCII: ^
{69, 38}, // ASCII: &
{70, 42}, // ASCII: *
{71, 45}, // ASCII: -
{72, 43}, // ASCII: +
{73, 61}, // ASCII: =
{74, 95}, // ASCII: _
{75, 40}, // ASCII: (
{76, 41}, // ASCII: )
{77, 123}, // ASCII: {
{78, 125}, // ASCII: }
{79, 91}, // ASCII: [
{80, 93}, // ASCII: ]
{81, 47}, // ASCII: slash
{82, 92}, // ASCII: backslash
{83, 124}, // ASCII: |
{84, 60}, // ASCII: <
{85, 62}, // ASCII: >
{86, 58}, // ASCII: :
{87, 59}, // ASCII: ;
{88, 46}, // ASCII: .
{89, 44}, // ASCII: ,
{90, 34}, // ASCII: "
{91, 39}, // ASCII: '
{92, 126}, // ASCII: ~
{93, 96}, // ASCII: `
};
// define the desired layout of the display of the 30 character string here.
// character width: if you change this you need to do a lot of manual adjusting
// in the code because the cursor with and erase before write on screen is all
// tied to this 'width' value.
#define CHAR_WIDTH 12
#define START_POS_PW 2
// the 3-line string positions with 17px height
#define PW_LINE_1 20
#define PW_LINE_2 37
#define PW_LINE_3 54
const int passwCursor[][2] = {
{(0 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(1 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(2 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(3 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(4 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(5 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(6 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(7 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(8 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(9 * CHAR_WIDTH) + START_POS_PW, PW_LINE_1},
{(0 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(1 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(2 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(3 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(4 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(5 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(6 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(7 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(8 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(9 * CHAR_WIDTH) + START_POS_PW, PW_LINE_2},
{(0 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(1 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(2 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(3 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(4 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(5 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(6 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(7 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(8 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3},
{(9 * CHAR_WIDTH) + START_POS_PW, PW_LINE_3}};
// variable for the 'options menu', see wState 0
#define PW_OPT_X 57
#define PW_OPT_Y1 97
#define PW_OPT_Y2 106
#define PW_OPT_Y3 116
const int pwOptCursor[][2] = {
{PW_OPT_X, PW_OPT_Y1},
{PW_OPT_X, PW_OPT_Y2},
{PW_OPT_X, PW_OPT_Y3}};
// Main state machine states
enum FSM
{
FSM_IDLE_FAN_OFF,
FSM_FAN_ON,
FSM_MANUAL_ON,
FSM_MANUAL_OFF,
FSM_DISABLE,
FSM_FAN_OFF_DELAY,
FSM_SET_THRESHOLD,
FSM_SET_HYSTERESIS,
FSM_SET_SWITCH_OFF_DELAY,
FSM_SET_WIFI_SSID,
FSM_SET_WIFI_CODE,
};
// set default Main state
FSM state = FSM_IDLE_FAN_OFF; // no action when starting
// set wifi Password menu Sub state
int wState = 0;
int wState_prev = 0; //not used!
// variables for button function demo - not used
bool displayRefresh = false;
// Three Button states: 0, 1, and 2.
int bState = 0;
// Change button increments this counter in Edit mode.
int counter = 0;
// LongPress on ModeButton will go into "edit" mode.
bool isEditting = false;
// In edit mode, the "field" is blinking. But when the Change button is
// Pressed or LongPressed, the blinking temporarily stops.
bool isBlinking = false;
// Menu
int menu_selection = 0; // For menu selection
bool in_menu = false;
bool enc_button = false;
// Rotary encoder
int enc_asc = 0;
int enc_val = 0;
int enc_pos = 0;
int enc_pos_prev = 0;
int enc_pos_curr = 0;
// Time in seconds to Sync NTP Time
const int sync_time = 60;
// Loop timer
unsigned long loop_interval = 1000L;
unsigned long main_prev_millis = 0;
// default wifi password if desired
// in this case I set one to debug and test
char wifiPass[31] = "1234567890ABCDFTYKNH&^%$FD@#(";
int wifiPassLen = 0;
int wifiPassLenMax = 30;
// 8888888b. 888 8888888888
// 888 "Y88b 888 888
// 888 888 888 888
// 888 888 .d88b. .d8888b 888 8888888 888 888 88888b. .d8888b
// 888 888 d8P Y8b d88P" 888 888 888 888 888 "88b d88P"
// 888 888 88888888 888 888 888 888 888 888 888 888
// 888 .d88P Y8b. Y88b. 888 888 Y88b 888 888 888 Y88b.
// 8888888P" "Y8888 "Y8888P 888 888 "Y88888 888 888 "Y8888P
//
//
// declare functions. In the Arduino IDE this is done also
// but in the background.
void stateMachine();
void checkEncoder();
void printWifiStatus();
void wifiStatus();
void wifiConnect();
void handleEvent(AceButton *button, uint8_t eventType, uint8_t /*buttonState*/);
time_t syncNTPTime();
// .d8888b. 888 .d88 88b.
// d88P Y88b 888 d88P" "Y88b
// Y88b. 888 d88P Y88b
// "Y888b. .d88b. 888888 888 888 88888b. 888 888
// "Y88b. d8P Y8b 888 888 888 888 "88b 888 888
// "888 88888888 888 888 888 888 888 Y88b d88P
// Y88b d88P Y8b. Y88b. Y88b 888 888 d88P Y88b. .d88P
// "Y8888P" "Y8888 "Y888 "Y88888 88888P" "Y88 88P"
// 888
// 888
// 888
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(115200);
Serial.println(F("setup(): begin"));
tft.begin();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
// Button uses the built-in pull up register.
pinMode(MODE_BUTTON_PIN, INPUT_PULLUP);
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
// Configure the ButtonConfig with the event handler.
ButtonConfig *buttonConfig = ButtonConfig::getSystemButtonConfig();
buttonConfig->setEventHandler(handleEvent);
buttonConfig->setFeature(ButtonConfig::kFeatureLongPress);
buttonConfig->setFeature(ButtonConfig::kFeatureRepeatPress);
buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterLongPress);
// disable functions to test only password string edit menu
// Wifi
//wifiConnect();
//wifiStatus();
// Time Setup
//timeClient.begin(0);
//setSyncProvider(syncNTPTime);
//setSyncInterval(sync_time);
// End of Setup
Serial.println(F("setup(): ready"));
}
// 888 .d88 88b.
// 888 d88P" "Y88b
// 888 d88P Y88b
// 888 .d88b. .d88b. 88888b. 888 888
// 888 d88""88b d88""88b 888 "88b 888 888
// 888 888 888 888 888 888 888 Y88b d88P
// 888 Y88..88P Y88..88P 888 d88P Y88b. .d88P
// 88888888 "Y88P" "Y88P" 88888P" "Y88 88P"
// 888
// 888
// 888
void loop()
{
//===> FAST TIMER <=========================================================
// Should be called every 4-5ms or faster, for the default debouncing time
// of ~20ms.
modeButton.check();
upButton.check();
downButton.check();
checkEncoder();
stateMachine();
//===> MAIN TIMER <=========================================================
unsigned long main_current_millis = millis();
if (main_current_millis - main_prev_millis >= 500)
{
main_prev_millis = main_current_millis;
// run everything here that needs to happen on
// 1 second timer
// DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG
//
// display several variables on the TFT display
// some only appear when needed
tft.fillRect(0, 0, 127, 17, TFT_BLACK);
tft.setCursor(0, 0, 1);
// print wifipassword array lenght
tft.setTextColor(TFT_MAGENTA);
tft.print("len");
tft.print(wifiPassLen);
//print enc_pos_prev
tft.setTextColor(TFT_ORANGE);
tft.print(" e");
tft.print(enc_pos);
//print enc_pos
tft.setTextColor(TFT_ORANGE1);
tft.print("|");
tft.print(enc_pos_prev);
//print wStatePrevious
tft.setTextColor(TFT_CYAN);
tft.print(" s");
tft.print(wState);
//print wStateCurrent
tft.setTextColor(TFT_DARKCYAN);
tft.print("|");
tft.print(wState_prev);
tft.setCursor(0, 9, 1);
// wifipassword array end position
if (wifiPassLen > 0)
{
tft.setTextColor(TFT_MAGENTA);
tft.print("end");
tft.print(wifiPassLen - 1);
}
if (wState == 1)
{
tft.setTextColor(TFT_RED);
tft.print(" dec");
tft.print(wifiPass[enc_pos], DEC);
}
if (wState == 2)
{
// print ASCII table -corresponding DEC.ASCII value
tft.setTextColor(TFT_BLUE);
tft.print(" dec");
tft.print((asciiVal[enc_val][1]));
// print ASCII table -position
tft.setTextColor(TFT_BLUE);
tft.print("|pos");
tft.print(enc_val);
}
//DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG
}
}
// 8888888888 .d8888b. 888b d888
// 888 d88P Y88b 8888b d8888
// 888 Y88b. 88888b.d88888
// 8888888 "Y888b. 888Y88888P888
// 888 "Y88b. 888 Y888P 888
// 888 "888 888 Y8P 888
// 888 Y88b d88P 888 " 888
// 888 "Y8888P" 888 888
//
//
//
// Main state machine
void stateMachine()
{
switch (state)
{
//##########################################################################
case FSM_IDLE_FAN_OFF:
{
// check actions
if (enc_button == true)
{
// reset enc button state
enc_button = false;
}
// DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG
// straight to the wifi password edit state
// set edit menu state
wState = 0;
// clear screen
tft.fillScreen(TFT_BLACK);
// set font color
tft.setTextColor(TFT_GREEN);
// go to next state
state = FSM_SET_WIFI_SSID;
}
break;
//##########################################################################
case FSM_SET_WIFI_SSID:
{
// go through the editing steps via a seperate state machine
switch (wState)
{
// ## ## ###### ######## ### ######## ######## #####
// ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ###### ## ## ## ## ###### ## ##
// ## ## ## ## ## ######### ## ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ##
// ### ### ###### ## ## ## ## ######## #####
case 0: // wifi password string - SETUP DISPLAY TEXT
{ // * <--- extra 'code block' to allow for local variables
//
// determine the lenght of the current array
wifiPassLen = (strlen(wifiPass));
// display current wifi password code string
for (int p = 0; p <= wifiPassLen; p++)
{
int x = passwCursor[p][0];
int y = passwCursor[p][1];
tft.setTextColor(TFT_DARKGREEN);
tft.setCursor(x + 2, y, 2); // font 2: 15px
tft.print(wifiPass[p]);
}
// display editing options text
tft.setTextColor(TFT_DARKGREEN);
tft.setCursor(PW_OPT_X + 10, PW_OPT_Y1, 1);
tft.print("BACKSPACE");
tft.setCursor(PW_OPT_X + 10, PW_OPT_Y2, 1);
tft.print("CLEAR ALL");
tft.setCursor(PW_OPT_X + 10, PW_OPT_Y3, 1);
tft.print("SAVE+EXIT");
// draw option menu cursor outlines
tft.drawCircle(PW_OPT_X, PW_OPT_Y1 + 3, 3, TFT_DARKGREEN1);
tft.drawCircle(PW_OPT_X, PW_OPT_Y2 + 3, 3, TFT_DARKGREEN1);
tft.drawCircle(PW_OPT_X, PW_OPT_Y3 + 3, 3, TFT_DARKGREEN1);
// selection cursor to default position
enc_pos = 0;
// previous position is needed to erase the cursor at that point
enc_pos_prev = 0;
// draw green selection cursor at the start position of the string
int x = passwCursor[enc_pos][0];
int y = passwCursor[enc_pos][1];
tft.drawRect(x, y, 12, 17, TFT_GREEN);
// jump to next step
wState_prev = 0;
wState = 1;
}
break;
//##########################################################################
//
// ## ## ###### ######## ### ######## ######## ##
// ## ## ## ## ## ## ## ## ## ## ####
// ## ## ## ## ## ## ## ## ## ##
// ## ## ## ###### ## ## ## ## ###### ##
// ## ## ## ## ## ######### ## ## ##
// ## ## ## ## ## ## ## ## ## ## ##
// ### ### ###### ## ## ## ## ######## ######
case 1: // wifi password string - CHARACTER SELECTION MODE
{
// only erase 'previous' cursor if needed, else you'll get erase/write
// on the same position resulting in flickering text.
if (enc_pos_prev != enc_pos)
{
// remove previous rectangle
int x = passwCursor[enc_pos_prev][0];
int y = passwCursor[enc_pos_prev][1];
tft.drawRect(x, y, 12, 17, TFT_BLACK);
}
// draw character edit rectangle
int x = passwCursor[enc_pos][0];
int y = passwCursor[enc_pos][1];
tft.drawRect(x, y, 12, 17, TFT_GREEN);
// check encoder rotation
byte i;
// get encoder direction value (no direction = '0', CW = '1', CCW = '2')
i = enc.rotate();
if (i == 1) //CW
{
// save previous cursor position in variable so we know at which position
// we need to erase the cursur when moving it
enc_pos_prev = enc_pos;
// increment cursor position counter
enc_pos++;
}
else if (i == 2) //CCW
{
// save previous cursor position in variable so we know at which position
// we need to erase the cursur when moving it
enc_pos_prev = enc_pos;
// decrement cursor position counter
enc_pos--;
}
// after rotation, check the new cursor position value
// prevent lower values that minimum
if (enc_pos < 0)
enc_pos = 0;
// handle maximum value overflow
if ((enc_pos == wifiPassLenMax) || (enc_pos > wifiPassLen))
{
// cursor position value greater than the wifiPassword string length.
// overflow to options menu
// but first remove selection cursor
int x = passwCursor[enc_pos_prev][0];
int y = passwCursor[enc_pos_prev][1];
tft.drawRect(x, y, 12, 17, TFT_BLACK);
// store the cursor position before it triggered this 'if' statement
enc_pos_curr = enc_pos_prev;
enc_pos = 0;
// go to options menu
wState_prev = 1;
wState = 3;
}
// check actions
if (enc_button == true)
{
// encoder button was pressed
// reset button state
enc_button = false;
// before jump to 'edit' state, set initial ASCII code to the current array value
// else an edit mistake would cuase us to search again for the right character.
// now if we selected for example 'N' instead of 'M', it's easy to correct
// by one turn of the rotary encoder.
//
// NOTE: if we want to add a new character at the END position of the
// wifi password string (which is not empty but has the 'null' character
// there) then we want to set the start value of the ASCII code range
// at the beginning of the ASCII table array.
//
// if we are adding a new character to the string:
if (enc_pos == wifiPassLen)
enc_val = 0;
// here we edit an existing string character:
else
{
// we want to find the ASCII value of the character currently
// selected from the wifi password string.
// bij searching the ASCII code array for the character value
// we can determine the corresponding position counter value:
for (int s = 0; s <= 93; s++)
{
if (wifiPass[enc_pos] == asciiVal[s][1])
{
enc_val = s;
}
}
}
// set edit state
wState_prev = 1;
wState = 2;
}
}
break;
//##########################################################################
//
// ## ## ###### ######## ### ######## ######## #######
// ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ##
// ## ## ## ###### ## ## ## ## ###### #######
// ## ## ## ## ## ######### ## ## ##
// ## ## ## ## ## ## ## ## ## ## ##
// ### ### ###### ## ## ## ## ######## #########
case 2: // wifi password string - EDIT MODE
{
// x and y values are set to the current cursor position
int x = passwCursor[enc_pos][0];
int y = passwCursor[enc_pos][1];
// make cursor RED
tft.drawRect(x, y, 12, 17, TFT_RED);
// if we want to ADD to the wifi password string then the edit
// cursor will be empty because we edit the existing 'null'
// character present at the end of the wifi password string.
// So if that is the case we need to display a starting ASCII
// character at the edit cursor position
if (wifiPass[enc_pos] == '\0')
{
// draw new ASCII character
tft.setCursor(x + 2, y, 2); // font 2: 15px
// display the ASCII code corresponding to the enc_val
tft.setTextColor(TFT_DARKGREEN);
tft.print((char)asciiVal[enc_val][1]);
}
// check encoder rotation
byte i;
i = enc.rotate();
if (i == 1) //CW
{
enc_val++;
//check for overflow of ASCII code range 33-126
if (enc_val > 93)
enc_val = 0;
// clear display area before displaying new character
tft.fillRect(x + 1, y + 1, 10, 15, TFT_BLACK);
// draw new ASCII character
tft.setCursor(x + 2, y, 2); // font 2: 15px
// display the ASCII code corresponding to the enc_val
tft.setTextColor(TFT_DARKGREEN);
tft.print((char)asciiVal[enc_val][1]);
}
else if (i == 2) //CCW
{
enc_val--;
// check for overflow of ASCII code range 33-126
if (enc_val < 0)
enc_val = 93;
// clear display area before displaying new character
tft.fillRect(x + 1, y + 1, 10, 15, TFT_BLACK);
// draw new ASCII character
tft.setCursor(x + 2, y, 2); // font 2: 15px
// display the ASCII code corresponding to the enc_val
tft.setTextColor(TFT_DARKGREEN);
tft.print((char)asciiVal[enc_val][1]);
}
// check actions
if (enc_button == true)
{
// reset enc button state
enc_button = false;
// write the current ASCII code to the wifi password string
// at the current cursor position
// * NOTE: enc_pos = character position of the wifi password string,
// * enc_val = the selected ASCII value to be written there.
wifiPass[enc_pos] = (char)asciiVal[enc_val][1];
// it is possible to add to an excisting wifi password string so check
// if a new character has been added to the end of the wifi password
if (enc_pos == wifiPassLen)
{
// the last 'null' character of the wifi password string has been
// overwritten so we need to properly end the wifi password string
// again with a new 'null' character.
wifiPass[enc_pos + 1] = '\0';
// update the lenght of the current wifi password string
wifiPassLen = (strlen(wifiPass));
}
// DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG
Serial.print("---");
Serial.print(wifiPass);
Serial.println("---");
// DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG--DEBUG
// move cursor 1 position forwar but only if the end of the wifi password
// string is not reached
if ((enc_pos + 1) != wifiPassLenMax)
{
// remove previous rectangle
int x = passwCursor[enc_pos][0];
int y = passwCursor[enc_pos][1];
tft.drawRect(x, y, 12, 17, TFT_BLACK);
enc_pos++;
enc_pos_prev++;
}
wState_prev = 2;
wState = 1;
}
}
break;
//##########################################################################
//
// ## ## ###### ######## ### ######## ######## #######
// ## ## ## ## ## ## ## ## ## ## ## ##
// ## ## ## ## ## ## ## ## ## ##
// ## ## ## ###### ## ## ## ## ###### #######
// ## ## ## ## ## ######### ## ## ##
// ## ## ## ## ## ## ## ## ## ## ## ##
// ### ### ###### ## ## ## ## ######## #######
case 3:
{ // wifi password string - OPTIONS MENU
// we have arrived here after the cursor position counter went over the
// maximum size of the wifi password string or after it went over the max
// lenght of the current password.
// Draw a cursor at the first menu option:
// only erase 'previous' cursor if needed, else you'll get erase/write
// on the same position resulting in flickering text.
if (enc_pos_prev != enc_pos)
{
// remove previous cursor
int x = pwOptCursor[enc_pos_prev][0];
int y = pwOptCursor[enc_pos_prev][1];
tft.fillCircle(x, y + 3, 2, TFT_BLACK);
// tft.drawRect(x, y, 7, 7, TFT_BLACK);
}
// draw edit cursor
int x = pwOptCursor[enc_pos][0];
int y = pwOptCursor[enc_pos][1];
// make the cursor red if on the 'CLEAR ALL' menu option because is
// is irreversable although not desasterous of course...
if (enc_pos == 1)
tft.fillCircle(x, y + 3, 2, TFT_RED);
else
tft.fillCircle(x, y + 3, 2, TFT_GREEN);
// check encoder rotation
byte i;
i = enc.rotate();
if (i == 1) //CW
{
enc_pos_prev = enc_pos;
enc_pos++;
}
else if (i == 2) //CCW
{
enc_pos_prev = enc_pos;
enc_pos--;
}
// deal with menu options overflow
if (enc_pos > 2)
enc_pos = 2;
// exit cursor selection mode
else if (enc_pos < 0)
{
// return to the wifi password string selection mode 'state 1'
// remove cursor
int x = pwOptCursor[enc_pos_prev][0];
int y = pwOptCursor[enc_pos_prev][1];
tft.fillCircle(x, y + 3, 2, TFT_BLACK);
// restore the cursor position before going back to the wifi password
// string selection mode and draw the green cursor.
if (wifiPassLen == wifiPassLenMax)
enc_pos = wifiPassLen - 1;
else
enc_pos = wifiPassLen;
// redraw the green selection cursor at the last position
x = passwCursor[enc_pos][0];
y = passwCursor[enc_pos][1];
tft.drawRect(x, y, 12, 17, TFT_DARKGREEN);
// jump back
wState_prev = 3;
wState = 1;
}
// check actions
if (enc_button == true)
{
// reset enc button state
enc_button = false;
//======================================================================
// menu option: DELETE LAST CHARACTER
if (enc_pos == 0)
{
// erase the last character of the wifi password string
wifiPass[wifiPassLen - 1] = '\0';
// recalculate the new string lenght
wifiPassLen = (strlen(wifiPass));
// redraw the display without returning to the cursor selection mode
// so we can delete multiple characters if needed
// determine the lenght of the current array
tft.fillRect(0, 15, 127, 70, TFT_BLACK);
for (int p = 0; p <= wifiPassLen; p++)
{
int x = passwCursor[p][0];
int y = passwCursor[p][1];
tft.setTextColor(TFT_DARKGREEN);
tft.setCursor(x + 2, y, 2); // font 2: 15px
tft.print(wifiPass[p]); // start at '0' with the wifiPassw array
}
}
//======================================================================
// menu option: CLEAR ALL
else if (enc_pos == 1)
{
wifiPass[0] = '\0';
// clear screen
tft.fillScreen(TFT_BLACK);
wState = 0;
}
//======================================================================
// menu option: EXIT
else if (enc_pos == 2)
{
state = FSM_IDLE_FAN_OFF;
}
}
}
break;
//##########################################################################
case 4:
break;
//##########################################################################
case 5:
break;
//##########################################################################
default:
break;
}
}
break;
default:
break;
}
}
// 888 888 8888888888 888
// 888 888 888 888
// 888 888 888 888
// .d8888b 88888b. .d88b. .d8888b 888 888 8888888 88888b. .d8888b .d88b. .d88888 .d88b. 888d888
// d88P" 888 "88b d8P Y8b d88P" 888 .88P 888 888 "88b d88P" d88""88b d88" 888 d8P Y8b 888P"
// 888 888 888 88888888 888 888888K 888 888 888 888 888 888 888 888 88888888 888
// Y88b. 888 888 Y8b. Y88b. 888 "88b 888 888 888 Y88b. Y88..88P Y88b 888 Y8b. 888
// "Y8888P 888 888 "Y8888 "Y8888P 888 888 8888888888 888 888 "Y8888P "Y88P" "Y88888 "Y8888 888
//
//
//
void checkEncoder()
{
}
// 8888888b. 888 888 888 d8b .d888 d8b .d8888b. 888
// 888 Y88b 888 888 o 888 Y8P d88P" Y8P d88P Y88b 888
// 888 888 888 888 d8b 888 888 Y88b. 888
// 888 d88P 888d888 888888 888 d888b 888 888 888888 888 "Y888b. 888888
// 8888888P" 888P" 888 888d88888b888 888 888 888 "Y88b. 888
// 888 888 888 88888P Y88888 888 888 888 "888 888
// 888 888 Y88b. 8888P Y8888 888 888 888 Y88b d88P Y88b.
// 888 888 "Y888 888P Y888 888 888 888 "Y8888P" "Y888
//
//
//
void printWifiStatus()
{
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
// 888 888 d8b .d888 d8b .d8888b. 888
// 888 o 888 Y8P d88P" Y8P d88P Y88b 888
// 888 d8b 888 888 888 888 888
// 888 d888b 888 888 888888 888 888 .d88b. 88888b. 88888b. .d88b. .d8888b 888888
// 888d88888b888 888 888 888 888 d88""88b 888 "88b 888 "88b d8P Y8b d88P" 888
// 88888P Y88888 888 888 888 888 888 888 888 888 888 888 888 88888888 888 888
// 8888P Y8888 888 888 888 Y88b d88P Y88..88P 888 888 888 888 Y8b. Y88b. Y88b.
// 888P Y888 888 888 888 "Y8888P" "Y88P" 888 888 888 888 "Y8888 "Y8888P "Y888
//
//
//
void wifiConnect()
{
// check for the WiFi module:
if (WiFi.status() == WL_NO_MODULE)
{
Serial.println("Communication with WiFi module failed!");
// don't continue
while (true)
;
}
String firmwareCurrent = WiFi.firmwareVersion();
String firmwareLatest = WIFI_FIRMWARE_LATEST_VERSION;
Serial.println("Current Wifi firmware: " + firmwareCurrent);
Serial.println("Latest Wifi firmware: " + firmwareLatest);
if (firmwareCurrent < firmwareLatest)
{
Serial.println("*** Please upgrade the firmware ***");
}
// attempt to connect to Wifi network:
while (status != WL_CONNECTED)
{
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);
}
}
// 888 888 d8b .d888 d8b .d8888b. 888 888
// 888 o 888 Y8P d88P" Y8P d88P Y88b 888 888
// 888 d8b 888 888 Y88b. 888 888
// 888 d888b 888 888 888888 888 "Y888b. 888888 8888b. 888888 888 888 .d8888b
// 888d88888b888 888 888 888 "Y88b. 888 "88b 888 888 888 88K
// 88888P Y88888 888 888 888 "888 888 .d888888 888 888 888 "Y8888b.
// 8888P Y8888 888 888 888 Y88b d88P Y88b. 888 888 Y88b. Y88b 888 X88
// 888P Y888 888 888 888 "Y8888P" "Y888 "Y888888 "Y888 "Y88888 88888P'
//
//
//
void wifiStatus()
{
Serial.println("Connected to wifi");
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
// print your board's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);
// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}
// 888 888 888 8888888888 888
// 888 888 888 888 888
// 888 888 888 888 888
// 88888b. 888 888 888888 888888 .d88b. 88888b. 8888888 888 888 .d88b. 88888b. 888888 .d8888b
// 888 "88b 888 888 888 888 d88""88b 888 "88b 888 888 888 d8P Y8b 888 "88b 888 88K
// 888 888 888 888 888 888 888 888 888 888 888 Y88 88P 88888888 888 888 888 "Y8888b.
// 888 d88P Y88b 888 Y88b. Y88b. Y88..88P 888 888 888 Y8bd8P Y8b. 888 888 Y88b. X88
// 88888P" "Y88888 "Y888 "Y888 "Y88P" 888 888 8888888888 Y88P "Y8888 888 888 "Y888 88888P'
//
//
//
// The event handler for the buttons.
void handleEvent(AceButton *button, uint8_t eventType, uint8_t /*buttonState*/)
{
uint8_t pin = button->getPin();
//====== MODE ==========================================================
if (pin == MODE_BUTTON_PIN)
{
switch (eventType)
{
// Interpret a Released event as a Pressed event, to distiguish it
// from a LongPressed event.
case AceButton::kEventReleased:
if (!isEditting)
{
enc_button = true;
}
break;
// LongPressed goes in and out of edit mode.
case AceButton::kEventLongPressed:
isEditting = !isEditting;
if (isEditting)
{
isBlinking = true;
}
Serial.println(F("Mode Button: Long Pressed"));
Serial.print(F("Editting: "));
Serial.print(isEditting ? F("true") : F("false"));
Serial.print(F("; blinking: "));
Serial.println(isBlinking ? F("true") : F("false"));
break;
}
}
//====== UP ==========================================================
else if (pin == UP_BUTTON_PIN)
{
switch (eventType)
{
case AceButton::kEventPressed:
case AceButton::kEventRepeatPressed:
isBlinking = false;
if (isEditting)
{
if (eventType == AceButton::kEventPressed)
{
Serial.println(F("Change Button: Pressed"));
}
else
{
Serial.println(F("Change Button: Repeat Pressed"));
}
counter++;
Serial.print(F("Counter: "));
Serial.print(counter);
Serial.print(F("; blinking: "));
Serial.println(isBlinking ? F("true") : F("false"));
}
break;
case AceButton::kEventReleased:
case AceButton::kEventLongReleased:
isBlinking = true;
if (isEditting)
{
Serial.println(F("Change Button: Released"));
Serial.print(F("Counter: "));
Serial.print(counter);
Serial.print(F("; blinking: "));
Serial.println(isBlinking ? F("true") : F("false"));
}
break;
}
}
}
/*
* syncNTPTime();
*
* Called as a callback on a timer every sync_time seconds.
* ========================================================================= */
time_t syncNTPTime()
{
unsigned long cur_time, update_time;
unsigned int drift_time;
cur_time = timeClient.getEpochTime();
timeClient.update();
update_time = timeClient.getEpochTime();
drift_time = (update_time - cur_time);
Serial.println("NTP Time Sync <=====================================");
Serial.print("NTP Epoch: ");
Serial.println(timeClient.getEpochTime());
Serial.print("NTP Time : ");
Serial.println(timeClient.getFormattedTime());
Serial.print("Epoch Pre Sync: ");
Serial.println(cur_time);
Serial.print("Epoch Post Sync: ");
Serial.println(update_time);
Serial.print("Drift Correct: ");
Serial.println(drift_time);
return timeClient.getEpochTime();
}