Skip to content

Instantly share code, notes, and snippets.

@tejashah88
Created December 17, 2025 16:46
Show Gist options
  • Select an option

  • Save tejashah88/66b48f3384f0e718ca1bc430292c0d01 to your computer and use it in GitHub Desktop.

Select an option

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.
//////////////////////
// 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