hello everyone, I have a problem with the program for my final project. in my program, when I run it, there is a problem with the use of the 4x4 keypad. when I try to press the button on the keypad it is very difficult, it is necessary to press the keypad many times so that the keypad output comes out. whether all my friends can help me to improve my program.
for my project program is below
#include <PubSubClient.h>
#include <Preferences.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Keypad.h>
#include <TinyGPSPlus.h>
// ================= OLED =================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ================ DS18B20 ===============
#define ONE_WIRE_BUS 4
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
// =============== Relay Lock =============
#define RELAY_PIN 2
// =============== Keypad 4x4 =============
const byte ROWS = 4, COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {14, 27, 26, 25};
byte colPins[COLS] = {19, 18, 17, 16};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// =============== WiFi & MQTT =============
const char* ssid = "Laswan";
const char* password = "30140728";
const char* mqtt_server = "broker.emqx.io";
const int mqtt_port = 1883;
WiFiClient wifiClient;
PubSubClient client(wifiClient);
const char* topicSuhu = "vaksin/suhu";
const char* topicStok = "vaksin/stok";
const char* topicDoor = "vaksin/doorlock";
const char* topicGPS = "vaksin/gps";
// ================= GPS ===================
TinyGPSPlus gps;
#define GPS_RX 32
#define GPS_TX 33
HardwareSerial gpsSerial(2);
// ============= Preferences ==============
Preferences prefs;
#define MAX_PIN_LENGTH 10
char pinCode[MAX_PIN_LENGTH] = "1234";
long vaksinMasuk = 0, vaksinKeluar = 0;
// ========== State & Variables ==========
enum State {
MAIN_MENU, PIN_INPUT, WAIT_CLOSE,
CHOICE_VAKSIN, SETTING_MENU, CHANGE_PIN,
INPUT_MASUK, INPUT_KELUAR
};
State currentState = MAIN_MENU;
unsigned long lastPublish = 0;
const long publishInterval = 5000;
String inputStr = "";
String latitudeStr = "0.000000";
String longitudeStr= "0.000000";
// ================ SETUP =================
void setup() {
Serial.begin(115200);
// I2C untuk OLED
Wire.begin();
// GPS serial
gpsSerial.begin(9600, SERIAL_8N1, GPS_RX, GPS_TX);
// Mode INPUT_PULLUP pada ROW pins untuk internal pull-up
for (byte i = 0; i < ROWS; i++) {
pinMode(rowPins[i], INPUT_PULLUP);
}
// kolom dibiarkan oleh library
// Atur debounce rendah dan polling cepat
keypad.setDebounceTime(1); // 1 ms debounce
keypad.setHoldTime(200); // 200 ms hold time
// Relay sebagai OUTPUT
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
// Inisialisasi WiFi & MQTT
setup_wifi();
client.setServer(mqtt_server, mqtt_port);
// Inisialisasi sensor suhu
sensors.begin();
// Inisialisasi OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("OLED gagal");
while (true) delay(10);
}
display.clearDisplay();
// Load PIN & stok dari NVS
loadPreferences();
// Tampilkan pesan inisialisasi
tampilPesan("Inisialisasi...\nTunggu...");
delay(1000);
}
// ================= LOOP ==================
void loop() {
// Update sensor & GPS
sensors.requestTemperatures();
float suhu = sensors.getTempCByIndex(0);
updateGPS();
// Baca satu key PRESSED secara event-based
char key = getKeyPressed();
// Publish MQTT periodik
if (millis() - lastPublish >= publishInterval) {
if (!client.connected()) reconnect();
client.loop();
publishData(suhu);
lastPublish = millis();
}
// State machine
switch (currentState) {
case MAIN_MENU:
tampilMainMenu(suhu);
if (key == 'A') { waitKeyRelease(); bukaKotak(); }
if (key == 'B') { waitKeyRelease(); menuSetting(); currentState = SETTING_MENU; }
break;
case PIN_INPUT:
if (key) {
if (key == '#') {
if (inputStr == String(pinCode)) {
tampilPesan("PIN Benar\nMembuka Kotak...");
delay(300);
digitalWrite(RELAY_PIN, HIGH);
publishDoorlock(true);
tampilPesan("Kotak Terbuka!\nTekan D utk Tutup");
currentState = WAIT_CLOSE;
} else {
tampilPesan("PIN Salah!\nKembali...");
delay(300);
currentState = MAIN_MENU;
}
inputStr = "";
waitKeyRelease();
} else {
inputStr += key;
tampilPesan("Masukkan PIN:\n" + inputStr);
}
}
break;
case WAIT_CLOSE:
if (key == 'D') {
waitKeyRelease();
digitalWrite(RELAY_PIN, LOW);
publishDoorlock(false);
tampilPesan("Kotak Terkunci!");
delay(300);
menuPilihanVaksin();
}
break;
case CHOICE_VAKSIN:
if (key == '1' || key == '2' || key == 'D') {
waitKeyRelease();
if (key == '1') tambahVaksin(true);
else if (key == '2') tambahVaksin(false);
else currentState = MAIN_MENU;
}
break;
case INPUT_MASUK:
case INPUT_KELUAR:
if (key) {
if (key == '#') {
if (currentState == INPUT_MASUK) vaksinMasuk += inputStr.toInt();
else vaksinKeluar += inputStr.toInt();
savePreferences();
inputStr = "";
tampilPesan("Data Tersimpan!\nKembali...");
delay(300);
currentState = MAIN_MENU;
}
else if (isDigit(key)) {
inputStr += key;
tampilPesan((currentState==INPUT_MASUK?"Vaksin Masuk:\n":"Vaksin Keluar:\n") + inputStr);
}
}
break;
case SETTING_MENU:
if (key == '1' || key == '2' || key == '3' || key == 'D') {
waitKeyRelease();
if (key == '1') gantiPIN();
else if (key == '2') tambahVaksin(true);
else if (key == '3') tambahVaksin(false);
else currentState = MAIN_MENU;
}
break;
case CHANGE_PIN:
if (key) {
if (key == '#') {
if (inputStr.length() >= 4) {
strncpy(pinCode, inputStr.c_str(), MAX_PIN_LENGTH-1);
pinCode[MAX_PIN_LENGTH-1] = '\0';
savePreferences();
tampilPesan("PIN Diubah!");
delay(300);
} else {
tampilPesan("Minimal 4 digit");
delay(300);
}
inputStr = "";
currentState = MAIN_MENU;
} else {
inputStr += key;
tampilPesan("PIN Baru:\n" + inputStr);
}
}
break;
}
}
// ============== FUNCTIONS ==============
// 1) WiFi & MQTT
void setup_wifi() {
Serial.print("Connect WiFi " ); Serial.print(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) { delay(300); Serial.print("."); }
Serial.println(" connected");
}
void reconnect() {
while (!client.connected()) {
Serial.print("MQTT connect...");
if (client.connect("ESP32Client")) Serial.println("ok");
else { Serial.print("fail, rc="); Serial.print(client.state()); Serial.println(" retry"); delay(2000);}
}
}
// 2) Publish data
void publishData(float suhu) {
client.publish(topicSuhu, String(suhu,2).c_str());
client.publish(topicStok, String(vaksinMasuk - vaksinKeluar).c_str());
client.publish(topicGPS, (latitudeStr + "," + longitudeStr).c_str());
}
void publishDoorlock(bool open) {
client.publish(topicDoor, open ? "TERBUKA" : "TERKUNCI");
}
// 3) Preferences (NVS)
void loadPreferences() {
prefs.begin("vaksin", true);
String p = prefs.getString("pin", "1234");
p.toCharArray(pinCode, MAX_PIN_LENGTH);
vaksinMasuk = prefs.getLong("in", 0);
vaksinKeluar= prefs.getLong("out", 0);
prefs.end();
}
void savePreferences() {
prefs.begin("vaksin", false);
prefs.putString("pin", String(pinCode));
prefs.putLong("in", vaksinMasuk);
prefs.putLong("out", vaksinKeluar);
prefs.end();
}
// 4) GPS update
void updateGPS() {
while (gpsSerial.available()) gps.encode(gpsSerial.read());
if (gps.location.isValid()) {
latitudeStr = String(gps.location.lat(), 6);
longitudeStr = String(gps.location.lng(), 6);
}
}
// 5) Keypad helper
char getKeyPressed() {
keypad.getKeys();
for (int i = 0; i < LIST_MAX; i++) {
if (keypad.key[i].stateChanged && keypad.key[i].kstate == PRESSED) {
return keypad.key[i].kchar;
}
}
return NO_KEY;
}
void waitKeyRelease() {
while (getKeyPressed() != NO_KEY) {
keypad.getKeys();
}
}
// 6) OLED display
void tampilPesan(const String& msg) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println(msg);
display.display();
}
void tampilMainMenu(float suhu) {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("====================");
display.println(" Cold Chain Storage");
display.println("--------------------");
display.print("Suhu : "); display.print(suhu,2); display.println(" C");
display.print("Stok : "); display.println(vaksinMasuk - vaksinKeluar);
display.print("Lat : "); display.println(latitudeStr);
display.print("Long : "); display.println(longitudeStr);
display.println("A:Open B:Setting");
display.display();
}
// 7) Menu actions
void bukaKotak() {
inputStr = "";
tampilPesan("Masukkan PIN:");
currentState = PIN_INPUT;
}
void menuSetting() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("1:Ganti PIN");
display.println("2:Vaksin Masuk");
display.println("3:Vaksin Keluar");
display.println("D:Kembali");
display.display();
}
void gantiPIN() {
inputStr = "";
tampilPesan("PIN Baru:\n#=Selesai");
currentState = CHANGE_PIN;
}
void tambahVaksin(bool masuk) {
inputStr = "";
tampilPesan(masuk ? "Vaksin Masuk:\n#=Selesai" : "Vaksin Keluar:\n#=Selesai");
currentState = masuk ? INPUT_MASUK : INPUT_KELUAR;
}
void menuPilihanVaksin() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("1:Vaksin Masuk");
display.println("2:Vaksin Keluar");
display.println("D:Kembali");
display.display();
currentState = CHOICE_VAKSIN;
}