Tuesday, August 19, 2025

How to make an Automatic Speed Controller for Electric Bike using Arduino?

Electric bikes have revolutionized personal transportation by combining the convenience of traditional bicycles with the power of electric motors. One of the most critical components of an e-bike is the speed controller, which manages the power delivery from the battery to the motor. While commercial speed controllers are readily available, building your own automatic speed controller using Arduino offers greater customization, cost-effectiveness, and learning opportunities for electronics enthusiasts and DIY builders.

An automatic speed controller serves as the brain of your electric bike's propulsion system, regulating motor speed based on various inputs such as throttle position, pedal assist sensors, and safety parameters. By leveraging Arduino's versatility and programmability, you can create a sophisticated control system that rivals commercial alternatives while maintaining full control over its functionality and features.

This comprehensive guide will walk you through the entire process of designing, building, and programming an automatic speed controller for your electric bike using Arduino. We'll cover everything from understanding the fundamental principles to implementing advanced features like regenerative braking, multiple riding modes, and safety systems.

Understanding Electric Bike Speed Controllers

Basic Principles of Speed Control

Electric bike speed controllers work by modulating the power delivered to the motor through a technique called Pulse Width Modulation (PWM). This method rapidly switches the power on and off at high frequencies, effectively controlling the average voltage and current supplied to the motor. The duty cycle of these pulses determines the motor's speed – a higher duty cycle results in more power and faster rotation.

The controller acts as an intermediary between the battery pack and the motor, converting the DC voltage from the battery into a controlled output that matches the motor's requirements. Modern electric bike motors typically operate on 24V, 36V, or 48V systems, with power ratings ranging from 250W to 1500W or more.

Types of Electric Bike Motors

Understanding your motor type is crucial for designing an effective speed controller. The three main types of motors used in electric bikes are:

Brushed DC Motors: These traditional motors use physical brushes to commutate the windings. They're simple to control but require regular maintenance and have lower efficiency compared to brushless alternatives.

Brushless DC (BLDC) Motors: These motors use electronic commutation instead of physical brushes, resulting in higher efficiency, longer lifespan, and quieter operation. However, they require more complex control algorithms.

Hub Motors: These are typically brushless motors integrated directly into the wheel hub. They can be either direct drive or geared, with geared motors providing higher torque at lower speeds.

Control Methods and Algorithms

Arduino-based speed controllers can implement various control methods depending on the motor type and desired performance characteristics. The most common approaches include:

Open-Loop Control: This simple method controls motor speed based solely on throttle input without feedback from the motor's actual speed or position. While easy to implement, it may not provide optimal performance under varying load conditions.

Closed-Loop Control: This advanced method uses feedback sensors to monitor actual motor performance and adjust control signals accordingly. This results in more precise speed control and better efficiency.

Sensor-based Control: For BLDC motors, Hall effect sensors or encoders provide position feedback, enabling more sophisticated control algorithms and features like regenerative braking.

Required Components and Materials

Building an Arduino-based speed controller requires careful selection of components to ensure reliable operation and adequate performance. Here's a comprehensive list of required materials organized by category:

Core Processing Components

ComponentSpecificationsQuantityPurpose
Arduino Mega 256016MHz, 256KB Flash, 54 I/O pins1Main microcontroller
Arduino UnoAlternative if fewer I/O pins needed1Backup option
Real-time Clock ModuleDS3231 or similar1Timing and logging
MicroSD Card ModuleSPI interface1Data logging

Power Electronics Components

ComponentSpecificationsQuantityPurpose
MOSFETsIRF3205 or equivalent, 55V/110A6-12Power switching
Gate DriversIR2110 or similar3-6MOSFET control
Current SensorsACS712 30A modules2-3Current monitoring
Voltage DividersPrecision resistorsMultipleVoltage sensing
CapacitorsVarious values, 63V+ ratingMultipleFiltering and decoupling
Inductors100µH-1mH power inductors2-4Current smoothing

Input and Control Components

