I am using an Arduino Nano to control a linear servo (Spektrum SPMSH2045L_) to move shift fork in my 3d printed 2-speed transmission.
The servo power is 3.3v coming from Arduino and common ground.
PWM frequency 333HZ and centering signal is a pause of 1520 microseconds. I have filtering capacitor .1 uf between +- on power supply to servo.
The servos have responded to movement and centering.
The problem is they are jittering and heating up very hot while they are not supposed to be moving.
They get hot enough to melt on just 3.3v just jittering.
Does anyone have ideas what could be causing this?
#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>
#include <Adafruit_DotStar.h>
#include <SPI.h>
#include <ESP32Servo.h> // Include the ESP32Servo library
// Initialize the U8G2 library for the DF Robot 1.51" transparent OLED in SPI mode
U8G2_SSD1306_128X64_NONAME_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 2, /* reset=*/ 3);
// Define the Dotstar LED pin configuration and number of LEDs
#define NUM_LEDS 21
#define DATA_PIN1 A7
#define CLOCK_PIN A5
#define DATA_PIN2 A0
Adafruit_DotStar strip1(NUM_LEDS, DATA_PIN1, CLOCK_PIN, DOTSTAR_BRG);
Adafruit_DotStar strip2(NUM_LEDS, DATA_PIN2, CLOCK_PIN, DOTSTAR_BRG);
// Define pins
#define ANALOG_PIN A2
#define BUTTON_PIN A1
#define RPM_SENSOR_PIN A3
#define CONTROL_PIN 9
#define EXTRA_LED_PIN 12
#define SERVO1_PIN 4
#define SERVO2_PIN 5
const float R1 = 3300.0; // Resistor R1 value in ohms (3.3k ohms)
const float R2 = 10000.0; // Resistor R2 value in ohms (10k ohms)
const float ADC_max = 4095.0; // ADC maximum value (12-bit ADC for ESP32)
const float V_ref = 3.3; // Reference voltage of ESP32 (3.3V)
// Updated calibration factor
const float calibration_factor = 4.15 / 4.39; // Corrected voltage / Measured voltage
#define NUM_READINGS 10 // Number of readings to average
unsigned long lastActivityTime = 0;
const unsigned long INACTIVITY_THRESHOLD = 60000; // 1 minute for normal use
int scrollPos = 128; // Initial scroll position for "SPINWEIGHT"
Servo servo1;
Servo servo2;
int servo1Pos = 1520; // Initial position (center) in microseconds
int servo2Pos = 1520; // Initial position (center) in microseconds
const int minPulseWidth = 544; // Minimum pulse width in microseconds
const int maxPulseWidth = 2400; // Maximum pulse width in microseconds
const int increment = 185; // Increment in microseconds (10% of the total range)
const int movementSpeed = 10; // Speed of servo movement
unsigned long lastScrollUpdate = 0;
unsigned long lastLedUpdate = 0;
unsigned long lastButtonCheck = 0;
unsigned long lastVoltageCheck = 0;
unsigned long lastRPMCheck = 0;
const unsigned long scrollInterval = 25; // 25 milliseconds for scrolling
const unsigned long ledInterval = 50; // 50 milliseconds for LED update
const unsigned long buttonCheckInterval = 50; // 50 milliseconds for button checking
const unsigned long voltageCheckInterval = 1000; // 1000 milliseconds for voltage check
const unsigned long rpmCheckInterval = 1000; // 1000 milliseconds for RPM check
int buttonValue = 0; // Move the buttonValue declaration to the global scope
int buttonNumber = 0; // Variable to store the current button number
volatile unsigned int rpmCount = 0;
unsigned int rpm = 0;
unsigned int maxRPM = 0;
bool sensorHigh = false;
float batteryVoltage = 0.0;
void setup() {
Serial.begin(115200);
delay(1000); // Wait for serial monitor to open
// Initialize the OLED display
u8g2.begin();
u8g2.setFlipMode(1); // Set flip mode to display text upside down
// Initialize Dotstar LED strips
strip1.begin();
strip1.setBrightness(25); // Set brightness to 25
strip1.show(); // Initialize all pixels to 'off'
strip2.begin();
strip2.setBrightness(25); // Set brightness to 25
strip2.show(); // Initialize all pixels to 'off'
pinMode(CONTROL_PIN, OUTPUT);
digitalWrite(CONTROL_PIN, HIGH); // Ensure power is on
pinMode(EXTRA_LED_PIN, OUTPUT);
analogWrite(EXTRA_LED_PIN, 255); // Set extra LED to red (assuming analogWrite controls the color)
lastActivityTime = millis();
// Center the servos at the start
servo1.setPeriodHertz(333); // Set PWM frequency to 333 Hz
servo1.attach(SERVO1_PIN, minPulseWidth, maxPulseWidth); // Attach servo
servo1.writeMicroseconds(servo1Pos);
delay(1000); // Allow servo to move to center position
servo1.detach(); // Detach servo
servo2.setPeriodHertz(333); // Set PWM frequency to 333 Hz
servo2.attach(SERVO2_PIN, minPulseWidth, maxPulseWidth); // Attach servo
servo2.writeMicroseconds(servo2Pos);
delay(1000); // Allow servo to move to center position
servo2.detach(); // Detach servo
Serial.println("Servos centered to 1520 µs");
pinMode(BUTTON_PIN, INPUT);
}
int readAnalogAverage(int pin, int numReadings) {
long total = 0;
for (int i = 0; i < numReadings; i++) {
total += analogRead(pin);
delay(1); // Small delay to ensure stable reading
}
return total / numReadings;
}
void loop() {
unsigned long currentTime = millis();
// Handle button check
if (currentTime - lastButtonCheck >= buttonCheckInterval) {
buttonValue = readAnalogAverage(BUTTON_PIN, NUM_READINGS);
// Print the raw button value for debugging
Serial.print("Button Value: ");
Serial.println(buttonValue);
// Determine which button is pressed based on the analog value
if (buttonValue > 4000) {
Serial.println("Button 1 Pressed");
buttonNumber = 1;
servo1.attach(SERVO1_PIN); // Attach servo
servo1Pos = constrain(servo1Pos + increment, minPulseWidth, maxPulseWidth);
servo1.writeMicroseconds(servo1Pos);
delay(movementSpeed); // Slow down the movement
servo1.detach(); // Detach servo
lastActivityTime = currentTime; // Reset inactivity timer
} else if (buttonValue > 3000 && buttonValue <= 4000) {
Serial.println("Button 2 Pressed");
buttonNumber = 2;
servo1.attach(SERVO1_PIN); // Attach servo
servo1Pos = constrain(servo1Pos - increment, minPulseWidth, maxPulseWidth);
servo1.writeMicroseconds(servo1Pos);
delay(movementSpeed); // Slow down the movement
servo1.detach(); // Detach servo
lastActivityTime = currentTime; // Reset inactivity timer
} else if (buttonValue > 2100 && buttonValue <= 3000) {
Serial.println("Button 3 Pressed");
buttonNumber = 3;
servo2.attach(SERVO2_PIN); // Attach servo
servo2Pos = constrain(servo2Pos + increment, minPulseWidth, maxPulseWidth);
servo2.writeMicroseconds(servo2Pos);
delay(movementSpeed); // Slow down the movement
servo2.detach(); // Detach servo
lastActivityTime = currentTime; // Reset inactivity timer
} else if (buttonValue > 500 && buttonValue <= 2100) { // Adjusted threshold for button 4
Serial.println("Button 4 Pressed");
buttonNumber = 4;
servo2.attach(SERVO2_PIN); // Attach servo
servo2Pos = constrain(servo2Pos - increment, minPulseWidth, maxPulseWidth);
servo2.writeMicroseconds(servo2Pos);
delay(movementSpeed); // Slow down the movement
servo2.detach(); // Detach servo
lastActivityTime = currentTime; // Reset inactivity timer
} else {
buttonNumber = 0; // No button pressed
}
lastButtonCheck = currentTime;
}
// Handle scrolling text update
if (currentTime - lastScrollUpdate >= scrollInterval) {
updateDisplay();
lastScrollUpdate = currentTime;
}
// Handle LED update
if (currentTime - lastLedUpdate >= ledInterval) {
theaterChaseRainbow(); // Add the theater chase rainbow effect
lastLedUpdate = currentTime;
}
// Handle voltage check update
if (currentTime - lastVoltageCheck >= voltageCheckInterval) {
updateVoltageDisplay();
lastVoltageCheck = currentTime;
}
// Handle RPM check
if (currentTime - lastRPMCheck >= rpmCheckInterval) {
updateRPM();
lastRPMCheck = currentTime;
}
checkInactivity(currentTime);
}
void updateDisplay() {
u8g2.clearBuffer(); // Clear the internal memory
// Scrolling "SPINWEIGHT" text
u8g2.setFont(u8g2_font_helvB12_tf); // Choose a suitable font
u8g2.drawStr(scrollPos, 12, "SPINWEIGHT"); // Draw "SPINWEIGHT"
scrollPos -= 2; // Move the text to the left at a consistent speed
if (scrollPos < -80) {
scrollPos = 128; // Reset position when it goes off-screen
}
// Display RPM value
u8g2.setCursor(0, 24);
u8g2.print("RPM: ");
u8g2.print(rpm);
// Display Max RPM value
u8g2.setCursor(0, 36);
u8g2.print("Max RPM: ");
u8g2.print(maxRPM);
// Display Voltage value
u8g2.setCursor(0, 48);
u8g2.print("V: ");
u8g2.print(batteryVoltage);
u8g2.sendBuffer(); // Transfer internal memory to the display
Serial.println("Display updated.");
}
void updateVoltageDisplay() {
int analogValue = readAnalogAverage(ANALOG_PIN, NUM_READINGS);
float voltage = (analogValue / ADC_max) * V_ref;
batteryVoltage = voltage * ((R1 + R2) / R2) * calibration_factor;
Serial.print("Battery Voltage: ");
Serial.print(batteryVoltage);
Serial.println(" V");
}
void theaterChaseRainbow() {
static int firstPixelHue = 0; // First pixel starts at red (hue 0)
static int chasePosition = 0;
strip1.clear();
strip2.clear();
for (int i = 0; i < NUM_LEDS; i++) {
int hue = firstPixelHue + (i * 65536L / NUM_LEDS);
uint32_t color = strip1.ColorHSV(hue, 255, 255);
// Apply fading for leading and trailing LEDs
if (i == chasePosition || i == (chasePosition + 1) % NUM_LEDS || i == (chasePosition + 2) % NUM_LEDS) {
strip1.setPixelColor(i, color);
strip2.setPixelColor(i, color);
} else if (i == (chasePosition + 3) % NUM_LEDS || i == (chasePosition + 4) % NUM_LEDS) {
strip1.setPixelColor(i, strip1.ColorHSV(hue, 255, 128));
strip2.setPixelColor(i, strip2.ColorHSV(hue, 255, 128));
} else if (i == (chasePosition + 5) % NUM_LEDS || i == (chasePosition + 6) % NUM_LEDS) {
strip1.setPixelColor(i, strip1.ColorHSV(hue, 255, 64));
strip2.setPixelColor(i, strip2.ColorHSV(hue, 255, 64));
}
}
strip1.show();
strip2.show();
chasePosition = (chasePosition + 1) % NUM_LEDS;
firstPixelHue += 65536 / 90; // One cycle of color wheel over 90 frames
}
void checkInactivity(unsigned long currentTime) {
if (currentTime - lastActivityTime >= INACTIVITY_THRESHOLD) { // Check if 1 minute of inactivity
attemptPowerOff(500, 500); // Test this first
lastActivityTime = millis(); // Reset the timer to avoid repeated attempts in quick succession
}
}
void attemptPowerOff(int delay1, int delay2) {
Serial.println("Attempting power off...");
digitalWrite(CONTROL_PIN, LOW);
delay(delay1);
digitalWrite(CONTROL_PIN, HIGH);
delay(delay2);
digitalWrite(CONTROL_PIN, LOW);
Serial.println("Power off sequence complete");
}
void updateRPM() {
int sensorValue = analogRead(RPM_SENSOR_PIN);
if (sensorValue > 2000 && !sensorHigh) {
sensorHigh = true;
rpmCount++;
} else if (sensorValue < 1000 && sensorHigh) {
sensorHigh = false;
}
if (millis() - lastRPMCheck >= 1000) {
rpm = rpmCount * 60; // Calculate RPM
if (rpm > maxRPM) {
maxRPM = rpm; // Store max RPM
}
rpmCount = 0;
lastRPMCheck = millis();
}
}