#if defined(ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#endif
#include "ThingsBoard.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Keypad.h>
#define WIFI_AP_NAME "Wokwi-GUEST"
#define WIFI_PASSWORD ""
#define THINGSBOARD_SERVER "thingsboard.cloud"
#define THINGSBOARD_ACCESSTOKEN "eN8M33rIKJ2jA0iclLIR"
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
#define TINGGI_LAYAR 64 // Tinggi layar OLED yang digunakan
#define LEBAR_LAYAR 128 // Lebar layar OLED yang digunakan
#define ROWS 4
#define COLS 4
#define RELAY_PIN 18
#define BUZZER_PIN 19
#define SENSOR_PIN 13
#define SERIAL_DEBUG_BAUD 9600
WiFiClient espClient;
ThingsBoard tb(espClient);
int status = WL_IDLE_STATUS;
Adafruit_SSD1306 oled(LEBAR_LAYAR, TINGGI_LAYAR, &Wire, -1);
char Keys[ROWS][COLS] = { //Membuat array keypad
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = { 14, 12, 26, 27 };
byte colPins[COLS] = { 5, 4, 2, 15 };
char customKey; //Variabel penampung input keypad
String passwordInput = ""; //Variabel penampung nilai angka
String password = ""; //Password
String temporaryPassword = "";
int temporaryPasswordDuration = 0;
bool temporaryPasswordEnable = false;
long temporaryPasswordUntill;
Keypad customKeypad = Keypad( makeKeymap(Keys), rowPins, colPins, ROWS, COLS); //Masukkan info keypad pada library
long unlockUntill;
long autoLockDuration = 0;
bool unlockStatus = false;
bool alertStatus = false;
bool subscribed = false;
void processSharedAttributeUpdate(const Shared_Attribute_Data &data) {
for (JsonObject::iterator it = data.begin(); it != data.end(); ++it) {
String key = it->key().c_str();
if(key == "password"){
password = it->value().as<String>();
sendMessage("Password Updated");
}else if(key == "autoLockDuration"){
autoLockDuration = it->value().as<int>()*1000; //otomatis lock setelah sekian detik
sendMessage("Auto Lock Duration Updated");
}else if(key == "temporaryPassword"){
temporaryPassword = it->value().as<String>();
sendMessage("Temporary Password Updated");
}else if(key == "temporaryPasswordDuration"){
temporaryPasswordDuration = it->value().as<int>()*60000; //durasi valid temporary PIN setelah sekian menit
}
}
}
Shared_Attribute_Callback callbackSharedRequest[1] = {
processSharedAttributeUpdate
};
void printRow(String s){
oled.setTextSize(1);
oled.setTextColor(WHITE);
oled.setCursor(0,10);
oled.print(s);
oled.display();
Serial.println(s);
}
void sendData(bool unlockStatus, String message){
tb.sendTelemetryBool("unlockStatus", unlockStatus);
sendMessage(message);
}
void sendMessage(String message){
Serial.println(message);
tb.sendTelemetryString("message", message.c_str());
}
void unlockDoor(bool status, String message){
oled.clearDisplay();
digitalWrite(RELAY_PIN, status);
Serial.print("process unlockDoor: ");
Serial.println(status?"Unlock":"Lock");
unlockStatus = status;
sendData(status, message);
if(status){
unlockUntill = millis() + autoLockDuration;
}
}
RPC_Response remoteUnlock(const RPC_Data &data){
bool status = data.as<bool>();
String s = "Remote ";
s += status?"Unlock":"Lock";
unlockDoor(status, s);
return RPC_Response("unlockStatus", unlockStatus);
}
RPC_Response remoteReboot(const RPC_Data &data){
ESP.restart();
return RPC_Response("unlockStatus", unlockStatus);
}
RPC_Response setTemporaryPasswordStatus(const RPC_Data &data){
bool status = data.as<bool>();
tb.sendAttributeBool("temporaryPasswordEnable", status);
temporaryPasswordEnable = status;
String s = "Temporary Password ";
if(temporaryPasswordEnable){
temporaryPasswordUntill = millis() + temporaryPasswordDuration;
s += "Aktif";
}else{
s += "Tidak Aktif";
}
sendMessage(s);
return RPC_Response("temporaryPasswordEnable", temporaryPasswordEnable);
}
RPC_Response updatePasswordSetting(const RPC_Data &data){
return RPC_Response("unlockStatus", unlockStatus);
}
// RPC handlers
RPC_Callback callbackRPC[] = {
{ "remoteUnlock", remoteUnlock },
{ "remoteReboot", remoteReboot },
{ "setTemporaryPasswordStatus", setTemporaryPasswordStatus },
{ "updatePasswordSetting", updatePasswordSetting },
};
void setup() {
Serial.begin(SERIAL_DEBUG_BAUD);
WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
InitWiFi();
sendMessage("Setting Up after booting");
pinMode(SENSOR_PIN, INPUT);
if (!tb.connected()) {
subscribed = false;
// Connect to the ThingsBoard
Serial.print("Connecting to: ");
Serial.print(THINGSBOARD_SERVER);
Serial.print(" with token ");
Serial.println(THINGSBOARD_ACCESSTOKEN);
if (!tb.connect(THINGSBOARD_SERVER, THINGSBOARD_ACCESSTOKEN)) {
Serial.println("Failed to connect");
return;
}
}
// Subscribe for RPC, if needed
if (!subscribed) {
Serial.println("Subscribing for RPC... ");
// Perform a subscription. All consequent data processing will happen in
// callbacks as denoted by callbacks[] array.
if (!tb.RPC_Subscribe(callbackRPC, COUNT_OF(callbackRPC))) {
Serial.println("Failed to subscribe for RPC");
return;
}
if (!tb.Shared_Attributes_Subscribe(callbackSharedRequest, 1)) {
Serial.println("Failed to subscribe for shared attribute updates");
return;
}
Serial.println("Subscribe done");
subscribed = true;
}
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("failed to start SSD1306 OLED"));
while (1);
}
tb.Shared_Attributes_Request("password");
tb.Shared_Attributes_Request("autoLockDuration");
tb.Shared_Attributes_Request("temporaryPassword");
tb.Shared_Attributes_Request("temporaryPasswordDuration");
tb.sendAttributeBool("temporaryPasswordEnable", temporaryPasswordEnable);
pinMode(RELAY_PIN, OUTPUT);
oled.clearDisplay();
oled.setTextSize(1); // Atur ukuran text
oled.setTextColor(WHITE); // Atur warna text
oled.setCursor(20, 20); // Atur posisi text pada display
oled.println("SELAMAT DATANG"); // Text yang dicetak
oled.display();
delay(2000);
oled.clearDisplay();// menampilkan display OLED
}
void loop(){
if(!unlockStatus){
if(!alertStatus && digitalRead(SENSOR_PIN) == HIGH){
tb.sendTelemetryBool("unexpectedDoorOpened", HIGH);
sendMessage("UNEXPECTED DOOR OPENED");
alertStatus = true;
tone(BUZZER_PIN, 2000);
}else if(alertStatus && digitalRead(SENSOR_PIN) == LOW){
tb.sendTelemetryBool("unexpectedDoorOpened", LOW);
Serial.println("PINTU TERTUTUP");
alertStatus = false;
tone(BUZZER_PIN, 1200);
noTone(BUZZER_PIN);
}
}
if(unlockStatus && digitalRead(SENSOR_PIN) == LOW && millis()>unlockUntill){
unlockDoor(LOW, "Auto Lock");
oled.clearDisplay();
}
if(temporaryPasswordEnable && millis()>temporaryPasswordUntill){
sendMessage("Temporary Password Disabled");
temporaryPasswordEnable = false;
tb.sendAttributeBool("temporaryPasswordEnable", temporaryPasswordEnable);
}
// Reconnect to WiFi, if needed
if (WiFi.status() != WL_CONNECTED) {
reconnect();
return;
}
// Reconnect to ThingsBoard, if needed
if (!tb.connected()) {
subscribed = false;
// Connect to the ThingsBoard
Serial.print("Connecting to: ");
Serial.print(THINGSBOARD_SERVER);
Serial.print(" with token ");
Serial.println(THINGSBOARD_ACCESSTOKEN);
if (!tb.connect(THINGSBOARD_SERVER, THINGSBOARD_ACCESSTOKEN)) {
Serial.println("Failed to connect");
return;
}
}
// Subscribe for RPC, if needed
if (!subscribed) {
Serial.println("Subscribing for RPC... ");
// Perform a subscription. All consequent data processing will happen in
// callbacks as denoted by callbacks[] array.
if (!tb.RPC_Subscribe(callbackRPC, COUNT_OF(callbackRPC))) {
Serial.println("Failed to subscribe for RPC");
return;
}
if (!tb.Shared_Attributes_Subscribe(callbackSharedRequest, 1)) {
Serial.println("Failed to subscribe for shared attribute updates");
return;
}
Serial.println("Subscribe done");
subscribed = true;
}
oled.setTextSize(1);
oled.setTextColor(WHITE);
oled.setCursor(0,0);
if(unlockStatus){
oled.print("Door Unlock"); //Tampilan pada layar LCD
}else{
oled.print("Input Password"); //Tampilan pada layar LCD
}
oled.display();
customKey = customKeypad.getKey(); //Baca input keypad
switch(customKey){
case '0' ... '9':
if(!unlockStatus){
tone(BUZZER_PIN, 500, 50);
oled.setTextSize(1);
oled.setTextColor(WHITE);
oled.setCursor(0,10);
passwordInput += String(customKey);
oled.print(passwordInput);
oled.display();
}
break;
case '#':
if(!unlockStatus){
oled.clearDisplay();
if(passwordInput == password){ //Jika password benar, maka
unlockDoor(HIGH, "Unlock with PIN");
tone(BUZZER_PIN, 1200);
delay(250);
noTone(BUZZER_PIN);
delay(100);
tone(BUZZER_PIN, 1200);
delay(250);
}else if(temporaryPasswordEnable && passwordInput == temporaryPassword){ //Jika password benar, maka
unlockDoor(HIGH, "Unlock with Temporary PIN");
tone(BUZZER_PIN, 1200);
delay(250);
noTone(BUZZER_PIN);
delay(100);
tone(BUZZER_PIN, 1200);
delay(250);
}else{
tone(BUZZER_PIN, 2000);
printRow("Pin Invalid"); //Tampilan LCD
delay(1000);
}
noTone(BUZZER_PIN);
passwordInput = "";
oled.clearDisplay(); break;
case '*':
if(unlockStatus){
unlockDoor(false, "Door Locked by User");
}else{
passwordInput = "";
}
oled.clearDisplay();
}
break;
}
tb.loop();
}
void InitWiFi()
{
Serial.print("Connecting to AP ...");
WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Connected to AP");
}
void reconnect() {
status = WiFi.status();
if ( status != WL_CONNECTED) {
WiFi.begin(WIFI_AP_NAME, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("Connected to AP");
}
}