ComponentSpecificationsQuantityPurpose
Throttle SensorHall effect or potentiometer1Speed input
Brake SensorsMagnetic or mechanical switches2Safety cutoffs
Display ModuleOLED or LCD, I2C interface1System status
Rotary EncoderWith push button1Settings adjustment
Temperature SensorsDS18B20 waterproof probes2-3Thermal monitoring

Safety and Protection Components

Proper protection is essential for safe operation of high-current electric bike systems. The following components provide multiple layers of safety:

Fuses and Circuit Breakers: High-current automotive fuses rated for your system voltage and maximum current, typically 30-60A for most e-bike applications.

Voltage Protection: Over-voltage and under-voltage protection circuits prevent damage from battery charging/discharging extremes.

Thermal Protection: Temperature monitoring and automatic shutdown capabilities protect against overheating.

Emergency Shutdown: Multiple redundant safety switches and emergency stop mechanisms.

Circuit Design and Schematic

The circuit design for an Arduino-based electric bike speed controller involves several interconnected subsystems, each responsible for specific functions. Understanding these subsystems and their interactions is crucial for successful implementation.

Power Supply Design

The power supply subsystem must provide stable, clean power to the Arduino and associated electronics while operating in the harsh environment of an electric bike. The design typically includes multiple voltage rails:

48V/36V/24V Main Rail: Direct connection to the battery pack, used for motor power delivery. This rail requires robust filtering and protection against voltage spikes and reverse polarity.

12V Auxiliary Rail: Created using a DC-DC converter from the main rail, this powers gate drivers and other medium-power components. The converter should be isolated and capable of handling the full voltage range of your battery pack.

5V Logic Rail: Generated from the 12V rail using a linear or switching regulator, this powers the Arduino and digital logic components. Clean, stable 5V is critical for reliable microcontroller operation.

3.3V Sensor Rail: Some sensors require 3.3V operation. This can be generated on the Arduino board or using an external regulator for better noise performance.

MOSFET Driver Circuit Design

The MOSFET driver circuit is the heart of the power electronics, translating low-voltage control signals from the Arduino into high-current switching actions. For a three-phase BLDC motor controller, you'll need six MOSFETs arranged in three half-bridge configurations.

Each half-bridge consists of a high-side and low-side MOSFET. The high-side MOSFET connects the motor winding to the positive battery terminal, while the low-side MOSFET connects it to ground. Proper gate drive circuitry ensures clean switching transitions and prevents shoot-through conditions that could damage the MOSFETs.

Gate drivers like the IR2110 provide the necessary voltage levels and drive current to rapidly switch the MOSFETs. These drivers include built-in dead-time generation, which creates a small delay between turning off one MOSFET and turning on its complement, preventing both MOSFETs in a half-bridge from conducting simultaneously.

Current Sensing and Feedback

Accurate current measurement is essential for implementing advanced control features and protecting the system from overcurrent conditions. The ACS712 current sensor modules provide isolated current measurement with good linearity and reasonable accuracy for this application.

Position the current sensors to monitor both the total battery current and individual phase currents. Battery current monitoring enables features like power limiting and energy consumption tracking, while phase current monitoring allows for more sophisticated motor control algorithms.

The current sensor outputs are analog voltages that require careful scaling and filtering before input to the Arduino's analog-to-digital converters. Use RC filters to reduce noise and ensure the voltage levels remain within the Arduino's 0-5V input range.

Sensor Integration

Multiple sensors provide feedback and monitoring capabilities essential for safe and efficient operation:

Hall Effect Sensors: For BLDC motors, three Hall sensors provide rotor position information. These sensors are typically built into the motor and provide digital outputs that change state as the rotor rotates.

Temperature Sensors: Monitor controller temperature, motor temperature, and battery temperature. DS18B20 sensors provide good accuracy and can be daisy-chained on a single digital pin.

Voltage Monitoring: Use precision voltage dividers to monitor battery voltage and individual cell voltages if using a multi-cell pack.

Speed Sensors: Reed switches or optical encoders on the wheel provide actual vehicle speed information for closed-loop control and display purposes.

