Sunday, 23 March 2025

Time to start behaving

Well, the endless rabbitholing seems determined to persist. The Waveshare ESP32S3 boards I got from Amazon seem to indeed "share" a lot in common with their ESP32 siblings. Namely I struggle to get them to behave at all. Even when following the manufacturer's instructions to the tee, I can't get the fuckers the work. And no, it's not just that The Stupid Fat Block doesn't know what he's doing - that surely can't be helping - but on the myriad forums, users of all capabilities and none are sharing the same frustration. I've got a life to live, so the ESP32 family can shove it.

So I'm going to fall back on the "proven" solutions ie "official Arduino boards made and sold by Arduino" (through Amazon) using microcontrollers of know parentage.

What happened to the ESP32S3, then?

Well, firstly the S3 and C3 boards are "more different" than I'd expected. For one thing, the S3 has more pins and for another, the board layout is completely different, as in the pins are on opposite sides of the board. TBH, all I was hoping for was a dual core board in the same form / footprint / pinout, so that the stepper speed control could run independently to the pulsing function. But then I completely failed to even get it to talk to me. I seemed  to have bricked the damned thing. Probably not bricked , technically speaking, but either way it was no use to man nor beast - and certainly not to The Stupid Fat Bloke.

So, to start with, I'm gong to immediately break the "official Arduino boards made and sold by Arduino" notion by instead using a Waveshare RP2040 Zero "mini" board using an RP2040. What???

Let's get this Waveshare RP2040 Zero running, then:

The whole point of this dual core shenanigan is to run the motor speed control in one core and the pulsing control in another, with minimal impact of one on the other. Can we finally, finally get there?

The setup for the Waveshare board appears to be pretty much the same as that required for the Arduino Pico The installation of the RP2040 into the board manager needs to be done first and seems to also download some relevant RP2040 examples.

Once you've got the board set up as a "Waveshare RP2040 Zero" in the Arduino IDE board manager, it's time to try out multi core processing. This code seems to work:

// Core0 setup
void setup() 
{
  Serial.begin(115200);
}
void loop() 
{
  Serial.printf("Core 0-1...\n");
}
// Core1 setup
void setup1() 
{
  Serial.printf("Core 1-1...\n");
}
void loop1() 
{
  Serial.printf("Core 1-2...\n");
}

This does almost nothing but at least it compiles and works. It's basically setup(), loop(), setup1(), loop1(), with relevant content for core0 and core1 within each segment. Pretty simple on the face of it.

So the next steps are: 
  • graft the pulsing software into one of the cores and convince myself it works.
  • graft the stepper speed control onto one of the cores etc etc
  • try to get both functions to coexist.

One issue with the Zero is that the RGB onboard LED isn't just a simple LED, so it's not possible to just turn it on. No problem - I'll rely on the serial monitor, although it's a bit of an annoyance.

Let's get the pin allocations decided. Here's the Zero's layout:


Looks as if I can use A0...A2 for the analogue inputs and D29 for the step output, keeping the external connections grouped together for no obviously compelling reason.

This works:

#include <AccelStepper.h>
// constants to set pin numbers = 13 - use built in LED for test / dev
const int pwmPin =  13; // the number of the green LED pin
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 = 5000;   // hard coded max on time
const int dirPin = 5;  // pin 5 used for DIR output
const int stepPin = 4;  // pin 4 used for STEP output
// Variables will change
int pwmState = HIGH; //ledState for PWM output
int pwmSensorValue = 0;  // value read from the pot
int periodSensorValue = 0;
int speedValue = 0;
long plot = 0;
AccelStepper stepper1(AccelStepper::DRIVER, stepPin, dirPin); // (Type of driver: with 2 pins, STEP, DIR)
long previousMillisPwm = 0;  //last time PWM output was updated
long previousMillisPeriod = 0;
// pwmPeriod is time between spots
long pwmPeriod = 2000; //interval between spots (milliseconds)
// must be long to prevent overflow
long pwmDuration = 2000; //interval for PWM output (milliseconds)
unsigned long currentMillis = 0; // initialise
void setup() 
{
  // Set maximum speed value for the stepper:
  stepper1.setMaxSpeed(1000);
  // set the pins to output mode
  Serial.begin(9600);
  pinMode(pwmPin, OUTPUT);
  digitalWrite(pwmPin, pwmState);
  currentMillis = millis();
}
// Core1 setup
void setup1() 
{
//  Serial.printf("Core 1-1...\n");
}
void loop() 
{
  analogRead(0); // could be any channel
  periodSensorValue = analogRead(periodAnalogIn); // 0 - 1023 count
  pwmPeriod = (maxPeriod * periodSensorValue / 1023); // map input to pwmPeriod - time between spots
  pwmSensorValue = analogRead(dutyCycleAnalogIn); // 0 - 1023 count
  pwmDuration = map(pwmSensorValue, 0, 1023, 0, pwmPeriod); // map input to pwmDuration - on time
  currentMillis = millis(); // capture the current time
  managePwm();
  managePeriod();
  serial_Plotter();  
  reportStatus();
}
void loop1() 
{
//   speedValue = analogRead(speedAnalogIn); // Define setSpeed() according to input A2
   speedValue = map(analogRead(speedAnalogIn), 0, 4095, 400, 4095); // Define setSpeed() according to input A2
   stepper1.setSpeed(speedValue); // Step the motor with a constant speed previously set by setSpeed();
   stepper1.runSpeed();
   Serial.print("Speed ");
   Serial.println(speedValue);
}
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(plot);
  Serial.println("\t");
}
void serial_Plotter() 
{
  if(pwmState == HIGH) 
  {
    plot=1;
  } else 
  {
    plot=0;
  }
}
void managePwm() 
{
  //check if it's time to change the PWM output yet 
  if(currentMillis - previousMillisPwm > pwmDuration) 
  {
    //store the time of this change
    pwmState = LOW;
    digitalWrite(pwmPin, pwmState);
  }
}
void managePeriod() 
{
  //check if it's time to reset the output yet 
  if(currentMillis - previousMillisPeriod > pwmPeriod) 
  {
    previousMillisPeriod = currentMillis;
    previousMillisPwm = currentMillis;
    pwmState = HIGH;
    digitalWrite(pwmPin, pwmState);
  }
}

...but the text outputs from core0 and core1 arrive on top of each other. That's not an issue really. 
 
20:21:07.245 -> 
20:21:07.245 -> Duty time is Speed 242604
20:21:07.245 -> Period time isSpeed 6041094
20:21:07.245 -> Speed Output604
20:21:07.245 -> 0Speed 604
20:21:07.245 -> 
20:21:07.245 -> Duty time isSpeed 604241
20:21:07.245 -> Speed Period time is604
20:21:07.245 -> 1089Speed 603Output
20:21:07.245 -> Speed 0604
20:21:07.245 -> 
20:21:07.245 -> Speed 603Duty time is
20:21:07.245 -> 241Speed 605Period time is
20:21:07.245 -> Speed 1089605
20:21:07.245 -> OutputSpeed 6050
20:21:07.245 -> Speed 
20:21:07.245 -> 605
20:21:07.245 -> Duty time isSpeed 605243
20:21:07.245 -> Speed Period time is605
etc etc

Next - check the physical inputs and outputs actually function as expected. I seem to be making progress finally....

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