Tom DeCricle
Published © GPL3+

Arduino Uno based Dynamometer with DSP

Digital Signal Processing with an Arduino Uno and controlling a DIY dynamometer to evaluate motors and generators

AdvancedShowcase (no instructions)Over 12 days894
Arduino Uno based Dynamometer with DSP

Things used in this project

Story

Read more

Schematics

Dynamometer Wiring Diagram

Code

Sensor Suite

C/C++
Code for deploying the sensor suite on the Dynamometer
/* DeLinometer Sensor Suite
 *  This program is for connecting to the Delinometer sensor
 *  array through a serial connection with an Arduino Uno.
 *  There are currently 3 functional sensors in the suite
 *  1) Tachometer - Functions by calculating the pulse length of the incoming signal from tachometer module. RPM is the output
 *  2) Thermocouple - Functions by setting up thermocouple object. A funtion call to thermocouple.readCelsius() gets data from Max6675 Thermocouple module through I2C
 *  3) Load Cell - Functions by setting up a scale object. A function call to 
 *      scale.begin(DOUT, CLK); scale.set_scale(); scale.tare(); initiates read sequence, 
 *      scale.set_scale(calibration_factor); scale.get_units()), 4) returns output
 */

// -----------Current Meter Variables -------------
#include "ACS712.h"
#define LoadCurrent A5
ACS712 sensor(ACS712_30A, LoadCurrent);



// -----------Voltage Meter Variables ----------------
#define MotorV A0
float DividerV = 0;
float DividerConst = 40.92;
float DividerVMap = 0;

// -----------Tachometer variables ----------
#define tachSignal 2
int rpm = 0;
float rpmFloat = 0;
float timePerRev = 0;
float timeArray[15] = {0, 0};
float rateOfChangeArray[15] = {0, 0};
int globali = 0;
int globalj = 1;
bool run1 = true;
///////////////////////////////////////////////////////////////////////////


// -----------Thermocouple variables
#include "max6675.h"

int thermoDO = 10;
int thermoCS = 11;
int thermoCLK = 12;
float temperature = 23.5;

MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
//////////////////////////////////////////////////////////////////////////


// -----------Load Cell variables
#include "HX711.h"

#define DOUT  9
#define CLK  8

HX711 scale;

float calibration_factor = -1721.68;
///////////////////////////////////////////////////////////////////////////////////

//------------- Setup necessary equipment
void setup() {
  Serial.begin(9600);
  pinMode(tachSignal, INPUT);   // Setting tachSignal as Input pin
  //Serial.print(0);
  //Serial.print(""); Serial.println(3500);
  delay(500);                                   // wait for MAX chip to stabilize
  sensor.calibrate();
  temperature = thermocouple.readCelsius();     // take first temperature reading
  scale.begin(DOUT, CLK);
  scale.set_scale();
  scale.tare(); //Reset the scale to 0

  long zero_factor = scale.read_average(); //Get a baseline reading
  //Serial.println(""); Serial.println("System - Ready");
}

//------------------Main Loop
void loop() {
  scale.set_scale(calibration_factor); 
  timePerRev = pulseIn(tachSignal, HIGH);
  float currentReading;
  currentReading = sensor.getCurrentDC();
  currentReading *= -1;
  dividerReading();
  //Serial.println(timePerRev);
  timeArray[globali] = timePerRev;
  rateOfChangeArray[globali] = timeArray[globali] - timeArray[globalj];
  //Serial.print("timePerRev = ");

  
  // This conditional statement is for catching output during first loop of data stream
  // If the rateOfChangeArray > 20k, the motor is accellerating and data is recorded.
  // After the first run, if the rateOfChangeArray < 55k, the signal is interpreted as valid 
  // Otherwise, the data is considered noise and not output by the system.
  // Values outside of specified range are not possible since the motor would be accellerating/decelerating at rates not possible by motor
  if (run1 == true) {
    if (rateOfChangeArray[globali] > 20000) {
      rpmFloat = (1 / timePerRev) * (1000000) * (60);
      rpm = round(rpmFloat);
      if ((rpm > 0) && (rpm < 3500)) {
        //Serial.print("RPM =, ");
        //Serial.print(temperature); Serial.print(", "); Serial.println(rpm);
        //Serial.println(timePerRev);
        Serial.print(millis()); Serial.print(", "); Serial.print((-1)*(scale.get_units()), 4); Serial.print(", "); Serial.print(thermocouple.readCelsius()); Serial.print(", "); Serial.print(rpm); Serial.print(", "); Serial.print(DividerVMap); Serial.print(", "); Serial.println(currentReading, 3);
        run1 = false;
      }
    }

  }

  else {
    //Serial.println(rateOfChangeArray[globali]);
    if (rateOfChangeArray[globali] < 55000) {
      rpmFloat = (1 / timePerRev) * (1000000) * (60);
      rpm = round(rpmFloat);
      if ((rpm > 0) && (rpm < 3700)) {
        //Serial.print("RPM =, ");
        //Serial.println(temperature); Serial.print(", ");
        Serial.print(millis()); Serial.print(", "); Serial.print((-1)*(scale.get_units()), 4); Serial.print(", "); Serial.print(thermocouple.readCelsius()); Serial.print(", "); Serial.print(rpm); Serial.print(", "); Serial.print(DividerVMap); Serial.print(", "); Serial.println(currentReading, 3);
        //Serial.println(timePerRev);
      }
    }
  }

  delay(200);
}