Arduino Programming and Code Structure

The software running on the Arduino is responsible for coordinating all aspects of the speed controller's operation. A well-structured program ensures reliable operation, easy maintenance, and the ability to add new features over time.

Main Program Structure

The Arduino program should follow a modular structure with clearly defined functions for each major subsystem. Here's the recommended organization:

cpp

// Global variables and constants
#define MOTOR_POLES 14
#define MAX_RPM 1000
#define PWM_FREQUENCY 20000

// System state variables
volatile bool motorRunning = false;
volatile int throttleValue = 0;
volatile int motorSpeed = 0;
float batteryVoltage = 0.0;
float motorCurrent = 0.0;

// Interrupt service routines
void throttleISR() {
  // Handle throttle input changes
}

void hallSensorISR() {
  // Handle Hall sensor state changes
}

void setup() {
  // Initialize hardware
  initializePWM();
  initializeSensors();
  initializeDisplay();
  initializeInterrupts();
  
  Serial.begin(115200);
  Serial.println("E-bike Controller Starting...");
}

void loop() {
  // Main control loop
  readSensors();
  updateDisplay();
  calculateMotorControl();
  updatePWMOutputs();
  checkSafetyConditions();
  
  delay(10); // 100Hz update rate
}

PWM Generation and Motor Control

For brushless DC motor control, you need to generate six PWM signals with precise timing and dead-time insertion. The Arduino's hardware PWM capabilities can be configured for high-frequency operation:

cpp

void initializePWM() {
  // Configure Timer 1 for PWM generation
  TCCR1A = 0;
  TCCR1B = 0;
  ICR1 = 800; // 20kHz PWM frequency at 16MHz
  
  // Configure PWM mode
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM13) | (1 << WGM12);
  
  // Set prescaler
  TCCR1B |= (1 << CS10);
  
  // Enable PWM outputs
  TCCR1A |= (1 << COM1A1) | (1 << COM1B1);
}

void updatePWMOutputs() {
  if (motorRunning && throttleValue > 0) {
    // Calculate commutation sequence based on Hall sensor inputs
    int hallState = readHallSensors();
    int pwmDuty = map(throttleValue, 0, 1023, 0, ICR1);
    
    // Apply appropriate PWM pattern for current commutation state
    switch (hallState) {
      case 1: // 001
        OCR1A = pwmDuty;
        OCR1B = 0;
        // Set appropriate MOSFET states
        break;
      case 2: // 010
        OCR1A = 0;
        OCR1B = pwmDuty;
        break;
      // ... additional cases for all six states
    }
  } else {
    // Turn off all PWM outputs
    OCR1A = 0;
    OCR1B = 0;
  }
}

Sensor Reading and Processing

Efficient sensor reading is crucial for responsive control. Use interrupt-driven reading where possible and implement appropriate filtering for analog sensors:

cpp

void readSensors() {
  // Read throttle position
  int rawThrottle = analogRead(A0);
  throttleValue = constrain(map(rawThrottle, 100, 900, 0, 1023), 0, 1023);
  
  // Read battery voltage
  int rawVoltage = analogRead(A1);
  batteryVoltage = (rawVoltage * 5.0 / 1023.0) * 15.0; // Voltage divider factor
  
  // Read motor current
  int rawCurrent = analogRead(A2);
  motorCurrent = (rawCurrent - 512) * 30.0 / 1023.0; // ACS712 scaling
  
  // Apply filtering
  static float filteredCurrent = 0;
  filteredCurrent = 0.9 * filteredCurrent + 0.1 * motorCurrent;
  motorCurrent = filteredCurrent;
}

int readHallSensors() {
  int hallA = digitalRead(2);
  int hallB = digitalRead(3);
  int hallC = digitalRead(4);
  
  return (hallA << 2) | (hallB << 1) | hallC;
}

Safety Systems Implementation

Safety is paramount in electric bike controllers. Implement multiple layers of protection:

cpp

