Biometric Attendance System with Google Sheet Integration

Published  June 1, 2022   4
Biometric Attendance System using Google Sheets

In industrial and domestic applications, attendance registering is important at each and every moment. Many industries face a lot of problems due to the lack of a proper attendance monitoring system. To overcome this, we must need a tamper-proof, unattended, and fast attendance registering system, and the best way for it is to use a biometrics identification system.

Among the various biometric identification systems, Fingerprint identification is one of the most well-known and commonly used ones as it is fast, reliable, and tamper-proof. Because of their uniqueness & consistency over time, fingerprints have been used for identification for many years, more recently becoming automated due to advancements in computing capabilities. So, we are going to use the very same technique for registering and maintaining the attendance record.

Biometric Attendance System Project

Each employee’s fingerprint will be stored within the fingerprint module with a unique ID. An SQLite database is kept and maintained with all the employee details with corresponding unique fingerprint ID. Once an employee scans their fingerprint, the fingerprint is verified and the unique ID is cross-referenced with the employee database. Once the employee is identified the attendance will be marked in the local database and the same data will be synced to Google Sheets. The attendance can be monitored through the Google Sheet itself.

The employee enrolment and database management are done through a web portal that is password protected and customizable. The administrator can also change other basic device settings including device name, network credentials, network settings, time, and the Google Script credentials. The web portal can be either accessed by the IP address or the MDNS address. The time is kept even during a power failure with an RTC module, so the attendance data is accurate.

Components Required to Build Biometric Attendance System

The components required to build this project are very few in number. The components required are the following:

  • ESP32 Development Board
  • R307 Finger Print Module
  • DS1307 RTC module
  • 128x64 SSD1306 OLED module
  • Perfboard
  • Wires
  • Connectors
  • 5V power supply

Biometric Attendance System Circuit Diagram

Here is the circuit diagram for the Biometric attendance system. The connections are pretty simple. Connect the VCC and GND pins of FR307, OLED display, and RTC module to the 3.3V pin and GND pins respectively. Then connect the SDA and SCL pins of the RTC module and the OLED display to the GPIO21 and GPIO22 respectively. Now connect the TX pin from the FR307 to GPIO16 and the RX pin to the GPIO17.

Biometric Attendance System Circuit Diagram

Here is the fully completed circuit-

Biometric Attendance System Connections

Enclosure for the Biometric Attendance System

We have created a simple yet elegant enclosure for this project. You can download the required 3D files from the GitHub repo. The case is printed with PLA material. Here is the 3D-printed enclosure.

Biometric Attendance System Enclosure

Here is the inside view of the enclosure. You can use some hot glue to fix the screen to the enclosure.

Enclosure for Biometric Attendance System

Creating Google Sheets for Attendance Logging

Once all the connections are done, it’s time to create the Google Sheets for the attendance logging. For that, go to Google sheets and log in with your Google account. Then create a new sheet exactly as given below. Name the sheet as attendance, and name the columns as follows - Date, Time, Employee_ID, Empolyee_Name, Employee_Email_ID, Position, and Punch_In_or_Out.

Google Sheets for Attendance Logging

Now save the sheet and select Apps Script from the Extensions Menu and create a new script with the following code in it and deploy it. If you want to know how to create and deploy Google Scripts for ESP32 data logging, please read the ESP32 Data Logging to Google Sheets with Google Scripts article.

var sheet_id = "1tLe_DtcYxT_M7vxxxxxxxxxxxxxxxxxxxxxxy7uZs6aQ8";
var sheet_name = "attendance";
function doGet(e){
var ss = SpreadsheetApp.openById(sheet_id);
var sheet = ss.getSheetByName(sheet_name);
var Date = e.parameter.date;
var Time = e.parameter.time;
var Employee_ID = e.parameter.empid;
var Employee_Name = e.parameter.empname;
var Employee_Email_ID = e.parameter.empemail;
var Position = e.parameter.emppos;
var Punch_In_or_Out = e.parameter.emppio;
sheet.appendRow([Date,Time,Employee_ID,Employee_Name,Employee_Email_ID,Position,Punch_In_or_Out]);
}

Replace the Sheet_id with your own Sheet ID. Save it and deploy it. Note down the Google Script ID, which will be entered into the Device for data upload.

HTML and CSS files

The device uses HTML with CSS and JavaScript to host and control the web portal. All the required files are attached below this article. Please download them and keep them in a folder named data in the sketch folder. There are multiple HTML pages for multiple functions and all are important.

SQLLite Database template

For the database, we will be using the SQLLite library for ESP32. Download and install the library from the link given in the code section below. And we will need a blank database template for the ESP32 to work. This file, named test1.db is also included in the downloaded file and kept in the data folder along with the HTML and CSS files.

Uploading HTML and SQL Database Files to the ESP32

To upload these files to the filesystem, we will need a tool called ESP32 filesystem uploader. To install this tool to your Arduino IDE, please follow the following steps

  • Go to the GitHub releases page and download the ESP32FS-1.0.zip.
  • Extract the zip file to the Tools folder in the Arduino IDE directory. After extracting the path would look like this “C:\Users\Username\Documents\Arduino\tools\ESP32FS\tool\ esp32fs.jar
  • Restart the Arduino IDE and you can use this tool by clicking on the Tools Menu.

Uploading HTML and SQL Database Files to ESP32

Arduino Code for Biometric Attendance System

Along with the main .ino file, there are four more files that need to be in the sketch folder. Download them from the link below the article and place them in the Sketch folder. The icon.h file contains all the icons used for the OLED display and the robotbold10.h and seg.h files are the font files for the OLED. The credit.h file contains the default parameters, you may change them according to your need. Even if you didn’t change them, you can change them later through the web portal.

Most of the libraries are already installed with the ESP32 board package. Others are included in the sketch folder itself. Download all the files from the project GitHub page and extract them to a folder. You don’t need to install any other libraries. Make sure you don’t change any file paths or header calls.

Note: Complete code is given at the end of the page.

And make sure to select No OTA(2MB APP/2MB SPIFFS) from the Partition Scheme option.

Biometric Attendance System Code

Once compiled, program it to the ESP32.

Initial Setup

Once programmed, the device will boot up and show the boot image and then will show the Wi-Fi symbol on the OLED screen. For the first boot or if no Wi-Fi credentials are saved, the device will look for a default WiFi network with SSID circuitdigest and password circuitdigest. Create a WiFi hotspot with these credentials or change your WiFi credentials to these temporarily. Once the network is detected, the device will connect to it and will complete the bootup.

Once booted, open your web browser on a PC or laptop which is connected to the same network, and go to the URL http: //biometric.local/. The same can be used with any Apple or IOS device. Keep in mind, that this URL won’t work with Android devices since Android doesn’t support MDNS. For accessing from such devices or any other devices, you can use the IP address of the device. Once gone to the URL you will be greeted with the login page. Login with default username admin and password admin.

Biometric Database Server Login

