Data Logger Shield + Motor Shield Compatibility?
Moderators: adafruit_support_bill, adafruit
Please be positive and constructive with your questions and comments.
- designCPU
- Posts: 41
- Joined: Sun Aug 24, 2014 2:24 pm
Data Logger Shield + Motor Shield Compatibility?
Hi. I'm using three of the v2 Adafruit Motor Shields, stacked, each running four twelve 12VDC computer fans. I have buttons connected to the system that allow the user to change settings for each of the fans. And I'd like to incorporate the Data Logger Shield to save these settings in case the system is is unplugged. Is this Motor Shield configuration compatible with the Data Logger Shield?
- Franklin97355
- Posts: 23912
- Joined: Mon Apr 21, 2008 2:33 pm
Re: Data Logger Shield + Motor Shield Compatibility?
The logger shield uses i2c address 0x68. If you are not using that for your motor drivers you should be OK.
- designCPU
- Posts: 41
- Joined: Sun Aug 24, 2014 2:24 pm
- designCPU
- Posts: 41
- Joined: Sun Aug 24, 2014 2:24 pm
Re: Data Logger Shield + Motor Shield Compatibility?
More problems. There seems to be some kind of issue between my R3 Uno, Data Logging Shield, V2 Motor Shield, and Quad Alphanumeric Display. I should note that my previous setup didn't have the SD shield or code for data logging, and everything worked fine. When I introduce the Data Logging Shield into the mix, my program doesn't get beyond the setup block. I thought it might be a conflict caused by chip select pin 10 on the SD shield, but when I omit the code initializing the quad-alphanumeric display, the motor and SD card shields play nice. I think I've isolated the offending lines of code:
If it's an incompatibility between the included libraries, maybe I'm just out of luck. I've tried re-ordering things, and nothing resolves the issue. Any ideas? Will all of these nightmares go away if I use the Adafruit I2C FRAM Breakout instead of the Data Logging Shield?
Code: Select all
#include <Wire.h>
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include <SD.h>
#include <Adafruit_MotorShield.h>
…
Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_DCMotor *m1 = AFMS.getMotor(1);
Adafruit_DCMotor *m2 = AFMS.getMotor(2);
Adafruit_DCMotor *m3 = AFMS.getMotor(3);
Adafruit_DCMotor *m4 = AFMS.getMotor(4);
void setup() {
Serial.begin(9600);
pinMode(10, OUTPUT);
…
if (SD.begin(10)) {
Serial.println("SD CARD INITIALIZATION DONE.");
} else {
Serial.println("SD CARD INITIALIZATION FAILED!");
}
…
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();
alpha4.begin(0x71); // SEEMS THAT THE ISSUE IS EITHER HERE OR IN THE AFMS.begin() LINE OF CODE
alpha4.writeDigitRaw(3, 0x0);
alpha4.writeDigitRaw(0, 0xFFFF);
alpha4.writeDisplay();
delay(150);
...
AFMS.begin(); // SEEMS THAT THE ISSUE IS EITHER HERE OR IN THE alpha4.begin(0x71) LINE OF CODE
…
}
...
- adafruit_support_mike
- Posts: 67454
- Joined: Thu Feb 11, 2010 2:51 pm
Re: Data Logger Shield + Motor Shield Compatibility?
The Motor Shield, SD breakout, and LED backpack are all compatible with each other in general, but their I2C addresses fall in the same range.. 0x68 for the Data Logger's RTC, 0x70 for the backpack, and 0x60-0x7F for the Motor Shield.
You said at the beginning that you're using three Motor Shields. What are their addresses?
You said at the beginning that you're using three Motor Shields. What are their addresses?
- designCPU
- Posts: 41
- Joined: Sun Aug 24, 2014 2:24 pm
Re: Data Logger Shield + Motor Shield Compatibility?
Their addresses are 0x60, 0x61 and 0x62. The LCD Backpack was modified to have address 0x71. I should mention that I tried to reassign Chip Select Pin #10 on the Adafruit Data Logging Shield by cutting its trace and tying CS to another digital pin, revising the code accordingly. That apparently killed the shield, because it doesn't even run the example ReadWrite program from the SD library. I replaced the Data Logging Shield with a Seeedstudio v3 SD Card Shield. This didn't seem to fix anything. I commented out all of the code, then gradually un-commented it, and now everything works. I have no idea why, but I'll take it. In case anyone's interested, here's the code:
Code: Select all
/*
- - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - -
Using the Adafruit Motor Shield v2 to randomly switch computer fans on and off.
Two buttons connected to the Arduino allow the user to choose a different sleep
interval for each of the 12 fans.
A 4-character alphanumeric display shows which fan is currently selected, and its
sleep interval.
The Data Logging Shield autosaves the current fan settings into a text file. The
settings are retained when the unit is powered off, then retrieved when it's
powered on.
2014 Shawn Jackson
- - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - - - -
*/
#include <Wire.h>
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include <SD.h>
#include <Adafruit_MotorShield.h>
// To save memory, use #define directive to enable
// on/off toggling of all Serial.print(F() statements.
//#define DEBUG
//#define DEBUG_EchoFanOnOff
//#define DEBUG_CheckUserChanges
//#define DEBUG_NewInterval
//#define DEBUG_SDCardStatus
//#define DEBUG_SDReadWrite
File myFile;
char *filename = "test.txt";
unsigned int h, i, j, k, m, n, p;
unsigned long lastSaveTime;
const int fanSelectPin = 2; // arduino pin number for the 'fan select' button
const int intrvlSelPin = 4; // arduino pin number for the 'interval select' button
const int sdChipSelectPin = 10;
const int FANSELECT = 1;
const int INTRVLSELECT = 2;
const int fanCount = 12;
int button1State; // Current reading from the input pin, to select motor
int button2State; // Current reading from the input pin, to control motor sleep interval
int lastButton1State = LOW; // Previous reading from the input pin
int lastButton2State = LOW; // ...
// Current selected fan. Range is between 0 and fanCount.
// When currentFan is 0, no fans are selected.
int currentFan = 0;
// 0 default values mean all fans are off on start
int fanSettings[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int previousSettings[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
// These variables increase quickly, need to declare as 'long'.
unsigned long lastDebounceTime1 = 0; // last time the motor selection output pin was toggled
unsigned long lastDebounceTime2 = 0; // last time the sleep interval output pin was toggled
const long debounceDelay = 50; // the debounce time; increase if the output flickers
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();
char onesdigit1 = '*', tensdigit1 = '*';
char onesdigit2 = '-', tensdigit2 = '-';
const int motorShieldCount = 3;
const int motorsPerShield = 4;
const int numMotors = motorShieldCount * motorsPerShield;
// Whether or not the fan is currently running
boolean fansRunning[numMotors] = {
false, false, false, false,
false, false, false, false,
false, false, false, false};
// Whether or not the fan is currently active
boolean activeStatus[numMotors] = {
false, false, false, false,
false, false, false, false,
false, false, false, false};
// Keep track of elapsed time overall, and for each fan
unsigned long currentMillis;
unsigned long previousMils[numMotors] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0};
const unsigned int minSleep = 0; // minimum time between switching on fan
const unsigned int maxSleep = 20; // maximum time between switching on fan
unsigned int onInterval = 3; // length of time fan should remain on
unsigned long offIntervals[numMotors] = {
0,0,0,0,
0,0,0,0,
0,0,0,0};
// Assign each stacked shield a different I2C address
Adafruit_MotorShield AFMS[motorShieldCount] = {
Adafruit_MotorShield(0x60),
Adafruit_MotorShield(0x61),
Adafruit_MotorShield(0x62)
};
Adafruit_DCMotor *dcMotors[numMotors] = {
AFMS[0].getMotor(1), AFMS[0].getMotor(2), AFMS[0].getMotor(3), AFMS[0].getMotor(4),
AFMS[1].getMotor(1), AFMS[1].getMotor(2), AFMS[1].getMotor(3), AFMS[1].getMotor(4),
AFMS[2].getMotor(1), AFMS[2].getMotor(2), AFMS[2].getMotor(3), AFMS[2].getMotor(4)};
int fanSpeed = 255; // A value between 0 and 255
void setup() {
Serial.begin(9600);
Serial.println(F("\n\n+ + + + + + + + + + + + + + + + + + + + + + + + +\n"));
Serial.println(F("Controlling Fans with Adafruit Motor Shield v2"));
#ifdef DEBUG
Serial.println(F("\n| DEBUGGING IS ON"));
#else
Serial.println(F("\n| DEBUGGING IS OFF"));
#endif
pinMode(fanSelectPin, INPUT);
pinMode(intrvlSelPin, INPUT);
// PREPARE SD CARD FOR I/O
pinMode(sdChipSelectPin, OUTPUT);
if (!SD.begin(sdChipSelectPin)) {
#ifdef DEBUG_SDCardStatus
Serial.println(F("SD CARD INITIALIZATION FAILED!"));
Serial.println();
#endif
} else {
#ifdef DEBUG_SDCardStatus
Serial.println(F("SD CARD INITIALIZATION OK!"));
Serial.println();
#endif
}
// START ALPHANUMERIC DISPLAY
initDisplay();
// READY THE MOTOR SHIELDS FOR COMMANDS
AFMS[0].begin();
AFMS[1].begin();
AFMS[2].begin();
// #ifdef DEBUG
// Serial.println(F("\n| DEBUGGING IS ON"));
// #else
// Serial.println(F("\n| DEBUGGING IS OFF"));
// #endif
// Serial.print(F("| NO FANS SELECTED\n\n"));
// Set the speed of the fans (0 is off, 255 is high)
for(m = 0; m < motorShieldCount; m++) {
for(n = 0; n < motorsPerShield; n++) {
dcMotors[m * motorsPerShield + n] -> setSpeed(fanSpeed);
}
}
delay(2000);
initializeFans();
#ifdef DEBUG
Serial.print(F("| NO FANS SELECTED\n\n"));
#endif
lastSaveTime = millis();
}
void loop() {
currentMillis = millis();
for(p = 0; p < numMotors; p++) {
if(activeStatus[p]) {
checkFan(p);
}
}
// HANDLE FAN SELECTION BUTTON PRESSES
int reading1 = digitalRead(fanSelectPin);
if (reading1 != lastButton1State) {
// reset the debouncing timer
lastDebounceTime1 = millis();
}
checkButton(reading1, FANSELECT);
// save the reading. Next time through the loop,
// it'll be the lastButton1State:
lastButton1State = reading1;
// HANDLE INTERVAL SELECTION BUTTON PRESSES
int reading2 = digitalRead(intrvlSelPin);
if (reading2 != lastButton2State) {
lastDebounceTime2 = millis();
}
checkButton(reading2, INTRVLSELECT);
lastButton2State = reading2;
// EVERY 5 SECONDS, WE'LL CHECK TO SEE IF THE USER MADE ANY CHANGES
// TO THE FAN INTERVALS. IF SHE DID, WE'LL UPDATE THE TEXT FILE
// CONTAINING THE SETTINGS FOR ALL THE FANS.
//
// FILE NAME: intervals.txt
//
// FILE CONTENTS: SINGLE LINE OF TEXT. NUMBERS, SEPARATED BY A SPACE,
// REPRESENTING EACH FAN'S SLEEP INTERVAL.
//
// Example:
// 5 2 17 16 7 14 9 18 5 13 0 8
//
if(millis() - lastSaveTime >= 5000) {
#ifdef DEBUG_CheckUserChanges
Serial.print("\nPROGRAM RUN TIME: ");
Serial.print(millis()/1000);
Serial.print(F(" SECONDS. CHECKING WHETHER FAN SETTINGS HAVE CHANGED...\n"));
#endif
updateFileIfSettingsChanged();
lastSaveTime = millis();
}
}
void checkFan(int selection) {
unsigned long elapsed = currentMillis - previousMils[selection]; // Calculate elapsed time
if(fansRunning[selection] == false) {
if(elapsed > offIntervals[selection]) {
// Randonly generate new wait interval
offIntervals[selection] = random(0, fanSettings[selection] * 1000);
#ifdef DEBUG_NewInterval
Serial.print(F("||: New sleep time for Fan "));
Serial.print(selection + 1);
Serial.print(F(" is "));
Serial.print((float)offIntervals[selection]/1000);
Serial.println(F(" seconds. "));
#endif
previousMils[selection] = currentMillis;
fansRunning[selection] = true;
dcMotors[selection] -> run(FORWARD);
#ifdef DEBUG_EchoFanOnOff
printStatus(selection + 1, true);
#endif
}
} else {
if(elapsed > onInterval * 1000) {
previousMils[selection] = currentMillis;
fansRunning[selection] = false;
dcMotors[selection] -> run(RELEASE);
#ifdef DEBUG_EchoFanOnOff
printStatus(selection + 1, false);
#endif
}
}
}
void checkButton (int reading, int option) {
switch(option) {
case FANSELECT:
if ((millis() - lastDebounceTime1) > debounceDelay) {
// Whatever the reading is at, it's been there for longer than
// the debounce delay, so take it as the actual current state:
// If the button state has changed:
if (reading != button1State) {
button1State = reading;
// Only do something if the new button state is HIGH
if (button1State == HIGH) {
#ifdef DEBUG
Serial.println(F("\n- - - - - - - -"));
#endif
currentFan = (currentFan < fanCount) ? (currentFan + 1) : 0;
if(currentFan == 0) {
#ifdef DEBUG
Serial.print(F("NO FANS SELECTED.\n"));
#endif
onesdigit1 = tensdigit1 = '*';
onesdigit2 = tensdigit2 = '-';
}
else {
onesdigit1 = '0' + (currentFan % 10);
tensdigit1 = '0' + ((currentFan / 10) % 10);
if(fanSettings[currentFan - 1] == 0) {
onesdigit2 = tensdigit2 = '-';
}
else {
onesdigit2 = '0' + (1 * fanSettings[currentFan - 1] % 10);
tensdigit2 = '0' + ((1 * fanSettings[currentFan - 1] / 10) % 10);
}
#ifdef DEBUG
Serial.print(F("FAN "));
Serial.print(currentFan);
Serial.print(F(" SELECTED.\n"));
if(fanSettings[currentFan - 1] > 0) {
Serial.print(F("||: Current sleep interval for Fan "));
Serial.print(currentFan);
Serial.print(F(" is "));
Serial.print((float)offIntervals[currentFan - 1]/1000);
Serial.println(F(" seconds."));
} else {
Serial.print(F("[]: Fan "));
Serial.print(currentFan);
Serial.print(F(" is currently off."));
}
Serial.println();
#endif
}
}
}
}
break;
case INTRVLSELECT:
if ((millis() - lastDebounceTime2) > debounceDelay) {
if (reading != button2State) {
button2State = reading;
if (button2State == HIGH && currentFan > 0 && currentFan < fanCount + 1) {
fanSettings[currentFan - 1] = (fanSettings[currentFan - 1] < maxSleep) ? (fanSettings[currentFan - 1] + 1) : 0;
if(fanSettings[currentFan - 1] == 0) {
onesdigit2 = tensdigit2 = '-';
#ifdef DEBUG
Serial.print(F("[]: Fan "));
Serial.print(currentFan);
Serial.print(F(" is now offline. Press the button to switch this fan back online."));
#endif
fansRunning[currentFan - 1] = false;
dcMotors[currentFan - 1] -> run(RELEASE);
activeStatus[currentFan - 1] = false;
}
else {
onesdigit2 = '0' + (1 * fanSettings[currentFan - 1] % 10);
tensdigit2 = '0' + ((1 * fanSettings[currentFan - 1] / 10) % 10);
#ifdef DEBUG
Serial.print(F(">>: Sleep range of Fan "));
Serial.print(currentFan);
Serial.print(F(" is now between "));
Serial.print(minSleep);
Serial.print(F(" and "));
Serial.print(minSleep + 1 * fanSettings[currentFan - 1]);
Serial.print(F(" seconds."));
#endif
activeStatus[currentFan - 1] = true;
previousMils[currentFan - 1] = currentMillis;
dcMotors[currentFan - 1] -> run(FORWARD);
}
#ifdef DEBUG
Serial.println();
#endif
}
}
}
break;
}
alpha4.writeDigitAscii(0, tensdigit1);
alpha4.writeDigitAscii(1, onesdigit1);
alpha4.writeDigitAscii(2, tensdigit2);
alpha4.writeDigitAscii(3, onesdigit2);
alpha4.writeDisplay();
}
void printStatus(int s1, boolean state) {
Serial.print(F("++: FAN "));
if(s1 < 10) {
Serial.print('0');
}
Serial.print(s1);
Serial.println(state ? " ON" : " OFF");
}
/* SD CARD I/O FUNCTIONS */
void errorMsg() {
#ifdef DEBUG
Serial.print(F("ERROR OPENING FILE "));
Serial.print(filename);
Serial.println();
#endif
}
String fansArrayToString() {
String settingsList = "";
for(h = 0; h < fanCount; h++) {
settingsList += String(fanSettings[h]);
if(h < fanCount-1) {
settingsList += ' ';
}
}
return settingsList;
}
void initializeFans() {
// WHEN ARDUINO IS PLUGGED IN, IF THERE'S A FAN SETTINGS
// FILE, READ IT INTO fanSettings[] ARRAY
// IF THIS initializeFans() FUNCTION FAILS BECAUSE THE
// SD CARD OR CONTENTS CAN'T BE ACCESSED, WE'LL HAVE THE
// INITIALIZED fanSettings[] ARRAY AS BACKUP. SO THE PROGRAM
// CAN STILL MOVE FORWARD IF SOMETHING'S WRONG WITH THE
// SD CARD.
int index = 0;
if(SD.exists(filename)) {
myFile = SD.open(filename);
if (myFile) {
#ifdef DEBUG_SDReadWrite
Serial.println();
Serial.print(F("PREVIOUSLY SAVED SLEEP RANGES LOADED FROM FILE "));
Serial.println(filename);
Serial.println(F("- - - - - - - - - - - - - - - - - - - - - - - - - - - -"));
#endif
while (myFile.available() && index < fanCount) {
fanSettings[index] = myFile.parseInt();
previousSettings[index] = fanSettings[index];
offIntervals[index] = random(0, fanSettings[index] * 1000);
#ifdef DEBUG_SDReadWrite
Serial.print(F(".. FAN "));
if(index + 1 < 10) {
Serial.print('0');
}
Serial.print(index + 1);
Serial.print(F(" | RANDOM SLEEP RANGE IS "));
Serial.print(minSleep);
Serial.print(F(" - "));
if(fanSettings[index] < 10) {
Serial.print(' ');
}
Serial.print(minSleep + 1 * fanSettings[index]);
Serial.print(F(" SECONDS"));
Serial.println();
#endif
if(fanSettings[index] > 0) {
activeStatus[index] = true;
dcMotors[index] -> run(FORWARD);
// ADDING 120ms ENSURES THAT IF THE UNIT WAS JUST NOW POWERED ON, AND
// THE FAN WAS PREVIOUSLY SET TO 'ON', THIS FAN WILL ACTUALLY PAUSE A
// SHORT MOMENT BEFORE SWITCHING ON
previousMils[index] = millis() + 120;
}
index += 1;
}
myFile.close();
} else {
errorMsg();
}
}
#ifdef DEBUG_SDReadWrite
Serial.println();
#endif
}
//
//
void writeFile() {
// DELETE PREVIOUS SETTINGS FILE IF
// IT EXISTS ON THE SD CARD
if(SD.exists(filename)) {
#ifdef DEBUG_SDReadWrite
Serial.print(F("\ERASE PREVIOUS SETTINGS FILE "));
Serial.print(filename);
Serial.print(F(" ... "));
#endif
SD.remove(filename);
delay(320);
#ifdef DEBUG_SDReadWrite
Serial.print(F("OK"));
Serial.println();
#endif
}
// CREATE NEW SETINGS FILE AND SAVE
// OUR CURRENT FAN SETTINGS.
myFile = SD.open(filename, FILE_WRITE);
if (myFile) {
String s = fansArrayToString();
#ifdef DEBUG_SDReadWrite
Serial.print(F("\nAUTOSAVE FAN SETTINGS FILE "));
Serial.print(filename);
Serial.print(F(" ... "));
#endif
myFile.println(s);
myFile.close();
delay(320);
#ifdef DEBUG_SDReadWrite
Serial.println(F("OK"));
Serial.println(s);
#endif
}
else {
errorMsg();
}
}
// IF FAN SETTINGS ARE DIFFERENT FROM PREVIOUSLY SAVED VALUES,
// UPDATE THE SETTINGS FILE.
void updateFileIfSettingsChanged() {
if(settingsChanged()) {
#ifdef DEBUG
Serial.println(F("USER CHANGED FAN SETTINGS. UPDATING FILE..."));
#endif
writeFile();
for(j = 0; j < fanCount; j++) {
previousSettings[j] = fanSettings[j];
}
}
}
// COMPARE PREVIOUS AND CURRENT FAN SETTINGS TO DETERMINE
// WHETHER THEY ARE IDENTICAL
boolean settingsChanged() {
for(k = 0; k < sizeof(previousSettings)/sizeof(previousSettings[0]); k++) {
if(previousSettings[k] != fanSettings[k]) {
return true;
}
}
return false;
}
// INITIATE QUAD ALPHANUMERIC LED DISPLAY
void initDisplay() {
// WE ASSIGNED OUR I2C ADDRESS TO 0x71 USING THE
// JUMPERS ON THE LCD BACKPACK. THAT ADDRESS MUST
// MATCH THE ONE WE SPECIFY IN alpha4.begin(****);
alpha4.begin(0x71); // pass in the address
// ANIMATED SEQUENCE OF CHARACTERS SIGNIFIES
// THAT WE'VE JUST TURNED IT ON
alpha4.writeDigitRaw(3, 0x0);
alpha4.writeDigitRaw(0, 0xFFFF);
alpha4.writeDisplay();
delay(120);
alpha4.writeDigitRaw(0, 0x0);
alpha4.writeDigitRaw(1, 0xFFFF);
alpha4.writeDisplay();
delay(120);
alpha4.writeDigitRaw(1, 0x0);
alpha4.writeDigitRaw(2, 0xFFFF);
alpha4.writeDisplay();
delay(120);
alpha4.writeDigitRaw(2, 0x0);
alpha4.writeDigitRaw(3, 0xFFFF);
alpha4.writeDisplay();
delay(120);
alpha4.clear();
alpha4.writeDisplay();
delay(200);
alpha4.writeDigitAscii(0, tensdigit1);
delay(120);
alpha4.writeDisplay();
alpha4.writeDigitAscii(1, onesdigit1);
delay(120);
alpha4.writeDisplay();
alpha4.writeDigitAscii(2, tensdigit2);
delay(120);
alpha4.writeDisplay();
alpha4.writeDigitAscii(3, onesdigit2);
delay(120);
alpha4.writeDisplay();
}
- adafruit_support_mike
- Posts: 67454
- Joined: Thu Feb 11, 2010 2:51 pm
Re: Data Logger Shield + Motor Shield Compatibility?
Ah, one of those..
They happen and generally defy explanation. As long as everything works the way it should now, you win. ;-)
They happen and generally defy explanation. As long as everything works the way it should now, you win. ;-)
Please be positive and constructive with your questions and comments.