Wednesday, 19 March 2025

MIG pulse control software for Arduino ESP32 C3/S3 Super Mini

Any resolution on the hardware yet?

After endless rabbitholing, I've gone for the smallest, simplest microcontroller in the end. No need for pointless displays or masses of IO here.

Currently, I'm using the "DUBEUYEW ESP32-C3 Development Board Mini " obtained through Amazon. 


Being a C3 version of the ESP32, it has a single core micro. Here is some useful background on it, including the pinout etc.

So obviously I've ordered an "S3" equivalent "Waveshare ESP32-S3 Mini Development Board, based on ESP32-S3FH4R2" which has 2 cores. 


This dual core S3 version should allow me to run the "MIG pulsing" software independently to the "torch motion" code, as I found the stepper code was rather lumpy and didn't get on well alongside the pulsing code. To be fair, the Arduino Uno R4 isn't the same as the ESP32, so I can't compare their speeds directly and can't be bothered to do any testing myself. 

NB: One lesson I learned when running my existing (Arduino Uno) code in the ESP32C3 is that the ADC has 4095 count instead of 1023. I guess (without checking) that it has an extra couple of bits. This required me to rescale the input by a factor of 4.

Got anywhere with the motor and driver?

Well, I've decided to ditch the stepper driver software for now, unless it turns out it's the only way to go. Instead, I'm hoping to pass the bulk of the task off to a pukka servo device like this, also from Waveshare

Although you can't see it easily from this angle, it's double ended and comes with metal flanges for each end. So I should be able to make up wheels with o-ring tyres, rather like the little bogey I previously constructed.

The datasheet for this ST3215 looks encouraging, although it uses an RS485 serial bus for control, so I'll need to look into that more closely. 

There are some examples to help applying this servo, although the default expectation is that you will use one of their ESP32-based "driver boards". They refer obliquely to "secondary development" where you can drive the servo yourself without the driver board. This is the route I'd want to go, as I don't want / need an additional ESP32 just to drive the servo.

But - how's the software coming along, Fatty?

Well at least that seems to have largely resolved itself for now. I've got the pulse frequency / duty cycle functions working nicely now:

  • Pulse width setting is managed within function managePwm();
  • Pulse frequency (period) is managed within function managePeriod();
  • These use "non-blocking" operations (rather than "delay" which causes the micro to go on hold for the duration)
  • Currently, motorSpeedIn is simply set by reading a pot and generating a value from 0 to 4095. I will use this to control the servo, once I've figured out what I need to do.
  • For debug purposes, I have a reportStatus() function to drive the serial monitor, showing the current values of pwmDuration, pwmPeriod and motorSpeedIn.

Here's the code in its entirety:

#include <Arduino.h>
// Muzzer 18th March 2025
// Select "LOLIN C3 Pico" to compile for the tiny ESP32
const int pwmPin =  8;            // the number of the (blue) LED pin on the ESP32 C3 Pico
const int dutyCycleAnalogIn = A0; // Analog input- PWM duty
const int periodAnalogIn = A1;    // Analog input - period (ms)
const int speedAnalogIn = A2;     // Analog input - speed (Hz)
const int maxPeriod = 3000;       // hard coded max pulse time in ms
const int minPeriod = 500;        // hard coded min pulse time in ms
const int minDuty = 20;           // hard coded min duty %
int pwmState = HIGH;              // Led state for PWM output
int pwmSensorValue = 0;           // Value read from the pot - for debug only
int periodSensorValue = 0;        // Input from pot - for debug only
int motorSpeedIn = 0;             //  
long previousMillisPwm = 0;       // Last time PWM output was updated
long previousMillisPeriod = 0;    // 
long pwmDuration = 2000;          // Interval for PWM output (milliseconds)
long pwmPeriod = 2000;            // Interval between spots (milliseconds)
unsigned long currentMillis = 0;  // Initialise current time
void setup() 
{
  Serial.begin(9600);             // Start the Serial Monitor
  pinMode(pwmPin, OUTPUT);        // Set the selected PWM output pin as an OUTPUT
}
void loop() 
{
  currentMillis = millis();                                 // capture the current time
  managePwm();                                              // Terminate mark when the time is right
  managePeriod();                                           // Start new mark at the end of the period
  reportStatus();                                           // Debug output to Serial Monitor
  motorSpeedIn = analogRead(speedAnalogIn);                 // Define setSpeed() according to input A2 (0...4095)
}
void reportStatus() 
{
  Serial.print("Duty time is");
  Serial.print("\t");
  Serial.print(pwmDuration);
  Serial.print("\t");
  Serial.print("Period time is");
  Serial.print("\t");
  Serial.print(pwmPeriod);
  Serial.print("\t");
  Serial.print("Output");
  Serial.print("\t");
  Serial.print(pwmState);
  Serial.print("\t");
  Serial.print("Speed Input");
  Serial.print("\t");
  Serial.println(motorSpeedIn);
}
void managePwm() 
{
  pwmDuration = map(analogRead(dutyCycleAnalogIn), 0, 4095, (pwmPeriod*minDuty/100), pwmPeriod); // map input to pwmDuration - on time
  //check if it's time to terminate the PWM output yet 
  if(currentMillis - previousMillisPwm > pwmDuration) 
  {
    //store the time of this change
    // LOW = OFF: end the pulse. NB: LOW actually lights up the LED!
    pwmState = LOW;
    digitalWrite(pwmPin, pwmState);
  }
}
void managePeriod() 
{
  pwmPeriod = map(analogRead(periodAnalogIn), 0, 4095, minPeriod, maxPeriod); // pwmPeriod = time between spots
  //check if it's time to reset the output to start the next pulse yet 
  if(currentMillis - previousMillisPeriod > pwmPeriod) 
  {
    previousMillisPeriod = currentMillis;
    previousMillisPwm = currentMillis;
    // HIGH = ON: reset the output for next pulse
    pwmState = HIGH;
    digitalWrite(pwmPin, pwmState);
  }
}

Obviously, Blogger has screwed up the formatting so that the tab settings (spaces) are all over the place. But it's all there for posterity.

Next:
  • Figure out how to drive this ST3215 servo thing from the ESP32 C3 / S3
  • Do I need to go to dual core operation to get the pulse and motion functions to play nicely?
  • Does the S3 work the same as the C3 or will I need to mess with the code? Should be relatively quick and easy to find out...

No comments:

Post a Comment

Time to start behaving

Well, the endless rabbitholing seems determined to persist. The Waveshare ESP32S3 boards I got from Amazon seem to indeed "share" ...