Once logged in go to the setting page. On the setting page, give the following data and save.

  • SSID: Your WiFi SSID.
  • Password: Your WiFi SSID.
  • Device Name: Default is biometric. If you change this your local address will be also changed (eg http:/ /biometric.local/).
  • Google Script ID:  Your Google Script ID.
  • Login Username: Login username, the default is admin.
  • Login Password: Login password, the default is admin.
  • Organization Name: The name which will be displayed on the OLED screen.
  • Auto IP/Manual IP: Select if you want to use DHCP or manual IP.
  • Manual IP & Gateway:  add your IP and gateway if you want to use the manual IP.
  • Date and time: Set the correct date and time.

Biometric Database Server Setup

Once you have entered all the details click on save. The device will warn that the device will reboot. Click on ok and the device will reboot and connect to the configured network. You can change these settings whenever you want by logging into the portal and adding the values to the settings page. Keep in mind that if you leave any field blank while clicking save, it will be reset to default.

Enrolling or Adding New ID:

Enrolling new ID is really simple. Go to the Enroll page and enter all the details. Once all the details are entered, click on Enroll and click on Ok when the browser prompted. Then follow the on-screen instructions on the OLED. Once the enrolment is done you will be automatically redirected to the Database page. If the Enrollment is successful, the new employee details will be added to the database and will be displayed in the table.

Biometric Attendance System Database

Removing an Employee from the Database:

To remove a user or an employee, go to the Database page and click on the Delete button near the user details and confirm by clicking on Ok when prompted. The user details and the fingerprint data will be deleted from the Database.

Biometric Attendance System Database Server

Normal Operation:

After the initial setup, when powered on, the device will check for the configured network. If the configured network is available, the device will connect to it and complete the booting. If the network is not available, the device will check for a few seconds and then complete the boot. If the network is not connected, the device will check for the network periodically and will connect to it when it’s available.

When an employee scans his/her finger if the fingerprint is identified the attendance will be marked on the local database and will be also uploaded to the google sheet if the connection is active. If the person is scanning for the first time on that day the attendance will be marked as Punch in. And if it’s the second time for the day, the attendance will be marked as Punchout. If the user scans again, i.e. More than two times a day, the device will display an error message saying Already punched out, try again tomorrow. This feature will prevent the employees from tampering with the attendance data.

The device will store the attendance data for 7 days. If the network is not available during any period, the device will automatically sync the unsynced data when the connection is established next time.

Accessing the Attendance Data:

To access the attendance data, simply open the google sheet you have created. All the synced data will be available on the Google Sheet.

Biometric Attendance Data

Now let’s see the main components and their specifications.

ESP32 Development Board

ESP32 Development Board Pinout

The development board consists of an ESP-WROOM-32 module. It’s a 32bit dual-core microcontroller with built-in WiFi and Bluetooth. ESP32 can work with a clock frequency up to 240MHz and has a RAM of 521kB. The ESP-WROOM-32 module is also equipped with an SPI flash storage of 4MB, which can be used for the application area as well as the file system. Here are the specifications of the ESP-WROOM-32 Development Kit.

Processors:

  • CPU: Xtensa dual-core (or single-core) 32-bit LX6 microprocessor, operating at 160 or 240 MHz and performing at up to 600 DMIPS
  • Ultra low power (ULP) co-processor

Memory:

  • 320 KiB RAM, 448 KiB ROM

Wireless connectivity:

  • Wi-Fi: 802.11 b/g/n
  • Bluetooth: v4.2 BR/EDR and BLE (shares the radio with Wi-Fi)

Peripheral interfaces:

  • 34 × programmable GPIOs
  • 12-bit SAR ADC up to 18 channels
  • 2 × 8-bit DACs
  • 10 × touch sensors (capacitive sensing GPIOs)
  • 4 × SPI
  • 2 × I²S interfaces
  • 2 × I²C interfaces
  • 3 × UART
  • SD/SDIO/CE-ATA/MMC/eMMC host controller
  • SDIO/SPI slave controller
  • Ethernet MAC interface with dedicated DMA and planned IEEE 1588 Precision Time Protocol support[4]
  • CAN bus 2.0
  • Infrared remote controller (TX/RX, up to 8 channels)
  • Motor PWM
  • LED PWM (up to 16 channels)
  • Hall effect sensor
  • Ultra-low power analog pre-amplifier

Security:

  • IEEE 802.11 standard security features all supported, including WPA, WPA2, WPA3 (depending on version)[5] and WLAN Authentication and Privacy Infrastructure (WAPI)
  • Secure boot
  • Flash encryption
  • 1024-bit OTP, up to 768-bit for customers
  • Cryptographic hardware acceleration: AES, SHA-2, RSA, elliptic curve cryptography (ECC), random number generator (RNG)

Power management:

  • Internal low-dropout regulator
  • Individual power domain for RTC
  • 5 μA deep sleep current
  • Wake up from GPIO interrupt, timer, ADC measurements, capacitive touch sensor interrupt

Finger Print Sensor Module

As already mentioned, fingerprints are one of the unique ways to identify an individual. There are multiple types of fingerprint sensors available in the market. The main categorization is based on how they acquire the fingerprint data. Optical fingerprint Sensors are capacitive fingerprint sensors.

Capacitive Fingerprint Sensor:

Capacitive Fingerprint Sensor

Capacitive sensors use arrays of tiny capacitor circuits to collect data. As capacitors store electrical charge, connecting them up to conductive plates on the surface of the scanner allows them to be used to track the details of a fingerprint. The stored charge will be changed slightly when a finger’s ridge is placed over the conductive plates. Conversely, an air gap will leave the charge at the capacitor relatively unchanged. An op-amp integrator circuit is used to track these changes, which can then be recorded by an analog-to-digital converter. Once captured, this digital data is analyzed to look for distinctive and unique fingerprint attributes. 

Optical Fingerprint Sensors:

Optical Fingerprint Sensors

The Optical Fingerprint sensors are on the Total Internal Reflection or TRI principle. The Optical Fingerprint sensors consist of a light source, prism, lens, and image sensor. The light from a source (typically an LED) enters the prism from one face at a certain angle, such that it is reflected from the adjacent face and exists through the third face. This light then passes through a lens and then reaches the image sensor. When there is no finger placed on the sensor, the light will completely reflect off from the surface and this produces plain images on the image sensor. When there is a finger placed on the sensor, some of the light is reflected and this creates the image of the fingerprint pattern. The light leaked along the surface of the prism is called Evanescent Waves.

Different materials have different reflective indices and interact differently with the evanescent waves. When the fingertip is placed over the scanner, the ridges are in tight contact with the scanner’s surface while the valleys are filled with air pockets. The skin and air have different reflective indices causing different evanescent waves, called Frustrated Total Internal Reflection (FTIR). As a result of different evanescent waves from ridges and valleys, the intensity of the total internally reflected light changes according to the pattern of the ridges and valleys. The image sensor captures a high contrast image recording the changed light intensity pattern, capturing the pattern of ridges and valleys as a high-contrast digital image. This image is then stored in the flash memory with a unique id as a template. This template is used to identify the user later.

R307 Finger Print Sensor Module:

