Created
December 17, 2025 16:46
-
-
Save tejashah88/66b48f3384f0e718ca1bc430292c0d01 to your computer and use it in GitHub Desktop.
MECH 340 - DC Motor Testing code. Use slash commands to control the motor with /pwm for basic control and /step for acceleration and deceleration control based on loaded testing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ////////////////////// | |
| // Driver Libraries // | |
| ////////////////////// | |
| #include <Servo.h> | |
| #include "SparkFun_ACS37800_Arduino_Library.h" | |
| #include <Wire.h> | |
| ////////////////////////// | |
| // I/O port definitions // | |
| ////////////////////////// | |
| #define SERVO_PIN 5 | |
| ////////////////////////// | |
| // Hardware Peripherals // | |
| ////////////////////////// | |
| Servo motorEsc; | |
| ACS37800 powerSensor; | |
| /////////////////////////////// | |
| // Motor ESC PWM definitions // | |
| /////////////////////////////// | |
| const int P_LOW = 1000; | |
| const int P_NEUTRAL = 1500; | |
| const int P_HIGH = 2000; | |
| ////////////////////////////// | |
| // Initialization Constants // | |
| ////////////////////////////// | |
| #define THRUSTER_DELAY 3000 | |
| #define FULL_ESC_INIT 0 | |
| //////////////////// | |
| // Data variables // | |
| //////////////////// | |
| static unsigned long GLOBAL_START_TIME_MS; | |
| int escPwmValue; | |
| float measuredVolts, measuredAmps, measuredWatts; | |
| const int SAMPLE_INTERVAL_MS = 20; // 50 Hz logging | |
| const int AVERAGE_COUNT = 20; // Moving average window | |
| //////////////////////////////////// | |
| // Step PWM profile state machine // | |
| //////////////////////////////////// | |
| enum StepProfileState { IDLE, ACCEL_STEP, DECEL_STEP }; | |
| StepProfileState stepProfileState = IDLE; | |
| unsigned long stepStartMs = 0; | |
| int pwmAccel = P_NEUTRAL; | |
| int pwmDecel = P_NEUTRAL; | |
| unsigned long durationAccel = 0; | |
| unsigned long durationDecel = 0; | |
| //////////////////// | |
| // Main functions // | |
| //////////////////// | |
| void setup() { | |
| Serial.begin(115200); | |
| // Power sensor initialization | |
| Wire.begin(); | |
| while (!powerSensor.begin()) { | |
| Serial.println("ACS37800 not detected. Check connections and I2C address. Retrying..."); | |
| delay(1000); | |
| } | |
| powerSensor.setNumberOfSamples(1023, true); | |
| powerSensor.setBypassNenable(true, true); | |
| // ESC setup | |
| Serial.println("Setting up motor ESC parameters..."); | |
| motorEsc.attach(SERVO_PIN, P_LOW, P_HIGH); | |
| Serial.println("ESC Initialization: Setting motor ESC output to NEUTRAL..."); | |
| motorEsc.writeMicroseconds(P_NEUTRAL); | |
| delay(THRUSTER_DELAY); | |
| Serial.println("Ready to test DC motor! Press any key to continue."); | |
| while (Serial.available() == 0) {} | |
| while (Serial.available() > 0) Serial.read(); | |
| // CSV Header | |
| Serial.print("Timestamp [ms],"); | |
| Serial.print("PWM Signal [us],"); | |
| Serial.print("Measured Voltage [V],"); | |
| Serial.print("Measured Current [A],"); | |
| Serial.print("Measured Power [W]"); | |
| Serial.println(); | |
| // Set the starting PWM value to neutral | |
| escPwmValue = P_NEUTRAL; | |
| motorEsc.writeMicroseconds(escPwmValue); | |
| // Record when profiling has started | |
| GLOBAL_START_TIME_MS = millis(); | |
| } | |
| void loop() { | |
| static unsigned long lastSampleMs = 0; | |
| // ----------------------------- | |
| // Command parsing from Serial | |
| // ----------------------------- | |
| if (Serial.available()) { | |
| String userInput = Serial.readStringUntil('\n'); | |
| userInput.trim(); | |
| if (userInput.length() > 0 && userInput.startsWith("/")) { | |
| // NOTE: We use extra block scoping to isolate local variables for user input | |
| { | |
| int _pwmValue; | |
| if (sscanf(userInput.c_str(), "/pwm %d", &_pwmValue) == 1) { | |
| if (_pwmValue >= P_LOW && _pwmValue <= P_HIGH) { | |
| escPwmValue = _pwmValue; | |
| } | |
| } | |
| } | |
| { | |
| int _pwmAccel, _durAccel, _pwmDecel, _durDecel; | |
| if (sscanf(userInput.c_str(), "/step %d %d %d %d", &_pwmAccel, &_durAccel, &_pwmDecel, &_durDecel) == 4) { | |
| // Validate PWM values are within range | |
| if (_pwmAccel >= P_LOW && _pwmAccel <= P_HIGH && _pwmDecel >= P_LOW && _pwmDecel <= P_HIGH) { | |
| // Validate durations are positive | |
| if (_durAccel > 0 && _durDecel > 0) { | |
| // Store the step profile parameters | |
| pwmAccel = _pwmAccel; | |
| pwmDecel = _pwmDecel; | |
| durationAccel = _durAccel; // Duration in milliseconds | |
| durationDecel = _durDecel; | |
| // Start the step profile | |
| stepProfileState = ACCEL_STEP; | |
| stepStartMs = millis(); | |
| } | |
| } | |
| } | |
| } | |
| // Empty fallback - unrecognized commands are ignored | |
| } | |
| } | |
| // ----------------------------- | |
| // Step PWM profile state machine | |
| // ----------------------------- | |
| if (stepProfileState != IDLE) { | |
| unsigned long stepElapsedMs = millis() - stepStartMs; | |
| switch (stepProfileState) { | |
| case ACCEL_STEP: | |
| escPwmValue = pwmAccel; | |
| if (stepElapsedMs >= durationAccel) { | |
| // Transition to deceleration step | |
| stepProfileState = DECEL_STEP; | |
| stepStartMs = millis(); // Reset timer for next step | |
| } | |
| break; | |
| case DECEL_STEP: | |
| escPwmValue = pwmDecel; | |
| if (stepElapsedMs >= durationDecel) { | |
| // Return to neutral and complete profile | |
| stepProfileState = IDLE; | |
| escPwmValue = P_NEUTRAL; | |
| } | |
| break; | |
| case IDLE: | |
| // Should not reach here, but handle gracefully | |
| break; | |
| } | |
| } | |
| // Update ESC signal | |
| motorEsc.writeMicroseconds(escPwmValue); | |
| // ----------------------------- | |
| // Fixed logging interval | |
| // ----------------------------- | |
| unsigned long nowMs = millis(); | |
| if (nowMs - lastSampleMs < SAMPLE_INTERVAL_MS) return; | |
| lastSampleMs = nowMs; | |
| // ----------------------------- | |
| // Averaged power sensor measurement | |
| // ----------------------------- | |
| float vSum = 0; | |
| float iSum = 0; | |
| float pSum = 0; | |
| for (int k = 0; k < AVERAGE_COUNT; k++) { | |
| float v, i, p; | |
| powerSensor.readInstantaneous(&v, &i, &p); | |
| vSum += v; | |
| iSum += abs(i); // Remove negative noise | |
| pSum += p; | |
| delayMicroseconds(200); // Smooth noise | |
| } | |
| measuredVolts = vSum / AVERAGE_COUNT; | |
| measuredAmps = iSum / AVERAGE_COUNT; | |
| measuredWatts = pSum / AVERAGE_COUNT; | |
| // Write a new line to the CSV | |
| unsigned long timestampMs = millis() - GLOBAL_START_TIME_MS; | |
| Serial.print(timestampMs); | |
| Serial.print(","); | |
| Serial.print(escPwmValue); | |
| Serial.print(","); | |
| Serial.print(measuredVolts, 4); | |
| Serial.print(","); | |
| Serial.print(measuredAmps, 4); | |
| Serial.print(","); | |
| Serial.print(measuredWatts, 4); | |
| Serial.println(); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment