We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
So it's quite weird. When the web socket server starts. It reboots the arduino. Can this library work with ethernet?
I am using;
Arduino Nano ESP32 with Headers [ABX00083] - ESP32-S3, USB-C, Wi-Fi, Bluetooth, HID Support, MicroPython Compatible https://www.amazon.co.uk/dp/B0C947BHK5?ref=ppx_yo2ov_dt_b_fed_asin_title
and AZDelivery ENC28J60 Ethernet Shield LAN Network Module compatible with Arduino including E-Book! https://www.amazon.co.uk/dp/B07D8SV85Q?ref=ppx_yo2ov_dt_b_fed_asin_title
#include <EthernetENC.h> #include <WebSocketsServer.h> // Links2004 WebSocket library #include <ArduinoJson.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define LED_PIN 46 #define OLED_RESET -1 #define I2C_SDA 5 #define I2C_SCL 4 // ENC28J60 pins for SPI communication #define CS_PIN 7 #define MISO_PIN 12 #define MOSI_PIN 11 #define SCK_PIN 13 // Network configuration byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192, 168, 0, 177); IPAddress gateway(192, 168, 0, 1); IPAddress subnet(255, 255, 255, 0); WebSocketsServer webSocket = WebSocketsServer(80); Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); bool displayAvailable = false; #define MAX_CLIENTS 10 #define MAX_ROOMS 5 #define MAX_LOG_LINES 5 #define MAX_PLAYERS_PER_ROOM 4 // Message queue structure #define MAX_QUEUE_SIZE 10 struct QueuedMessage { String message; bool used; }; struct VirtualLockbox { bool isLocked; int deviceCount; bool isInitialized; }; struct Room { String roomId; VirtualLockbox lockbox; bool isActive; }; struct MessageQueue { QueuedMessage messages[MAX_QUEUE_SIZE]; int head; int tail; }; MessageQueue messageQueues[MAX_CLIENTS]; Room rooms[MAX_ROOMS]; // Circular buffer for log messages String logLines[MAX_LOG_LINES]; int currentLogLine = 0; struct WebSocketClient { uint8_t num; // WebSocket client number String clientId; String roomId; bool isHost; bool isExternal; bool isPlayer; String label; }; WebSocketClient wsClients[MAX_CLIENTS]; uint8_t clientIds[MAX_ROOMS][MAX_CLIENTS]; uint8_t clientCount[MAX_ROOMS] = {0}; uint8_t playerCount[MAX_ROOMS] = {0}; // Queue management functions void initQueue(int clientIndex) { messageQueues[clientIndex].head = 0; messageQueues[clientIndex].tail = 0; for (int i = 0; i < MAX_QUEUE_SIZE; i++) { messageQueues[clientIndex].messages[i].used = false; } } void queueMessage(int clientIndex, const String& message) { MessageQueue& queue = messageQueues[clientIndex]; int nextTail = (queue.tail + 1) % MAX_QUEUE_SIZE; if (nextTail != queue.head) { queue.messages[queue.tail].message = message; queue.messages[queue.tail].used = true; queue.tail = nextTail; } } void setLockboxLED(bool locked) { digitalWrite(LED_PIN, !locked ? HIGH : LOW); addLog(String("LED ") + (locked ? "ON" : "OFF")); } void sendQueuedMessages(uint8_t num) { int clientIndex = -1; for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num == num) { clientIndex = i; break; } } if (clientIndex != -1) { MessageQueue& queue = messageQueues[clientIndex]; while (queue.head != queue.tail) { if (queue.messages[queue.head].used) { webSocket.sendTXT(num, queue.messages[queue.head].message); queue.messages[queue.head].used = false; } queue.head = (queue.head + 1) % MAX_QUEUE_SIZE; } } } void addLog(const String& message) { Serial.println(message); logLines[currentLogLine] = message; currentLogLine = (currentLogLine + 1) % MAX_LOG_LINES; if (displayAvailable) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0, 0); display.print("IP: "); display.println(Ethernet.localIP()); display.drawLine(0, 9, SCREEN_WIDTH - 1, 9, WHITE); int y = 11; for (int i = 0; i < MAX_LOG_LINES; i++) { int index = (currentLogLine - 1 - i + MAX_LOG_LINES) % MAX_LOG_LINES; if (!logLines[index].isEmpty()) { display.setCursor(0, y); String truncated = logLines[index]; if (truncated.length() > 21) { truncated = truncated.substring(0, 18) + "..."; } display.println(truncated); y += 10; } } display.display(); } } int findRoomIndex(const String& roomId) { for (int i = 0; i < MAX_ROOMS; i++) { if (clientCount[i] > 0 && wsClients[clientIds[i][0]].roomId == roomId) { return i; } } for (int i = 0; i < MAX_ROOMS; i++) { if (clientCount[i] == 0) { return i; } } return -1; } void broadcastToRoom(uint8_t sender, String& message) { // Changed to non-const reference String roomId; for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num == sender) { roomId = wsClients[i].roomId; break; } } if (!roomId.isEmpty()) { int roomIndex = findRoomIndex(roomId); if (roomIndex != -1) { for (int i = 0; i < clientCount[roomIndex]; i++) { uint8_t clientNum = wsClients[clientIds[roomIndex][i]].num; webSocket.sendTXT(clientNum, message); } } } } void initializeRoomLockbox(const String& roomId) { int roomIndex = findRoomIndex(roomId); if (roomIndex != -1) { rooms[roomIndex].roomId = roomId; rooms[roomIndex].isActive = true; rooms[roomIndex].lockbox = {false, 0, true}; DynamicJsonDocument doc(256); doc["type"] = "lockbox_init"; String message; serializeJson(doc, message); for (int i = 0; i < clientCount[roomIndex]; i++) { uint8_t clientNum = wsClients[clientIds[roomIndex][i]].num; webSocket.sendTXT(clientNum, message); } addLog("Room " + roomId + " created with virtual lockbox"); } } void cleanupRoomLockbox(const String& roomId) { int roomIndex = findRoomIndex(roomId); if (roomIndex != -1) { rooms[roomIndex].isActive = false; rooms[roomIndex].lockbox = {false, 0, false}; setLockboxLED(false); addLog("Room " + roomId + " destroyed, lockbox cleaned up"); } } void handleLockboxMessage(uint8_t num, const String& type, const JsonObject& data) { String roomId; for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num == num) { roomId = wsClients[i].roomId; break; } } int roomIndex = findRoomIndex(roomId); if (roomIndex == -1 || !rooms[roomIndex].isActive) return; VirtualLockbox& lockbox = rooms[roomIndex].lockbox; if (type == "lockbox_init") { lockbox.deviceCount = 0; lockbox.isLocked = false; lockbox.isInitialized = true; setLockboxLED(false); DynamicJsonDocument doc(256); doc["type"] = "lockbox_init"; String message; serializeJson(doc, message); broadcastToRoom(num, message); addLog("Lockbox initialized in room: " + roomId); } else if (type == "lockbox_device_added") { if (!lockbox.isInitialized) return; lockbox.deviceCount++; DynamicJsonDocument doc(256); doc["type"] = "lockbox_device_added"; doc["data"]["deviceCount"] = lockbox.deviceCount; String message; serializeJson(doc, message); broadcastToRoom(num, message); addLog("Device added to lockbox in room " + roomId + ": " + String(lockbox.deviceCount)); if (lockbox.deviceCount >= 4 && !lockbox.isLocked) { lockbox.isLocked = true; setLockboxLED(true); DynamicJsonDocument lockDoc(256); lockDoc["type"] = "devices_locked"; lockDoc["data"]["locked"] = true; String lockMessage; serializeJson(lockDoc, lockMessage); broadcastToRoom(num, lockMessage); addLog("Lockbox locked in room: " + roomId); } } else if (type == "unlock_devices") { if (!lockbox.isInitialized) return; lockbox.isLocked = false; setLockboxLED(false); DynamicJsonDocument doc(256); doc["type"] = "unlock_devices"; String message; serializeJson(doc, message); broadcastToRoom(num, message); addLog("Lockbox unlocked in room: " + roomId); } } void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: { Serial.printf("[WebSocket] Client #%u Disconnected\n", num); String clientId; String roomId; bool wasHost = false; bool wasPlayer = false; int clientIndex = -1; // Find the disconnected client's information for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num == num) { clientId = wsClients[i].clientId; roomId = wsClients[i].roomId; wasHost = wsClients[i].isHost; wasPlayer = wsClients[i].isPlayer; clientIndex = i; break; } } if (clientIndex != -1) { int roomIndex = findRoomIndex(roomId); if (roomIndex != -1) { // Update player count if necessary if (wasPlayer) { playerCount[roomIndex]--; } // Remove client from room's client list for (int j = 0; j < clientCount[roomIndex]; j++) { if (clientIds[roomIndex][j] == clientIndex) { // Shift remaining clients for (int k = j; k < clientCount[roomIndex] - 1; k++) { clientIds[roomIndex][k] = clientIds[roomIndex][k + 1]; } clientCount[roomIndex]--; break; } } // If room is empty, clean up if (clientCount[roomIndex] == 0) { cleanupRoomLockbox(roomId); playerCount[roomIndex] = 0; addLog("Room " + roomId + " reset"); } else { // Notify remaining clients about disconnection DynamicJsonDocument doc(256); doc["type"] = "user_disconnected"; doc["data"]["clientId"] = clientId; doc["data"]["isPlayer"] = wasPlayer; doc["data"]["wasHost"] = wasHost; doc["data"]["playerCount"] = playerCount[roomIndex]; String message; serializeJson(doc, message); for (int j = 0; j < clientCount[roomIndex]; j++) { uint8_t targetNum = wsClients[clientIds[roomIndex][j]].num; webSocket.sendTXT(targetNum, message); } // Reassign host if necessary if (wasHost) { reassignHost(roomId); } } } // Clear client data wsClients[clientIndex] = {0, "", "", false, false, false, ""}; initQueue(clientIndex); addLog("Client disconnected: " + clientId); } break; } case WStype_CONNECTED: { Serial.printf("[WebSocket] Client #%u Connected\n", num); String url = String((char*)payload); String clientId = ""; String roomId = ""; bool isPlayer = false; String label = ""; // Extract parameters from connection URL int clientIdStart = url.indexOf("clientId="); int roomIdStart = url.indexOf("roomId="); int isPlayerStart = url.indexOf("isPlayer="); int labelStart = url.indexOf("label="); if (clientIdStart != -1) { clientIdStart += 9; // Length of "clientId=" int clientIdEnd = url.indexOf('&', clientIdStart); if (clientIdEnd == -1) clientIdEnd = url.length(); clientId = url.substring(clientIdStart, clientIdEnd); } if (roomIdStart != -1) { roomIdStart += 7; // Length of "roomId=" int roomIdEnd = url.indexOf('&', roomIdStart); if (roomIdEnd == -1) roomIdEnd = url.length(); roomId = url.substring(roomIdStart, roomIdEnd); } if (isPlayerStart != -1) { isPlayerStart += 9; // Length of "isPlayer=" int isPlayerEnd = url.indexOf('&', isPlayerStart); if (isPlayerEnd == -1) isPlayerEnd = url.length(); String isPlayerStr = url.substring(isPlayerStart, isPlayerEnd); isPlayer = (isPlayerStr == "true"); } if (labelStart != -1) { labelStart += 6; // Length of "label=" int labelEnd = url.indexOf('&', labelStart); if (labelEnd == -1) labelEnd = url.length(); label = url.substring(labelStart, labelEnd); } if (clientId.isEmpty()) { webSocket.disconnect(num); return; } // Check for reconnection bool isReconnection = false; int existingIndex = -1; for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num != 0 && wsClients[i].clientId == clientId) { existingIndex = i; isReconnection = true; break; } } // Handle reconnection if (isReconnection) { addLog("Client reconnecting: " + clientId); wsClients[existingIndex] = {0, "", "", false, false, false, ""}; } // Find empty slot for new connection int clientIndex = -1; for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num == 0) { clientIndex = i; break; } } if (clientIndex != -1) { int roomIndex = findRoomIndex(roomId); if (roomIndex == -1) { roomIndex = findRoomIndex(""); } if (roomIndex != -1) { if (clientCount[roomIndex] == 0) { initializeRoomLockbox(roomId); } // Check player limit if (isPlayer && playerCount[roomIndex] >= MAX_PLAYERS_PER_ROOM) { webSocket.disconnect(num); addLog("Room full - connection rejected"); return; } bool isHost = (isPlayer && playerCount[roomIndex] == 0); wsClients[clientIndex] = {num, clientId, roomId, isHost, false, isPlayer, label}; clientIds[roomIndex][clientCount[roomIndex]++] = clientIndex; if (isPlayer) { playerCount[roomIndex]++; addLog("Player count in room " + roomId + ": " + String(playerCount[roomIndex])); } initQueue(clientIndex); sendQueuedMessages(num); // Notify room about new connection DynamicJsonDocument doc(256); doc["type"] = "user_entered"; doc["data"]["clientId"] = clientId; doc["data"]["isPlayer"] = isPlayer; doc["data"]["playerCount"] = playerCount[roomIndex]; String message; serializeJson(doc, message); for (int i = 0; i < clientCount[roomIndex]; i++) { uint8_t targetNum = wsClients[clientIds[roomIndex][i]].num; if (targetNum != num) { // Don't send to the new client webSocket.sendTXT(targetNum, message); } } if (isHost) { notifyHostAssigned(roomId, clientId); } addLog(String(isPlayer ? "Player" : "Spectator") + " connected: " + clientId + (isReconnection ? " (reconnected)" : "")); } else { addLog("No room available"); webSocket.disconnect(num); } } else { addLog("Max clients reached"); webSocket.disconnect(num); } break; } case WStype_TEXT: { Serial.printf("[WebSocket] Received text from #%u\n", num); String message = String((char*)payload); DynamicJsonDocument doc(1024); DeserializationError error = deserializeJson(doc, message); if (error) { addLog("JSON parse failed"); return; } String type = doc["type"]; // Handle update_socket_client_id message if (type == "update_socket_client_id") { String oldClientId = doc["data"]["oldClientId"]; String newClientId = doc["data"]["newClientId"]; for (int i = 0; i < MAX_CLIENTS; i++) { if (wsClients[i].num == num && wsClients[i].clientId == oldClientId) { wsClients[i].clientId = newClientId; addLog("Updated client ID: " + oldClientId + " -> " + newClientId); break; } } return; } // Handle lockbox messages if (type == "lockbox_init" || type == "lockbox_device_added" || type == "unlock_devices") { handleLockboxMessage(num, type, doc["data"]); return; } // Broadcast other messages to room broadcastToRoom(num, message); break; } case WStype_BIN: case WStype_ERROR: case WStype_FRAGMENT_TEXT_START: case WStype_FRAGMENT_BIN_START: case WStype_FRAGMENT: case WStype_FRAGMENT_FIN: default: break; } } void notifyHostAssigned(const String& roomId, const String& clientId) { DynamicJsonDocument doc(256); doc["type"] = "host_assigned"; doc["data"]["isHost"] = true; doc["data"]["clientId"] = clientId; String message; serializeJson(doc, message); addLog("Host: " + clientId); int roomIndex = findRoomIndex(roomId); if (roomIndex != -1) { for (int i = 0; i < clientCount[roomIndex]; i++) { uint8_t clientNum = wsClients[clientIds[roomIndex][i]].num; webSocket.sendTXT(clientNum, message); } } } void reassignHost(const String& roomId) { int roomIndex = findRoomIndex(roomId); if (roomIndex == -1 || clientCount[roomIndex] == 0) { return; } // Find first non-host, non-external client for (int i = 0; i < clientCount[roomIndex]; i++) { uint8_t id = clientIds[roomIndex][i]; if (!wsClients[id].isHost && !wsClients[id].isExternal) { wsClients[id].isHost = true; notifyHostAssigned(roomId, wsClients[id].clientId); addLog("New host assigned: " + wsClients[id].clientId); break; } } } bool parseUrlParams(const String& url, String& clientId, String& roomId, bool& isPlayer) { // Example URL format: /ws?clientId=123&roomId=456&isPlayer=true int clientIdStart = url.indexOf("clientId="); int roomIdStart = url.indexOf("roomId="); int isPlayerStart = url.indexOf("isPlayer="); if (clientIdStart == -1 || roomIdStart == -1) { return false; } clientIdStart += 9; // Length of "clientId=" int clientIdEnd = url.indexOf('&', clientIdStart); if (clientIdEnd == -1) clientIdEnd = url.length(); clientId = url.substring(clientIdStart, clientIdEnd); roomIdStart += 7; // Length of "roomId=" int roomIdEnd = url.indexOf('&', roomIdStart); if (roomIdEnd == -1) roomIdEnd = url.length(); roomId = url.substring(roomIdStart, roomIdEnd); if (isPlayerStart != -1) { isPlayerStart += 9; // Length of "isPlayer=" int isPlayerEnd = url.indexOf('&', isPlayerStart); if (isPlayerEnd == -1) isPlayerEnd = url.length(); String isPlayerStr = url.substring(isPlayerStart, isPlayerEnd); isPlayer = (isPlayerStr == "true"); } else { isPlayer = false; } return true; } bool initializeDisplay() { Serial.println("Attempting to initialize OLED display..."); Wire.begin(I2C_SDA, I2C_SCL); Wire.beginTransmission(0x3C); if (Wire.endTransmission() != 0) { Serial.println("Display not found on I2C bus"); return false; } if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("SSD1306 allocation failed"); return false; } display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); display.setCursor(0,0); display.println("Initializing..."); display.display(); Serial.println("OLED display initialized successfully"); return true; } bool initializeEthernet() { Serial.println("Initializing Ethernet..."); SPI.begin(SCK_PIN, MISO_PIN, MOSI_PIN, CS_PIN); Ethernet.init(CS_PIN); if (Ethernet.begin(mac)) { Serial.print("DHCP assigned IP: "); Serial.println(Ethernet.localIP()); return true; } else { Serial.println("DHCP failed, trying static IP..."); Ethernet.begin(mac, ip, gateway, subnet); if (Ethernet.hardwareStatus() == EthernetNoHardware) { Serial.println("Ethernet shield not found"); return false; } if (Ethernet.linkStatus() == LinkOFF) { Serial.println("Ethernet cable is not connected"); return false; } Serial.print("Static IP: "); Serial.println(Ethernet.localIP()); return true; } } void setup() { Serial.begin(115200); Serial.println("Starting up..."); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, HIGH); // Initialize rooms for (int i = 0; i < MAX_ROOMS; i++) { rooms[i].isActive = false; rooms[i].lockbox = {false, 0, false}; } // Initialize client array for (int i = 0; i < MAX_CLIENTS; i++) { wsClients[i] = {0, "", "", false, false, false, ""}; initQueue(i); } // Initialize display displayAvailable = initializeDisplay(); if (!displayAvailable) { Serial.println("WARNING: Display initialization failed - continuing without display"); } // Initialize Ethernet with verification if (!initializeEthernet()) { Serial.println("Failed to initialize Ethernet"); digitalWrite(LED_PIN, LOW); return; } // Wait for Ethernet to be fully ready delay(1000); // Verify Ethernet status before starting WebSocket if (Ethernet.linkStatus() == LinkON && Ethernet.localIP()) { Serial.println("Ethernet is ready. Starting WebSocket server..."); // Initialize WebSocket server webSocket.begin(); webSocket.onEvent(webSocketEvent); addLog("WebSocket Server Ready"); addLog("IP: " + Ethernet.localIP().toString()); digitalWrite(LED_PIN, HIGH); // Indicate successful setup } else { Serial.println("Ethernet not ready. Cannot start WebSocket server."); digitalWrite(LED_PIN, LOW); } } // Modified loop with debug prints and state tracking void loop() { static unsigned long lastDebugPrint = 0; static bool wsActive = false; static int loopCount = 0; // Basic loop counter for debugging loopCount++; // Print debug info every 5 seconds if (millis() - lastDebugPrint >= 5000) { Serial.printf("Loop count: %d, Ethernet status: %s, IP: %s\n", loopCount, Ethernet.linkStatus() == LinkON ? "ON" : "OFF", Ethernet.localIP().toString().c_str()); lastDebugPrint = millis(); loopCount = 0; } // Check Ethernet status if (Ethernet.linkStatus() == LinkON && Ethernet.localIP()) { if (!wsActive) { Serial.println("Ethernet is connected, starting WebSocket server"); webSocket.begin(); webSocket.onEvent(webSocketEvent); wsActive = true; } // Only run WebSocket loop if we're properly connected webSocket.loop(); } else { if (wsActive) { Serial.println("Ethernet disconnected, stopping WebSocket server"); wsActive = false; } } // Maintain Ethernet connection Ethernet.maintain(); // Monitor display connection static unsigned long lastDisplayCheck = 0; if (millis() - lastDisplayCheck >= 30000) { // Check every 30 seconds lastDisplayCheck = millis(); if (!displayAvailable) { Wire.beginTransmission(0x3C); if (Wire.endTransmission() == 0) { displayAvailable = initializeDisplay(); if (displayAvailable) { addLog("Display connected"); } } } else { Wire.beginTransmission(0x3C); if (Wire.endTransmission() != 0) { displayAvailable = false; Serial.println("Display disconnected"); } } } // Small delay to prevent tight looping delay(1); }
The text was updated successfully, but these errors were encountered:
No branches or pull requests
So it's quite weird. When the web socket server starts. It reboots the arduino. Can this library work with ethernet?
I am using;
Arduino Nano ESP32 with Headers [ABX00083] - ESP32-S3, USB-C, Wi-Fi, Bluetooth, HID Support, MicroPython Compatible
https://www.amazon.co.uk/dp/B0C947BHK5?ref=ppx_yo2ov_dt_b_fed_asin_title
and
AZDelivery ENC28J60 Ethernet Shield LAN Network Module compatible with Arduino including E-Book!
https://www.amazon.co.uk/dp/B07D8SV85Q?ref=ppx_yo2ov_dt_b_fed_asin_title
The text was updated successfully, but these errors were encountered: