Hello everyone, I am working on an animatronic prop with my daughters to encourage science and engineering. We have 3D printed all the parts and have been tinkering with the Arduino, servos, and now the code. We closely followed Bill’s YouTube video “Using SD Cards with Arduino - Record Servo Motor Movements”. We went step by step and learned significantly, especially since we have no coding experience. We interpolated a lot of information and used his code, along with bits from other code and got to the point where we can connect four (4) pots, and control four (4) servos, but only record 1 channel. We worked out the errors, but we are not getting to record all channels, and in the playback, the servos are sped up and do not follow the timing we established. We have spent hours trying to resolve these kinks, but no luck, moral is low.
We followed a similar post on this forum to compose a code, but it included LED inputs we do not need (and do not clearly understand), and we also get errors. Therefore, we need assistance with a code that will do the following:
- Servos set to an arbitrary start position.
- Move pots that in turn move servos.
- Press a physical button (or “R” in the serial monitor) to start recording the pots and servos.
- Record those movements for a period of time (8GB card should store a lot of input).
- Press a physical button (or “P” in the serial monitor) to stop the recording and store it on the SD card and play it back.
- We need to be able to power everything down, transport, and when plugged back it, the servos begin to play the pre-recorded movements indefinitely.
- BONUS: We would like to add more servos to the code, but we can hopefully figure out the pattern in the code (which we have for some other things), and expand on it.
I have attached the code to record and playback which is a combination of things I have put together, the code recognizes the pots and servos, and lets me move them, but it only records one, and the playback is fast. We omitted a SERVO controller for now to simplify things and go stragiht off the arduino. Thank you again so much!
This is my attempt at the record code and the playback code.
RECORD Code from DroneBot Workshop 2019:
/*
Servo position recorder
servo-record.ino
Records servo movements on SD card
Displays results on Serial Monitor
DroneBot Workshop 2019
*/
// Include libraries
#include <SPI.h>
#include <SD.h>
#include <Servo.h>
// CS pin for SD Card Module
const int chipSelect = 4;
// Analog pin for potentiometer
int analogPin = A0;
int analogPin1 = A1;
int analogPin2 = A2;
int analogPin3 = A3;
// Integer to hold potentiometer value
int val1 = 0;
int val2 = 0;
int val3 = 0;
int val4 = 0;
// Create a Servo object
Servo myservo;
Servo myservo2;
Servo myservo3;
Servo myservo4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
// Attach servos to the servo object
myservo.attach(9);
myservo2.attach(8);
myservo3.attach(7);
myservo4.attach(6);
}
void loop() {
// make a string for assembling the data to log:
String dataString = "";
String dataString2 = "";
String dataString3 = "";
String dataString4 = "";
// Read pot value and append to the string
// Map to range of 0-180 for servo
for (int analogPin = 0; analogPin < 4; analogPin++) {
int sensor = analogRead(analogPin);
dataString += String(sensor);
if (analogPin < 4) {
dataString += ",";
}
}
val1 = map(analogRead(analogPin), 0, 1023, 0, 180);
dataString += String(val1);
myservo.write(val1);
val2 = map(analogRead(analogPin1), 0, 1023, 0, 180);
dataString2 += String(val2);
myservo2.write(val2);
val3 = map(analogRead(analogPin2), 0, 1023, 0, 180);
dataString3 += String(val3);
myservo3.write(val3);
val4 = map(analogRead(analogPin3), 0, 1023, 0, 180);
dataString4 += String(val4);
myservo4.write(val4);
// Write to the servo
// Delay to allow servo to settle in position
myservo.write(val1);
myservo2.write(val2);
myservo3.write(val3);
myservo4.write(val4);
delay(15);
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("servopo1.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile) {
dataFile.println(dataString);
dataFile.close();
// print to the serial port too:
Serial.println(dataString);
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening servopo1.txt");
}
}
PLAYBACK Code from DroneBot Workshop 2019:
/*
Servo position playback
servo-playback.ino
Plays back servo movements from SD card
Used with servo-record.ino
Displays results on Serial Monitor
DroneBot Workshop 2019
*/
// Include libraries
#include <SPI.h>
#include <SD.h>
#include <Servo.h>
// CS pin for SD Card Module
const int chipSelect = 4;
// String to hold one line of text
String buffer;
// Create a Servo object
Servo myservo;
Servo myservo2;
Servo myservo3;
Servo myservo4;
void setup() {
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
// Attach servo on pin 9 to the servo object
myservo.attach(9);
myservo2.attach(8);
myservo3.attach(7);
myservo4.attach(6);
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("servopo1.txt");
// If the file is available, read it
if (dataFile) {
while (dataFile.available()) {
// Write one line to buffer
buffer = dataFile.readStringUntil('\n');
// Print to serial monitor
Serial.println(buffer);
// Convert string to integer and position servo
myservo.write(buffer.toInt());
myservo2.write(buffer.toInt());
myservo3.write(buffer.toInt());
myservo4.write(buffer.toInt());
delay(15);
}
dataFile.close();
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening servopo1.txt");
}
}
void loop() {
}
Please place your code using the following button. I tried to just copy/paste your code in an editor and it was double spaced. I'd like to help. Sounds like a cool challenge, but I'm not willing to spend 30 minutes simply so I can read your code.
Thanks,
Inq
3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide
@hinobot Lot's of hep available under the Help Menu including a practice forum where you can try something like posting code to see what it looks like.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
First off, you should open the file in the setup() section. If it doesn't open, print a message and hang there until the user stops the process. By creating and reopening it each time through the loop, you're slowing the loop() considerably. Once the file is open you are free to continue writing to it from loop() as long as you want.
It is difficult to understand the flow of control in the recording section (I just haven't gotten to the playback side yet). You have a variable called dataString to which you append the (comma separated) values of the pots each time through the loop. However, you don't use anything to delimit the end of the line (of 4 readings) and you pollute this data by subsequently appending another value (mapped to a servo position val1 and converted to a string) without any delimiter,
Inside loop() first, you have a for loop which reads each of the pots, converts the raw value to a string and appends that onto a string (called dataString) containing all 4 results separated by commas. So far, so good, but you don't then distinguish the "end of line" in any way.
You then REREAD each pin (why, you already have a value from above), you then map it from the 0-1023 range of the pot to a 0-180 range for the servo. You then write the mapped value to the servo. This time, you save the first (and only the first) value again to dataString without any delimiters and save the other values (val2, val3, val4) to dataString2, dataString3 and dataString4.
If that's not confusing enough, you then rewrite val1 to val4 to their respective servos again.
I think your first task here is to decide what you want in the data strings. Maybe create a dataString1 to correspond to the dataString2, ...3, ...4 if you think you still need them. It's hard to tell because you do append values to them but you never save them to the file anywhere.
You should also consider whether you need to store the raw values (i.e. directly from the pots as you do now) or the values as mapped for servo positions. Which will you really need for playback ?
After you've got that debugged, post the updated file again and note any problems observed, but only for the "record" part; the "playback" part needs to be treated separately (and can only be done once you decide what information you'll be storing and how you'll store it).
Anything seems possible when you don't know what you're talking about.
Industrial robots were programmed using a very similar kind of action... move to, then record point and then play them back. It took huge amounts of time to say... paint a door panel. I'd bet, they still do, but maybe they've advanced since the last time I was near one mid '80s.
From just your description (haven't looked at your code yet) it sounds like you have two separate problems:
- Only recording or only playing back one channel.
- Not recording or not playing back the time component of the data.
How are you adding the time component?
I imagine you want to move the body around until you get it "just right" then you want to take a snapshot. Then you move it some more and take another snapshot. Kind of like they do with Claymation videos. When you play yours back the servos move to each position, "one snapshot" at a time. I imagine it takes far more time to "stage" some snapshots than others, so you have to add the time manually (I'm guessing).
Does this sound like what you want to do?
3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide
@inq My IBM account back in the early 70's was Ford in Oakville Ont. They had huge car painting robots and I was told they 'programmed' them by an expert human painter grasping the paint gun and actually painting a door for example. The robot would then perform the exact same motions mistakes and all. I don't know but am guessing there were a small group of these expert human painters and maybe they held a competition to see who could paint the door the most efficiently, least time, least material but still get full coverage. I wonder if that is how it is still done, or is artificial vision used now?
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@inq My IBM account back in the early 70's was Ford in Oakville Ont. They had huge car painting robots and I was told they 'programmed' them by an expert human painter grasping the paint gun and actually painting a door for example. The robot would then perform the exact same motions mistakes and all. I don't know but am guessing there were a small group of these expert human painters and maybe they held a competition to see who could paint the door the most efficiently, least time, least material but still get full coverage. I wonder if that is how it is still done, or is artificial vision used now?
I'd bet Tesla factory are doing something like that with vision and AI.
3 lines of code = InqPortal = Complete IoT, App, Web Server w/ GUI Admin Client, WiFi Manager, Drag & Drop File Manager, OTA, Performance Metrics, Web Socket Comms, Easy App API, All running on ESP8266...
Even usable on ESP-01S - Quickest Start Guide
This is easier to read and why does the other method add another CRLF to the one already there.
// RECORD Code from DroneBot Workshop 2019 /* Servo position recorder servo-record.ino Records servo movements on SD card Displays results on Serial Monitor DroneBot Workshop 2019 https://dronebotworkshop.com */ // Include libraries #include <SPI.h> #include <SD.h> #include <Servo.h> // CS pin for SD Card Module const int chipSelect = 4; // Analog pin for potentiometer int analogPin = A0; int analogPin1 = A1; int analogPin2 = A2; int analogPin3 = A3; // Integer to hold potentiometer value int val1 = 0; int val2 = 0; int val3 = 0; int val4 = 0; // Create a Servo object Servo myservo; Servo myservo2; Servo myservo3; Servo myservo4; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.print("Initializing SD card..."); // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); // don't do anything more: while (1); } Serial.println("card initialized."); // Attach servos to the servo object myservo.attach(9); myservo2.attach(8); myservo3.attach(7); myservo4.attach(6); } void loop() { // make a string for assembling the data to log: String dataString = ""; String dataString2 = ""; String dataString3 = ""; String dataString4 = ""; // Read pot value and append to the string // Map to range of 0-180 for servo for (int analogPin = 0; analogPin < 4; analogPin++) { int sensor = analogRead(analogPin); dataString += String(sensor); if (analogPin < 4) { dataString += ","; } } val1 = map(analogRead(analogPin), 0, 1023, 0, 180); dataString += String(val1); myservo.write(val1); val2 = map(analogRead(analogPin1), 0, 1023, 0, 180); dataString2 += String(val2); myservo2.write(val2); val3 = map(analogRead(analogPin2), 0, 1023, 0, 180); dataString3 += String(val3); myservo3.write(val3); val4 = map(analogRead(analogPin3), 0, 1023, 0, 180); dataString4 += String(val4); myservo4.write(val4); // Write to the servo // Delay to allow servo to settle in position myservo.write(val1); myservo2.write(val2); myservo3.write(val3); myservo4.write(val4); delay(15); // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. File dataFile = SD.open("servopo1.txt", FILE_WRITE); // if the file is available, write to it: if (dataFile) { dataFile.println(dataString); dataFile.close(); // print to the serial port too: Serial.println(dataString); } // if the file isn't open, pop up an error: else { Serial.println("error opening servopo1.txt"); } }
// PLAYBACK Code from DroneBot Workshop 2019: /* Servo position playback servo-playback.ino Plays back servo movements from SD card Used with servo-record.ino Displays results on Serial Monitor DroneBot Workshop 2019 https://dronebotworkshop.com */ // Include libraries #include <SPI.h> #include <SD.h> #include <Servo.h> // CS pin for SD Card Module const int chipSelect = 4; // String to hold one line of text String buffer; // Create a Servo object Servo myservo; Servo myservo2; Servo myservo3; Servo myservo4; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.print("Initializing SD card..."); // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); // don't do anything more: while (1); } Serial.println("card initialized."); // Attach servo on pin 9 to the servo object myservo.attach(9); myservo2.attach(8); myservo3.attach(7); myservo4.attach(6); // open the file. note that only one file can be open at a time, // so you have to close this one before opening another. File dataFile = SD.open("servopo1.txt"); // If the file is available, read it if (dataFile) { while (dataFile.available()) { // Write one line to buffer buffer = dataFile.readStringUntil('\n'); // Print to serial monitor Serial.println(buffer); // Convert string to integer and position servo myservo.write(buffer.toInt()); myservo2.write(buffer.toInt()); myservo3.write(buffer.toInt()); myservo4.write(buffer.toInt()); delay(15); } dataFile.close(); } // if the file isn't open, pop up an error: else { Serial.println("error opening servopo1.txt"); } } void loop() { }
@inq No doubt.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@robotbuilder In the very deep dark recesses of my mind I seem to recall that conversion between windows and *nix causes that.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
@inq Thank you, this was our first post, I'll get it right the next time. The reason I am using the two (2) separate codes is because we don't know any better 😆. We are working off the original codes created by DroneBot Workshop 2019 by adding the extra servos to the best of our knowledge. Using the code for one servo works great, but as we expand the code. it gets jumbled. As far as timing, we just need to control the servos in real time, and then play them back in the same pattern. We are building a pair of eyes that go in a painting for our haunted house. The eyes remain closed for some time, open, move around for a bit, stare off for a moment, and then close. The idea is that the pattern repeat over and over again. Think of a tape recorder that captures information in real time and then plays it back again.
We attached a sketch of the setup.
@zander Got it, thanks!
@robotbuilder Thanks for the repost!
@hinobot Ok, so what is the purpose of the SD card?
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
You won't achieve the synchronicity you desire unless you specifically add timing marks to your sketch and write them to the SD card as well as the values because the length of time the processor will take for each "line" that it has to collect, adapt and store to the SD card is different from the length of time required to read and process the line from the SD card.
If you want replay and display to match, then you need to add code to set and utilize those markers.
Anything seems possible when you don't know what you're talking about.
@will @hinobot I don't understand the reason for the card, just write the sketch and it loops forever, no need to store it on a card unless I missed something.
First computer 1959. Retired from my own computer company 2004.
Hardware - Expert in 1401, 360, fairly knowledge in PC plus numerous MPU's & MCU's
Major Languages - Machine language, 360 Macro Assembler, Intel Assembler, PL/I and PL1, Pascal, Basic, C plus numerous job control and scripting languages.
My personal scorecard is now 1 PC hardware fix (circa 1982), 1 open source fix (at age 82), and 2 zero day bugs in a major OS.
As I understand it, they want to record a series of 4-tuples corresponding to the position of 4 pots. These pot values are then printed off to the SD and also to the serial display and the corresponding angle is sent off to the 4 servos.
At some time(s) later, the file will be read and the 4-tuples restored to numeric values and again be used to set the angles on the servos.
So the first sketch captures and stores the servo positions to the SD card and the second sketch reads the SD card contents and replays the servo movements.
At least, that's my take on it 🙂
Anything seems possible when you don't know what you're talking about.