Audio input and servo driving me mad, may be haunted.

Hi All,

More work on the puppet head mouth mech :slight_smile:

Imgur: The magic of the Internet Images for setup and video.

The video shows the mouth moving by itself…(could be shileding / some kind of interference?)

Looks like I have an issue now where the audio is either really delayed, or the puppet is haunted.

I tried to add some smoothing into the code to prevent the delays.

I.e I think the code pre-smoothing was queuing up causing the noises… Ie, it would try to articulate the motor every 90 ms and queue these up.

Adding a longer delay simply made the mouth too far out of sync. Adding smoothing, I thought was a way to get an average and only apply that to the mouth articulation and prevent the queuing.

Any ideas brain trust?

Thank you

Pre smoothing

#include <Servo.h>



Servo mouth;



int sensorPin = A0; // Audio input



void setup() {

// put your setup code here, to run once:



mouth.attach(9,500,2500); // Pin 9 min 500 max 2500 for H-KING HK-15328D servos to go 180 (600 /2300 for MG90s)

mouth.write(0); // default mouth position (closed)

delay(2000);

pinMode(sensorPin, INPUT);



Serial.begin(9600);



}



void loop() {







int noiseLevel = analogRead(sensorPin);



if (noiseLevel > 25)

{

if (noiseLevel * 13 < 500) // degrees before mouth hits petg tube

{

mouth.write(noiseLevel * 13);

}

delay(90);

}



else {



mouth.write(0);



}





//Serial.print("Volume is :");

Serial.println(noiseLevel);



// testing servo alone

//mouth.write(0);

//delay(3000);

//mouth.write(70);

//delay(3000);





}

Post smoothing

#include <Servo.h>



Servo mouth;



int sensorPin = A0; // Audio input

const int numReadings = 20;

int readings[numReadings]; // the readings from the analog input

int readIndex = 0; // the index of the current reading

int total = 0; // the running total

int average = 0; // the average

int noiseLevel;



void setup()

{

mouth.attach(9,500,2500); // Pin 9 min 500 max 2500 for H-KING HK-15328D servos to go 180 (600 /2300 for MG90s)



mouth.write(0); // default mouth position (closed)



pinMode(sensorPin, INPUT);



Serial.begin(9600);





// set all readings to 0



for (int thisReading = 0; thisReading < numReadings; thisReading++)

{

readings[thisReading] = 0;

}



}



void loop()

{



// subtract the last reading:



total = total - readings[readIndex];

// read from the sensor:

readings[readIndex] = analogRead(sensorPin);

total = total + readings[readIndex]; // add the reading to the total:

readIndex = readIndex + 1; // advance to the next position in the array:



// if we're at the end of the array...



if (readIndex >= numReadings)

{

// ...wrap around to the beginning:

readIndex = 0;



// calculate the average:

average = total / numReadings;

// send it to the computer as ASCII digits

Serial.println(average);

noiseLevel = average;

delay(50); // delay in between reads for stability

}

if (noiseLevel > 25)

{

if (noiseLevel * 13 < 500) // degrees before mouth hits petg tube

{

mouth.write(noiseLevel * 13);

}

}



else {



mouth.write(0);

}





}

The issue may be with the fact that you are calculating a servo position and then writing it out and not waiting any length of time. Servo motors do not move instantaneously to the new position. If you look at the the servo example in the library, they move 1 degree and then wait 15 msec before moving again.

You have not shown your wiring. Please show a wiring diagram, schematic, or similar. It is slightly unusual to set the mode of an input to INPUT unless you have a pullup or pulldown resistor - which I cannot see.

Setting the mode of an input pin to INPUT without a pullup or pulldown resistor will lead to strange results.

Thanks, looks like the url didn’t highlight that well.

Reference pics

blh64:
The issue may be with the fact that you are calculating a servo position and then writing it out and not waiting any length of time. Servo motors do not move instantaneously to the new position. If you look at the the servo example in the library, they move 1 degree and then wait 15 msec before moving again.

…which is itself strange, because updates are only sent to the servo every 20ms or so.