void checkSafetyConditions() {
  // Check emergency brake inputs
  if (digitalRead(BRAKE_PIN_1) || digitalRead(BRAKE_PIN_2)) {
    emergencyStop();
    return;
  }
  
  // Check overcurrent condition
  if (abs(motorCurrent) > MAX_CURRENT) {
    emergencyStop();
    Serial.println("OVERCURRENT PROTECTION ACTIVATED");
    return;
  }
  
  // Check overvoltage/undervoltage
  if (batteryVoltage > MAX_VOLTAGE || batteryVoltage < MIN_VOLTAGE) {
    emergencyStop();
    Serial.println("VOLTAGE OUT OF RANGE");
    return;
  }
  
  // Check controller temperature
  float temperature = readTemperature();
  if (temperature > MAX_TEMP) {
    emergencyStop();
    Serial.println("THERMAL PROTECTION ACTIVATED");
    return;
  }
}

void emergencyStop() {
  motorRunning = false;
  OCR1A = 0;
  OCR1B = 0;
  // Turn off all MOSFET gates
  digitalWrite(GATE_ENABLE_PIN, LOW);
}

Advanced Control Features

Modern electric bike controllers benefit from sophisticated control algorithms that improve performance, efficiency, and user experience. These advanced features set DIY controllers apart from basic commercial alternatives.

Regenerative Braking Implementation

Regenerative braking converts the motor's kinetic energy back into electrical energy during deceleration, extending battery range and providing additional braking force. Implementation requires careful coordination between the motor controller and brake sensors:

cpp

bool regenerativeBrakingEnabled = true;
int regenStrength = 30; // Percentage of maximum regen

void handleRegenerativeBraking() {
  if (!regenerativeBrakingEnabled) return;
  
  bool frontBrake = digitalRead(FRONT_BRAKE_PIN);
  bool rearBrake = digitalRead(REAR_BRAKE_PIN);
  
  if (frontBrake || rearBrake) {
    // Calculate regenerative braking strength based on speed
    float currentSpeed = getCurrentSpeed(); // From wheel sensor
    float regenCurrent = (currentSpeed / MAX_SPEED) * (regenStrength / 100.0) * MAX_REGEN_CURRENT;
    
    // Apply regenerative braking by reversing motor operation
    if (currentSpeed > 5.0) { // Only above minimum speed
      applyRegenerativeBraking(regenCurrent);
    }
  }
}

void applyRegenerativeBraking(float regenCurrent) {
  // Modify PWM patterns to create regenerative current flow
  // This requires sophisticated commutation control
  int hallState = readHallSensors();
  int regenPWM = (int)(regenCurrent / MAX_CURRENT * ICR1);
  
  // Apply braking commutation sequence (opposite to motoring)
  // Implementation depends on specific motor configuration
}

Multiple Riding Modes

Different riding conditions require different performance characteristics. Implementing selectable riding modes enhances versatility:

cpp

enum RidingMode {
  ECO_MODE = 0,
  NORMAL_MODE = 1,
  SPORT_MODE = 2,
  TURBO_MODE = 3
};

RidingMode currentMode = NORMAL_MODE;

struct ModeSettings {
  int maxPower;        // Percentage of total power
  int acceleration;    // Acceleration curve steepness
  bool regenEnabled;   // Regenerative braking availability
  int topSpeed;        // Maximum speed limit
};

ModeSettings modeConfigs[4] = {
  {40, 30, true, 20},   // Eco: 40% power, gentle acceleration, regen on, 20 km/h max
  {70, 50, true, 32},   // Normal: 70% power, moderate acceleration, regen on, 32 km/h max
  {90, 80, true, 45},   // Sport: 90% power, aggressive acceleration, regen on, 45 km/h max
  {100, 100, false, 50} // Turbo: 100% power, maximum acceleration, no regen, 50 km/h max
};