In this project, we are going to use such a sensor, the popular R307 Optical Fingerprint Sensor. They are manufactured by Hangzhou Grow Technology Co., Ltd. The R307 module operates at a voltage of 4.2V~6V and 50 mA with a storage capacity of 1000 impressions. R307 has both UART and USB 2.0 interfaces to communicate with a computer system at a baud rate in multiples of 9600 bps.

R307 Optical Fingerprint Sensor

It is capable of both 1:1 and 1:N matching with FAR (False Acceptance Rate) less than 0.001 %. The module can scan a live finger in less than 0.5 seconds and supports five security levels (1~5; 5 is the highest). The operating temperature range of this sensor is -10˚C to 40˚C, making it deployable in most of the locations.

fingerprint scanner internal parts

R307 has a glass top where a fingertip can be placed for scanning. Below the glass top is placed a prism. The inside of the sensor is divided into two parts using a light barrier. On one side of the light barrier is a PCB consisting of four blue LED lights. On the other side of the light barrier is an image sensor connected to a processor.

R307 Module Pinout

The outer PCB has the processor, connector, and other circuit elements. The prism, along with blue LEDs and an image sensor, is arranged such that light transmitted by blue LEDs is internally reflected through the prism to the image sensor. The R307 uses the AS606 DSP from Synochip for signal processing and the TTP223 touch sensor from Tontek for proximity detection.

Fingerprint Sensor Module

The R307 module can be operated at both 5V and 3.3V. If the sensor is used with a 3.3V microcontroller, the 3.3V jumper on the PCB must be shorted and for 5V usage keep the jumper open. When connected via a USB port, the device will create a virtual com port and will communicate through it. The first four pins on the connector are for power and UART communication. The last two pins are for touch detection. If pin number 6 is connected to the 3.3V supply, pin 5 will go high when a finger is placed over the sensor. This is useful for manual scanning

Pin Number

Name

Description

1

VCC

Positive Supply (VCC)

2

GND

Supply Ground (GND)

3

TXD

UART TX

4

RXD

UART RX

5

Touch

Finger detection signal

6

3.3V

Finger detection power

Here is the pinout table for the R307 Sensor.

DS1307 RTC Module:

DS1307 RTC Module

For timekeeping, we will use the rather popular DS1307 RTC module. It contains a DS1307 real-time clock IC with a backup battery. It’s one of the easiest to use RTCs out there, with simply using I2C commands to set and retrieve the time and date. The clock/calendar provides seconds, minutes, hours, days, date, month, and year information. The end of the month date is automatically adjusted for months with fewer than 31 days, including corrections for leap year. The DS1307 has a built-in power sense circuit that detects power failures and automatically switches to the backup supply. Timekeeping operation continues while the part operates from the backup supply. The module includes a CR2032 coin cell battery which should be good for a few years at least. Along with the DS1307 real-time clock chip, the module also has an Atmel 24C32 EEPROM chip which is handy for storing data without worrying about power loss. There is also space on the board to solder our own DS18B20 temperature sensor.

Supporting Files

