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
Component | Specifications | Quantity | Purpose |
---|---|---|---|
Arduino Mega 2560 | 16MHz, 256KB Flash, 54 I/O pins | 1 | Main microcontroller |
Arduino Uno | Alternative if fewer I/O pins needed | 1 | Backup option |
Real-time Clock Module | DS3231 or similar | 1 | Timing and logging |
MicroSD Card Module | SPI interface | 1 | Data logging |
Power Electronics Components
Component | Specifications | Quantity | Purpose |
---|---|---|---|
MOSFETs | IRF3205 or equivalent, 55V/110A | 6-12 | Power switching |
Gate Drivers | IR2110 or similar | 3-6 | MOSFET control |
Current Sensors | ACS712 30A modules | 2-3 | Current monitoring |
Voltage Dividers | Precision resistors | Multiple | Voltage sensing |
Capacitors | Various values, 63V+ rating | Multiple | Filtering and decoupling |
Inductors | 100µH-1mH power inductors | 2-4 | Current smoothing |
Input and Control Components
Component | Specifications | Quantity | Purpose |
---|---|---|---|
Throttle Sensor | Hall effect or potentiometer | 1 | Speed input |
Brake Sensors | Magnetic or mechanical switches | 2 | Safety cutoffs |
Display Module | OLED or LCD, I2C interface | 1 | System status |
Rotary Encoder | With push button | 1 | Settings adjustment |
Temperature Sensors | DS18B20 waterproof probes | 2-3 | Thermal 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