TheMemberFormerlyKnownAsAWOL:
...which is itself strange, because updates are only sent to the servo every 20ms or so.

Yes, and the statement made by blh64 is not completely correct. After every servo.write() there should be a gap of at least 15ms or so before the next servo.write() to make sure that the first servo.write() is actually executed and not overwritten before it is executed. the sending of the pulse happens in the background as 'Prince' points out about every 20ms or so. Can't see anything obviously wrong in your code, but without it being auto-formatted and with all the white-space it is all tad hard to see for me. Show the wiring !

All wiring (real and fritzing) are in the imgur link.

Thanks for the help so far

So how are you powering the nano ? i mean really, i can not see any power connection to it ! that won't work.

Using universal power supply 6 volts for servo.
Seperate USB for nano.

regardless of how you power the Arduino, 4xAA batteries are probably not quite enough to power you servo which (it being not the smallest type, 12kg/cm torque no ?) may draw something in the range of 2A, if the arduino is power from there as well, that will cause it to reset, though you could power the Arduino through it USB, to make sure that that is not happening. That still leaves the Servo, 4x C-cell should be enough.

Cheers,

Just had an oh, I should have been using 12v moment. (nope, confirmed 6v)

Double checked specs of the Servo

HobbyKing™ High Torque Servo MG/BB W/Proof 12.8kg / 0.22sec / 58g

Specs:
Voltage: 4.5-6V
Speed: 0.26sec/60deg(4.8v) 0.22sec/60deg (6.0v)
Torque: 10kg.cm (4.8v) 12.8kg.cm (6.0v)
Size: 40.9mm x 20mm x37.75 mm
Weight: 58gram
Motor: coreless
Gear Material: Metal
Ball Bearing: 2
Type: analogue
Spline: 24

Pic here of the power supply:

Might not be obvious but, power from PSU goes to Power on servo,
Earth from PSU goes to Arduino earth
Earth from Arduino goes to servo earth
Pin 9 from Arduino goes to servo input.

What values are you seeing for your print of "average"? Why not also put some Serial.prints in to check exactly what you are writing to the servo?

The value of (noiseLevel * 13) where noiseLevel can be 0-1023 seems a bit high. Servo.write expects 0-180. From what I can see you only do the write if the value is between 325 (25 * 13) and 500. That means you throw away almost all the possible results and then write invalid values.

If you're intending to use microseconds rather than angles then it would make more sense to use writeMicroseconds() instead of write(angle). The Servo does do some defaulting when you give a value outside the angle limits but you really don't want to rely on that because it makes debugging almost impossible.

Or perhaps the code you posted isn't the code you're using?

Steve

Powering an Arduino and a servo from the same power supply is NOT a good plan. The servo can suck so much current from its supply that the Arduino resets or does other strange things. There should be two separate power supplies with their grounds connected together.

Correct. I’m powering the Nano from a seperate USB source.

Your averaging technique seems incorrect. You could be reading the pin at any point of the waveform, so you're not getting the true sound level.

I once had to measure sound level from a microphone and found some code posted on Adaafruit to be helpful, it shows how to measure "peak to peak amplitude".

Nice, I'll give that a shot. Thank you

slipstick:
The value of (noiseLevel * 13) where noiseLevel can be 0-1023 seems a bit high. Servo.write expects 0-180. From what I can see you only do the write if the value is between 325 (25 * 13) and 500. That means you throw away almost all the possible results and then write invalid values.

If you're intending to use microseconds rather than angles then it would make more sense to use writeMicroseconds() instead of write(angle). The Servo does do some defaulting when you give a value outside the angle limits but you really don't want to rely on that because it makes debugging almost impossible.

Steve

Ah, this makes sense. I did originally have mapping to convert the
"mouth.attach(9,500,2500); to 0-180. Not sure why I ended up abandoning that.
Still pretty new so now I know I can do it without the conversion it should help. Thank you. :slight_smile:

I think you got the power thing sorted (it just wasn't clear in the pictures) i would do a simple pre-programmed sweep to confirm that. Then since you have usb plugged in anyway, use serial to debug the rest.