Trying to modify an existing sketch, guidance appreciated

Hiya! I'm building a Halloween prop based on an animatronic body bag project I found on Thingiverse. The project is many years old at this point and the build required a lot of little tweaks here and there to assemble properly. The original parts list included some servos that are no longer made by Futaba so I had to buy the updated versions. The code, however, doesn't quite work as expected. The servo pulses tend to turn the pulleys in one direction and then keep pulling in that direction after a series of rather long delays. The expected outcome should be like this video:

Here's the code. Your guidance is greatly appreciated:

// easing servo movements with low pass filter
// the servo signal must be connected directly to the arduino pin 2
 
// Time interval, this executes one piece of code from time to time
// Download Metro lybrary and instructions:
// http://www.arduino.cc/playground/Code/Metro
#include <Metro.h>
int duration = 1000; // duration of the interval in miliseconds
Metro intervaller = Metro (duration);
 
// MegaServo library
// download and instructions:
// http://www.arduino.cc/playground/Code/MegaServo
//#include <MegaServo.h>
//MegaServo servos;
 
#include <Servo.h>
 
// servos minimum and maximum position
#define MIN_POS 800 // the minuimum pulse width for your servos
#define MAX_POS 2200 // maximum pulse width for your servos
#define MIN_TIM 1000 // minimum time to wait between loops
#define MAX_TIM 1000 // maximum time to wait between loops
 
Servo left;
Servo right;
const int soundPin =  13;
 
// servo pin
//#define s0_pin 9
//#define s1_pin 10
 
// variables to hold new destination positions
int d0; // destination0
int d1;
int d0_sh; // destination0 to be smoothed
int d1_sh;
int repeat;
int loop_num;

// the filter to be aplied
// this value will be multiplied by "d0" and added to "d0_sh"
float filtro = 0.1; // 0.01 to 1.0
 
// setup runs once, when the sketch starts
void setup() {
  // set sound pin
  pinMode(soundPin, OUTPUT);
  // set servo pin
  right.attach(9);
  left.attach(10);
}
 
// main program loop, executes forever after setup()
void loop() {
  delay(1000);  
  int sensorValue = analogRead(A2);

  do{
      digitalWrite(soundPin, LOW);
      delay(1000);
      digitalWrite(soundPin, HIGH);
      
      loop_num = random(400, 800);
      repeat = random(MIN_TIM, MAX_TIM);

      if (intervaller.check() == 1) {
        // calculate a new random position between max and min values
        d0 = random(MIN_POS, MAX_POS);
        d1 = random(MIN_POS, MAX_POS);
   
        // resets interval with a new random duration
        intervaller.interval(random(50,500));
       }
   
      // smooth the destination value
      d0_sh = d0_sh * (1.0-filtro) + d0 * filtro;
      d1_sh = d1_sh * (1.0-filtro) + d1 * filtro;
   
      // assign new position to the servo
      right.write(d0_sh);
      left.write(d1_sh);

      // delay to make the servo move
      delay(20);

      //then pause
      // Serial.println(sensorValue);
      delay(repeat);  
  } while(200 < sensorValue && sensorValue < 400);
}

Is there any documentation of the code, like flow charts, explaining the idea for how the code is designed?

Unfortunately the original project is POORLY documented in the extreme. There's hardly anything approaching a guide on how to assemble the parts. The code comments aren't even that helpful. Getting it all together was a matter of intuition and trial and error.