// --- Voltage Divider function
void dividerReading() {
  for (int i = 0; i < 10; i++) {
    DividerV += analogRead(MotorV);
    delayMicroseconds(10);
  }
  DividerV = DividerV / 10;
  DividerVMap = DividerV / DividerConst;
}

Dynamometer Machine Control

C/C++
This code is to control the motor on the Dynamometer and includes the Terminal based menu for running the machine
//Tom DeLine Motor Speed Controller for the DeLineDynamometer
//This is a motor speed controller to drive a 24V brushed DC Motor.


//--Global Variables used throughout the program
int PWMSignal = 3;          //Assigning the name PWMSignal to pin 3 of the Arduino
int tachSignal = 4;         //Assigning the name tachSignal to pin 2 of the Arduino
unsigned long RPM = 0;
unsigned long timePerRev = 0;
int POT = A0;               //Assigning the name POT to pin A0
int PWMSpeedInt = 0;        //Integer for the value given to the PWM rate (0-255)
int OperatorSelectInt;      //Integer to hold the value of the machine operator's input from menu selection
String OperatorSelectStr, PWMSpeedStr; //Strings for data input from serial console


///--------- Arduino Functions required (Setup & Loop)----------------
void setup() {
  pinMode(PWMSignal, OUTPUT); //Declaring PWMSignal pin as output
  pinMode(tachSignal, INPUT);       //Declaring tachSignal pin as input
  Serial.begin(9600);        //Setting the serial console operating speed
}

void loop() {
  PWMSpeedInt = 0;            //Makes sure that PWM signal is set to zero after each run
  Serial.println("\n");
  Serial.println("DeLine-Dynamometer Console, \nPlease select an Operating Mode\n");
  Mode();                     //Takes operator into menu selection tree
  PWMSpeedInt = 0;

}
///---------END Arduino Functions required (Setup & Loop)----------------


//------------------Dynamometer Operation Modes--------------------------
//1) Dynamometer Mode
//2) Debug Mode
void Mode() {
  Serial.println("1) Dyna      2) Debug");
  while (Serial.available() == 0) {};             //holds program at this line until operator input is entered
  OperatorSelectStr = Serial.readString();        //assigns serial string to variable
  OperatorSelectInt = OperatorSelectStr.toInt();  //converts string to integer for use in switch-case statement
  Serial.print("You Selected: ");
  Serial.println(OperatorSelectInt);
  switch (OperatorSelectInt) {                    //if OperatorSelectInt is 1, 2, default - go to specified menu item
    case 1 :
      Profile();                                  //enters Dynamometer mode an begins next tree menu item at pofile()
      break;
    case 2 :
      Debug();                                    //not used a the moment
      break;
    default :                                     //if none of the above is the case, default catches all other entries and program menu tree is restarted
      Serial.println("\nNot a valid Entry\n\n");
      break;
  }
}