void applyRidingMode() {
  ModeSettings settings = modeConfigs[currentMode];
  
  // Scale throttle response based on mode
  int scaledThrottle = (throttleValue * settings.maxPower) / 100;
  
  // Apply acceleration curve
  static int lastThrottle = 0;
  int throttleDelta = scaledThrottle - lastThrottle;
  int maxDelta = (settings.acceleration * 10) / 100; // Limit change rate
  
  if (abs(throttleDelta) > maxDelta) {
    scaledThrottle = lastThrottle + (throttleDelta > 0 ? maxDelta : -maxDelta);
  }
  
  lastThrottle = scaledThrottle;
  
  // Enable/disable regenerative braking
  regenerativeBrakingEnabled = settings.regenEnabled;
  
  // Apply speed limiting
  float currentSpeed = getCurrentSpeed();
  if (currentSpeed > settings.topSpeed) {
    scaledThrottle = 0; // Cut power when speed limit exceeded
  }
  
  // Use scaled throttle for motor control
  calculateMotorControl(scaledThrottle);
}

Pedal Assist Integration

Pedal assist systems (PAS) provide motor assistance proportional to pedaling effort, creating a natural riding experience:

cpp

volatile int pedalCadence = 0;
volatile unsigned long lastPedalTime = 0;
int assistLevel = 3; // 1-5 assist levels

void pedalSensorISR() {
  unsigned long currentTime = micros();
  unsigned long timeDelta = currentTime - lastPedalTime;
  
  if (timeDelta > 100000) { // Debounce: minimum 100ms between pulses
    pedalCadence = 60000000 / timeDelta; // RPM calculation
    lastPedalTime = currentTime;
  }
}

void calculatePedalAssist() {
  static unsigned long lastCadenceUpdate = 0;
  
  // Reset cadence if no pedaling detected
  if (millis() - lastPedalTime > 3000) {
    pedalCadence = 0;
  }
  
  // Calculate assist power based on cadence and assist level
  int assistPower = 0;
  if (pedalCadence > 10) { // Minimum pedaling threshold
    float assistMultiplier = assistLevel * 0.2; // 20% per assist level
    assistPower = (int)(pedalCadence * assistMultiplier * 10);
    assistPower = constrain(assistPower, 0, 1023);
  }
  
  // Combine with throttle input
  int combinedThrottle = max(throttleValue, assistPower);
  calculateMotorControl(combinedThrottle);
}

Smart Battery Management

Intelligent battery management extends battery life and improves performance:

cpp

struct BatteryState {
  float voltage;
  float current;
  float temperature;
  int stateOfCharge; // Percentage
  bool balancing;
  unsigned long cycleCount;
};

BatteryState battery;

void updateBatteryManagement() {
  battery.voltage = readBatteryVoltage();
  battery.current = readBatteryCurrent();
  battery.temperature = readBatteryTemperature();
  
  // Calculate state of charge using voltage and current integration
  battery.stateOfCharge = calculateSOC();
  
  // Apply power limiting based on battery state
  if (battery.stateOfCharge < 20) {
    // Reduce maximum power in low battery condition
    applyPowerLimiting(0.7); // 70% of normal power
  } else if (battery.stateOfCharge < 10) {
    // Severe power reduction in critical battery condition
    applyPowerLimiting(0.4); // 40% of normal power
  }
  
  // Temperature-based protection
  if (battery.temperature > 45.0) {
    applyPowerLimiting(0.5); // Reduce power when battery is hot
  } else if (battery.temperature < 0.0) {
    applyPowerLimiting(0.6); // Reduce power when battery is cold
  }
}

void applyPowerLimiting(float limitFactor) {
  // Reduce maximum available power
  int limitedThrottle = (int)(throttleValue * limitFactor);
  calculateMotorControl(limitedThrottle);
}

User Interface and Display System

A comprehensive user interface provides riders with essential information and allows customization of controller settings. Modern e-bike controllers benefit from clear, informative displays that work well in various lighting conditions.

OLED Display Integration

OLED displays offer excellent visibility and low power consumption, making them ideal for e-bike applications:

cpp

