Problem with servo movement code

Hi,

I'm new to the forum, so please excuse me if I've posted this in the wrong section, and feel free to move it if you have the power to do so.

I'm currently building a cuckoo clock, and I've got the model done etc., but i'm having an issue with the code. (Both sound and servo, but currently only concerned with the servo).

Unfortunately for me, I am currently unable to afford the hardware (as cheap as it may be) so I'm getting a friend in Germany to help me out with building it.
We are intending to use a simple quartz clock movement like you'd find in most modern cheap clocks, an ESP32-WROOM-32S, an SG90 servo, and a speaker.

To give you a run down of the process, the clock body and mechanism have been 3d printed.
The intention is, to get the ESP to connect to WIFI, get the correct time from an NTP server, then disconnect the WIFI (hopefully to save a bit of power).
Then once it's obtained the time, it ticks over internally. It should then check to see if the minute is = to 00, and if it is, play a short melody using the internal DAC attached to the speaker, then check the hour, and use the servo to push the cuckoo out and in the appropriate amount of times, while also playing a "Chime" sound the same amount of time.

So far, I've managed to get he WIFI and NTP working correctly, and as expected, the time does continue to tick over internally, but as yet, I've not been able to get the servo to move.

I have 2 example sketches below. One that sort of works, and one that doesn't, but i am unable to understand why the second one doesn't. (For the moment, I've removed the "minute" check, just so I can test it at any time based on the hours)

The first one is as follows.

#include <ESP32Servo.h>
//#include "SoundData.h";
//#include "XT_DAC_Audio.h";
#include <WiFi.h>;
#include "time.h";

// create address reference to SoundData.h (function name then address in brackets)
//XT_Wav_Class PlayMelody(SONG);

// create address reference to SoundData.h
//XT_Wav_Class PlayChime(SPACERS);

// Create the main player class object.
// Use GPIO 25, one of the 2 DAC pins and timer 1
//XT_DAC_Audio_Class DacAudio(25, 1);

// create servo object to control a servo
Servo myservo;
// variable to store the servo position
int pos = 0;
// Recommended PWM GPIO pins on the ESP32 include 2,4,12-19,21-23,25-27,32-33
int servoPin = 14;

//setup wifi and time constructs
const char* ssid = "SSID";
const char* password = "PASS";

const char* ntpServer = "de.pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
int second;
int minute;
int hour;
int day;
int month;
int year;
int weekday;
long current;
struct tm timeinfo;

//setup variables for chime amount with value of 100 (outside normal range)
//int chimeAmount = 100;

void printLocalTime() //time check function
{

if (!getLocalTime(&timeinfo)) {
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %d %B %Y %H:%M:%S");
}

void setup()
{
Serial.begin(115200);

{
// standard 50 hz servo
myservo.setPeriodHertz(50);
// attaches the servo on pin 14 to the servo object
myservo.attach(servoPin, 500, 2400);
}
//connect to WiFi
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");

//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();

//disconnect WiFi as it's no longer needed
WiFi.disconnect(true);
WiFi.mode(WIFI_OFF);
}

void loop()
{

delay(1000);

printLocalTime();
ServoOp((hour));
//second = timeinfo.tm_sec;
//minute = timeinfo.tm_min;
//hour = timeinfo.tm_hour;
//day = timeinfo.tm_mday;
//month = timeinfo.tm_mon + 1;
//year = timeinfo.tm_year + 1900;
//weekday = timeinfo.tm_wday + 1;

}
void ServoOp(int hr )
{
int n=0;
if (hr > 12) hr=hr-12;
if (hr == 0) hr=12;

n=0;
while (n<hr)
//DacAudio.PlayWav(&PlayChime);
{
for(pos = 0; pos < 86; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(10); // waits 10ms for the servo to reach the position
}
for(pos = 86; pos>=0; pos-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(10); // waits 10ms for the servo to reach the position
}
{
hr--;
}
}
}

With this sketch, the servo does move, but it does not move the appropriate amount of times. It always seems to move 12 times (out and in (so 24 in total).

This next sketch, as you can see, has changed only very little (the logic at the top of "void servoOP),
and yet the servo does not move at all.

I am unable to understand why this simple logic change stops the servo from being activated.
To save space, I have out code which is the same

void loop()
{

delay(1000);

printLocalTime();
ServoOp((hour));
}

void ServoOp(int hr )
{
int n=0;
if (hr < 6) hr = 0;
if ((hr >= 6) && (hr <= 12) hr = hour);
if ((hr > 12) && (hr <= 18) hr = hour - 12;
/*

  • if (hr == 0)hr = 0;
    if (hr < 6)hr = 0;
    if (hr == 6) hr = 6;
    if (hr == 7) hr = 7;
    if (hr == 8) hr = 8;
    if (hr == 9) hr = 9;
    if (hr == 10) hr = 10;
    if (hr == 11) hr = 11;
    if (hr == 12) hr = 12;
    if (hr == 13) hr = 1;
    if (hr == 14) hr = 2;
    if (hr == 15) hr = 3;
    if (hr == 16) hr = 4;
    if (hr == 17) hr = 5;
    if (hr == 18) hr = 6;
    if (hr == 19) hr = 0;
    if (hr == 20) hr = 0;
    if (hr == 21) hr = 0;
    if (hr == 22) hr = 0;
    if (hr == 23) hr = 0;
    if (hr == 24) hr = 0;
    */
    n=0;
    while (n<hr)
    {
    for(pos = 0; pos < 86; pos += 1) // goes from 0 degrees to 180 degrees
    { // in steps of 1 degree
    myservo.write(pos); // tell servo to go to position in variable 'pos'
    delay(10); // waits 10ms for the servo to reach the position
    }
    for(pos = 86; pos>=0; pos-=1) // goes from 180 degrees to 0 degrees
    {
    myservo.write(pos); // tell servo to go to position in variable 'pos'
    delay(10); // waits 10ms for the servo to reach the position
    }
    {
    hr--;
    }
    }
    }

(I have commented out a section where I tried a different method, just in case I was being too complex, but again, nothing.

I've also tried using the following in the "void servoOP", but again, this also doesn't do anything.

void ServoOp(int hr )
{

if (hr > 12) hr = hr - 12;
for (int i=0;i<hr;i++) {
for (pos=0;pos<86;pos++) {
myservo.write(pos);
delay(10);
}
//DacAudio.PlayWav(&PlayMelody);
for (pos=86;pos>=0;pos++) {
myservo.write(pos);
delay(10);
}
}

If anyone is able to give me any clues as to where I have gone wrong with all of this, it would be greatly appreciated :slight_smile:

Sorry about that. I was looking for the code tag, but I couldn't find it.
Currently, the servo being used is a simple TowerPro SG90.
The reason I put that setPeriodHertz bit in, is it was provided in the example for the ESP32 servo library.

At present, the servo only works if I use the code

void ServoOp(int hr ) 
{ 
 int n=0; 
   if (hr > 12) hr=hr-12;
   if (hr == 0) hr=12;

If I use any of these

void ServoOp(int hr ) 
{ 
 int n=0; 
   if (hr < 6) hr = 0;
   if ((hr >= 6) && (hr <= 12) hr = hour);
   if ((hr > 12) && (hr <= 18) hr = hour - 12;

or

void ServoOp(int hr ) 
{ 
if (hr == 0)hr = 0;
   if (hr < 6)hr = 0;
   if (hr == 6) hr = 6;
   if (hr == 7) hr = 7;
   if (hr == 8) hr = 8;
   if (hr == 9) hr = 9;
   if (hr == 10) hr = 10;
   if (hr == 11) hr = 11;
   if (hr == 12) hr = 12;
   if (hr == 13) hr = 1;
   if (hr == 14) hr = 2;
   if (hr == 15) hr = 3;
   if (hr == 16) hr = 4;
   if (hr == 17) hr = 5;
   if (hr == 18) hr = 6;
   if (hr == 19) hr = 0;
   if (hr == 20) hr = 0;
   if (hr == 21) hr = 0;
   if (hr == 22) hr = 0;
   if (hr == 23) hr = 0;
   if (hr == 24) hr = 0;

The servo does nothing at all.

The servo is currently connected to pin 14, 5V pin, and GND, and is powered by USB.

Initially, I was using this code, but it didn't work either

#include <ESP32Servo.h>
#include "SoundData.h";
#include "XT_DAC_Audio.h";
#include <WiFi.h>;
#include "time.h";


// create address reference to SoundData.h (function name then address in brackets)
XT_Wav_Class PlayMelody(SONG);

// create address reference to SoundData.h
XT_Wav_Class PlayChime(SPACERS);

// Create the main player class object.
// Use GPIO 25, one of the 2 DAC pins and timer 1
XT_DAC_Audio_Class DacAudio(25, 1);

// create servo object to control a servo
Servo myservo;
// variable to store the servo position
int pos = 0;
// Recommended PWM GPIO pins on the ESP32 include 2,4,12-19,21-23,25-27,32-33
int servoPin = 14;

//setup wifi and time constructs
const char* ssid       = "SSID";
const char* password   = "PASS";

const char* ntpServer = "de.pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;
int second;
int minute;
int hour;
int day;
int month;
int year;
int weekday;
long current;
struct tm timeinfo;

//setup variables for chime amount with value of 100 (outside normal range)
int chimeAmount = 100;

void printLocalTime()   //time check function
{

  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %d %B %Y %H:%M:%S");
}





void setup()
{
  Serial.begin(115200);

  {
    // standard 50 hz servo
    myservo.setPeriodHertz(50);
    // attaches the servo on pin 14 to the servo object
    myservo.attach(servoPin, 500, 2400);
  }
  //connect to WiFi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" CONNECTED");

  //init and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
  printLocalTime();

  //disconnect WiFi as it's no longer needed
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
}

void loop()
{

  delay(1000);

  printLocalTime();
  second = timeinfo.tm_sec;
  minute = timeinfo.tm_min;
  hour = timeinfo.tm_hour;
  day = timeinfo.tm_mday;
  month = timeinfo.tm_mon + 1;
  year = timeinfo.tm_year + 1900;
  weekday = timeinfo.tm_wday + 1;


  //checks to see if minutes = 00, then sets the chimeAmount variable to the hour, with adjustments for 24hr time --- will only chime between 6AM and 6PM in this setup - If minutes is not = 00 go back to start
  if (minute == 00);
  {
    if (hour < 6);
    {
      return;
    }
    if (hour <= 12);
    {
      (chimeAmount = hour);
    }
    if ((hour >= 13) && (hour <= 18));
    {
      chimeAmount = (hour - 12);
    }
  }
  //checks value of chimeAmount and if > 0, play melody, check if completed, then run ServoAndChime in a loop until chimeAmount = 0
  if (chimeAmount > 0);
  {
    DacAudio.PlayWav(&PlayMelody);  // call to play melody object created at top of code
  }
  while (chimeAmount > 0)
  {
    DacAudio.PlayWav(&PlayChime);  // call to play chime object created at top of code
    // goes from 0 degrees to 86 degrees in steps of 1 degree
    for (pos = 0; pos <= 86; pos += 1)
    {
      // tell servo to go to position in variable 'pos'
      myservo.write(pos);
      // waits 15ms for the servo to reach the position
      delay(15);
    }
    for (pos = 86; pos >= 0; pos -= 1)
      // goes from 86 degrees to 0 degrees in steps of 1 degree
    {
      // tell servo to go to position in variable 'pos'
      myservo.write(pos);
      // waits 15ms for the servo to reach the position
      delay(15);
      chimeAmount--; //-1 from counter til it reaches 0
    }
  }
}

And yes, i've tried altering it so it will go off any any hour (not only between 6am and 6pm) but it still refused to work for me.

Ok! I moved the chimeAmount--; in to its own set of brackets, and it seems to be behaving itself for the moment! :slight_smile:
Many many thanks for that indeed! Also it does seem to be working on the 5V alone at present as well!

welbot:
Also it does seem to be working on the 5V alone at present as well!

Yes a single small servo like an SG90 usually will work, particularly when it isn't trying to move a load but is just waving the servo arm about.

Try it with bigger servos or more than one servo or a heavy load on that one and you'll soon realise why we suggest not using the Arduino 5V pin to power servos.

Steve