How to use SD and WiFi at the same time ?

Hi all,

I have a WiFi shield on an Arduino Uno (rev 1), and I'm trying to use the wifi and the SD card in the same sketch. I can use the SD card in its own sketch, and I can use the the WiFi in a separate sketch. But as soon as I try and use them in the same sketch, things behave oddly.

Can anyone point me at an example that uses both the WiFi shield and the SD card in the same sketch, or advise where I'm going wrong here please?

The only change between working and failing is the addition of the following lines to the setup() method

  pinMode(10, OUTPUT);
   
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

Working example code

#include <SPI.h>
#include <WiFi.h>
#include <SD.h>
#include <LiquidCrystal595.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal595 lcd(5,6,8);

// Wifi network details
char ssid[] = "MySSID";     //  your network SSID (name)
char pass[] = "passwordtomynetwork";    // your network password
int wifiStatus = WL_IDLE_STATUS;     // the Wifi radio's status
WiFiClient client;
boolean lastConnected = false; 

/* setup
   Setup various pin modes, LCD display and ensure that we are connected to the wifi network
*/
void setup() {
  Serial.begin(9600);

  Serial.println("Arduino in setup()");
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  lcd.print("Temp: ");
  
  wifiStatus = connectToNetwork();
}

/* connectToNetwork
   Connect to a wifi network
   
   - if we are able to connect to the network, we briefly display the SSID on the LCD then Net: OK
   - if we fail to connect to the network, we display Net: BAD on the LCD
*/
int connectToNetwork() {
  while(!Serial) ;
  
  Serial.println("Initializing Wifi...");
  printMacAddress();
  
  Serial.print("Attempting to connect to WPA network: [");
  Serial.print(ssid);
  Serial.println("]");
  lcd.setCursor(0, 1);
  lcd.print("Connecting...");
  wifiStatus = WiFi.begin(ssid, pass);
  
   // if you're not connected, stop here:
  if ( wifiStatus != WL_CONNECTED) {
    Serial.println("Couldn't get a wifi connection");
    lcd.setCursor(0, 1);
    lcd.print("Net: BAD       ");
    while(true);
  }
  // if you are connected, print out info about the connection:
  else {
    Serial.println("Connected to network");
    lcd.setCursor(0, 1);
    String ssidString = ssid;
    int padCount = 16 - ssidString.length();
    if (padCount > 0) {
      for (int i = 0; i < padCount; i++) {
        ssidString + ssidString + " ";        
      }
    }
    lcd.print(ssidString);
    delay(2000);  // Change this value to define how long the SSID is displayed on the LCD before we proceed 
    
    lcd.setCursor(0,1);   
    lcd.print("NET: OK          ");
  }
  return wifiStatus;
}

/* printMacAddress
   Finds the WiFi shield's mac address and prints it to the serial output. This is used for debugging
*/
void printMacAddress() {
  // the MAC address of your Wifi shield
  byte mac[6];                    

  // print your MAC address:
  WiFi.macAddress(mac);
  Serial.print("MAC: ");
  Serial.print(mac[5],HEX);
  Serial.print(":");
  Serial.print(mac[4],HEX);
  Serial.print(":");
  Serial.print(mac[3],HEX);
  Serial.print(":");
  Serial.print(mac[2],HEX);
  Serial.print(":");
  Serial.print(mac[1],HEX);
  Serial.print(":");
  Serial.println(mac[0],HEX);
}

Working example output

Arduino in setup()
Initializing Wifi...
MAC: 7A:C4:E:94:EA:2A
Attempting to connect to WPA network: [MySSID]
Connected to network

Failure example code

#include <SPI.h>
#include <WiFi.h>
#include <SD.h>
#include <LiquidCrystal595.h>

// initialize the library with the numbers of the interface pins
LiquidCrystal595 lcd(5,6,8);

// Wifi network details
char ssid[] = "MySSID";     //  your network SSID (name)
char pass[] = "passwordtomynetwork";    // your network password
int wifiStatus = WL_IDLE_STATUS;     // the Wifi radio's status
WiFiClient client;
boolean lastConnected = false; 

/* setup
   Setup various pin modes, LCD display and ensure that we are connected to the wifi network
*/
void setup() {
  Serial.begin(9600);

  Serial.println("Arduino in setup()");
  
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  lcd.print("Temp: ");

  pinMode(10, OUTPUT);
   
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  wifiStatus = connectToNetwork();
}