#include <Wire.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void initializeDisplay() {
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("SSD1306 allocation failed");
    return;
  }
  
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
  display.display();
}

void updateMainDisplay() {
  display.clearDisplay();
  
  // Speed display (large font)
  display.setTextSize(3);
  display.setCursor(0, 0);
  display.print((int)getCurrentSpeed());
  display.setTextSize(1);
  display.print(" km/h");
  
  // Battery level indicator
  int batteryPercent = battery.stateOfCharge;
  display.setCursor(0, 35);
  display.print("Batt: ");
  display.print(batteryPercent);
  display.print("%");
  
  // Draw battery icon
  drawBatteryIcon(90, 35, batteryPercent);
  
  // Riding mode
  display.setCursor(0, 45);
  display.print("Mode: ");
  switch(currentMode) {
    case ECO_MODE: display.print("ECO"); break;
    case NORMAL_MODE: display.print("NORM"); break;
    case SPORT_MODE: display.print("SPORT"); break;
    case TURBO_MODE: display.print("TURBO"); break;
  }
  
  // Power output
  display.setCursor(0, 55);
  display.print("Power: ");
  display.print((int)(motorCurrent * batteryVoltage));
  display.print("W");
  
  display.display();
}

void drawBatteryIcon(int x, int y, int percent) {
  // Battery outline
  display.drawRect(x, y, 30, 12, WHITE);
  display.drawRect(x + 30, y + 3, 2, 6, WHITE);
  
  // Battery fill based on percentage
  int fillWidth = (28 * percent) / 100;
  display.fillRect(x + 1, y + 1, fillWidth, 10, WHITE);
}

Menu System Implementation

A hierarchical menu system allows users to adjust settings without requiring external programming tools:

cpp

enum MenuState {
  MAIN_DISPLAY,
  MAIN_MENU,
  SETTINGS_MENU,
  ASSIST_MENU,
  DISPLAY_MENU,
  INFO_MENU
};

MenuState currentMenuState = MAIN_DISPLAY;
int menuSelection = 0;
bool menuChanged = true;

void handleMenuNavigation() {
  static unsigned long lastButtonPress = 0;
  static bool buttonPressed = false;
  
  // Read rotary encoder and button
  int encoderChange = readRotaryEncoder();
  bool buttonState = !digitalRead(MENU_BUTTON_PIN);
  
  // Handle button press with debouncing
  if (buttonState && !buttonPressed && (millis() - lastButtonPress > 200)) {
    buttonPressed = true;
    lastButtonPress = millis();
    handleMenuSelect();
  } else if (!buttonState) {
    buttonPressed = false;
  }
  
  // Handle encoder rotation
  if (encoderChange != 0) {
    menuSelection += encoderChange;
    menuChanged = true;
  }
  
  // Update display if menu changed
  if (menuChanged) {
    updateMenuDisplay();
    menuChanged = false;
  }
}

void handleMenuSelect() {
  switch (currentMenuState) {
    case MAIN_DISPLAY:
      currentMenuState = MAIN_MENU;
      menuSelection = 0;
      break;
      
    case MAIN_MENU:
      switch (menuSelection % 4) {
        case 0: currentMenuState = SETTINGS_MENU; break;
        case 1: currentMenuState = ASSIST_MENU; break;
        case 2: currentMenuState = DISPLAY_MENU; break;
        case 3: currentMenuState = INFO_MENU; break;
      }
      menuSelection = 0;
      break;
      
    case SETTINGS_MENU:
      handleSettingsSelect();
      break;
      
    default:
      // Return to main display from any submenu
      currentMenuState = MAIN_DISPLAY;
      break;
  }
  menuChanged = true;
}

void updateMenuDisplay() {
  display.clearDisplay();
  display.setTextSize(1);
  
  switch (currentMenuState) {
    case MAIN_MENU:
      displayMainMenu();
      break;
    case SETTINGS_MENU:
      displaySettingsMenu();
      break;
    case ASSIST_MENU:
      displayAssistMenu();
      break;
    // ... other menu cases
  }
  
  display.display();
}

