// #include "esp_system.h"
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <ArduinoJson.h> // Include the ArduinoJson library
#include <Preferences.h>
// TFT display settings
#define TFT_LED 5
#define TFT_SCK 18
#define TFT_MOSI 23
#define TFT_DC 2
#define TFT_RESET 4
#define TFT_CS 15
// Color theme
#define THEME_COLOR ILI9341_BLUE
#define ACCENT_COLOR ILI9341_DARKCYAN
#define TEXT_COLOR ILI9341_WHITE
#define HIGHLIGHT_COLOR ILI9341_GREEN
#define ERROR_COLOR ILI9341_RED
#define INFO_COLOR ILI9341_YELLOW
#define BACKGROUND_COLOR ILI9341_BLACK
// Display settings for prices
#define PRICE_TEXT_SIZE 3
#define INFO_TEXT_SIZE 1
#define HEADER_FOOTER_HEIGHT 30
#define HEADER_FOOTER_TEXT_SIZE 2
Preferences preferences;
// Replace these with your desired AP settings
// const char *apSSID = "ESP32-Config"; generated with run tim emac adddress
const char *apPassword = "12345678"; // Password must be at least 8 characters
// Define button pin
const int buttonPin = 0; // Commonly used for button inputs
// Application state
enum AppState {
SHOW_INFO,
ENABLE_AP,
SHOW_PRICE
};
volatile AppState currentState = SHOW_INFO;
volatile bool buttonPressed = false;
String lastProductName = "";
String lastMrp = "";
String lastSellingPrice = "";
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCK, TFT_RESET);
// WiFi credentials
const char* ssid = "";
const char* password = "";
// Server URL for device registration
const char* serverUrl = "http://your-central-server.com/register";
WebServer server(80); // Initialize HTTP server on port 80
// helper functions
void clearScreen() {
tft.fillScreen(BACKGROUND_COLOR);
}
void displayMessage(String message, int16_t x, int16_t y, uint16_t textColor = ILI9341_WHITE, uint16_t bgColor = ILI9341_BLACK, bool clear = false) {
if (clear) {
clearScreen();
}
tft.setCursor(x, y);
tft.setTextColor(textColor, bgColor);
tft.println(message);
}
void drawHeader(String title) {
tft.fillRect(0, 0, tft.width(), HEADER_FOOTER_HEIGHT, THEME_COLOR);
tft.setCursor(10, (HEADER_FOOTER_HEIGHT / 2) - 8); // Center text vertically
tft.setTextColor(TEXT_COLOR, THEME_COLOR);
tft.setTextSize(HEADER_FOOTER_TEXT_SIZE);
tft.println(title);
}
void drawFooter(String text) {
tft.fillRect(0, tft.height() - HEADER_FOOTER_HEIGHT, tft.width(), HEADER_FOOTER_HEIGHT, ACCENT_COLOR);
tft.setCursor(10, tft.height() - HEADER_FOOTER_HEIGHT + (HEADER_FOOTER_HEIGHT / 2) - 8);
tft.setTextColor(TEXT_COLOR, ACCENT_COLOR);
tft.setTextSize(HEADER_FOOTER_TEXT_SIZE);
tft.println(text);
}
void displayInfo(String info) {
clearScreen();
drawHeader("ESP32 Info");
tft.setTextColor(INFO_COLOR);
tft.setTextSize(INFO_TEXT_SIZE);
tft.setCursor(10, HEADER_FOOTER_HEIGHT + 10);
tft.println(info);
drawFooter("Ready for Price Update");
}
void displayWiFiStatus(String status, bool connected = false) {
clearScreen();
drawHeader("WiFi Status");
displayMessage(status, 10, 50, ILI9341_WHITE, ILI9341_BLACK);
if (connected) {
drawFooter("Connected");
} else {
drawFooter("Disconnected");
}
}
void handleButtonPress() {
if (digitalRead(buttonPin) == LOW) {
buttonPressed = true;
}
}
void displayAPModeInfo(String ssid) {
clearScreen();
drawHeader("AP Mode Active");
// Display the AP SSID
displayMessage("SSID: " + ssid, 10, 50, TEXT_COLOR, BACKGROUND_COLOR, true);
// Display AP IP Address
String ipDisplay = "IP: ";
ipDisplay += WiFi.softAPIP().toString();
displayMessage(ipDisplay, 10, 90, TEXT_COLOR, BACKGROUND_COLOR, false);
// Instruction for configuration
String instruction = "Connect & go to:";
instruction += "\n192.168.4.1/config";
displayMessage(instruction, 10, 130, INFO_COLOR, BACKGROUND_COLOR, false);
drawFooter("Configure Device");
}
void startAPMode() {
// Get the MAC address
String mac = WiFi.macAddress();
String apSSID = "ESP32_" + mac; // Prefix "ESP32_" to make it clear it's an ESP32 device
WiFi.disconnect(); // Disconnect from any current WiFi connections
WiFi.mode(WIFI_AP); // Set mode to Access Point
WiFi.softAP(apSSID, apPassword); // Start the Access Point
Serial.println("AP Mode Started. Connect to WiFi SSID: " + String(apSSID));
Serial.print("AP IP Address: ");
Serial.println(WiFi.softAPIP());
// Display AP mode information on TFT
displayAPModeInfo(apSSID);
}
void connectToWiFi() {
WiFi.mode(WIFI_STA); // Switch to STA mode
// Attempt to load saved WiFi credentials
preferences.begin("wifi", true); // ReadOnly
String savedSSID = preferences.getString("ssid", "");
String savedPassword = preferences.getString("password", "");
preferences.end();
if (savedSSID != "" && savedPassword != "") {
WiFi.begin(savedSSID.c_str(), savedPassword.c_str());
displayWiFiStatus("Connecting to WiFi...");
Serial.println(savedSSID.c_str());
Serial.println(savedPassword.c_str());
unsigned long startTime = millis();
// Timeout for WiFi connection attempt
while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000) {
delay(500);
Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
displayWiFiStatus("Connected to WiFi!", true);
// Check for saved price information and display accordingly
displayLastKnownPrice();
return;
}
}
// If no saved credentials or failed to connect, start AP mode
startAPMode();
}
void reconnectToWiFi() {
// Clears any existing WiFi connections
WiFi.disconnect(true);
delay(1000); // Short delay before reconnecting
// Reconnect to WiFi with potentially new credentials
connectToWiFi();
}
void registerDevice() {
if (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
http.begin(serverUrl);
http.addHeader("Content-Type", "application/json");
String postData = "{\"mac\":\"" + WiFi.macAddress() + "\", \"ip\":\"" + WiFi.localIP().toString() + "\"}";
int httpCode = http.POST(postData);
if (httpCode > 0) {
String response = http.getString();
Serial.println("Registration response: " + response);
} else {
Serial.println("Registration failed");
}
http.end();
}
}
void displayDebugInfo() {
clearScreen();
drawHeader("Device Info");
// Define starting position for Y and increment for each line
int yPos = HEADER_FOOTER_HEIGHT + 20; // Start just below the header
int lineSpacing = 30; // Space between lines for larger text
int textSize = 2; // Larger text size for visibility
// Set text size and color
tft.setTextSize(textSize);
tft.setTextColor(TEXT_COLOR, BACKGROUND_COLOR); // Text color without background
// Display IP Address
tft.setCursor(10, yPos);
tft.println("IP: " + WiFi.localIP().toString());
yPos += lineSpacing;
// Display MAC Address
tft.setCursor(10, yPos);
tft.println("MAC: " + WiFi.macAddress());
yPos += lineSpacing;
// Display Listening Port
tft.setCursor(10, yPos);
tft.println("Port: 80");
yPos += lineSpacing;
// Ensure there's enough space for "Waiting for price data..."
yPos = tft.height() - HEADER_FOOTER_HEIGHT - 20; // Position above footer
tft.setCursor(10, yPos);
tft.println("Waiting for price data...");
// Footer stays as it was
drawFooter("Ready");
}
void displayPrice(const String& productName, const String& mrp, const String& sellingPrice) {
clearScreen();
// Update global variables
lastProductName = productName;
lastMrp = mrp;
lastSellingPrice = sellingPrice;
// Product Name at the top
tft.setTextColor(TEXT_COLOR, BACKGROUND_COLOR);
tft.setTextSize(4); // Slightly larger for product name visibility
tft.setCursor(10, 10); // Start a bit from the top
tft.println(productName);
// MRP with simulated strikethrough
int mrpYPos = 100; // Adjust position after product name for visibility
tft.setTextSize(3);
tft.setCursor(10, mrpYPos);
tft.print("MRP: " + mrp + "/-"); // Use print for continuous flow
int mrpTextWidth = (6 + mrp.length()) * 6 * 3; // Adjust width calculation for textSize 3
tft.drawLine(10, mrpYPos + 20, 10 + mrpTextWidth, mrpYPos + 20, ERROR_COLOR); // Adjust line thickness and position
// Selling Price - Highlighted and Larger
int sellingPriceYPos = mrpYPos + 60; // Adjust position after MRP
tft.setTextSize(5); // Increase text size for emphasis
tft.setTextColor(HIGHLIGHT_COLOR, BACKGROUND_COLOR);
tft.setCursor(10, sellingPriceYPos);
tft.println( sellingPrice + "/-"); // Directly display price
}
void displayLastKnownPrice() {
if (lastProductName != "" && lastMrp != "" && lastSellingPrice != "") {
displayPrice(lastProductName, lastMrp, lastSellingPrice);
} else {
// Handle case where there is no last known price
clearScreen();
drawHeader("No Price Data");
tft.setTextColor(ERROR_COLOR, BACKGROUND_COLOR);
tft.setTextSize(2);
tft.setCursor(10, 100); // Center on screen
tft.println("No price data available.");
drawFooter("Please update");
}
}
// Assuming you have a function to setup server routes
void setupServerRoutes() {
server.on("/", HTTP_GET, []() {
server.send(200, "text/html", "<h1>Welcome to ESP32</h1><p>Please use the /config endpoint to configure WiFi settings.</p>");
});
server.on("/config", HTTP_GET, []() {
String mac = WiFi.macAddress(); // Retrieve MAC address
String html = "<html><body><h1>WiFi Configuration</h1>"
"<p>Device MAC: " + mac + "</p>"
"<form action='/saveconfig' method='POST'>"
"SSID: <input type='text' name='ssid'><br>"
"Password: <input type='password' name='password'><br>"
"<input type='submit' value='Save'></form>"
"</body></html>";
server.send(200, "text/html", html);
});
server.on("/saveconfig", HTTP_POST, []() {
if (server.hasArg("ssid") && server.hasArg("password")) {
preferences.begin("wifi", false);
preferences.clear();
preferences.putString("ssid", server.arg("ssid"));
preferences.putString("password", server.arg("password"));
preferences.end();
server.send(200, "text/html", "<h1>Configuration Saved. Please manually restart the device.</h1>");
// No immediate restart; instruct user to manually restart to reapply credentials
delay(2000);
reconnectToWiFi();
displayDebugInfo();
} else {
server.send(400, "text/html", "<h1>Invalid Request</h1>");
}
});
server.on("/price", HTTP_POST, []() {
if (server.hasArg("plain")) {
StaticJsonDocument<256> doc;
deserializeJson(doc, server.arg("plain"));
String productName = doc["product"].as<String>();
String mrp = doc["mrp"].as<String>();
String sellingPrice = doc["sellingPrice"].as<String>();
// Update global variables and display price
lastProductName = productName;
lastMrp = mrp;
lastSellingPrice = sellingPrice;
displayPrice(productName, mrp, sellingPrice);
server.send(200, "text/plain", "Price data received");
} else {
server.send(500, "text/plain", "POST body missing");
}
});
}
// setup start
void setup() {
Serial.begin(115200);
// delay(1000); // Wait a bit for the serial connection to become stable
// // Print the reset reason
// esp_reset_reason_t reason = esp_reset_reason();
// Serial.print("Reset reason: ");
// switch(reason) {
// case ESP_RST_UNKNOWN: Serial.println("Unknown"); break;
// case ESP_RST_POWERON: Serial.println("Power on reset"); break;
// case ESP_RST_EXT: Serial.println("External reset (reset pin)"); break;
// case ESP_RST_SW: Serial.println("Software reset"); break;
// case ESP_RST_PANIC: Serial.println("Software panic reset"); break;
// case ESP_RST_INT_WDT: Serial.println("Interrupt watchdog reset"); break;
// case ESP_RST_TASK_WDT: Serial.println("Task watchdog reset"); break;
// case ESP_RST_WDT: Serial.println("Other watchdog reset"); break;
// case ESP_RST_DEEPSLEEP: Serial.println("Exited from deep sleep"); break;
// case ESP_RST_BROWNOUT: Serial.println("Brownout reset (voltage drop)"); break;
// // Add any other specific reset reasons you are interested in
// default: Serial.println("Other reason"); break;
// }
pinMode(TFT_LED, OUTPUT);
digitalWrite(TFT_LED, HIGH); // Enable TFT backlight
preferences.begin("wifi", false);
tft.begin();
tft.setRotation(1); // Set screen rotation
tft.fillScreen(ILI9341_BLACK);
tft.setTextSize(1);
tft.setTextColor(ILI9341_WHITE);
// Connect to WiFi network
connectToWiFi();
// Register device with central server
registerDevice();
// Setup server endpoint to receive price updates
setupServerRoutes(); // This sets up all routes
server.begin();
// Display device debug information
displayDebugInfo();
pinMode(buttonPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(buttonPin), handleButtonPress, FALLING);
}
void loop() {
// Check if the button was pressed
if (buttonPressed) {
// Move to the next state
AppState previousState = currentState;
currentState = static_cast<AppState>((currentState + 1) % 3);
buttonPressed = false; // Reset button press flag
Serial.printf("currentState: %d\n", currentState);
// Handle the new state
switch (currentState) {
case SHOW_INFO:
if (previousState == ENABLE_AP) {
// If we're coming from AP mode, attempt to reconnect to WiFi
reconnectToWiFi();
}
displayDebugInfo();
break;
case ENABLE_AP:
// Function to start AP mode and display AP details
startAPMode();
break;
case SHOW_PRICE:
if (previousState == ENABLE_AP) {
// If we're coming from AP mode, attempt to reconnect to WiFi
reconnectToWiFi();
}
// Assume there's a function to display the last known price
displayLastKnownPrice();
break;
}
}
// Continuously handle client requests
server.handleClient();
}