HaniyaFarhaFahmiyaa Sadath
Published

ServeSync - Smart restaurant ordering and table management

Smart restaurant system using XIAO ESP32-S3 and ESP-NOW for digital ordering, food status notifications, and table cleaning alerts.

IntermediateProtipOver 2 days12
ServeSync - Smart restaurant ordering and table management

Things used in this project

Hardware components

Seeed Studio XIAO ESP32S3
Seeed Studio XIAO ESP32S3
×2
Rotary Encoder with Push-Button
Rotary Encoder with Push-Button
×1
12Γ—12 mm 4-Pin Tactile Push Button Switch (Red)
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
×1
LED (generic)
LED (generic)
×2
Solderless Breadboard Full Size
Solderless Breadboard Full Size
×2
Resistor 330 ohm
Resistor 330 ohm
×2
Jumper wires (generic)
Jumper wires (generic)
×16

Software apps and online services

Visual Studio Code Extension for Arduino
Microsoft Visual Studio Code Extension for Arduino

Story

Read more

Schematics

circuit diagram

servesync - circuit diagram

Circuit diagram of the ServeSync demonstration setup using two Seeed Studio XIAO ESP32-S3 boards. The Customer unit includes a push button and notification LED, while the Staff unit includes a push button, notification LED, and rotary encoder. The two XIAO ESP32-S3 boards communicate wirelessly using ESP-NOW. The demo represents one customer table, with the system designed to support table selection and status notifications.

Code

kitchen - staff arduino code

C/C++
Development Environment: Arduino IDE
Microcontroller: Seeed Studio XIAO ESP32-S3
Programming Language: Arduino C/C++
Communication: ESP-NOW
#include <WiFi.h>
#include <esp_now.h>
#include "esp_mac.h"
#include <esp_wifi.h>

// ============================================
// ENCODER PINS
// ============================================
#define CLK 32
#define DT  33
#define SW  25

// ============================================
// STAFF PINS
// ============================================
// P4 = Food Control Button
// P5 = Cleaning Alert LED
#define STAFF_BUTTON_PIN 4
#define STAFF_LED_PIN    5

// ============================================
// DESTINATION MAC ADDRESSES
// ============================================
uint8_t broadcastMAC[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
uint8_t customerMAC[]  = { 0xB0, 0xCB, 0xD8, 0x09, 0x4B, 0x54 };

// ============================================
// MESSAGE TYPES
// ============================================
#define MSG_COOKING          10
#define MSG_READY            11
#define MSG_TABLE_FINISHED   12

// ============================================
// TABLE FOOD STATUS (Per Table 1-4)
// ============================================
int tableStatus[5] = { 0, 0, 0, 0, 0 };

// ============================================
// TABLE CLEANING STATUS (Per Table 1-4)
// ============================================
bool tableNeedsCleaning[5] = { false, false, false, false, false };

// ============================================
// ENCODER & SELECTION STATES
// ============================================
int selectedTable  = 1;
int confirmedTable = 1;
int lastCLK = HIGH;

// ============================================
// STAFF P5 LED CLEANING ALERT STATE
// ============================================
bool cleaningAlert      = false;
bool staffLEDState      = false;
unsigned long lastStaffLEDChange = 0;

// ============================================
// INCOMING DATA PROCESSING
// ============================================
void processIncomingData(const uint8_t *data, int len) {
  if (len < 2) return;

  uint8_t msg           = data[0];
  uint8_t receivedTable = data[1];

  if (msg == MSG_TABLE_FINISHED) {
    if (receivedTable >= 1 && receivedTable <= 4) {
      tableNeedsCleaning[receivedTable] = true;
      cleaningAlert  = true;
      staffLEDState  = true;
      digitalWrite(STAFF_LED_PIN, HIGH);
      lastStaffLEDChange = millis();

      Serial.println();
      Serial.println("==============================");
      Serial.print("CUSTOMER FINISHED - TABLE ");
      Serial.print(receivedTable);
      Serial.println(" NEEDS CLEANING");
      Serial.println("STAFF P5 LED = BLINKING");
      Serial.println("==============================");
      Serial.println();
    }
  }
}

#if defined(ESP_ARDUINO_VERSION_MAJOR) && ESP_ARDUINO_VERSION_MAJOR >= 3
void OnDataRecv(const esp_now_recv_info_t *info, const uint8_t *data, int len) {
  processIncomingData(data, len);
}
#else
void OnDataRecv(const uint8_t *mac, const uint8_t *data, int len) {
  processIncomingData(data, len);
}
#endif

// ============================================
// SEND FOOD STATUS VIA ESP-NOW
// ============================================
void sendFoodStatus(uint8_t table, uint8_t message) {
  uint8_t payload[2];
  payload[0] = message;
  payload[1] = table;

  esp_now_send(broadcastMAC, payload, sizeof(payload));
  esp_now_send(customerMAC,  payload, sizeof(payload));
  esp_now_send(NULL,         payload, sizeof(payload));

  if (message == MSG_COOKING) {
    Serial.println();
    Serial.print("TABLE ");
    Serial.print(table);
    Serial.println(" -> COOKING");
  } else if (message == MSG_READY) {
    Serial.println();
    Serial.print("TABLE ");
    Serial.print(table);
    Serial.println(" -> READY");
  }
}

// ============================================
// SETUP
// ============================================
void setup() {
  Serial.begin(115200);
  delay(500);

  pinMode(CLK, INPUT_PULLUP);
  pinMode(DT,  INPUT_PULLUP);
  pinMode(SW,  INPUT_PULLUP);
  lastCLK = digitalRead(CLK);

  pinMode(STAFF_BUTTON_PIN, INPUT_PULLUP);
  pinMode(STAFF_LED_PIN, OUTPUT);
  digitalWrite(STAFF_LED_PIN, LOW);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE);

  uint8_t kitchenMAC[6];
  esp_read_mac(kitchenMAC, ESP_MAC_WIFI_STA);

  Serial.println();
  Serial.println("==============================");
  Serial.println("KITCHEN / STAFF ESP32");
  Serial.println("==============================");
  Serial.printf("Kitchen MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
    kitchenMAC[0], kitchenMAC[1], kitchenMAC[2],
    kitchenMAC[3], kitchenMAC[4], kitchenMAC[5]);

  if (esp_now_init() != ESP_OK) {
    Serial.println("ESP-NOW INIT FAILED");
    return;
  }

  esp_now_register_recv_cb(OnDataRecv);

  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, broadcastMAC, 6);
  peerInfo.channel = 1;
  peerInfo.encrypt = false;
  esp_now_add_peer(&peerInfo);

  esp_now_peer_info_t customerPeer = {};
  memcpy(customerPeer.peer_addr, customerMAC, 6);
  customerPeer.channel = 1;
  customerPeer.encrypt = false;
  esp_now_add_peer(&customerPeer);

  Serial.println("KITCHEN ESP-NOW READY");
  Serial.println("P4 = FOOD STATUS BUTTON");
  Serial.println("P5 = CLEANING ALERT LED");
  Serial.println("ENCODER = 32 / 33 / 25");

  Serial.print("TABLE ");
  Serial.println(selectedTable);
}

