Hey @UKHeliBob I have a similar sketch I'm working on that will fire a servo after a delay until a counter stops it. It works fine when the delay between the servo movements are a few seconds or a minute but I want the interval to be 24 hours. If I do that, the servo just moves back and forth a few degrees. Is there a reason it can't handle the long interval. Sketch is below:
/*
Sketch generated by the Arduino IoT Cloud Thing "Untitled"
https://create.arduino.cc/cloud/things/185cf3fd-641b-4013-9ca8-8a5d0b3c36f5
Arduino IoT Cloud Variables description
The following variables are automatically generated and updated when changes are made to the Thing
String uptimeStr;
float uptime;
int count;
int position;
bool ledStatus;
Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
which are called when their values are changed from the Dashboard.
These functions are generated with the Thing and added at the end of this sketch.
*/
#include "thingProperties.h"
#include <Servo.h>
Servo myservo;
// Array to store the degrees for each move
int moveDegrees[8] = {15, 37, 62, 85, 112, 135, 157, 179};
const long oneSecond = 1000; // a second is a thousand milliseconds
const long fiveSeconds = oneSecond * 5;
const long oneMinute = oneSecond * 60;
const long oneHour = oneMinute * 60;
const long oneDay = oneHour * 24;
unsigned long startTime; // Variable to store the start time
const int ledPin = LED_BUILTIN; // or another digital pin if your board doesn't have a built-in LED
void setup() {
// Initialize serial and wait for port to open:
Serial.begin(9600);
// This delay gives the chance to wait for a Serial Monitor without blocking if none is found
delay(1500);
//set up the servo
myservo.attach(9); // Attach the servo on pin 9
myservo.write(0); // Move to initial position
// Defined in thingProperties.h
initProperties();
// Connect to Arduino IoT Cloud
ArduinoCloud.begin(ArduinoIoTPreferredConnection);
/*
The following function allows you to obtain more information
related to the state of network and IoT Cloud connection and errors
the higher number the more granular information you’ll get.
The default is 0 (only errors).
Maximum is 4
*/
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
count = 0;
// set the start time of the uptime counter
startTime = millis();
// code for LED status light
pinMode(ledPin, OUTPUT); // Set the LED pin as an output
// Initialize ledStatus
ledStatus = true; // Assuming the device is powered on
digitalWrite(ledPin, ledStatus ? HIGH : LOW); // Set the LED according to ledStatus
}
void loop() {
ArduinoCloud.update();
// Calculate uptime in minutes
uptime = (millis() - startTime) / 3600000.0; // Convert milliseconds to minutes
// Calculate uptime in hours and minutes
unsigned long currentUptime = millis() - startTime; // Uptime in milliseconds
int hours = currentUptime / 3600000; // Convert to hours
int minutes = (currentUptime % 3600000) / 60000; // Convert remainder to minutes
// Format the uptime as a string "HH:MM" and assign to uptimeStr
uptimeStr = String(hours) + ":" + (minutes < 10 ? "0" : "") + String(minutes);
// Your code here
if (count < 7) {
// Move the servo to the next position
myservo.write(moveDegrees[count]);
delay(fiveSeconds); // Wait for one day before next move
// Debugging print statements
// Serial.print("Count: ");
// Serial.println(count);
// Serial.print("Servo Position: ");
// Serial.println(moveDegrees[count]);
count++;
} else if (count == 7) {
myservo.write(moveDegrees[count]); // Move to 179 degrees
delay(500); // Short delay
myservo.write(0); // Move back to initial position
delay(fiveSeconds); // Wait for 5 seconds
// Reset count to start the sequence over
count = 0;
// Debugging print statements
Serial.println("Moved to 179 and back to 0");
}
// Toggle the LED state
ledStatus = !ledStatus;
// Update the physical LED to match the ledStatus
digitalWrite(ledPin, ledStatus ? HIGH : LOW);
// Wait for one half second
delay(500);
}
Yes. When I change the delay variable to oneDay, the servo moves to the counterclockwise 15 degree position and then back clockwise to the 0 position, over and over until I pull the plug.
Hmm. It works fine with a delay of one second or five seconds. But if I change the delay to one hour, I get the same behavior as the one-day delay. I thought it might be that the servo doesn't like staying in an intermediate position for too long but the odd behavior with the longer delays happens immediately. So it seems more of a software problem.
I can't be sure this is a, or your, This is not your problem, but in general times are best keep in unsigned long variables, like
const unsigned long oneSecond = 1000; // a second is a thousand milliseconds
const unsigned long fiveSeconds = oneSecond * 5;
const unsigned long oneMinute = oneSecond * 60;
const unsigned long oneHour = oneMinute * 60;
const unsigned long oneDay = oneHour * 24;
Rather than sit around all day waiting to see if the numbers you are feeding to delay() are correct, you could
Thanks. I tried printing the variables and the numbers are correct in milliseconds for the oneMinute etc. variables. If I run a simpler version via the Arduino IDE, it seems to work fine. I haven't waited an hour yet but after the first servo movement to first position in the array, it stays there nicely, seemingly patiently waiting for an hour to expire rather than cycling back and forth. I had hoped to make this work with a Cloud dashboard so I could monitor it from afar but I may have to retreat to the simple code below with no IoT Cloud connectivity.
// need to set the interval time to 24 hours
#include <Servo.h>
Servo myservo;
// Array to store the degrees for each move
int moveDegrees[8] = {15, 37, 62, 85, 112, 135, 157, 179};
int count = 0;
const long oneSecond = 1000; // a second is a thousand milliseconds
const long fiveSeconds = oneSecond * 5;
const long oneMinute = oneSecond * 60;
const long oneHour = oneMinute * 60;
const long oneDay = oneHour * 24;
void setup() {
myservo.attach(9); // Attach the servo on pin 9
myservo.write(0); // Move to initial position
delay(fiveSeconds); // Initial delay for setup
Serial.begin(9600);
}
void loop() {
if (count < 7) {
// Move the servo to the next position
myservo.write(moveDegrees[count]);
Serial.println(oneMinute);
Serial.println(oneHour);
Serial.println(oneDay);
delay(oneHour); // Wait for 2 seconds before next move
// Debugging print statements
// Serial.print("Count: ");
// Serial.println(count);
// Serial.print("Servo Position: ");
// Serial.println(moveDegrees[count]);
count++;
} else if (count == 7) {
myservo.write(moveDegrees[count]); // Move to 179 degrees
delay(500); // Short delay
myservo.write(0); // Move back to initial position
delay(oneHour); // Wait for 5 seconds
// Reset count to start the sequence over
count = 0;
// Debugging print statements
Serial.println("Moved to 179 and back to 0");
}
}
That's a good idea. I did not think of the variation I present below, but it is another approach. One nice thing is that it uses the original biggish numbers, so there's no missing an integer maths error
void myDelay(unsigned long t)
{
Serial.print("delay "); Serial.print(t);
if (t > 100000) t /= 1000; // more than 100 seconds 1000x speed
if (t > 10000) t /= 100; // more than 10 seconds 10x
Serial.print(" ("); Serial.print(t); Serial.println(")");
delay(t);
}
# define delay myDelay
Placed at the top of the sketch, this pattern allows adjustable speed up. Above I just arbitrarily chose a few break points (give me a break! I don't got all day here!), but I can imagine some kind of whizzy logarithmic function that would gently bend the arc of time over a wide range.
I would like to add some kind of smart optional printing to it. For now I just print the original delay argument and what it gets changed to.
I put this in @cmharges's Cloud free version and ran it to see the servo doing its thing.
This may be abuse of the # define mechanism, if so sue me.
you don't get minutes with that division but hours
--
using long delay() as you do will prevent calling ArduinoCloud.update(); frequently ... You need to call that as often as possible, meaning don't block things with delay
#define warpFactor 10
#define millis() (millis()*warpFactor)
const long interval = 1000;
unsigned long lastTime;
void setup() {
Serial.begin(115200);
Serial.println("Printing a line every " + String(interval) + " milliseconds");
Serial.println("Warp factor: " + String(warpFactor));
}
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - lastTime >= interval) {
Serial.println(currentMillis);
lastTime = currentMillis;
}
}
Only the first couple of lines do the job.
I've used it several times, but I guess there will be programs where it may do something funky when the interval is not an integer multiple of warpFactor.
Thanks everybody. The delay messing up communication with the Cloud makes sense. I'm not sure that makes a difference in the servo functionality though. All the Thing variables are read only, so the lack up Cloud updates should only affect the monitoring.
I may just be dense here but why would changing how we make the delay short make a difference? Right now, if (in the Cloud version) if I set the delay via a hard-coded integer or a variable that I can conveniently change above, I'll will get the same servo result, right?
Or am I missing something fundamental here and that's why I can get long delays via the stand-alone version but not via the Cloud version?
When you do integer arithmetic, there's all kindsa opportunities for surprises. For example when someone says something breaks about thirty seconds in, we immediately ask if isn't more like 32.768 seconds, and look at whether maybe a signed 16 bit integer was used where an unsigned 32 bit integer woukd have been better.
So figuring with big numbers needs some attention and is normally checked when someone says a short delay was different to a long delay.
If you just change the constants, you may hide the problem. So any scheme that can speed things up might better involve the programmer's original constants, and do the speed up in a different way.
The same thing obtains with floating point and mixing integer maths with floating point. That's why my first thought was my first thought, and my second was to just be sure by printing the constants.
Whoa. Interesting. I (of course) had no idea. Thank you.
I've ruled out bad math as the problem though. Here's why: fixed integers delays (1000, 60000, 3600000) and the calculated ones (oneSecond, oneMinute, oneHour) all work from the base sketch below. I can load this via USB from the IDE or from the USB via the online editor and they all work.
It's seems it's only when I link it to a Thing that it breaks. The only difference between the base sketch (first below) and the Thing sketch are the lines that get added in Thing set-up. Even a simplified version of the Thing sketch with just Thing variables, properties and the Cloud connections (second below) and no time calculations has the back-and-forth problem.
BASE SKETCH
#include <Servo.h>
Servo myservo;
// Array to store the degrees for each move
int moveDegrees[8] = {15, 37, 62, 85, 112, 135, 157, 179};
int count = 0;
const long oneSecond = 1000; // a second is a thousand milliseconds
const long fiveSeconds = oneSecond * 5;
const long oneMinute = oneSecond * 60;
const long oneHour = oneMinute * 60;
const long oneDay = oneHour * 24;
void setup() {
myservo.attach(9); // Attach the servo on pin 9
myservo.write(0); // Move to initial position
delay(fiveSeconds); // Initial delay for setup
Serial.begin(9600);
}
void loop() {
if (count < 7) {
// Move the servo to the next position
myservo.write(moveDegrees[count]);
Serial.println(oneMinute);
Serial.println(oneHour);
Serial.println(oneDay);
delay(oneHour); // Wait for 2 seconds before next move
// Debugging print statements
// Serial.print("Count: ");
// Serial.println(count);
// Serial.print("Servo Position: ");
// Serial.println(moveDegrees[count]);
count++;
} else if (count == 7) {
myservo.write(moveDegrees[count]); // Move to 179 degrees
delay(500); // Short delay
myservo.write(0); // Move back to initial position
delay(oneHour); // Wait for 5 seconds
// Reset count to start the sequence over
count = 0;
// Debugging print statements
Serial.println("Moved to 179 and back to 0");
}
}
THING SKETCH
/*
Sketch generated by the Arduino IoT Cloud Thing "Untitled"
https://create.arduino.cc/cloud/things/185cf3fd-641b-4013-9ca8-8a5d0b3c36f5
Arduino IoT Cloud Variables description
The following variables are automatically generated and updated when changes are made to the Thing
String uptimeStr;
float uptime;
int count;
int position;
bool ledStatus;
Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
which are called when their values are changed from the Dashboard.
These functions are generated with the Thing and added at the end of this sketch.
*/
#include "thingProperties.h"
#include <Servo.h>
Servo myservo;
// Array to store the degrees for each move
int moveDegrees[8] = {15, 37, 62, 85, 112, 135, 157, 179};
void setup() {
// Initialize serial and wait for port to open:
Serial.begin(9600);
// This delay gives the chance to wait for a Serial Monitor without blocking if none is found
delay(1500);
//set up the servo
myservo.attach(9); // Attach the servo on pin 9
myservo.write(0); // Move to initial position
// Defined in thingProperties.h
initProperties();
// Connect to Arduino IoT Cloud
ArduinoCloud.begin(ArduinoIoTPreferredConnection);
/*
The following function allows you to obtain more information
related to the state of network and IoT Cloud connection and errors
the higher number the more granular information you’ll get.
The default is 0 (only errors).
Maximum is 4
*/
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
count = 0;
}
void loop() {
ArduinoCloud.update();
if (count < 7) {
// Move the servo to the next position
myservo.write(moveDegrees[count]);
delay(10000); // Wait for one second before next move
count++;
} else if (count == 7) {
myservo.write(moveDegrees[count]); // Move to 179 degrees
delay(500); // Short delay
myservo.write(0); // Move back to initial position
delay(10000); // Wait for 1 second
// Reset count to start the sequence over
count = 0;
}
}
That might be enough although My bet would be on the fact that If you put a long delay between updates the connection is dropped and this library is not able to recover for some reason when the loop loops and you call update()
Note that the documentation states explicitly not to use delay.
Restructure your code using a state machine and using millis and get rid of the delays so that you can call update() often enough.
As they should. You could restructure your code as @J-M-L suggests. I think everyone would like to see you take the next steps. Whenever.
A quick test might resolve the diagnosis. If we write a delay function that periodically called
ArduinoCloud.update();
we could leave the current logic intact. I have not (yet) tested this tested this, it works.
const unsigned long updateInterval = 60000UL; // cloud thing once a minute
void updateDelay(unsigned long t)
{
static unsigned long lastUpdate;
static unsigned long delayTimer;
delayTimer = millis();
lastUpdate = millis();
for (; ; ) {
unsigned long now = millis();
if (now - delayTimer >= t) break;
if (now - lastUpdate >= updateInterval) {
ArduinoCloud.update();
lastUpdate = now;
}
}
return;
}
# define delay updateDelay
It's the replace *delay*() # define hack with a bit of "blink without delay" thrown in there. A profane bit of code, to be sure.
Added: If you only use short delays, the update function won't be called, so it won't solve a long sequence of short delays, so anything like that should make its own update call. Or modify the function.
OK someone here who I shall not name is a bit more clever. A few tweaks and the short delay problem is solved:
void iAmALive()
{
Serial.println(" doing that delay thing");
}
const unsigned long updateInterval = 7000UL; // cloud thing once every seven seconds
void updateDelay(unsigned long t)
{
static unsigned long lastUpdate;
static unsigned long delayTimer;
delayTimer = millis();
// lastUpdate = millis();
for (; ; ) {
unsigned long now = millis();
if (now - lastUpdate >= updateInterval) {
iAmALive();
lastUpdate = now;
}
if (now - delayTimer >= t) break;
}
return;
}
# define delay updateDelay
She calls iAmALive() every seven seconds, but you get the idea. updateDelay() tracks the need for an update across calls. When it is time, it calls out.