void displayMainMenu() {
  display.setCursor(0, 0);
  display.println("MAIN MENU");
  display.println();
  
  const char* menuItems[] = {"Settings", "Assist", "Display", "Info"};
  for (int i = 0; i < 4; i++) {
    if (i == (menuSelection % 4)) {
      display.print("> ");
    } else {
      display.print("  ");
    }
    display.println(menuItems[i]);
  }
}

Data Logging and Diagnostics

Comprehensive logging helps track performance and diagnose issues:

cpp

#include <SD.h>

File logFile;
unsigned long lastLogTime = 0;
const unsigned long LOG_INTERVAL = 1000; // Log every second

void initializeDataLogging() {
  if (!SD.begin(10)) {
    Serial.println("SD card initialization failed");
    return;
  }
  
  // Create new log file with timestamp
  char filename[20];
  sprintf(filename, "ride_%08lu.csv", millis());
  
  logFile = SD.open(filename, FILE_WRITE);
  if (logFile) {
    // Write CSV header
    logFile.println("Time,Speed,Current,Voltage,Power,Mode,Battery%,Temp");
    logFile.close();
  }
}

void logData() {
  if (millis() - lastLogTime < LOG_INTERVAL) return;
  
  logFile = SD.open("current_ride.csv", FILE_WRITE);
  if (logFile) {
    // Log timestamp
    logFile.print(millis());
    logFile.print(",");
    
    // Log speed
    logFile.print(getCurrentSpeed());
    logFile.print(",");
    
    // Log electrical parameters
    logFile.print(motorCurrent);
    logFile.print(",");
    logFile.print(batteryVoltage);
    logFile.print(",");
    logFile.print(motorCurrent * batteryVoltage);
    logFile.print(",");
    
    // Log system state
    logFile.print(currentMode);
    logFile.print(",");
    logFile.print(battery.stateOfCharge);
    logFile.print(",");
    logFile.println(readTemperature());
    
    logFile.close();
    lastLogTime = millis();
  }
}

void displayDiagnostics() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);
  
  display.println("DIAGNOSTICS");
  display.println();
  
  // System status
  display.print("Uptime: ");
  display.print(millis() / 1000);
  display.println("s");
  
  display.print("Hall: ");
  display.println(readHallSensors(), BIN);
  
  display.print("Throttle: ");
  display.println(throttleValue);
  
  display.print("Temp: ");
  display.print(readTemperature());
  display.println("C");
  
  display.print("Errors: ");
  display.println(getErrorCount());
  
  display.display();
}

Safety Systems and Protection Features

Safety is the most critical aspect of electric bike controller design. Multiple protection systems must work together to prevent accidents and component damage while maintaining reliable operation under all conditions.

Overcurrent and Overvoltage Protection

Current and voltage protection systems form the first line of defense against electrical faults:

cpp

// Protection thresholds
const float MAX_MOTOR_CURRENT = 30.0; // Amperes
const float MAX_BATTERY_VOLTAGE = 54.6; // For 48V system
const float MIN_BATTERY_VOLTAGE = 39.0; // For 48V system
const float OVERCURRENT_TIME_LIMIT = 10; // Seconds before shutdown

// Protection state variables
bool overcurrentProtectionActive = false;
unsigned long overcurrentStartTime = 0;
bool overvoltageProtectionActive = false;
bool undervoltageProtectionActive = false;

void checkElectricalProtection() {
  // Monitor motor current
  float absoluteCurrent = abs(motorCurrent);
  
  if (absoluteCurrent > MAX_MOTOR_CURRENT) {
    if (!overcurrentProtectionActive) {
      overcurrentProtectionActive = true;
      overcurrentStartTime = millis();
      Serial.println("WARNING: Overcurrent detected");
    }
    
    // Allow brief overcurrent for

No comments:

Post a Comment

Popular Post

Why customers prefer RayMing's PCB assembly service?

If you are looking for dedicated  PCB assembly  and prototyping services, consider the expertise and professionalism of high-end technician...