Code
#include <stdio.h>
#include <stdlib.h>
#include <SPI.h>
#include <FS.h>
#include "SPIFFS.h"
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Wire.h>
#include <HTTPClient.h>
#include "src/Adafruit_SSD1306/Adafruit_GFX.h"
#include "src/Adafruit_SSD1306/Adafruit_SSD1306.h"
#include "src/Adafruit_SSD1306/RTClib.h"
#include "src/Adafruit_Fingerprint_Sensor_Library/Adafruit_Fingerprint.h"
#include "src/esp32_arduino_sqlite3_lib/sqlite3.h"               //https://github.com/siara-cc/esp32_arduino_sqlite3_lib
#include "config/credits.h"
#include "config/robotbold10.h"
#include "config/seg.h"
#include "config/icons.h"
RTC_DS1307 rtc;
char nameoftheday[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
char month_name[12][12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int day_, month_, year_, hour24_, hour12_, minute_, second_, dtw_;
uint8_t id;
WebServer server(80);
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define Buzzer 25 //Buzzer 
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
HardwareSerial mySerial(2); //ESP32 Hardware Serial 2
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
String mdnsdotlocalurl = "";

//Variables to save values from HTML form
String ssid_;
String pass_;
String ip_;
String gateway_;
String dispname_;
String wwwid_;
String wwwpass_;
String gsid_;
String mdns_;
String dhcpcheck;
String Empid, Sqid, Empname, EmpEmail, EmpPos, Empfid, sts7, sts8, sts9, sts10;
int SQLID = 0;
bool booting = false;

// File paths to save input values permanently
const char* ssidPath = "/ssid.txt";
const char* passPath = "/pass.txt";
const char* ipPath = "/ip.txt";
const char* gatewayPath = "/gateway.txt";
const char* dispnamePath = "/dispname.txt";
const char* wwwidPath = "/wwwid.txt";
const char* wwwpassPath = "/wwwpass.txt";
const char* gsidPath = "/gsid.txt";
const char* mdnsPath = "/mdns.txt";
const char* dhcpcheckPath = "/dhcpcheck.txt";
bool apmode = false;
IPAddress localIP(0, 0, 0, 0);
IPAddress gatewayIP(0, 0, 0, 0);
IPAddress subnetMask(255, 255, 255, 0);
uint8_t max_connections = 8; //Maximum Connection Limit for AP
sqlite3 *test1_db;
const char* data = "Callback function called";
char *zErrMsg = 0;
int openDb(const char *, sqlite3 **);
int db_exec(sqlite3 *, const char *);
int sqlreturn = 0;
static int callback(void *data, int argc, char **argv, char **azColName);
unsigned long lastwificheck = 0;

//Class declaration
void oledDisplayCenter();
String readFile();
void writeFile();
static int callback1();
int db_exec1();
int openDb();
static int callback();
int db_exec();
bool loadFromSPIFFS();
bool is_authentified();
void handleLogin();
void logout();
void handleRoot();
void Settings();
void handleNotFound();
void insertRecord();
void save();
void deleteRecord();
void showRecords();
void newRecordTable();
void getssid();
void getmdns();
void getip();
void getfpid();
int FingerprintID();
uint8_t deleteFingerprint();
uint8_t getFingerprintEnroll();
void connectwifi();
void offlinedataupload();


String web_content = "";


void setup() {
  booting = true;
  pinMode (Buzzer, OUTPUT);
  Serial.begin(115200);
  mySerial.begin(57600, SERIAL_8N1, 16, 17);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.drawBitmap(0, 20, logo_bmp, LOGO_WIDTH, LOGO_HEIGHT, 1);
  display.display();
  delay(1000);
  display.clearDisplay();
  if (finger.verifyPassword())
  {
    Serial.println("Fingerprint Sensor Connected");

  }

  else
  {
    display.clearDisplay();
    display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(25, 0);            // Start at top-left corner
    display.println(("Sensor"));
    display.setCursor(25, 35);
    display.println("Error");
    display.display();

    Serial.println("Unable to find Sensor");
    delay(3000);
    Serial.println("Check Connections");

    while (1) {
      delay(1);
    }
  }
  display.clearDisplay();
  rtc.begin();
  SPIFFS.begin(); // For SPIFFS

  // Load values saved in SPIFFS
  ssid_ = readFile(SPIFFS, ssidPath);
  Serial.println(ssid_);
  pass_ = readFile(SPIFFS, passPath);
  Serial.println(pass_);
  ip_ = readFile(SPIFFS, ipPath);
  Serial.println(ip_);
  gateway_ = readFile(SPIFFS, gatewayPath);
  Serial.println(gateway_);
  dispname_ = readFile(SPIFFS, dispnamePath);
  if (dispname_ == "")
  {
    dispname_ = DEFAULT_displayname;
  }
  Serial.println(dispname_);
  wwwid_ = readFile(SPIFFS, wwwidPath);
  if (wwwid_ == "")
  {
    wwwid_ = DEFAULT_wwwusername;
  }
  Serial.println(wwwid_);
  wwwpass_ = readFile(SPIFFS, wwwpassPath);
  if (wwwpass_ == "")
  {
    wwwpass_ = DEFAULT_wwwpassword;
  }
  Serial.println(wwwpass_);
  gsid_ = readFile(SPIFFS, gsidPath);
  Serial.println(gsid_);
  mdnsdotlocalurl = readFile(SPIFFS, mdnsPath);
  Serial.println(mdnsdotlocalurl);
  dhcpcheck = readFile(SPIFFS, dhcpcheckPath);
  Serial.println(dhcpcheck);

  display.drawBitmap(32, 0, logo_wifi, 64, 61, 1);
  display.display();
  display.clearDisplay();

  connectwifi();
  if (mdnsdotlocalurl == "")
  {
    mdnsdotlocalurl = DEFAULT_mdns;
  }

  if (!MDNS.begin(mdnsdotlocalurl.c_str())) {
    Serial.println("Error setting up MDNS responder!");
  }

  MDNS.addService("http", "tcp", 80);

  Serial.print("http://");
  Serial.print(mdnsdotlocalurl);
  Serial.println(".local");





  server.on("/", handleRoot);
  server.onNotFound(handleNotFound);
  server.on("/insert", insertRecord);
  server.on("/delete", deleteRecord);
  server.on("/show", showRecords);
  server.on("/newRecordTable", newRecordTable);
  server.on("/Settings", Settings);
  server.on("/save", save);
  server.on("/getssid", getssid);
  server.on("/getfpid", getfpid);
  server.on("/getmdns", getmdns);
  server.on("/getip", getip);
  server.on("/login", handleLogin);
  server.on("/signout", logout);



  //here the list of headers to be recorded
  const char * headerkeys[] = {"User-Agent", "Cookie"} ;
  size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
  //ask server to track these headers
  server.collectHeaders(headerkeys, headerkeyssize );
  server.begin();


  int rc;
  sqlite3_initialize();

  if (openDb("/spiffs/test1.db", &test1_db))
    return;
  booting = false;

}



void loop() {

  server.handleClient();
  FingerprintID();
  if (WiFi.status() != WL_CONNECTED && millis() - lastwificheck > 1800000)
  {
    connectwifi();

  }
}

void oledDisplayCenter(String text, int x, int y) {
  int16_t x1;
  int16_t y1;
  uint16_t width;
  uint16_t height;

  display.getTextBounds(text, x, y, &x1, &y1, &width, &height);
  display.setCursor((SCREEN_WIDTH - width) / 2, y);
  display.print(text); // text to display
}

// Read File from SPIFFS
String readFile(fs::FS &fs, const char * path) {
  Serial.printf("Reading file: %s\r\n", path);

  File file = fs.open(path);
  if (!file || file.isDirectory()) {
    Serial.println("- failed to open file for reading");
    return String();
  }

  String fileContent;
  while (file.available()) {
    fileContent = file.readStringUntil('\n');
    break;
  }
  return fileContent;
}

// Write file to SPIFFS
void writeFile(fs::FS &fs, const char * path, const char * message) {
  Serial.printf("Writing file: %s\r\n", path);

  File file = fs.open(path, FILE_WRITE);
  if (!file) {
    Serial.println("- failed to open file for writing");
    return;
  }
  if (file.print(message)) {
    Serial.println("- file written");
  } else {
    Serial.println("- frite failed");
  }
}


const char* data1 = "Callback function called";
static int callback1(void *data1, int argc, char **argv, char **azColName) {
  int i;
  String t = "";
  String m = "";
  Serial.printf("%s: ", (const char*)data1);
  for (i = 0; i < argc; i++) {
    Serial.printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    //printf(buffer, "%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    if (i == 0)
    {
      Sqid = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 1)
    {
      Empid = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 2)
    {
      Empname = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 3)
    {
      EmpEmail = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 4)
    {
      EmpPos = argv[5] ? argv[i] : "NULL";
    }
    else if (i == 5)
    {
      Empfid = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 6)
    {
      sts7 = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 7)
    {
      sts8 = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 8)
    {
      sts9 = argv[i] ? argv[i] : "NULL";
    }
    else if (i == 9)
    {
      sts10 = argv[i] ? argv[i] : "NULL";
    }
    m = argv[i] ? argv[i] : "NULL";
    //t += String(azColName[i] + m);
    Serial.println(m);
  }
  sqlreturn = m.toInt();
  Serial.printf("\n");
  return 0;
}

char *zErrMsg1 = 0;
int db_exec1(sqlite3 *db, const char *sql) {
  Serial.println(sql);
  long start = micros();
  int rc = sqlite3_exec(db, sql, callback1, (void*)data1, &zErrMsg1);
  if (rc != SQLITE_OK) {
    Serial.printf("SQL error: %s\n", zErrMsg1);
    sqlite3_free(zErrMsg1);
  } else {
    Serial.printf("Operation done successfully\n");
  }
  Serial.print(F("Time taken:"));
  Serial.println(micros() - start);
  return rc;
}




/*--------------------------------------------------------*/
int openDb(const char *filename, sqlite3 **db) {
  int rc = sqlite3_open(filename, db);
  if (rc) {
    Serial.printf("Can't open database: %s\n", sqlite3_errmsg(*db));
    return rc;
  } else {
    Serial.printf("Opened database successfully\n");
  }
  return rc;
}
/*--------------------------------------------------------*/

int db_exec(sqlite3 *db, const char *sql) {
  Serial.println('\n');
  int rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
  if (rc != SQLITE_OK) {
    Serial.printf("SQL error: %s\n", zErrMsg);
    sqlite3_free(zErrMsg);
  }
  return rc;
}


static int callback(void *data, int argc, char **argv, char **azColName) {
  web_content += "<tr>";
  for (int i = 0; i < argc; i++) {
    web_content += "<td>";
    web_content += argv[i];
    web_content += "</td>";
  }
  web_content += "<td><button onClick =\"del('";
  web_content += argv[0];
  web_content += "')\">Delete</button></td></tr>";
  return 0;
}
bool loadFromSPIFFS(String path) {
  String dataType = "text/html";

  Serial.print("Requested page -> ");
  Serial.println(path);
  if (SPIFFS.exists(path)) {
    File dataFile = SPIFFS.open(path, "r");
    if (!dataFile) {
      handleNotFound();
      return false;
    }

    if (server.streamFile(dataFile, dataType) != dataFile.size()) {
      Serial.println("Sent less data than expected!");
    } else {
      Serial.println("Page served!");
    }

    dataFile.close();
  } else {
    handleNotFound();
    return false;
  }
  return true;
}
//Check if header is present and correct
bool is_authentified() {
  Serial.println("Enter is_authentified");
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
    if (cookie.indexOf("ESPSESSIONID=1") != -1) {
      Serial.println("Authentification Successful");
      return true;
    }
  }
  Serial.println("Authentification Failed");
  return false;
}



//login page, also called for disconnect
void handleLogin() {
  String msg;
  if (server.hasHeader("Cookie")) {
    Serial.print("Found cookie: ");
    String cookie = server.header("Cookie");
    Serial.println(cookie);
  }
  if (server.hasArg("DISCONNECT")) {
    Serial.println("Disconnection");
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
    server.send(301);
    return;
  }
  if (server.hasArg("USERNAME") && server.hasArg("PASSWORD")) {
    Serial.println(server.hasArg("USERNAME"));
    Serial.println(server.hasArg("PASSWORD"));
    if (server.arg("USERNAME") == wwwid_ &&  server.arg("PASSWORD") == wwwpass_) {
      server.sendHeader("Location", "/");
      server.sendHeader("Cache-Control", "no-cache");
      server.sendHeader("Set-Cookie", "ESPSESSIONID=1");
      server.send(301);
      Serial.println("Log in Successful");
      return;
    }
    msg = "Wrong username/password! try again.";
    Serial.println("Log in Failed");
  }
  loadFromSPIFFS("/Login.html");
}
/*--------------------------------------------------------*/
void logout() {

  server.sendHeader("Cache-Control", "no-cache");
  server.sendHeader("Set-Cookie", "ESPSESSIONID=0");
  server.sendHeader("Location", "/login");
  server.sendHeader("Cache-Control", "no-cache");
  server.send(301);
  Serial.println("Signout ");
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void handleRoot() {
  if (!is_authentified()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  loadFromSPIFFS("/db.html");
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void Settings() {
  if (!is_authentified()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  //server.send(200, "text/html", web_page);
  loadFromSPIFFS("/Settings.html");

}
/*--------------------------------------------------------*/


void handleNotFound() {

  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for ( uint8_t i = 0; i < server.args(); i++ ) {
    message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
  }

  server.send ( 404, "text/plain", message );

}

/*--------------------------------------------------------*/
void insertRecord() {
  web_content = "";
  String sql = "";
  String eemployee_id = server.arg("memployee_id");
  String ename = server.arg("mname");
  String eemail_id = server.arg("memail_id");
  String epos = server.arg("mpos");
  String efpid = server.arg("mfpid");

  id = efpid.toInt();

  if (getFingerprintEnroll() == true)
  {
    sql = "insert into attendance(id,eid,employee_name,employee_email,position,fpid) values(" + efpid + ",'" + eemployee_id + "','" + ename + "','" + eemail_id + "','" + epos + "'," + efpid + ")";
    Serial.println(sql);
    if (db_exec(test1_db, sql.c_str()) == SQLITE_OK) {
      web_content += "OK";
      Serial.println(web_content);
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
      //display.setCursor(0, 60);            // Start at top-left corner
      //display.println(("Enroll Sucess"));
      oledDisplayCenter("Enroll Sucess!!", 0, 60);
      display.display();
      delay(2000);
    }
    else {
      web_content += "FAIL";
    }
  }
  else {
    web_content += "FAIL";
  }

  server.send (200, "text/html", web_content );
}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void save() {
  web_content = "";
  String _ssid = server.arg("ssid");
  String _password = server.arg("password");
  String _mdns = server.arg("mdns");
  String _gsid = server.arg("gsid");
  String _aip = server.arg("aip");
  String _mip = server.arg("mip");
  String _gateway = server.arg("gateway");
  String _dispname = server.arg("dispname");
  String _wwwid = server.arg("wwwid");
  String _wwwpass = server.arg("wwwpass");
  Serial.println(_ssid);
  Serial.println(_password);
  Serial.println(_mdns);
  Serial.println(_gsid);
  Serial.println(_aip);
  Serial.println(_mip);
  Serial.println(_gateway);
  Serial.println(_dispname);
  Serial.println(_wwwid);
  Serial.println(_wwwpass);
  if (_ssid != "")
  {
    writeFile(SPIFFS, ssidPath, _ssid.c_str());
  }
  else
  {
    Serial.println("Balnk SSID");
  }
  if (_password != "")
  {
    writeFile(SPIFFS, passPath, _password.c_str());
  }
  else
  {
    Serial.println("Balnk WiFi Password");
  }
  if (_mdns != "")
  {
    writeFile(SPIFFS, mdnsPath, _mdns.c_str());
  }
  else
  {
    Serial.println("Balnk mdns");
  }
  if (_gsid != "")
  {
    writeFile(SPIFFS, gsidPath, _gsid.c_str());
  }
  else
  {
    Serial.println("Balnk GSID");
  }
  if (_aip != "")
  {
    writeFile(SPIFFS, dhcpcheckPath, _aip.c_str());
  }
  else
  {
    Serial.println("Balnk IP config");
  }
  if (_mip != "")
  {
    writeFile(SPIFFS, ipPath, _mip.c_str());
  }
  else
  {
    Serial.println("Balnk Manual IP");
  }
  if (_gateway != "")
  {
    writeFile(SPIFFS, gatewayPath, _gateway.c_str());
  }
  else
  {
    Serial.println("Balnk Gateway");
  }
  if (_dispname != "")
  {
    writeFile(SPIFFS, dispnamePath, _dispname.c_str());
  }
  else
  {
    Serial.println("Balnk Display Name");
  }
  if (_wwwid != "")
  {
    writeFile(SPIFFS, wwwidPath, _wwwid.c_str());
  }
  else
  {
    Serial.println("Balnk Web User ID");
  }
  if (_wwwpass != "")
  {
    writeFile(SPIFFS, wwwpassPath, _wwwpass.c_str());
  }
  else
  {
    Serial.println("Balnk Web password");
  }

  String dtd = server.arg("dtd");
  String dtm = server.arg("dtm");
  String dty = server.arg("dty");
  String tmh = server.arg("tmh");
  String tmm = server.arg("tmm");
  String tms = server.arg("tms");
  String tmapm = server.arg("tmapm");
  Serial.println("Date received");

  Serial.println(dty);
  Serial.println(dtm);
  Serial.println(dtd);
  Serial.println(tmh);
  Serial.println(tmm);
  Serial.println(tmapm);
  if (dtd == "1" && dtm == "1" && dty == "2022")
   {
  Serial.println("Default date received!!! not saved");
  }
  else
  {
    year_ = dty.toInt();
    month_ = dtm.toInt();
    day_ = dtd.toInt();
    int ampm = tmapm.toInt();
    if (ampm == 2 && tmh.toInt() < 12)
    {
      hour24_ = tmh.toInt() + 12;
    }
    else if (ampm == 1 && tmh.toInt() == 12)
    {
      hour24_ = 0;
    }
    else
    {
      hour24_ = tmh.toInt();
    }
    minute_ = tmm.toInt();
    second_ = tms.toInt();;
    Serial.println("Date rescived");
    Serial.println(year_);
    Serial.println(month_);
    Serial.println(day_);
    Serial.println(hour24_);
    Serial.println(minute_);
    rtc.adjust(DateTime(year_, month_, day_, hour24_, minute_, second_));
    DateTime now = rtc.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(" (");
    Serial.print(nameoftheday[now.dayOfTheWeek()]);
    Serial.print(") ");
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
  }
  web_content += "OK";
                 server.send (200, "text/html", web_content );
                 ESP.restart();

}
/*--------------------------------------------------------*/
void deleteRecord() {
  web_content = "";
  String sql = "";
  String id = server.arg("id");
  int dfpid = id.toInt();
  sql = "delete from attendance where id=" + id;
  Serial.println(sql);
  if (db_exec(test1_db, sql.c_str()) == SQLITE_OK) {
    web_content += "OK";
    Serial.println(web_content);
    deleteFingerprint(dfpid);
  }
  else
    web_content += "FAIL";
  server.send (200, "text/html", web_content );
}
/*--------------------------------------------------------*/
void showRecords() {
  if (!is_authentified()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  web_content = "<table style='width:90%; margin-left:5%'><tr><th>Sl.No</th><th>Empl.ID</th><th>Employee Name</th><th>Employee Email</th><th>Position</th><th>FID</th><th>DEL</th></tr>";
  String sql = "Select * from attendance";
  if (db_exec(test1_db, sql.c_str()) == SQLITE_OK) {
    web_content += "</table>";
  }
  else
    web_content = "FAIL";
  server.send (200, "text/html", web_content );

}


/*--------------------------------------------------------*/
void newRecordTable() {
  if (!is_authentified()) {
    server.sendHeader("Location", "/login");
    server.sendHeader("Cache-Control", "no-cache");
    server.send(301);
    return;
  }
  String sql = "";
  Sqid = "NULL";
  sql = "SELECT * FROM attendance ORDER BY id DESC LIMIT 1";
  int temp = db_exec1(test1_db, sql.c_str());
  Serial.print("SQID : ");
  Serial.println(Sqid);
  if (Sqid == "NULL")
  {
    Sqid = "1";
  }
  else
  {
    int qid = Sqid.toInt();
    qid = qid + 1;
    Sqid = String(qid);
  }
  loadFromSPIFFS("/Enroll.html");

}
/*--------------------------------------------------------*/


/*--------------------------------------------------------*/
void getssid() {

  server.send(200, "text/plane", ssid_);

}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void getmdns() {

  server.send(200, "text/plane", mdnsdotlocalurl);

}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void getip() {

  server.send(200, "text/plane", ip_);

}
/*--------------------------------------------------------*/
/*--------------------------------------------------------*/
void getfpid() {


  server.send(200, "text/plane", Sqid);

}
/*--------------------------------------------------------*/
// returns -1 if failed, otherwise returns ID #
int FingerprintID()
{
  DateTime now = rtc.now();
  uint8_t p = finger.getImage();
  if (p != FINGERPRINT_OK)
  {
    display.clearDisplay();
    //display.setTextSize(2);             // Normal 1:1 pixel scale
    display.setFont(&Roboto_Bold_10);
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.setCursor(0, 10);            // Start at top-left corner
    display.println(nameoftheday[now.dayOfTheWeek()]);
    display.setCursor(25, 10);
    display.println(now.day());
    display.setCursor(42, 10);
    display.println(month_name[now.month() - 1]);
    display.setCursor(67, 10);
    display.println(now.year(), DEC);
    //display.setCursor(0, 64);
    //display.print("Semicon Media Pvt Ltd");
    oledDisplayCenter(dispname_, 0, 60);
    if (WiFi.status() == WL_CONNECTED) {
      display.drawBitmap(110, 0, wifi_bmp, 12, 10, 1);
    }
    else
    {
      display.drawBitmap(110, 0, nowifi_bmp, 12, 10, 1);
    }
    display.setFont(&DSEG7_Classic_Bold_30);

    display.setCursor(0, 48);
    if (now.hour() < 10 || (now.hour() > 12 && now.hour() < 22))
    {
      display.print("0");
    }
    if (now.hour() < 13)
    {
      display.print(now.hour());
    }
    else
    {
      display.print(now.hour() - 12);
    }
    if ((now.second() % 2) == 0)
    {
      display.print(":");
    }
    else
    {
      display.print(" ");
    }

    if (now.minute() < 10)
    {
      display.print("0");
    }
    display.print(now.minute());
    display.setFont(&Roboto_Bold_10);
    if (now.hour() < 13)
    {
      display.print("AM");
    }
    else
    {
      display.print("PM");
    }
    display.display();
    //Serial.println("Waiting For Valid Finger");
    return -1;
  }

  p = finger.image2Tz();
  if (p != FINGERPRINT_OK)
  {
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, messy_bmp, 35, 45, 1);
    oledDisplayCenter("Messy Image Try Again", 0, 60);
    display.display();

    Serial.println("Messy Image Try Again");
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(1500);
    display.clearDisplay();
    return -1;
  }

  p = finger.fingerFastSearch();
  if (p != FINGERPRINT_OK)  {

    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Invalid ID Try Again", 0, 60);
    display.display();
    Serial.println("Not Valid Finger");
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    display.clearDisplay();
    return -1;
  }

  // found a match!
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
  oledDisplayCenter("Finger Print Verified", 0, 60);
  display.display();
  Serial.println("Finger Print Verified");
  delay(1000);
  display.clearDisplay();
  int mnt, dday;
  String yr;
  if (now.month()  == 1)
  {
    mnt = 12;
  }
  else {
    mnt = now.month();
  }
  if ( now.day() < 7)
  {
    dday = 31 - (7 - (now.day() - 1));
  }
  else
  {
    dday = now.day() - 7;
  }
  if (now.month()  == 1 && now.day() < 7)
  {
    yr = String(now.year() - 1, DEC);
  }
  else {
    yr = String(now.year(), DEC);
  }
  String temp = String(dday) + month_name[mnt - 1] + yr;
  String sql = "drop table if exists date" + temp;
  int  rc = db_exec1(test1_db, sql.c_str());
  String att;
  temp = String(now.day()) + month_name[now.month() - 1] + String(now.year(), DEC);
  Serial.print("table name : ");
  Serial.println(temp);
  sql = "create table if not exists date" + temp + " (id INTEGER,date TEXT,time TEXT, eid TEXT,employee_name TEXT,employee_email TEXT,position TEXT,attend TEXT,fpid INTEGER,uploadsts INTEGER)";
  rc = db_exec1(test1_db, sql.c_str());

  Serial.print("Fingerprint ID : ");
  Serial.println(finger.fingerID);
  String SFPID = String (finger.fingerID);
  sql = "SELECT EXISTS(SELECT 1 from date" + temp + " where id =" + SFPID + " LIMIT 1)";
  rc = db_exec1(test1_db, sql.c_str());
  Serial.print("SQL retun : ");
  Serial.println(sqlreturn);
  if (sqlreturn == 1)
  {
    att = "Punch Out";
  }
  else
  {
    att = "Punch In";
  }
  sql = "select count(*) from date" + temp + " where id = " + SFPID;
  rc = db_exec1(test1_db, sql.c_str());
  Serial.print("No times : ");
  Serial.println(sqlreturn);
  if (sqlreturn < 2)
  {
    String hr = "";
    if (now.hour() < 10 || (now.hour() > 12 && now.hour() < 22))
    {
      hr += "0";
    }
    if (now.hour() < 13)
    {
      hr += String(now.hour());
    }
    else
    {
      hr += String(now.hour() - 12);
    }
    if (now.minute() < 10)
    {
      hr += ".0" + String(now.minute());
    }
    else
    {
      hr += "." + String(now.minute());
    }
    if (now.hour() < 12)
    {
      hr += " AM";
    }
    else
    {
      hr += " PM";
    }

    sql = "Select * from attendance where id =" + SFPID;
    rc = db_exec1(test1_db, sql.c_str());

    String urlFinal = "https://script.google.com/macros/s/" + gsid_ + "/exec?" + "date=" + temp + "&time=" + hr + "&empid=" + Empid + "&empname=" + Empname + "&empemail=" + EmpEmail + "&emppos=" + EmpPos + "&emppio=" + att;
    urlFinal.replace(" ", "%20");
    String uploadstatus = "";
    Serial.println(urlFinal);
    HTTPClient http;
    http.begin(urlFinal.c_str());
    http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
    int httpCode = http.GET();
    Serial.print("HTTP Status Code: ");
    Serial.println(httpCode);
    //---------------------------------------------------------------------
    //getting response from google sheet
    String payload;
    if (httpCode == 200) {
      uploadstatus = "0";
    }
    else
    {
      uploadstatus = "1";
    }
    //---------------------------------------------------------------------
    http.end();

    sql = "insert into date" + temp + "(id,date,time,eid,employee_name,employee_email,position,attend,fpid,uploadsts) values(" + Sqid + ",'" + temp + "','" + hr + "','" + Empid + "','" + Empname + "','" + EmpEmail + "','" + EmpPos + "','" + att + "'," + Empfid + "," + uploadstatus + ")";
    Serial.println(sql);
    if (db_exec(test1_db, sql.c_str()) == SQLITE_OK) {
      display.drawBitmap(0, 10, welcome_bmp, 128, 17, 1);
      oledDisplayCenter(Empname, 0, 50);
      display.display();
      Serial.print("Welcome : ");
      Serial.println(Empname);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(1000);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(2000);
      display.clearDisplay();
    }
  }
  else {
    oledDisplayCenter("Already Punched Out", 0, 20);
    oledDisplayCenter("Try Again Tomorrow", 0, 50);
    display.display();
    Serial.print("Attendance already logged : ");
    Serial.println(Empname);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(1000);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(2000);
    display.clearDisplay();
  }
  return finger.fingerID;
}

uint8_t deleteFingerprint(uint8_t id) {
  uint8_t p = -1;

  p = finger.deleteModel(id);

  if (p == FINGERPRINT_OK) {
    Serial.println("Deleted!");
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
  } else if (p == FINGERPRINT_BADLOCATION) {
    Serial.println("Could not delete in that location");
  } else if (p == FINGERPRINT_FLASHERR) {
    Serial.println("Error writing to flash");
  } else {
    Serial.print("Unknown error: 0x"); Serial.println(p, HEX);
  }

  return p;
}



uint8_t getFingerprintEnroll() {

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.drawBitmap(47, 0, finger_bmp, 35, 45, 1);
  display.setCursor(0, 60);            // Start at top-left corner
  display.println(("Place Finger to Enroll"));
  display.display();
  int p = -1;
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
      case FINGERPRINT_OK:
        Serial.println("Image taken");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
        oledDisplayCenter("Image taken", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        break;
      case FINGERPRINT_NOFINGER:
        Serial.println(".");
        break;
      case FINGERPRINT_PACKETRECIEVEERR:
        Serial.println("Communication error");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
        oledDisplayCenter("Communication error", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        break;
      case FINGERPRINT_IMAGEFAIL:
        Serial.println("Imaging error");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
        oledDisplayCenter("Imaging error", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        break;
      default:
        Serial.println("Unknown error");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
        oledDisplayCenter("Unknown error", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        break;
    }
  }

  // OK success!

  p = finger.image2Tz(1);
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
      oledDisplayCenter("Image converted", 0, 60);
      display.display();
      delay(1000);
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, messy_bmp, 35, 45, 1);
      oledDisplayCenter("Image too messy", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("Communication errorr", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("No fingerprint features", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("Imaging error", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    default:
      Serial.println("Unknown error");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("Unknown error", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
  }

  Serial.println("Remove finger");
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.drawBitmap(47, 0, finger_bmp, 35, 45, 1);
  oledDisplayCenter("Remove finger", 0, 60);
  display.display();
  digitalWrite (Buzzer, HIGH); //turn buzzer on
  delay(500);
  digitalWrite (Buzzer, LOW); //turn buzzer on
  delay(500);
  delay(1000);
  p = 0;
  while (p != FINGERPRINT_NOFINGER) {
    p = finger.getImage();
  }
  Serial.print("ID "); Serial.println(id);
  p = -1;
  Serial.println("Place same finger again");
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  display.drawBitmap(47, 0, finger_bmp, 35, 45, 1);
  oledDisplayCenter("Place same finger again", 0, 60);
  display.display();
  while (p != FINGERPRINT_OK) {
    p = finger.getImage();
    switch (p) {
      case FINGERPRINT_OK:
        Serial.println("Image taken");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
        oledDisplayCenter("Image taken", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        break;
      case FINGERPRINT_NOFINGER:
        Serial.print(".");
        break;
      case FINGERPRINT_PACKETRECIEVEERR:
        Serial.println("Communication error");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
        oledDisplayCenter("Communication error", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        break;
      case FINGERPRINT_IMAGEFAIL:
        Serial.println("Imaging error");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
        oledDisplayCenter("Imaging error", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        break;
      default:
        Serial.println("Unknown error");
        display.clearDisplay();
        display.setTextColor(SSD1306_WHITE);        // Draw white text
        display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
        oledDisplayCenter("Unknown error", 0, 60);
        display.display();
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, HIGH); //turn buzzer on
        delay(500);
        digitalWrite (Buzzer, LOW); //turn buzzer on
        break;
    }
  }

  // OK success!

  p = finger.image2Tz(2);
  switch (p) {
    case FINGERPRINT_OK:
      Serial.println("Image converted");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
      oledDisplayCenter("Image converted", 0, 60);
      display.display();
      delay(1000);
      break;
    case FINGERPRINT_IMAGEMESS:
      Serial.println("Image too messy");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, messy_bmp, 35, 45, 1);
      oledDisplayCenter("Image too messy", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    case FINGERPRINT_PACKETRECIEVEERR:
      Serial.println("Communication error");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("Communication error", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    case FINGERPRINT_FEATUREFAIL:
      Serial.println("Could not find fingerprint features");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("No fingerprint features", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    case FINGERPRINT_INVALIDIMAGE:
      Serial.println("Could not find fingerprint features");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("Imaging error", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
    default:
      Serial.println("Unknown error");
      display.clearDisplay();
      display.setTextColor(SSD1306_WHITE);        // Draw white text
      display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
      oledDisplayCenter("Unknown error", 0, 60);
      display.display();
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, HIGH); //turn buzzer on
      delay(500);
      digitalWrite (Buzzer, LOW); //turn buzzer on
      return p;
  }

  // OK converted!
  Serial.print("Creating model for #");  Serial.println(id);

  p = finger.createModel();
  if (p == FINGERPRINT_OK) {
    Serial.println("Prints matched!");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
    oledDisplayCenter("Prints matched!", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Communication error", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  } else if (p == FINGERPRINT_ENROLLMISMATCH) {
    Serial.println("Fingerprints did not match");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Fingerprints didn't match", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  } else {
    Serial.println("Unknown error");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Unknown error", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  }

  Serial.print("ID "); Serial.println(id);
  p = finger.storeModel(id);
  if (p == FINGERPRINT_OK) {
    Serial.println("Stored!");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, ok_bmp, 35, 45, 1);
    oledDisplayCenter("Image Stored!", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
  } else if (p == FINGERPRINT_PACKETRECIEVEERR) {
    Serial.println("Communication error");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Communication error", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  } else if (p == FINGERPRINT_BADLOCATION) {
    Serial.println("Could not store in that location");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Location error", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  } else if (p == FINGERPRINT_FLASHERR) {
    Serial.println("Error writing to flash");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Error writing to flash", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  } else {
    Serial.println("Unknown error");
    display.clearDisplay();
    display.setTextColor(SSD1306_WHITE);        // Draw white text
    display.drawBitmap(47, 0, invalid_bmp, 35, 45, 1);
    oledDisplayCenter("Unknown error", 0, 60);
    display.display();
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, HIGH); //turn buzzer on
    delay(500);
    digitalWrite (Buzzer, LOW); //turn buzzer on
    return p;
  }

  return true;
}


void connectwifi()
{
  if (ssid_ == "" ) {  // no ip // made it DHCP
    Serial.print("\n No wifi config found Connecting to Default Wifi ");
    Serial.println(DEFAULT_WIFI_SSID);
    WiFi.mode(WIFI_STA);
    int connectcount = 0;
    WiFi.begin(DEFAULT_WIFI_SSID, DEFAULT_WIFI_PASS);
    while (WiFi.status() != WL_CONNECTED) {
      delay(250);
      Serial.print(".");

    }
    if (WiFi.status() == WL_CONNECTED) {
      Serial.print("\n Connected. IP adress: ");
      Serial.println(WiFi.localIP());
    }
  }
  else {
    Serial.print("\n Wifi config found Connecting to  Wifi");
    WiFi.mode(WIFI_STA);
    if (dhcpcheck == "2" && ip_ != "" && gateway_ != "") {
      // fixed ip
      localIP.fromString(ip_.c_str());
      gatewayIP.fromString(gateway_.c_str());
      if (!WiFi.config(localIP, gatewayIP, subnetMask)) {
        Serial.println("STA Failed to configure");
      }

    } else {
      //dhcp
    }
    int connectcount = 0;
    WiFi.begin(ssid_.c_str(), pass_.c_str());
    while (WiFi.status() != WL_CONNECTED) {
      delay(250);
      Serial.print(".");
      connectcount++;
      if (connectcount > 50)
      {
        Serial.println("Failed to Connect to WiFi...");
        break;
      }
    }
    if (WiFi.status() == WL_CONNECTED) {
      Serial.print("\n Connected. IP adress: ");
      Serial.println(WiFi.localIP());
      if (booting == false)
      {
        String urlFinal = "https://script.google.com/macros/s/" + gsid_ + "/exec?";
        String uploadstatus = "";
        Serial.println(urlFinal);
        HTTPClient http;
        http.begin(urlFinal.c_str());
        http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
        int httpCode = http.GET();
        Serial.print("HTTP Status Code: ");
        Serial.println(httpCode);
        int j = httpCode;
        http.end();
        //---------------------------------------------------------------------
        //getting response from google sheet
        String payload;
        if (j == 200) {
          offlinedataupload();
        }
        else
        {
          Serial.print("Device offline");
        }
        //---------------------------------------------------------------------


      }
    }
  }
  lastwificheck = millis();
}


void offlinedataupload()
{
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);        // Draw white text
  oledDisplayCenter("Please Waite!!", 0, 20);
  oledDisplayCenter("Uploading Offline Data", 0, 40);
  display.display();
  int j;
  for (int i = 6; i > -1; i--)
  {

    DateTime now = rtc.now();
    int mnt, dday;
    String yr;
    if (now.month()  == 1)
    {
      mnt = 12;
    }
    else {
      mnt = now.month();
    }
    if ( now.day() < 7)
    {
      dday = 31 - (i - (now.day() - 1));
    }
    else
    {
      dday = now.day() - i;
    }
    if (now.month()  == 1 && now.day() < 7)
    {
      yr = String(now.year() - 1, DEC);
    }
    else {
      yr = String(now.year(), DEC);
    }
    String temp = "date" + String(dday) + month_name[mnt - 1] + yr;
    while (1)
    {

      String number = "1";
      String sql = "Select * from " + temp + " where uploadsts =" + number;
      int rc = db_exec1(test1_db, sql.c_str());
      if (sqlreturn != 1 || j != 200)
      {
        break;
      }
      else
      {


        sql = "Select * from " + temp + " where id =" + Sqid + " and attend ='" + sts8 + "'";
        rc = db_exec1(test1_db, sql.c_str());

        String urlFinal = "https://script.google.com/macros/s/" + gsid_ + "/exec?" + "date=" + Empid + "&time=" + Empname + "&empid=" + EmpEmail + "&empname=" + EmpPos + "&empemail=" + Empfid + "&emppos=" + sts7 + "&emppio=" + sts8;
        urlFinal.replace(" ", "%20");
        String uploadstatus = "";
        Serial.println(urlFinal);
        HTTPClient http;
        http.begin(urlFinal.c_str());
        http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS);
        int httpCode = http.GET();
        Serial.print("HTTP Status Code: ");
        Serial.println(httpCode);
        j = httpCode;
        //---------------------------------------------------------------------
        //getting response from google sheet
        String payload;
        if (httpCode == 200) {
          sql = "update " + temp + " set uploadsts = '0' where id = " + Sqid + " and attend ='" + sts8 + "'";
          Serial.println(sql);
          rc = db_exec(test1_db, sql.c_str());
        }
        else
        {
          delay(1);
        }
        //---------------------------------------------------------------------
        http.end();

      }//
    }
  }

}
Video

Have any question realated to this Article?

Ask Our Community Members

Comments

Hey there! I noticed that you encountered a similar problem with the R307 fingerprint sensor module. I'm currently facing the same issue, and I was wondering if you were able to find a solution for it. Did you manage to fix the problem or make any progress?

Any insights or suggestions would be greatly appreciated. Thanks in advance!

Contact, therealratanak@gmail.com