/* connectToNetwork
   Connect to a wifi network
   
   - if we are able to connect to the network, we briefly display the SSID on the LCD then Net: OK
   - if we fail to connect to the network, we display Net: BAD on the LCD
*/
int connectToNetwork() {
  while(!Serial) ;
  
  Serial.println("Initializing Wifi...");
  printMacAddress();
  
  Serial.print("Attempting to connect to WPA network: [");
  Serial.print(ssid);
  Serial.println("]");
  lcd.setCursor(0, 1);
  lcd.print("Connecting...");
  wifiStatus = WiFi.begin(ssid, pass);
  
   // if you're not connected, stop here:
  if ( wifiStatus != WL_CONNECTED) {
    Serial.println("Couldn't get a wifi connection");
    lcd.setCursor(0, 1);
    lcd.print("Net: BAD       ");
    while(true);
  }
  // if you are connected, print out info about the connection:
  else {
    Serial.println("Connected to network");
    lcd.setCursor(0, 1);
    String ssidString = ssid;
    int padCount = 16 - ssidString.length();
    if (padCount > 0) {
      for (int i = 0; i < padCount; i++) {
        ssidString + ssidString + " ";        
      }
    }
    lcd.print(ssidString);
    delay(2000);  // Change this value to define how long the SSID is displayed on the LCD before we proceed 
    
    lcd.setCursor(0,1);   
    lcd.print("NET: OK          ");
  }
  return wifiStatus;
}

/* printMacAddress
   Finds the WiFi shield's mac address and prints it to the serial output. This is used for debugging
*/
void printMacAddress() {
  // the MAC address of your Wifi shield
  byte mac[6];                    

  // print your MAC address:
  WiFi.macAddress(mac);
  Serial.print("MAC: ");
  Serial.print(mac[5],HEX);
  Serial.print(":");
  Serial.print(mac[4],HEX);
  Serial.print(":");
  Serial.print(mac[3],HEX);
  Serial.print(":");
  Serial.print(mac[2],HEX);
  Serial.print(":");
  Serial.print(mac[1],HEX);
  Serial.print(":");
  Serial.println(mac[0],HEX);
}

Failure example output

Arduino in setup()
inMAC:

Both use the SPI bus. Disable the wifi SPI before starting the SD.

void setup() {
  Serial.begin(9600);

  // disable wifi while starting SD
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);
   
  Serial.print("Starting SD...");
  if (!SD.begin(4)) {
    Serial.println("failed");
  }
  else Serial.println("ok");

  // If that starts the SD ok, then start wifi here

  Serial.println("initialization done.");
}

Does that setup work?

SurferTim:
Both use the SPI bus. Disable the wifi SPI before starting the SD.

Does that setup work?

That setup then completes the SD init, but fails to progress on the WiFi init.

Do I then have to disable the SD SPI before trying to start the WiFi? And do I have to keep swapping that each time I wish to use one or the other ?

If that works ok, then copy and paste your wifi startup where I put the comment. Then it should start the wifi ok. The SD library begin() function call returns with the SD SPI disabled (D4 HIGH).

edit. Once these SS lines are initialized, you do not need to manipulate them anywhere else. The libraries will take care of that in the low level read/write functions.

SurferTim:
If that works ok, then copy and paste your wifi startup where I put the comment. Then it should start the wifi ok. The SD library begin() function call returns with the SD SPI disabled (D4 HIGH).

edit. Once these SS lines are initialized, you do not need to manipulate them anywhere else. The libraries will take care of that in the low level read/write functions.

Huh, ok - thanks!

Interestingly it works if I have the SD and the WiFi setup all in the setup() method. If I do the SD and then do the WiFi in a separate method (like I had it with connectToNetwork, then it fails still.

Any idea why that is ?

Again - thanks for the help - it makes the setup a bit more ugly, but it lets me keep going :slight_smile:

If I do the SD and then do the WiFi in a separate method (like I had it with connectToNetwork, then it fails still.

Fails how? Doesn't print the mac? Doesn't connect? What is the last thing displayed before the fail?

SurferTim:
Fails how? Doesn't print the mac? Doesn't connect? What is the last thing displayed before the fail?

After uploading the sketch, all I get is

Arduino in

If I then press the reset button on the arduino, I then see

I?Arduino in 
Arduino in 

Maybe running out of SRAM? Try using the F() function to keep the static strings in program memory like this.

  Serial.println(F("Arduino in setup()"));

Do the rest to all the other static strings. See if that helps.

SurferTim:
Maybe running out of SRAM? Try using the F() function to keep the static strings in program memory like this.

  Serial.println(F("Arduino in setup()"));

Do the rest to all the other static strings. See if that helps.

That seems to have done the trick - I wasn't aware that my use of static strings like this could hurt me in the long run. I've now added this to all of them. Now to just look up the F function and see what cost this magic comes at :slight_smile:

Thanks loads for your help!

I haven't noticed much overhead. I think it just sets them as a PROGMEM data type instead of copying them into SRAM.