//--------------Modes-------------------//
//  Two Modes
//    1) Profile Mode
//    2) Debug Mode
void Profile() {
  Serial.print("\nPlease select a Profile\n");
  Serial.println("1) Sweep      2) Max Speed    3) Speed Select   4) Potentiometer Mode");
  while (Serial.available() == 0) {};             //holds program at this line until operator input is entered
  OperatorSelectStr = Serial.readString();        //assigns serial string to variable
  OperatorSelectInt = OperatorSelectStr.toInt();  //converts string to integer for use in switch-case statement
  Serial.print("You Selected: ");
  Serial.println(OperatorSelectInt);
  Serial.print("\n");

  switch (OperatorSelectInt) {                  //if OperatorSelectInt is 1, 2, 3, 4, default - go to specified menu item
    case 1:
      Sweep();                                  //enters Sweep Menu and begins next tree
      break;
    case 2:
      MaxSpeed();                               //enters MaxSpeed Menu and begins next tree
      break;
    case 3:
      SpeedSelect();                            //enters Speed Select Menu and begins next tree
      break;
    case 4:
      POTMode();                                //enters potentiometer speed menu and begins next tree
      break;
    default :                                   //if none of the above is the case, default catches all other entries and program menu tree is restarted
      Serial.println("\nNot a valid Entry\nStarting Over\n");
      break;
  }
}
// Mode - Debug

void Debug() {
  Serial.println("\nIn Debug function\n");
  POTMode();
}
//----------------End Modes ------------------//


//----------------Dynamometer Speed Profiles--------------------------
// 1)Sweep
// 2)Max Speed
// 3)Speed Select
// 4)Potentiometer Operation
//----------------Profiles -------------------//
void Sweep() {
  Serial.println("Select an Option");
  Serial.println("1) Begin Test");
  while (Serial.available() == 0) {               //holds program at this line until operator input is entered
  };
  OperatorSelectStr = Serial.readString();        //assigns serial string to variable
  OperatorSelectInt = OperatorSelectStr.toInt();  //converts string to integer for use in switch-case statement

  PWMSpeedInt = 0;
  switch (OperatorSelectInt) {                    //if OperatorSelectInt is 1, default - go to specified menu item
    case 1 :                                      //Operator selected 1 to begin test

      //checking difference between the two
      //Loop for speeding motor up to max speed
      //loop adds 15 to PWMSPeedInt (0+15=15)
      //then checks if its value is less than 255 (max PWM value)
      //if it is <= PWMSpeedInt, increment the PWMSpeedInt by 1 and enter loop
      //repeat until loop PWMSPeedInt > 255
      for (PWMSpeedInt + 50; PWMSpeedInt <= 255; PWMSpeedInt+=10) {
        analogWrite(PWMSignal, PWMSpeedInt);
        delay(5000);
      }

      //Loop for slowing the motor down.
      //loop starts at PWMSpeedInt value
      //checks if the value is still greater than zero
      //if true, subtract 1 from PWMSpeedInt and enter the loop
      for (PWMSpeedInt; PWMSpeedInt >= 0; PWMSpeedInt--) {
        analogWrite(PWMSignal, PWMSpeedInt);
        delay(25);
      }
      PWMSpeedInt = 0;
      break;
    default :                                     //if none of the above is the case, default catches all other entries and program menu tree is restarted
      Serial.println("\nNot a valid Entry\nStarting Over\n");
      break;
  }
}

void MaxSpeed() {
  Serial.println("Select an Option");
  Serial.println("1) Begin Test");
  while (Serial.available() == 0) {               //holds program at this line until operator input is entered
  };
  OperatorSelectStr = Serial.readString();        //assigns serial string to variable
  OperatorSelectInt = OperatorSelectStr.toInt();  //converts string to integer for use in switch-case statement

  PWMSpeedInt = 0;
  switch (OperatorSelectInt) {                    //if OperatorSelectInt is 1, default - go to specified menu item
    case 1 :                                      //Operator selected 1 to begin test

      //checking difference between the two
      //Loop for speeding motor up to max speed
      //loop adds 15 to PWMSPeedInt (0+15=15)
      //then checks if its value is less than 255 (max PWM value)
      //if it is <= PWMSpeedInt, increment the PWMSpeedInt by 1 and enter loop
      //repeat until loop PWMSPeedInt > 255
      for (PWMSpeedInt + 30; PWMSpeedInt <= 255; PWMSpeedInt++) {
        analogWrite(PWMSignal, PWMSpeedInt);
        delay(50);
      }

      int time1 = millis();                       //starting a timer to run program for specific amount of time
      int time2 = millis();                       //second timer element
      int timeDiff = time2 - time1;
      while (timeDiff < 30000) {                  //while the amount of time that has passed is less than 20 seconds, set time 2 to new millis() value and compare the difference
        time2 = millis();
        timeDiff = time2 - time1;
      };

      //Loop for slowing the motor down.
      //loop starts at PWMSpeedInt value
      //checks if the value is still greater than zero
      //if true, subtract 1 from PWMSpeedInt and enter the loop
      for (PWMSpeedInt; PWMSpeedInt >= 0; PWMSpeedInt--) {
        analogWrite(PWMSignal, PWMSpeedInt);
        delay(25);
      }
      break;
    default :                                     //if none of the above is the case, default catches all other entries and program menu tree is restarted
      Serial.println("\nNot a valid Entry\nStarting Over\n");
      break;
  }
}

