-
Notifications
You must be signed in to change notification settings - Fork 512
Description
A dynamically allocated Servo will infinite loop when attached if the program uses a large amount of static memory.
I am using platform.io for the build system with the following platformio.ini file for all snippets below. Each snippet is in main.cpp
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:pico]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board = pico
framework = arduino
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
monitor_speed = 115200
platform_packages =
framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git#master
There are multiple factors at play here, so this issue will unfortunately have to be a wall of text.
Minimum reproducible example
#include "Arduino.h"
#include "Servo.h"
typedef struct {
//can have more fields or no additional fields. Reproduces either way.
Servo servo;
} ServoHolder;
ServoHolder* holder; // create servo holder object to hold a servo
// twelve servo objects can be created on most boards
double foo[20000];
void setup() {
Serial.begin(115200);
while(!Serial);
delay(1000);
Serial.println("Starting");
holder = static_cast<ServoHolder*>(malloc(sizeof(ServoHolder)));
Serial.println("Attaching servo");
Serial.println(sizeof(ServoHolder));
Serial.println(sizeof(Servo));
Serial.println((uint32_t) holder);
delay(1000);
holder->servo.attach(16); // attaches the servo on GIO16 to the servo object
Serial.println("Attached servo");
holder->servo.writeMicroseconds(1500);
delay(1000);
for(int i = 0; i < 10000; i++) {
Serial.print(foo[i]);
}
}
void loop() {
Serial.println("looping");
int pos;
for (pos = 1000; pos <= 2000; pos += 10) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
holder->servo.writeMicroseconds(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 2000; pos >= 1000; pos -= 10) { // goes from 180 degrees to 0 degrees
holder->servo.writeMicroseconds(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}Actual
With the code above, I get the following output:
Starting
Attaching servo
28
28
537041248
After this, there is no more output (adding code turning on the built-in LED shows that no code following this runs at all).
Expected
When I remove the foo array and comment the line printing items, I get the following output:
Starting
Attaching servo
28
28
536881248
Attached servo
looping
looping
looping
This shows that the allocated memory is large enough to hold a servo object, and that the object is allocated properly.
Global Servo
I also modified the program to use a global servo object as shown below.
#include "Arduino.h"
#include "Servo.h"
typedef struct {
Servo servo;
} ServoHolder;
ServoHolder* holder; // create servo holder object to hold a servo
// twelve servo objects can be created on most boards
Servo servo;
double foo[20000];
void setup() {
Serial.begin(115200);
while(!Serial);
delay(1000);
Serial.println("Starting");
holder = static_cast<ServoHolder*>(malloc(sizeof(ServoHolder)));
Serial.println("Attaching servo");
Serial.println(sizeof(ServoHolder));
Serial.println(sizeof(Servo));
Serial.println((uint32_t) holder);
delay(1000);
servo.attach(16); // attaches the servo on GIO16 to the servo object
Serial.println("Attached servo");
servo.writeMicroseconds(1500);
delay(1000);
for(int i = 0; i < 10000; i++) {
Serial.print(foo[i]);
}
}
void loop() {
Serial.println("looping");
int pos;
for (pos = 1000; pos <= 2000; pos += 10) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
servo.writeMicroseconds(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 2000; pos >= 1000; pos -= 10) { // goes from 180 degrees to 0 degrees
servo.writeMicroseconds(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}The output is as expected:
Starting
Attaching servo
28
28
537041280
Attached servo
0.000.000.000.000.000.000.000.00{many more 0.00s omitted to save space}looping
looping
Static memory allocation
For completeness's sake, I also wrote a version of this using dynamically allocated memory for foo.
#include "Arduino.h"
#include "Servo.h"
typedef struct {
Servo servo;
} ServoHolder;
ServoHolder* holder; // create servo holder object to hold a servo
// twelve servo objects can be created on most boards
double* foo;
void setup() {
Serial.begin(115200);
while(!Serial);
delay(1000);
Serial.println("Starting");
foo = static_cast<double*>(malloc(20000 * sizeof(double)));
holder = static_cast<ServoHolder*>(malloc(sizeof(ServoHolder)));
Serial.println("Attaching servo");
Serial.println(sizeof(ServoHolder));
Serial.println(sizeof(Servo));
Serial.println((uint32_t) holder);
delay(1000);
holder->servo.attach(16); // attaches the servo on GIO16 to the servo object
Serial.println("Attached servo");
holder->servo.writeMicroseconds(1500);
delay(1000);
for(int i = 0; i < 10000; i++) {
Serial.print(foo[i]);
}
}
void loop() {
Serial.println("looping");
int pos;
for (pos = 1000; pos <= 2000; pos += 10) { // goes from 0 degrees to 180 degrees
// in steps of 1 degree
holder->servo.writeMicroseconds(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 2000; pos >= 1000; pos -= 10) { // goes from 180 degrees to 0 degrees
holder->servo.writeMicroseconds(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}The attach call freezes as before:
Starting
Attaching servo
28
28
537041256