It doesn't seem to be terribly involved as-is, though. I'm honestly not sure what these references to soundPin and sensorValue are but everything else makes sense to me (If I'm understanding the code correctly): For each servo select a random position value between a given range, apply a filter that eases out of the servo motion so the motion is a little more human and it's not just two servos jerking wildly, write them to the servo motors, wait a second, execute it again.

Okey to Your reply #3. You have learned a lot.
To the quote: Instal temporary serial.print of key variables to find out what's going on inside the code.
Timing problems take a lot of time, more time than is available now, to figure out what's happening. Debug prints is a way to dig deeper into the code mystery acting.

1 Like

You have 360 degree servos where pulse width midway between maximum and minimum is the "stop" position, above that midpoint/value will rotate CW, below that midpoint/value will rotate CCW. The farther from "stop" the faster the rotation. You can solve this (turn in one direction) by finding the "stop" value and adding or subtracting from that value depending on the need to turn the servo CW or CCW.

In a simulation, 180 degree servos turn both directions.

Paste this into the "diagram.json" tab of the wokwi simulator... and your code into the "sketch.ino" tab. You will need to add a .H and .CPP for the Metro.h library found here: GitHub - nusolar/Metro: Metro library for Arduino or Wiring

diagram.json
{
  "version": 1,
  "author": "Anonymous maker",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-nano", "id": "nano", "top": -33.6, "left": -29.3, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo1", "top": -117.2, "left": 172.8, "attrs": {} },
    { "type": "wokwi-servo", "id": "servo2", "top": -203.6, "left": 172.8, "attrs": {} },
    { "type": "wokwi-vcc", "id": "vcc1", "top": -200.84, "left": 144, "attrs": {} },
    { "type": "wokwi-gnd", "id": "gnd1", "top": -38.4, "left": 153, "attrs": {} },
    { "type": "wokwi-potentiometer", "id": "pot1", "top": 8.3, "left": 220.6, "attrs": {} },
    {
      "type": "wokwi-buzzer",
      "id": "bz1",
      "top": -160.8,
      "left": -65.4,
      "attrs": { "volume": "0.1" }
    }
  ],
  "connections": [
    [ "nano:9", "servo1:PWM", "green", [ "v0" ] ],
    [ "nano:10", "servo2:PWM", "green", [ "v0" ] ],
    [ "vcc1:VCC", "servo2:V+", "red", [ "v0" ] ],
    [ "vcc1:VCC", "servo1:V+", "red", [ "v0" ] ],
    [ "gnd1:GND", "servo1:GND", "black", [ "v0" ] ],
    [ "gnd1:GND", "servo2:GND", "black", [ "v0" ] ],
    [ "pot1:GND", "nano:GND.1", "black", [ "v9.6", "h-67.2" ] ],
    [ "pot1:SIG", "nano:A2", "green", [ "v19.2", "h-221.2" ] ],
    [ "pot1:VCC", "nano:5V", "red", [ "v28.8", "h-173.6", "v-76.8" ] ],
    [ "nano:13", "bz1:2", "green", [ "v9.6", "h-9.6" ] ],
    [ "gnd1:GND", "bz1:1", "black", [ "v-19.2", "h-201.6" ] ]
  ],
  "dependencies": {}
}

I used a potentiometer to simulate the "200 to 400" range.

1 Like

Ohhhhh. I think this was the missing piece for me. This is super helpful. I wasn't aware of the simulator tool.

If I understand it, pulse width is different from a position on a 360 degree circle. So if the range is 200 to 400, the stop position would be 300?

Here's the simulation: servos - Wokwi ESP32, STM32, Arduino Simulator

The 200 to 400 (centimeter = 7 to 14 feet) range is your sensor for when people approach.

A 180 servo will only travel 180 degrees. You will need to adjust this...

// servos minimum and maximum position
#define MIN_POS 800 // the minuimum pulse width for your servos
#define MAX_POS 2200 // maximum pulse width for your servos
#define MIN_TIM 1000 // minimum time to wait between loops
#define MAX_TIM 1000 // maximum time to wait between loops

"800" and "2200" are the "0 degrees" and "180 degrees" of a 180 degree servo... for the 360 servo, these values will be "full CW" and "full CCW" with some value between as "STOP"

The min/max time to wait between loops might (or might not) effect the servos which want these pulses at 50Hz.

I commented the delays out and it works as expected.

1 Like

I am guessing, but the audio portion might mess with the servos. Audio and servos use a timer, of which there are two... but usually "timer1" is used by all the libraries unless expressly made to use timer2. Maybe a DFPlayerMini is being used, in I/O mode, to make the noises on command.

I have an Adafruit soundboard that I'm going to wire up separately to a button and a breadboard so I don't even need the code for the audio. I appreciate you working this all out with me.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.