#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <Preferences.h>
#include <FastLED.h>
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
// ===================================
// 1. CONFIGURAÇÕES DO M5STACK ATOM S3 LITE
// ===================================
// LED RGB (WS2812)
#define LED_PIN 35
#define NUM_LEDS 1
CRGB leds[NUM_LEDS];
// Botão
#define BTN_PIN 41
// ===================================
// 2. CONFIGURAÇÕES DE REDE E BROKER
// ===================================
String ssid = "";
String password = "";
String printer_bluetooth_name = "";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
const char* mqtt_client_id = "ESP32_Atom_Printer";
const char* mqtt_topic_sub = "senai/iot/pedidos";
bool config_saved = false;
bool printer_connected = false;
bool ble_config_mode = false;
// Objetos
WiFiClient espClient;
PubSubClient client(espClient);
Preferences preferences;
// Bluetooth Serial para impressora
HardwareSerial BTSerial(1); // UART1 para comunicação com impressora
// BLE para configuração
BLEServer* pServer = nullptr;
BLECharacteristic* pCharSSID = nullptr;
BLECharacteristic* pCharPassword = nullptr;
BLECharacteristic* pCharPrinter = nullptr;
BLECharacteristic* pCharStatus = nullptr;
bool deviceConnected = false;
// UUIDs para o serviço BLE
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHAR_UUID_SSID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define CHAR_UUID_PASSWORD "beb5483e-36e1-4688-b7f5-ea07361b26a9"
#define CHAR_UUID_PRINTER "beb5483e-36e1-4688-b7f5-ea07361b26aa"
#define CHAR_UUID_STATUS "beb5483e-36e1-4688-b7f5-ea07361b26ab"
const size_t JSON_DOC_SIZE = 1024;
// ===================================
// 3. COMANDOS ESC/POS
// ===================================
const char INIT_PRINTER[] = "\x1B\x40";
const char CUT_FULL[] = "\x1D\x56\x00";
const char ALIGN_CENTER[] = "\x1B\x61\x01";
const char ALIGN_LEFT[] = "\x1B\x61\x00";
const char ALIGN_RIGHT[] = "\x1B\x61\x02";
const char BOLD_ON[] = "\x1B\x45\x01";
const char BOLD_OFF[] = "\x1B\x45\x00";
const char DOUBLE_SIZE_ON[] = "\x1D\x21\x11";
const char NORMAL_SIZE[] = "\x1D\x21\x00";
// ===================================
// 4. FUNÇÕES DE LED (STATUS VISUAL)
// ===================================
void setLED(CRGB color) {
leds[0] = color;
FastLED.show();
}
void blinkLED(CRGB color, int times = 3) {
for (int i = 0; i < times; i++) {
setLED(color);
delay(200);
setLED(CRGB::Black);
delay(200);
}
}
// Status:
// Azul piscando = Modo configuração
// Ciano = BLE ativo (aguardando conexão)
// Verde = WiFi conectado, aguardando pedidos
// Amarelo = Imprimindo
// Vermelho = Erro
// ===================================
// 5. FUNÇÕES BLE (CONFIGURAÇÃO VIA BLUETOOTH)
// ===================================
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
Serial.println("📱 Cliente BLE conectado!");
setLED(CRGB::Blue);
}
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
Serial.println("📱 Cliente BLE desconectado!");
setLED(CRGB::Cyan);
delay(500);
pServer->startAdvertising(); // Reinicia advertising
Serial.println("BLE Advertising reiniciado");
}
};
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue();
String strValue = String(value.c_str());
if (pCharacteristic->getUUID().toString() == CHAR_UUID_SSID) {
ssid = strValue;
Serial.println("✓ SSID recebido via BLE: " + ssid);
}
else if (pCharacteristic->getUUID().toString() == CHAR_UUID_PASSWORD) {
password = strValue;
Serial.println("✓ Senha recebida via BLE: " + String(password.length()) + " caracteres");
}
else if (pCharacteristic->getUUID().toString() == CHAR_UUID_PRINTER) {
printer_bluetooth_name = strValue;
Serial.println("✓ Impressora recebida via BLE: " + printer_bluetooth_name);
}
// Verifica se todas as configurações foram recebidas
if (ssid.length() > 0 && password.length() > 0 && printer_bluetooth_name.length() > 0) {
String status = "OK|SSID:" + ssid + "|Printer:" + printer_bluetooth_name;
pCharStatus->setValue(status.c_str());
pCharStatus->notify();
Serial.println("\n✓ Todas as configurações recebidas!");
Serial.println("Salvando e reiniciando em 3 segundos...");
blinkLED(CRGB::Green, 3);
saveConfig();
delay(3000);
ESP.restart();
}
}
};
void initBLE() {
Serial.println("Inicializando BLE...");
BLEDevice::init("ATOM_S3_PRINTER");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
// Característica SSID
pCharSSID = pService->createCharacteristic(
CHAR_UUID_SSID,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
);
pCharSSID->setCallbacks(new MyCallbacks());
pCharSSID->setValue("Aguardando...");
// Característica Password
pCharPassword = pService->createCharacteristic(
CHAR_UUID_PASSWORD,
BLECharacteristic::PROPERTY_WRITE
);
pCharPassword->setCallbacks(new MyCallbacks());
// Característica Printer
pCharPrinter = pService->createCharacteristic(
CHAR_UUID_PRINTER,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE
);
pCharPrinter->setCallbacks(new MyCallbacks());
pCharPrinter->setValue("Aguardando...");
// Característica Status (somente leitura + notificação)
pCharStatus = pService->createCharacteristic(
CHAR_UUID_STATUS,
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_NOTIFY
);
pCharStatus->addDescriptor(new BLE2902());
pCharStatus->setValue("Aguardando configuracao");
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("✓ BLE iniciado! Nome: ATOM_S3_PRINTER");
Serial.println("Aguardando conexão BLE...");
setLED(CRGB::Cyan);
}
void stopBLE() {
if (pServer != nullptr) {
BLEDevice::deinit(true);
pServer = nullptr;
Serial.println("✓ BLE desativado");
}
}
// ===================================
// 6. FUNÇÕES DE CONFIGURAÇÃO
// ===================================
void loadConfig() {
preferences.begin("atomprint", false);
ssid = preferences.getString("ssid", "");
password = preferences.getString("password", "");
printer_bluetooth_name = preferences.getString("printer", "");
config_saved = preferences.getBool("saved", false);
preferences.end();
Serial.println("=== Configurações Carregadas ===");
Serial.println("SSID: " + (ssid.length() > 0 ? ssid : "Não configurado"));
Serial.println("Impressora BT: " + (printer_bluetooth_name.length() > 0 ? printer_bluetooth_name : "Não configurado"));
}
void saveConfig() {
preferences.begin("atomprint", false);
preferences.putString("ssid", ssid);
preferences.putString("password", password);
preferences.putString("printer", printer_bluetooth_name);
preferences.putBool("saved", true);
preferences.end();
config_saved = true;
Serial.println("\n✓ Configurações salvas com sucesso!");
blinkLED(CRGB::Green, 3);
}
void resetConfig() {
preferences.begin("atomprint", false);
preferences.clear();
preferences.end();
ssid = "";
password = "";
printer_bluetooth_name = "";
config_saved = false;
Serial.println("\n✓ Configurações resetadas!");
blinkLED(CRGB::Red, 5);
delay(1000);
ESP.restart();
}
// ===================================
// 7. CONFIGURAÇÃO VIA SERIAL MONITOR (USB)
// ===================================
void serialConfig() {
Serial.println("\n=== MODO CONFIGURAÇÃO ===");
Serial.println("Escolha o método:");
Serial.println("1. BLE - Configurar via Bluetooth (digite 'BLE')");
Serial.println("2. USB - Configurar via Serial Monitor");
Serial.println("\nComandos USB:");
Serial.println("WIFI:<SSID>:<SENHA>");
Serial.println("PRINTER:<NOME_BT>");
Serial.println("STATUS - Ver configurações");
Serial.println("SAVE - Salvar e reiniciar");
Serial.println("RESET - Apagar configurações");
Serial.println("BLE - Ativar modo BLE\n");
String tempSSID = ssid;
String tempPassword = password;
String tempPrinter = printer_bluetooth_name;
while (true) {
// LED azul piscando = modo config
blinkLED(CRGB::Blue, 1);
if (Serial.available()) {
String command = Serial.readStringUntil('\n');
command.trim();
Serial.println("Comando recebido: " + command);
if (command.startsWith("WIFI:")) {
int firstColon = command.indexOf(':', 5);
if (firstColon > 0) {
tempSSID = command.substring(5, firstColon);
tempPassword = command.substring(firstColon + 1);
Serial.println("✓ WiFi configurado:");
Serial.println(" SSID: " + tempSSID);
Serial.println(" Senha: " + String(tempPassword.length()) + " caracteres");
} else {
Serial.println("✗ Formato incorreto! Use: WIFI:<SSID>:<SENHA>");
}
}
else if (command.startsWith("PRINTER:")) {
tempPrinter = command.substring(8);
tempPrinter.trim();
Serial.println("✓ Impressora configurada: " + tempPrinter);
}
else if (command == "STATUS") {
Serial.println("\n--- Configurações Atuais ---");
Serial.println("SSID: " + (tempSSID.length() > 0 ? tempSSID : String("Não configurado")));
Serial.println("Senha: " + (tempPassword.length() > 0 ? String("***") : String("Não configurado")));
Serial.println("Impressora: " + (tempPrinter.length() > 0 ? tempPrinter : String("Não configurado")));
Serial.println("----------------------------\n");
}
else if (command == "SAVE") {
if (tempSSID.length() > 0 && tempPassword.length() > 0 && tempPrinter.length() > 0) {
ssid = tempSSID;
password = tempPassword;
printer_bluetooth_name = tempPrinter;
saveConfig();
Serial.println("\n✓ Configurações salvas!");
Serial.println("Reiniciando...");
delay(2000);
ESP.restart();
} else {
Serial.println("✗ Configure WiFi e Impressora antes de salvar!");
}
}
else if (command == "RESET") {
Serial.println("Resetando configurações...");
delay(1000);
resetConfig();
}
else if (command == "BLE") {
Serial.println("\n🔵 Ativando modo BLE...");
Serial.println("Use um app BLE (nRF Connect, Serial Bluetooth Terminal)");
Serial.println("Procure por: ATOM_S3_PRINTER");
Serial.println("\nCaracterísticas:");
Serial.println("- SSID: " + String(CHAR_UUID_SSID));
Serial.println("- Password: " + String(CHAR_UUID_PASSWORD));
Serial.println("- Printer: " + String(CHAR_UUID_PRINTER));
Serial.println("- Status: " + String(CHAR_UUID_STATUS));
initBLE();
ble_config_mode = true;
// Aguarda configuração via BLE
while (ble_config_mode) {
delay(100);
// Se recebeu todas as configs, o callback irá reiniciar
}
}
else {
Serial.println("✗ Comando desconhecido: " + command);
}
}
delay(100);
}
}
// ===================================
// 8. FUNÇÕES DE REDE
// ===================================
void connectWiFi() {
Serial.print("Conectando ao WiFi: ");
Serial.println(ssid);
setLED(CRGB::Yellow);
WiFi.begin(ssid.c_str(), password.c_str());
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n✓ WiFi Conectado!");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
setLED(CRGB::Green);
delay(1000);
} else {
Serial.println("\n✗ Falha ao conectar WiFi!");
setLED(CRGB::Red);
delay(2000);
}
}
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("Conectando ao MQTT...");
setLED(CRGB::Yellow);
if (client.connect(mqtt_client_id)) {
Serial.println(" ✓ Conectado!");
client.subscribe(mqtt_topic_sub);
Serial.println("Inscrito no tópico: " + String(mqtt_topic_sub));
setLED(CRGB::Green);
delay(500);
} else {
Serial.print(" ✗ Falha (rc=");
Serial.print(client.state());
Serial.println(") Tentando novamente em 5s...");
setLED(CRGB::Red);
delay(5000);
}
}
}
// ===================================
// 9. FUNÇÕES DA IMPRESSORA (UART)
// ===================================
void connectPrinter() {
Serial.print("Conectando à impressora via UART...");
setLED(CRGB::Yellow);
// Inicializa UART1 para comunicação com impressora Bluetooth
// RX=GPIO2, TX=GPIO1 (ajuste conforme seu módulo Bluetooth)
BTSerial.begin(9600, SERIAL_8N1, 2, 1);
printer_connected = true;
Serial.println("✓ UART inicializado!");
setLED(CRGB::Green);
delay(1000);
}
void printData(const char* data) {
if (printer_connected) {
BTSerial.print(data);
} else {
Serial.println("✗ Impressora não conectada!");
}
}
void processAndPrintOrder(JsonObject order) {
Serial.println("\n=== Processando Pedido ===");
setLED(CRGB::Yellow);
int pedido_id = order["pedido_id"] | 0;
int mesa = order["mesa"] | 0;
String cliente = order["cliente"] | "Cliente";
Serial.println("Pedido #" + String(pedido_id));
Serial.println("Mesa: " + String(mesa));
Serial.println("Cliente: " + cliente);
// Inicializa impressora
printData(INIT_PRINTER);
delay(100);
// Cabeçalho
printData(ALIGN_CENTER);
printData(DOUBLE_SIZE_ON);
printData("PEDIDO\n");
printData(NORMAL_SIZE);
printData("================================\n");
// Informações do pedido
printData(ALIGN_LEFT);
printData(BOLD_ON);
printData("Pedido #: ");
printData(BOLD_OFF);
printData(String(pedido_id).c_str());
printData("\n");
printData(BOLD_ON);
printData("Mesa: ");
printData(BOLD_OFF);
printData(String(mesa).c_str());
printData("\n");
printData(BOLD_ON);
printData("Cliente: ");
printData(BOLD_OFF);
printData(cliente.c_str());
printData("\n");
printData("================================\n");
// Itens
JsonArray itens = order["itens"];
if (itens) {
printData(BOLD_ON);
printData("ITENS:\n");
printData(BOLD_OFF);
for (JsonObject item : itens) {
int qtd = item["quantidade"] | 1;
String nome = item["nome"] | "Item";
float preco = item["preco"] | 0.0;
printData(String(qtd).c_str());
printData("x ");
printData(nome.c_str());
printData("\n");
Serial.println(" " + String(qtd) + "x " + nome);
}
}
printData("================================\n");
// Total
float total = order["total"] | 0.0;
printData(ALIGN_RIGHT);
printData(BOLD_ON);
printData("TOTAL: R$ ");
printData(String(total, 2).c_str());
printData("\n");
printData(BOLD_OFF);
printData(ALIGN_CENTER);
printData("\n\n");
printData(CUT_FULL);
Serial.println("✓ Pedido impresso!");
setLED(CRGB::Green);
delay(2000);
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.println("\n📩 Mensagem MQTT recebida!");
JsonDocument doc;
DeserializationError error = deserializeJson(doc, payload, length);
if (error) {
Serial.print("✗ Erro ao analisar JSON: ");
Serial.println(error.f_str());
setLED(CRGB::Red);
delay(1000);
return;
}
JsonObject root = doc.as<JsonObject>();
processAndPrintOrder(root);
}
// ===================================
// 10. SETUP E LOOP
// ===================================
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n╔════════════════════════════════════╗");
Serial.println("║ ATOM S3 LITE - SMART PRINTER ║");
Serial.println("╚════════════════════════════════════╝\n");
// Inicializa LED
FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(50);
setLED(CRGB::Blue);
// Inicializa botão
pinMode(BTN_PIN, INPUT_PULLUP);
// Verifica se botão está pressionado (reset config)
if (digitalRead(BTN_PIN) == LOW) {
Serial.println("⚠ Botão pressionado - Resetando configurações...");
blinkLED(CRGB::Red, 5);
resetConfig();
}
// Carrega configurações
loadConfig();
// Se não configurado, entra em modo config
if (!config_saved || ssid.length() == 0 || printer_bluetooth_name.length() == 0) {
Serial.println("⚠ Configuração necessária!");
serialConfig();
}
// Conecta WiFi
connectWiFi();
if (WiFi.status() != WL_CONNECTED) {
Serial.println("✗ WiFi não conectado. Entrando em modo configuração...");
serialConfig();
}
// Configura MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqttCallback);
// Conecta impressora
connectPrinter();
Serial.println("\n✓ Sistema pronto!");
Serial.println("Aguardando pedidos...\n");
setLED(CRGB::Green);
}
void loop() {
// Verifica botão (pressionar por 3s = reset)
static unsigned long btnPressTime = 0;
if (digitalRead(BTN_PIN) == LOW) {
if (btnPressTime == 0) {
btnPressTime = millis();
} else if (millis() - btnPressTime > 3000) {
Serial.println("\n⚠ Reset solicitado via botão!");
resetConfig();
}
} else {
btnPressTime = 0;
}
// Mantém WiFi conectado
if (WiFi.status() != WL_CONNECTED) {
Serial.println("✗ WiFi desconectado. Reconectando...");
setLED(CRGB::Red);
connectWiFi();
}
// Mantém MQTT conectado
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
// LED verde = aguardando
if (WiFi.status() == WL_CONNECTED && client.connected()) {
setLED(CRGB::Green);
}
delay(10);
}