// ============================================
// MAIN LOOP
// ============================================
void loop() {
  unsigned long now = millis();

  // P5 LED BLINKING (CLEANING ALERT)
  if (cleaningAlert) {
    if (now - lastStaffLEDChange >= 500) {
      lastStaffLEDChange = now;
      staffLEDState = !staffLEDState;
      digitalWrite(STAFF_LED_PIN, staffLEDState);
    }
  }

  // ENCODER ROTATION
  int currentCLK = digitalRead(CLK);
  if (currentCLK != lastCLK) {
    if (digitalRead(DT) != currentCLK) {
      selectedTable++;
    } else {
      selectedTable--;
    }
    if (selectedTable > 4) selectedTable = 1;
    if (selectedTable < 1) selectedTable = 4;
    confirmedTable = selectedTable;
    Serial.print("TABLE ");
    Serial.println(selectedTable);
    delay(100);
  }
  lastCLK = currentCLK;

  // ENCODER BUTTON
  if (digitalRead(SW) == LOW) {
    delay(50);
    if (digitalRead(SW) == LOW) {
      confirmedTable = selectedTable;
      Serial.print("SELECTED TABLE ");
      Serial.println(confirmedTable);
      while (digitalRead(SW) == LOW) delay(10);
      delay(100);
    }
  }

  // STAFF P4 BUTTON (FOOD CONTROL / CLEAN ACK)
  if (digitalRead(STAFF_BUTTON_PIN) == LOW) {
    delay(50);
    if (digitalRead(STAFF_BUTTON_PIN) == LOW) {
      int activeTargetTable = (confirmedTable >= 1 && confirmedTable <= 4)
                              ? confirmedTable : selectedTable;

      if (tableNeedsCleaning[activeTargetTable]) {
        tableNeedsCleaning[activeTargetTable] = false;

        bool anyPending = false;
        for (int i = 1; i <= 4; i++) {
          if (tableNeedsCleaning[i]) anyPending = true;
        }
        if (!anyPending) {
          cleaningAlert  = false;
          staffLEDState  = false;
          digitalWrite(STAFF_LED_PIN, LOW);
        }
        tableStatus[activeTargetTable] = 0;

        Serial.println();
        Serial.println("==============================");
        Serial.print("TABLE ");
        Serial.print(activeTargetTable);
        Serial.println(" CLEANED");
        Serial.println("STAFF P5 LED = OFF");
        Serial.println("==============================");
        Serial.println();

      } else {
        int currentStatus = tableStatus[activeTargetTable];
        if (currentStatus == 2) {
          tableStatus[activeTargetTable] = 3;
          sendFoodStatus(activeTargetTable, MSG_READY);
        } else {
          tableStatus[activeTargetTable] = 2;
          sendFoodStatus(activeTargetTable, MSG_COOKING);
        }
      }

      while (digitalRead(STAFF_BUTTON_PIN) == LOW) delay(10);
      delay(100);
    }
  }

  delay(10);
}

Kitchen/Staff XIAO ESP32-S3 – ESP-NOW Control

This code is used to control the Kitchen/Staff unit of ServeSync. It manages table selection, food-status updates, and cleaning alerts using the XIAO ESP32-S3.

Credits

Haniya
1 project β€’ 0 followers
Farha
0 projects β€’ 0 followers
Fahmiyaa Sadath
0 projects β€’ 0 followers

Comments