void SpeedSelect() {
  PWMSpeedInt = 0;
  Serial.println("Enter Speed from 1 - 255");
  while (Serial.available() == 0) {            //holds program at this line until operator input is entered
  };
  PWMSpeedStr = Serial.readString();          //assigns serial string to variable
  PWMSpeedInt = PWMSpeedStr.toInt();          //converts string to integer for use in switch-case statement

  if ((PWMSpeedInt > 255) || (PWMSpeedInt <= 0)) {
    Serial.println("Not a valid entry\nStarting Over\n");
  }
  else if ((PWMSpeedInt <= 255) || (PWMSpeedInt > 0)) {
    Serial.print("\nYou entered: ");
    Serial.println(PWMSpeedInt);
    Serial.println("Please press '1' to start");
    while (Serial.available() == 0) {};               //holds program at this line until operator input is entered
    OperatorSelectStr = Serial.readString();          //assigns serial string to variable
    OperatorSelectInt = OperatorSelectStr.toInt();    //converts string to integer for use in switch-case statement

    switch (OperatorSelectInt) {                      //if OperatorSelectInt is 1, default - go to specified menu item
      case 1 :

        //Loop for speeding motor up
        for (int i = 0; i <= PWMSpeedInt; i++) {
          analogWrite(PWMSignal, i);
          delay(50);
        }

        int time1 = millis();                         //starting a timer to run program for specific amount of time
        int time2 = millis();                         //second timer element
        int timeDiff = time2 - time1;                 //checking difference between the two
        while (timeDiff < 20000) {                    //while the amount of time that has passed is less than 20 seconds, set time 2 to new millis() value and compare the difference
          time2 = millis();
          timeDiff = time2 - time1;
        };

        //Loop for slowing the motor down.
        //loop starts at PWMSpeedInt value
        //checks if the value is still greater than zero
        //if true, subtract 1 from PWMSpeedInt and enter the loop
        for (PWMSpeedInt; PWMSpeedInt >= 0; PWMSpeedInt--) {
          analogWrite(PWMSignal, PWMSpeedInt);
          delay(25);
        }
        break;

      default : Serial.println("Not a valid Entry\n Starting over\n");  //if none of the above is the case, default catches all other entries and program menu tree is restarted
        break;
    }
  }

  else  {
    Serial.println("Not a valid Entry\n Starting over\n");
    PWMSpeedInt = 0;
  }
}

void POTMode() {
  int POTValue = analogRead(POT);
  Serial.println("\n1) to begin POT Mode");
  while (Serial.available() == 0) {};              //holds program at this line until operator input is entered
  OperatorSelectStr = Serial.readString();         //assigns serial string to variable
  OperatorSelectInt = OperatorSelectStr.toInt();   //converts string to integer for use in switch-case statement
  while (POTValue >= 200) {
    Serial.println("Reset POT first");
    POTValue = analogRead(POT);
  };
  int time1 = millis();
  int time2 = millis();
  int timeDiff = time2 - time1;
  // Serial.available() == 0;
  ESAATSPotMode();

}

//----------------End Profiles --------------------//


//--------Emergency Stop and Automatic Timeout service -----------------//
void ESAATSPotMode() {
  int time1 = millis();
  int time2 = millis();
  int timeDiff = time2 - time1;
  // Serial.available() == 0;
  while (timeDiff < 300000) {
    if (Serial.available() > 0) {
      timeDiff = 3000000;
    }
    else
    {
      PWMSpeedInt = analogRead(POT);
      PWMSpeedInt = map(PWMSpeedInt, 0, 1023, 0, 255);
      analogWrite(PWMSignal, PWMSpeedInt);
      time2 = millis();
      timeDiff = time2 - time1;
    }

  }
  for (PWMSpeedInt; PWMSpeedInt > 0; PWMSpeedInt--) {
    analogWrite(PWMSignal, PWMSpeedInt);
    delay(25);
  }
}

Credits

Tom DeCricle
6 projects • 2 followers
Electrical Engineer with experience in Schematic Design, PCB Layout, PCB Fabrication & Assembly Procurement, Hardware/Firmware/Software Dev
Contact

Comments

Please log in or sign up to comment.