SD and microSD cards are an ideal way to store large amounts of non-volatile data for your Arduino projects. Today I will show you how to use SD card modules in your Arduino designs. I’ll also build a servo motor position recorder that can record and playback the motion of a servo motor.
Full article with code - https://dbot.ws/sdcard
Today I will take a look at SD and microSD card modules. These are a simple way of adding storage to your Arduino projects.
SD cards have been around since the turn of the century and are used in a variety of electronic devices. I’m sure you probably have several of them for your phone, tablet, music player or camera. If you use a Raspberry Pi a microSD card is used to hold your operating system and data, it basically takes the place of a hard drive in this case.
It is very simple to use SD cards with the Arduino. They connect via the SPI bus and the Arduino IDE has a built-in library to work with them.
I’ll show you how to connect a microSD card module to an Arduino Uno and how to use some of the sample code that is included with the library. This includes code to read and write files to the card, as well as a Datalogger that can record the position of three potentiometers.
After we master the sample code we will create a small project.
Our project will have a servo motor and potentiometer along with the microSD card module. You will be able to move the servo using the pot and record its movements on the microSD card. After that, I’ll show you how to play those movements back.
You can use this code “as-is” or expand upon it to use more servos, perhaps to control a robot arm. You could also use it to record DC motors, LEDs or pretty well anything else you can think of.
Here are the contents of today's video:
00:00 - Introduction
02:20 - SD and microSD Card Module introduction
05:44 - ReadWrite Example
11:53 - Datalogger Example
18:56 - DumpFile Example
21:46 - Servo Recorder
27:25 - Servo Playback
As always you will find a detailed article to accompany this video on the DroneBot Workshop website. Check it out at https://dbot.ws/sdcard <. The article also has the code for the servo motor recorder and player in a handy ZIP file.
"Never trust a computer you can’t throw out a window." — Steve Wozniak
Bill,
I have a question about using SC Cards to record servo motor movements. I am working on a sixteen-channel servo controller. What I would like to do is build up the channels one at a time which would allow me to be able to go back and just rerecord one channel without affecting any of the other channels. What would you suggest to be able to do this? Thank You for your help.
Tim Lewis
Did you mean SD cards ?
Assuming that you only need to store values 0-180 as the angle of the servo, each datum can be contained within a single byte.
So, on your SD card, use the first 2 bytes to save the count of samples stored on the card, where each sample is the full 16 bytes of servo positions.
Therefore, the address on the card of the first entry for servo N is 2+N (i.e. N bytes past the length counter). Each subsequent sample reading for that servo will be at 2+N+16 and so on.
So, for sample number S (for S=0,1,2,...) and servo number N (N=0,1,2,...,15), the address of its value on the card will be 2+16*S+N.
So, to record a value for servo you can use the command
seek(2+16*s+N);
to set the address and position for this samples servo N value and read or write from that position.
This way, you don't even have to implement your servos from 0 to 1 to 2 ... but can implement them in any order without corrupting the other values.
Anything seems possible when you don't know what you're talking about.
Yes, I meant SD Card. Thank You for your Idea I would like to try this out. I am still not sure to implement it but like to get this working.
If it helps, think of it as a giant array of 16 byte arrays. The 16 bytes represent your servo values but the list extends along the SD card instead of your Arduino's memory.
So, you simulate moving to the index of the sample desired by passing over all of the 16 byte servo value lists before that sample and then go to the appropriate index of your servo in that list of 16 values.
If you always store the same number of samples, then you don't even need the 2 bytes at the start (i.e. the count of samples stored).
Anything seems possible when you don't know what you're talking about.
I am trying to combine the servo record and servo playback example code into a single code that will be using buttons to-do record, playback, stop functions. The functions are not looping.
/*
SD Card Servo Record/Playback with Button Control
by Tim Lewis 9/23/2021
to store on SD Card One Channel Servo Data and Play it back.
using Push Buttons for Recod and Playback control.
*/
// Include libraries
#include <SPI.h>
#include <SD.h>
#include <Servo.h>
// CS pin for SD Card Module
const int chipSelect = 53;
// String to hold one line of text
String buffer;
// Analog pin for potentiometer
int analogPin = 0;
// Integer to hold potentiometer value
int val = 0;
// Create a Servo object
Servo myservo;
// Buttons Constants
const int RedBtn = 22; // Red Button 1
const int GrnBtn = 23; // Green Button 2
const int BlueBtn = 24; // Blue Button 3
void setup() {
// Buttons
pinMode(RedBtn, INPUT); // Button 1 as Input
pinMode(GrnBtn, INPUT); // Button 2 as Input
pinMode(BlueBtn, INPUT); // Button 3 as Input
digitalWrite(RedBtn, HIGH); // Pullup
digitalWrite(GrnBtn, HIGH); // Pullup
digitalWrite(BlueBtn, HIGH); // Pullup
// 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 14 to the servo object
myservo.attach(14);
}
void loop() {
// Button Control Code Here
if(digitalRead(RedBtn) == LOW) // If button 1 pressed Recording Console Inputs
{
Record(); // Record Function
}
if(digitalRead(GrnBtn) == LOW) // If button 2 pressed PlayBack Mode
{
Serial.println("Playing Back Servo File"); // Print "Playing Back Servo File" on Serial Monitor
Play(); // Playback Function
}
}
void Record() //Function to Record the movements of the Animatronic Servos
{
// Put Record Code Here
// while the STOP button NOT is pressed, continue looping
// while (! (digitalRead(BlueBtn) == HIGH )) // Stop Button not pressed
// make a string for assembling the data to log:
String dataString = "";
// Read pot value and append to the string
// Map to range of 0-180 for servo
val = map(analogRead(analogPin), 0, 1023, 0, 180);
dataString += String(val);
// Write to the servo
// Delay to allow servo to settle in position
myservo.write(val);
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("servopos.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 servopos.txt");
}
}
void Play() //Function to PlayBack the movements of the Animatronic Servos
{
Serial.println("Playing Back Record Servo Movements"); // Print "Playback" on Serial Monitor
// Put Playback Code Here to Play
// 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("servopos.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());
delay(15);
}
dataFile.close();
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening servopos.txt");
}
}
Just a quick comment on the code that may help (or may not)
1. the pins numbers look suspect for an arduino uno
2. the stop button is commented out in the record function
3. put in a few more Serial.println("Ive reached point x") in your code so you can trace at what point your program is failing you.
@byron I am running it on a Mega 2560 Board. it will not compile with them uncommented.
while (! (digitalRead(BlueBtn) == HIGH ))
I suspect you just uncommented the statement above
Dont forget that the statements in a while loop need to be surrounded by {}
so
while(!(digitalRead(BlueBtn) == HIGH)) {
// your statments to execute if the condition is true
}
Sorry, I didn't mean to uncomment anything I just copied it into the Arduino IDE and went through it restoring the indents to show how much better it looks. If the indents go then reading the code becomes much more difficult visually. IMHO
@robotbuilder - Sorry robotbuilder my post got addressed to the wrong robo. Your efforts to present a readable bit of code meant I could actually read the code without going boggle eyed and could off some advice to robomaster.
I meant to tell @robomaster that he probably uncommented that bit of code without putting in some extra brackets when he said that uncommenting that bit of code meant is did not compile.
Doh! 🙄
I'd suggest that you change the pinMode commands for your buttons to INPUT_PULLUP from just INPUT. That will cause your buttons to be tied to the positive rail through internal resistors so they'll always stay HIGH until closed manually.
Also, can you please describe exactly what you mean by "The functions are not looping." It's not clear which functions you're talking about nor how they're supposed to loop.
Anything seems possible when you don't know what you're talking about.
I got the Record Function now looping and I can break out of it. But when I try to do the same thing with the Playback Function it does not break out of it. It does playback the file and then stops when it runs out of data. I can live with that but it would be nice to be able to stop it from playing back when pushing the stop button. (Blue Button)
Thank You for all your help.
Tim
You only test if the blue button is down BEFORE you even open the file to be played back.
To interrupt the playback, you'll have to move the button test inside the "while (dataFile.available()) {" section of the sketch.
For example ...
while (dataFile.available() && digitalRead(BlueBtn)) {
That is while there's more data on the file and the blue button is still UP ... etc
Anything seems possible when you don't know what you're talking about.