From eb8d925821e3f1c42cf9baf0d8e55fcdfe64f10f Mon Sep 17 00:00:00 2001 From: Muhammad Hazimi Bin Yusri <mhby1g21@soton.ac.uk> Date: Wed, 26 Mar 2025 11:14:05 +0000 Subject: [PATCH] integrated imu with wifi car --- wifi-integrated.ino | 629 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 629 insertions(+) create mode 100644 wifi-integrated.ino diff --git a/wifi-integrated.ino b/wifi-integrated.ino new file mode 100644 index 0000000..816008d --- /dev/null +++ b/wifi-integrated.ino @@ -0,0 +1,629 @@ +#include <WiFiS3.h> +#include <Wire.h> +#include "SparkFun_BMI270_Arduino_Library.h" + +// WiFi credentials +const char ssid[] = "SOTON-IoT"; +const char pass[] = "pUguZXWtBDzl"; +int status = WL_IDLE_STATUS; +unsigned long lastConnectionAttempt = 0; +const unsigned long connectionRetryInterval = 1000; // 1 second between reconnection attempts + +// Server settings +WiFiServer server(80); +WiFiClient client; + +// Motor control pins +// Motor A - Left side +const int ENA = 10; // PWM pin for left motor speed +const int IN1 = 9; // Direction control +const int IN2 = 8; // Direction control + +// Motor B - Right side +const int ENB = 5; // PWM speed control +const int IN3 = 7; // Direction control +const int IN4 = 6; // Direction control + +// LED indicators +const int WIFI_LED = 13; // Built-in LED for WiFi status +const int MOVING_LED = 12; // Optional LED for movement status + +// Motor speed settings +const int SPEED = 200; // Normal speed (0-255) +const int TURN_SPEED = 180; // Speed during turns + +// Movement state tracking +enum MovementState { + STATE_STOP, + STATE_FORWARD, + STATE_BACKWARD, + STATE_LEFT, + STATE_RIGHT +}; + +MovementState currentState = STATE_STOP; +unsigned long lastCommandTime = 0; +const unsigned long commandTimeout = 0; // Disabled by setting to 0 + +// PID control settings +float Kp = 1.5; // Proportional gain +float Ki = 0.1; // Integral gain +float Kd = 0.05; // Derivative gain + +// PID variables +float error = 0, lastError = 0, integral = 0; +int Direction = 0; +int leftSpeed = 0; +int rightSpeed = 0; +int baseSpeed = 200; // Base motor speed (0-255) + +// IMU setup +BMI270 imu; +uint8_t i2cAddress = BMI2_I2C_PRIM_ADDR; // 0x68 +bool imuConnected = false; + +// Simple watchdog implementation +unsigned long lastWatchdogTime = 0; +const unsigned long watchdogInterval = 1000; // Check every second + +// Command queue for reliability +String lastCommand = "stop"; +bool newCommandReceived = false; + +// Connection monitoring +unsigned long lastConnectionCheck = 0; +const unsigned long connectionCheckInterval = 5000; // Check connection every 5 seconds +bool hasValidIP = false; + +// IMU usage flag - can be toggled via commands +bool useIMU = true; + +// DNS settings to help with connectivity +IPAddress dns(8, 8, 8, 8); // Google DNS + +void setup() { + // Initialize serial + Serial.begin(9600); + Serial.println("EMG-controlled car with IMU stabilization starting up..."); + + // Initialize motor pins + pinMode(ENA, OUTPUT); + pinMode(IN1, OUTPUT); + pinMode(IN2, OUTPUT); + pinMode(ENB, OUTPUT); + pinMode(IN3, OUTPUT); + pinMode(IN4, OUTPUT); + + // Initialize LED indicators + pinMode(WIFI_LED, OUTPUT); + pinMode(MOVING_LED, OUTPUT); + + // Initialize motors to stopped state + stopMotors(); + digitalWrite(MOVING_LED, LOW); + + // Initialize I2C and IMU + Wire.begin(); + + // Try to initialize BMI270 + Serial.println("Connecting to BMI270 IMU..."); + if(imu.beginI2C(i2cAddress) == BMI2_OK) { + Serial.println("BMI270 connected successfully!"); + // Configure IMU + imu.setGyroODR(BMI2_GYR_ODR_100HZ); // 100 Hz update rate + imuConnected = true; + } else { + Serial.println("Warning: BMI270 not connected. Operating without IMU stabilization."); + imuConnected = false; + useIMU = false; + } + + // Connect to WiFi - try multiple times if needed + bool connected = false; + for (int attempt = 0; attempt < 3 && !connected; attempt++) { + Serial.print("WiFi connection attempt #"); + Serial.println(attempt + 1); + connected = connectToWiFi(); + if (!connected) { + delay(5000); // Wait between attempts + } + } + + // Initialize watchdog time + lastWatchdogTime = millis(); +} + +void loop() { + // Simple software watchdog + simpleWatchdog(); + + // Check WiFi connection periodically + unsigned long currentMillis = millis(); + if (currentMillis - lastConnectionCheck >= connectionCheckInterval) { + lastConnectionCheck = currentMillis; + checkWiFiConnection(); + } + + // Only handle client requests if we have a valid IP + if (hasValidIP) { + // Check for new HTTP connections + handleHTTPClient(); + + // Process any new commands + if (newCommandReceived) { + processCommand(lastCommand); + newCommandReceived = false; + } + } + + // Check for command timeout (safety feature) + if (commandTimeout > 0 && currentState != STATE_STOP && + (currentMillis - lastCommandTime >= commandTimeout)) { + Serial.println("Command timeout - stopping motors for safety"); + executeStop(); + } + + // Apply IMU-based PID control if moving and IMU is enabled + if (imuConnected && useIMU && (currentState == STATE_FORWARD || currentState == STATE_BACKWARD)) { + applyIMUControl(); + } +} + +void applyIMUControl() { + // Update IMU data + imu.getSensorData(); + + // Use gyro Z-axis (yaw) for direction control (degrees per second) + float gyroZ = imu.data.gyroZ; + + // Calculate error (desired yaw rate = 0 for straight) + error = 0 - gyroZ; + + // Update integral (dt = 10ms) + integral += error * 0.01; + integral = constrain(integral, -50, 50); // Limit windup + + // Calculate derivative + float derivative = (error - lastError) / 0.01; + + // PID output + float output = Kp * error + Ki * integral + Kd * derivative; + + // Adjust motor speeds based on current state + if (currentState == STATE_FORWARD) { + Direction = 1; + leftSpeed = baseSpeed - output; + rightSpeed = baseSpeed + output; + } else if (currentState == STATE_BACKWARD) { + Direction = -1; + leftSpeed = baseSpeed + output; + rightSpeed = baseSpeed - output; + } + + // Constrain speeds to valid PWM range + leftSpeed = constrain(leftSpeed, 0, 255); + rightSpeed = constrain(rightSpeed, 0, 255); + + // Apply speeds to motors + analogWrite(ENA, leftSpeed); + analogWrite(ENB, rightSpeed); + + // Store error for next iteration + lastError = error; + + // Small delay for timing consistency + delay(10); +} + +void simpleWatchdog() { + // Simple software watchdog instead of hardware watchdog + unsigned long currentMillis = millis(); + if (currentMillis - lastWatchdogTime >= watchdogInterval) { + lastWatchdogTime = currentMillis; + + // Report current state periodically + if (currentState != STATE_STOP) { + Serial.print("Currently "); + Serial.print(getStateString(currentState)); + Serial.print(" - IMU stabilization: "); + Serial.println(useIMU && imuConnected ? "ON" : "OFF"); + } + + // Periodically check IP validity if we think we're connected + if (WiFi.status() == WL_CONNECTED) { + IPAddress ip = WiFi.localIP(); + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + hasValidIP = false; + Serial.println("WARNING: No valid IP address obtained"); + } else { + if (!hasValidIP) { + // Just got a valid IP + hasValidIP = true; + server.begin(); + printWiFiStatus(); + } + } + } + } +} + +bool connectToWiFi() { + // Check for WiFi module + if (WiFi.status() == WL_NO_MODULE) { + Serial.println("Communication with WiFi module failed!"); + // Blink error code on LED + blinkLED(WIFI_LED, 5, 500); + return false; + } + + // Print firmware version + String fv = WiFi.firmwareVersion(); + Serial.print("Firmware version: "); + Serial.println(fv); + + // Set WiFi LED to indicate connecting + digitalWrite(WIFI_LED, HIGH); + + // Disconnect if already connected + WiFi.disconnect(); + delay(1000); + + // Configure WiFi to use Google DNS to help with IP acquisition + WiFi.setDNS(dns); + + // Attempt to connect to WiFi network + Serial.print("Attempting to connect to Network: "); + Serial.println(ssid); + + status = WiFi.begin(ssid, pass); + + // Wait for connection or timeout + int attempts = 0; + while (status != WL_CONNECTED && attempts < 30) { // 15 seconds timeout + delay(500); + Serial.print("."); + attempts++; + status = WiFi.status(); + digitalWrite(WIFI_LED, !digitalRead(WIFI_LED)); // Blink while connecting + } + + if (status == WL_CONNECTED) { + Serial.println("\nConnected to WiFi"); + digitalWrite(WIFI_LED, HIGH); // Solid on for connected + + // Check for valid IP address + IPAddress ip = WiFi.localIP(); + + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + Serial.println("Connected to WiFi but no valid IP address obtained. Will retry..."); + digitalWrite(WIFI_LED, LOW); + hasValidIP = false; + return false; + } + + hasValidIP = true; + printWiFiStatus(); + + server.begin(); + Serial.println("EMG-controlled car server started"); + return true; + } else { + Serial.println("\nFailed to connect to WiFi"); + digitalWrite(WIFI_LED, LOW); + hasValidIP = false; + return false; + } + + lastConnectionAttempt = millis(); +} + +void checkWiFiConnection() { + if (WiFi.status() != WL_CONNECTED || !hasValidIP) { + digitalWrite(WIFI_LED, LOW); + if (WiFi.status() != WL_CONNECTED) { + Serial.println("WiFi connection lost!"); + } else { + Serial.println("Connected but no valid IP address!"); + } + + // Only try reconnecting if enough time has passed since last attempt + unsigned long currentMillis = millis(); + if (currentMillis - lastConnectionAttempt >= connectionRetryInterval) { + connectToWiFi(); + lastConnectionAttempt = currentMillis; + } + } else { + digitalWrite(WIFI_LED, HIGH); // Ensure LED is on when connected + + // Double-check IP validity + IPAddress ip = WiFi.localIP(); + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + hasValidIP = false; + Serial.println("IP address became invalid!"); + } + } +} + +void handleHTTPClient() { + // Check if client has connected + client = server.available(); + + if (client) { + Serial.println("New client connected"); + + // HTTP request buffer + String currentLine = ""; + String httpRequest = ""; + boolean currentLineIsBlank = true; + unsigned long clientConnectTime = millis(); + + // Process request while client is connected or until timeout + while (client.connected() && (millis() - clientConnectTime < 3000)) { + if (client.available()) { + char c = client.read(); + httpRequest += c; + + // End of HTTP request line + if (c == '\n' && currentLineIsBlank) { + // Send HTTP response + sendHTTPResponse(); + break; + } + + // End of a line + if (c == '\n') { + currentLineIsBlank = true; + currentLine = ""; + } + else if (c != '\r') { + // Character in current line + currentLineIsBlank = false; + currentLine += c; + } + } + } + + // Parse command from HTTP request + if (httpRequest.indexOf("GET /") != -1) { + parseCommand(httpRequest); + } + + // Allow time for data to be sent + delay(10); + + // Close the connection + client.stop(); + Serial.println("Client disconnected"); + } +} + +void sendHTTPResponse() { + String response = "HTTP/1.1 200 OK\r\n"; + response += "Content-Type: application/json\r\n"; + response += "Connection: close\r\n\r\n"; + + // Create JSON response with current status + response += "{\r\n"; + response += " \"status\": \"ok\",\r\n"; + response += " \"movement\": \"" + getStateString(currentState) + "\",\r\n"; + response += " \"imu_enabled\": " + String((useIMU && imuConnected) ? "true" : "false") + ",\r\n"; + response += " \"command_received\": true,\r\n"; + response += " \"ip\": \"" + WiFi.localIP().toString() + "\"\r\n"; + response += "}\r\n"; + + client.print(response); +} + +String getStateString(MovementState state) { + switch(state) { + case STATE_STOP: return "stopped"; + case STATE_FORWARD: return "forward"; + case STATE_BACKWARD: return "backward"; + case STATE_LEFT: return "left"; + case STATE_RIGHT: return "right"; + default: return "unknown"; + } +} + +void parseCommand(String request) { + int start = request.indexOf("GET /") + 5; + int end = request.indexOf(" HTTP"); + + if (start != -1 && end != -1) { + String command = request.substring(start, end); + Serial.println("Received command: " + command); + + // Queue the command for processing + lastCommand = command; + newCommandReceived = true; + } +} + +void processCommand(String command) { + // Update last command time for timeout monitoring + lastCommandTime = millis(); + + // Process the command + if (command == "forward") { + executeForward(); + } + else if (command == "backward") { + executeBackward(); + } + else if (command == "left") { + executeLeft(); + } + else if (command == "right") { + executeRight(); + } + else if (command == "stop") { + executeStop(); + } + else if (command == "imu_on") { + // Enable IMU stabilization if available + if (imuConnected) { + useIMU = true; + Serial.println("IMU stabilization enabled"); + } else { + Serial.println("IMU not connected, cannot enable stabilization"); + } + } + else if (command == "imu_off") { + // Disable IMU stabilization + useIMU = false; + Serial.println("IMU stabilization disabled"); + } + else if (command == "ping") { + // Just for connection testing + Serial.println("Ping received - connection test successful"); + } + else if (command == "reconnect") { + // Force WiFi reconnection + Serial.println("Reconnect command received - restarting WiFi"); + hasValidIP = false; + connectToWiFi(); + } + else { + Serial.println("Unknown command: " + command); + } +} + +void executeForward() { + Serial.println("Moving forward" + String((useIMU && imuConnected) ? " with IMU stabilization" : "")); + + // Reset PID variables + error = 0; + lastError = 0; + integral = 0; + + // Motor A forward + digitalWrite(IN1, HIGH); + digitalWrite(IN2, LOW); + + // Motor B forward + digitalWrite(IN3, HIGH); + digitalWrite(IN4, LOW); + + // Set initial motor speeds + analogWrite(ENA, SPEED); + analogWrite(ENB, SPEED); + + currentState = STATE_FORWARD; + digitalWrite(MOVING_LED, HIGH); +} + +void executeBackward() { + Serial.println("Moving backward" + String((useIMU && imuConnected) ? " with IMU stabilization" : "")); + + // Reset PID variables + error = 0; + lastError = 0; + integral = 0; + + // Motor A backward + digitalWrite(IN1, LOW); + digitalWrite(IN2, HIGH); + + // Motor B backward + digitalWrite(IN3, LOW); + digitalWrite(IN4, HIGH); + + // Set initial motor speeds + analogWrite(ENA, SPEED); + analogWrite(ENB, SPEED); + + currentState = STATE_BACKWARD; + digitalWrite(MOVING_LED, HIGH); +} + +void executeLeft() { + Serial.println("Turning left"); + + // Motor A forward + digitalWrite(IN1, HIGH); + digitalWrite(IN2, LOW); + analogWrite(ENA, SPEED); + + // Motor B backward for sharp turn + digitalWrite(IN3, LOW); + digitalWrite(IN4, HIGH); + analogWrite(ENB, TURN_SPEED); + + currentState = STATE_LEFT; + digitalWrite(MOVING_LED, HIGH); +} + +void executeRight() { + Serial.println("Turning right"); + + // Motor A backward for sharp turn + digitalWrite(IN1, LOW); + digitalWrite(IN2, HIGH); + analogWrite(ENA, TURN_SPEED); + + // Motor B forward + digitalWrite(IN3, HIGH); + digitalWrite(IN4, LOW); + analogWrite(ENB, SPEED); + + currentState = STATE_RIGHT; + digitalWrite(MOVING_LED, HIGH); +} + +void executeStop() { + Serial.println("Stopping motors"); + + // Stop both motors + digitalWrite(IN1, LOW); + digitalWrite(IN2, LOW); + analogWrite(ENA, 0); + + digitalWrite(IN3, LOW); + digitalWrite(IN4, LOW); + analogWrite(ENB, 0); + + currentState = STATE_STOP; + digitalWrite(MOVING_LED, LOW); +} + +void stopMotors() { + // Stop both motors immediately (for initialization or emergencies) + digitalWrite(IN1, LOW); + digitalWrite(IN2, LOW); + analogWrite(ENA, 0); + + digitalWrite(IN3, LOW); + digitalWrite(IN4, LOW); + analogWrite(ENB, 0); + + currentState = STATE_STOP; +} + +void blinkLED(int pin, int times, int delayMs) { + for (int i = 0; i < times; i++) { + digitalWrite(pin, HIGH); + delay(delayMs); + digitalWrite(pin, LOW); + delay(delayMs); + } +} + +void printWiFiStatus() { + Serial.print("SSID: "); + Serial.println(WiFi.SSID()); + + IPAddress ip = WiFi.localIP(); + Serial.print("IP Address: "); + Serial.println(ip); + + long rssi = WiFi.RSSI(); + Serial.print("Signal strength (RSSI): "); + Serial.print(rssi); + Serial.println(" dBm"); + + Serial.println("To control this car, send HTTP requests to:"); + Serial.print("http://"); + Serial.print(ip); + Serial.println("/command"); + Serial.println("Where command is: forward, backward, left, right, stop, imu_on, imu_off, or reconnect"); +} \ No newline at end of file -- GitLab