#include <LiquidCrystal_I2C.h>
#include <PubSubClient.h>
#include <Keypad.h>
#include <WiFi.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
#define ENCODER_CLK 18
#define ENCODER_DT 19
#define ENCODER_SW 5
const byte ROWS = 4;
const byte COLS = 4;
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char *mqtt_broker = "broker.hivemq.com";
const char *mqtt_username = "public";
const char *mqtt_password = "public";
const int mqtt_port = 1883;
const char* topicJumlah = "PKM/jumlah";
const char* topicJarak = "PKM/jarak";
const char* topicDiameter = "PKM/diameter";
char pesanJumlah[8];
char pesanJarak[8];
char pesanDiameter[8];
char hexaKeys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
uint8_t colPins[COLS] = {13, 16, 14, 27};
uint8_t rowPins[ROWS] = {26, 25, 33, 32};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
int nilai1 = 0;
int nilai2 = 0;
int nilai3 = 0;
int counter = 0;
int last = 0;
bool buttonPressed = false;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
char menu, customKey;
enum {menuUtama, menu1, menu2, menu3, menu4, simpan};
int jumlah;
int jarak;
int diameter;
byte gauge_empty[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111}; // empty middle piece
byte gauge_fill_1[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111}; // filled gauge - 1 column
byte gauge_fill_2[8] = {B11111, B11000, B11000, B11000, B11000, B11000, B11000, B11111}; // filled gauge - 2 columns
byte gauge_fill_3[8] = {B11111, B11100, B11100, B11100, B11100, B11100, B11100, B11111}; // filled gauge - 3 columns
byte gauge_fill_4[8] = {B11111, B11110, B11110, B11110, B11110, B11110, B11110, B11111}; // filled gauge - 4 columns
byte gauge_fill_5[8] = {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111}; // filled gauge - 5 columns
byte gauge_left[8] = {B11111, B10000, B10000, B10000, B10000, B10000, B10000, B11111}; // left part of gauge - empty
byte gauge_right[8] = {B11111, B00001, B00001, B00001, B00001, B00001, B00001, B11111}; // right part of gauge - empty
byte gauge_mask_left[8] = {B01111, B11111, B11111, B11111, B11111, B11111, B11111, B01111}; // mask for rounded corners for leftmost character
byte gauge_mask_right[8] = {B11110, B11111, B11111, B11111, B11111, B11111, B11111, B11110}; // mask for rounded corners for rightmost character
byte warning_icon[8] = {B00100, B00100, B01110, B01010, B11011, B11111, B11011, B11111}; // warning icon - just because we still have one custom character left
byte gauge_left_dynamic[8];
byte gauge_right_dynamic[8];
int cpu_gauge = 0;
char buffer[10];
int move_offset = 0;
const int gauge_size_chars = 16;
char gauge_string[gauge_size_chars+1];
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
Serial.begin(115200);
client.setServer(mqtt_broker, mqtt_port);
client.setCallback(callback);
lcd.init();
lcd.backlight();
pinMode(ENCODER_CLK, INPUT);
pinMode(ENCODER_DT, INPUT);
pinMode(ENCODER_SW, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCODER_CLK), readEncoder, FALLING);
lcd.createChar(7, gauge_empty);
lcd.createChar(1, gauge_fill_1);
lcd.createChar(2, gauge_fill_2);
lcd.createChar(3, gauge_fill_3);
lcd.createChar(4, gauge_fill_4);
lcd.createChar(0, warning_icon);
setupWifi();
}
void loop() {
if (!client.connected()) {
koneksiMQTT();
}
client.loop();
customKey = customKeypad.getKey();
resetMenu();
switch (menu) {
case menuUtama:
lcd.setCursor(1, 0);
lcd.print("Atur Variasi");
lcd.setCursor(1, 1);
lcd.print("Menu 2");
lcd.setCursor(1, 2);
lcd.print("Menu 3");
lcd.setCursor(1, 3);
lcd.print("Menu 4");
counter = getCounter(0, 3, counter);
if (last != counter) {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(0, counter);
lcd.print(">");
last = counter;
}
if (counter == 0) lcd.setCursor(0, 0);
else if (counter == 1) lcd.setCursor(0, 1);
else if (counter == 2) lcd.setCursor(0, 2);
else if (counter == 3) lcd.setCursor(0, 3);
lcd.print(">");
if (digitalRead(ENCODER_SW) == LOW) {
unsigned long currentTime = millis();
if ((currentTime - lastDebounceTime) > debounceDelay) {
if (!buttonPressed) {
buttonPressed = true;
switch (counter) {
case 0:
menu = menu1;
break;
case 1:
menu = menu2;
break;
case 2:
menu = menu3;
break;
case 3:
menu = menu4;
break;
}
}
}
lastDebounceTime = currentTime;
}
else {
buttonPressed = false;
}
delay(50);
break;
case menu1:
lcd.setCursor(1, 0);
lcd.print("Jumlah = ");
lcd.setCursor(1, 1);
lcd.print("Jarak = ");
lcd.setCursor(1, 2);
lcd.print("Diameter = ");
lcd.setCursor(10, 3);
lcd.print("Kirim");
counter = getCounter(0, 3, counter);
if (last != counter) {
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 2);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print(" ");
lcd.setCursor(9, 3);
lcd.print(" ");
last = counter;
}
if (counter == 0) {
lcd.setCursor(0, 0);
}
else if (counter == 1) {
lcd.setCursor(0, 1);
}
else if (counter == 2) {
lcd.setCursor(0, 2);
}
else if (counter == 3) {
lcd.setCursor(9, 3);
}
lcd.print(">");
if (customKey && counter == 0) {
isi1();
}
else if (customKey && counter == 1) {
isi2();
}
else if (customKey && counter == 2) {
isi3();
}
if (digitalRead(ENCODER_SW) == LOW) {
unsigned long currentTime = millis();
if ((currentTime - lastDebounceTime) > debounceDelay) {
if (!buttonPressed) {
buttonPressed = true;
switch (counter) {
case 3:
menu = simpan;
break;
}
}
}
lastDebounceTime = currentTime;
}
else {
buttonPressed = false;
}
delay(50);
break;
case simpan:
load_simpan();
break;
}
}
void readEncoder() {
int dtValue = digitalRead(ENCODER_DT);
if (dtValue == HIGH) {
counter++;
}
if (dtValue == LOW) {
counter--;
}
}
int getCounter(int kecil, int besar, int nilai) {
noInterrupts();
nilai = counter;
if (nilai > besar) {
nilai = kecil;
}
if (nilai < kecil) {
nilai = besar;
}
interrupts();
return nilai;
}
void resetMenu() {
static char last_menu = -1;
if (last_menu != menu) {
lcd.clear();
last_menu = menu;
}
}
void isi1(){
lcd.setCursor(12,0);
lcd.print(" ");
lcd.setCursor(12,0);
lcd.print(customKey);
nilai1 = nilai1 * 10 + (customKey - '0');
jumlah = nilai1;
}
void isi2(){
lcd.setCursor(12,1);
lcd.print(" ");
lcd.setCursor(12,1);
lcd.print(customKey);
nilai2 = nilai2 * 10 + (customKey - '0');
jarak = nilai2;
}
void isi3(){
lcd.setCursor(12,2);
lcd.print(" ");
lcd.setCursor(12,2);
lcd.print(customKey);
nilai3 = nilai3 * 10 + (customKey - '0');
diameter = nilai3;
}
void load_simpan(){
float units_per_pixel = (gauge_size_chars*5.0)/100.0;
int value_in_pixels = round(cpu_gauge * units_per_pixel);
int tip_position = 0;
if (value_in_pixels < 5) {tip_position = 1;}
else if (value_in_pixels > gauge_size_chars*5.0-5) {tip_position = 3;}
else {tip_position = 2;}
move_offset = 4 - ((value_in_pixels-1) % 5);
for (int i=0; i<8; i++) {
if (tip_position == 1) {gauge_left_dynamic[i] = (gauge_fill_5[i] << move_offset) | gauge_left[i];}
else {gauge_left_dynamic[i] = gauge_fill_5[i];}
gauge_left_dynamic[i] = gauge_left_dynamic[i] & gauge_mask_left[i];
}
for (int i=0; i<8; i++) {
if (tip_position == 3) {gauge_right_dynamic[i] = (gauge_fill_5[i] << move_offset) | gauge_right[i];}
else {gauge_right_dynamic[i] = gauge_right[i];}
gauge_right_dynamic[i] = gauge_right_dynamic[i] & gauge_mask_right[i];
}
lcd.createChar(5, gauge_left_dynamic);
lcd.createChar(6, gauge_right_dynamic);
for (int i=0; i<gauge_size_chars; i++) {
if (i==0) {gauge_string[i] = byte(5);}
else if (i==gauge_size_chars-1) {gauge_string[i] = byte(6);}
else {
if (value_in_pixels <= i*5) {gauge_string[i] = byte(7);}
else if (value_in_pixels > i*5 && value_in_pixels < (i+1)*5) {gauge_string[i] = byte(5-move_offset);} // tip
else {gauge_string[i] = byte(255);}
}
}
lcd.setCursor(0,1);
sprintf(buffer, "CPU:%3d%% ", cpu_gauge);
lcd.print(buffer);
lcd.write(byte(0));
lcd.setCursor(0,2);
lcd.print(gauge_string);
cpu_gauge = cpu_gauge +1;
cpu_gauge = constrain(cpu_gauge, 0, 100);
if(cpu_gauge == 100){
lcd.clear();
lcd.setCursor(0,1);
lcd.print("Data Terkirim");
sprintf(pesanJumlah, "%d", jumlah);
client.publish(topicJumlah, pesanJumlah);
sprintf(pesanJarak, "%d", jarak);
client.publish(topicJarak, pesanJarak);
sprintf(pesanDiameter, "%d", diameter);
client.publish(topicDiameter, pesanDiameter);
delay(500);
nilai1 = 0;
nilai2 = 0;
nilai3 = 0;
cpu_gauge = 0;
menu = menuUtama;
counter = 0;
}
delay(100);
}
void setupWifi() {
WiFi.begin(ssid, password);
/*lcd.setCursor(0, 1);
lcd.print("Connecting to ");
lcd.setCursor(-4, 2);
lcd.print(ssid);*/
while (WiFi.status() != WL_CONNECTED) {
delay(250);
//spinner();
Serial.println("Connecting to WiFi..");
}
Serial.println("Connected to the Wi-Fi network");
/*lcd.clear();
lcd.setCursor(0, 1);
lcd.print("WiFi");
lcd.setCursor(-4,2);
lcd.print("Connected");
delay(2000);
lcd.clear();*/
}
/*void spinner() {
static int8_t counter = 0;
const char* glyphs = "\xa1\xa5\xdb";
lcd.setCursor(11, 2); // Changed to row 1
lcd.print(glyphs[counter++]);
if (counter == strlen(glyphs)) {
counter = 0;
}
}*/
void koneksiMQTT() {
while (!client.connected()) {
String client_id = "esp32-client-";
client_id += String(WiFi.macAddress());
/*lcd.setCursor(0, 1);
lcd.print("Koneksi");
lcd.setCursor(-4, 2);
lcd.print("MQTT");
lcd.print("..........");
delay(2000);*/
Serial.printf("The client %s connects to the public MQTT broker\n", client_id.c_str());
if (client.connect(client_id.c_str(), mqtt_username, mqtt_password)) {
Serial.println("Mosquito MQTT broker connected");
/*lcd.clear();
lcd.setCursor(0, 1);
lcd.print("MQTT");
lcd.setCursor(-4, 2);
lcd.print("Terkoneksi");
delay(2000);
lcd.clear();*/
} else {
/*lcd.clear();
lcd.setCursor(0, 1);
lcd.print("Gagal");
lcd.setCursor(-4, 2);
lcd.print("Koneksi");*/
Serial.print("failed with state ");
Serial.print(client.state());
delay(2000);
}
}
}
void callback(char *topic, byte *payload, unsigned int length) {
Serial.print("Message:");
for (int i = 0; i < length; i++) {
Serial.print((char) payload[i]);
}
Serial.println();
}