Shopping Cart

Posts

Running multiple slave devices on Arduino SPI bus

Stack of shields

Stack of shields

This is Part 1 of 3-part series of articles. Part 2 talks about hardware modifications while Part 3 explains how to deal with incompatible data formats.

Serial Peripheral Interface AKA SPI bus is a popular way to communicate peripheral IO controllers to a micro over short distances and many microcontrollers have SPI interface built-in, including Arduino. Consequently, many Arduino shields use SPI to communicate to Arduino; USB Host shield is one of them. When more than one such shield is connected to Arduino, several SPI slave devices start sharing SPI bus. SPI is designed to allow bus sharing and if correct signals are present at correct moments all devices get along just fine. In practice, however, nothing usually works as desired, shields are interfering and fixing it requires hardware and software modifications. I was recently tasked with connecting 3 SPI slave devices to Arduino and this article shows how I did it.

A reader is expected to be familiar with SPI bus in general and Arduino implementation in particular. In addition, Tronixstuff has SPI tutorial, which is a little bit more user-friendly.

1. The problem

Arduino shields with SPI interface generally have 2 sources of interference – 1 on hardware level and one in software.

SPI bus consists of 3 shared data lines – SCK, MISO and MOSI, plus one “Slave Select” AKA SS line per each device. All transfers are initiated by the host and proceed in both directions simultaneously. Only one device, the one whose SS line is asserted low, is participating in the transfer by driving its MISO line – all other devices are expected to have their MISO line in a third state.

Due to peculiarities in Atmega SPI implementation, designers of Arduino shields prefer using default SS line, which is assigned to pin 10 on standard-sized Arduino boards. If two shields are driven by the same SS, they will have their MISO line active at the same time. Best-case consequence of it is only one of the shields in the stack being able to communicate, worst-case is destroyed MISO line transmitters. Therefore, the first step in any multi-shield SPI project is to make sure each shield uses its own SS line and never expect this to be the case in default configuration.

A software interference arises from the fact that SPI subsystem needs to be initialized before use and support software for each shield tries to do it at some point. Depending on how the code is written, this may or may not pose a problem. In any case, a review of the code is often necessary.

2. The solution

General advice given to developers struggling with SPI interference is to separate SS signals of interfering devices and fix the initialization code. The advice is valid but at the same time quite difficult to apply. Very often a shield designer forgets to add ability to route SS to a different pin. A software developer may hardcode the SS in SPI routines. Sometimes, a certain shield is designed such that MISO stays active all the time interfering with other shields with no regard to SS state – unfortunately, this design mistake can’t be easily fixed. Different combinations of shields require different hardware/software modifications. The final solution is usually difficult to realize but not impossible if implementation steps are done right and good testing has been performed after each step. The next section shows how I combined 3 SPI devices – my USB Host shield and Arduino WiFi shield, which also contains an SD card reader with SPI interface acting as an independent device.

3. Implementation

The following items have been used in the project:

    1. Arduino board. I used Mega 2560; it is possible to use Duemilanove or UNO board for testing but any application code won’t fit into 32K of program memory they provide.
    2. Arduino WiFi Shield plus FAT-formatted Micro-SD card installed into Micro-SD slot of the shield.
    3. USB Host Shield 2.0. If you already have it and already stripped male pins of 2×3 ICSP connector, you will also need a replacement stackable 2×3 header. The assembled version of the shield requires replacing all the headers with stackable variety – check in the components section of the store if you want to do this.
    4. A USB device, preferably GPS receiver with compatible chipset. The GPS is handy since it generates steady stream of data on its own.

First of all, I need to establish a baseline or “known good state”. I simply connect unmodified USB Host shield to Arduino board and run board_qc example, which should complete without errors, except for GPIO part, which requires a test fixture to pass. I’m not going to make any hardware modifications to WiFi shield so it is presumed good (and mine indeed worked from the get go), however, if you are new to Arduino it is a good idea to disconnect USB Host shield, connect WiFi shield and work through some WiFi and SD examples to make sure your hardware is functional.

Both shields use ICSP header for SPI signals. On standard sized Arduino board SPI is also routed to pins 11-13 which means they can’t be used for anything else. On Megas, however, pins 11-13 can be used as general-purpose IO pins. Both shields use pin 10 for SS, therefore one of the shields would have to be modified. According to the documentation, WiFi shield also uses pins 7 and 4. USB Host shield uses pin 9. The only interference here seems to be SS at pin 10. USB Host shield has features designed to help relocating pins so naturally I prefer to work with it and move its SS signal to pin 8. The following 3 pictures illustrate the USB Host shield after modifications:

USB Host shield modification

USB Host shield modification

UHS mod SS closeup

UHS mod SS closeup

UHS mod ICSP closeup

UHS mod ICSP closeup

Picture on the left shows modified shield. The middle picture is a closeup of modified SS pin. To re-route, I cut a trace inside SS jumper and then soldered a wire from SS pad to pin 8. Picture on the right shows how ICSP connector should look like – male pins of the 2×3 header shall be left protruding from the PCB to provide SPI lines to WiFi shield which will be stacked on top of USB Host shield.

When the mod is done it is time to check the functionality. Since I’m going to do many code modifications, it’s a good idea to make a work copy of the whole Arduino IDE tree and work from this copy. It is important to make sure the compiler picks the modified source libraries – on Windows simply open arduino.exe from the work copy directory and then open all the sketches from the IDE window.

The first test is to make sure the hardware mod is working. Here’s the steps:

    1. Open Usb.h file in USB Host library directory, find a line which looks like this -> https://github.com/felis/USB_Host_Shield_2.0/blob/master/Usb.h#L61 and change it to

    typedef MAX3421e<P8, P9>		MAX3421E;

    This will assign pin 8 to SS in USB Host shield code.

    2. Attach USB Host shield to Arduino board and run board_qc sketch. If it fails, close the IDE and open it again to force it to re-read the modified library.
    3. Now, attach the WiFi shield on top of USB Host shield. Two pins to the left of RESET/power pins, introduced in R3, need to be bent outwards slightly in order to fit (see the title picture). It is also necessary to install a jumper between IREF and 3.3V pins of the shield as described on the product page in Using the Shield with Older Boards section.

    When WiFi shield is attached run the board_qc again. This time, it may pass or fail. If it fails, it is likely because uninitialized pins 10 and 4 keep MISO transmitters on WiFi shield active. To deactivate them, add pin initialization code in the very beginning of the setup() function. This is how modified setup() should look like:

     
    void setup()
    {
      Serial.begin( 115200 );
      pinMode(10, OUTPUT);
      pinMode(4, OUTPUT);
      digitalWrite(10, HIGH);
      digitalWrite(4, HIGH);
     
    ...

    Recompile the sketch, load it and run. It shall pass this time.

To test the USB Host shield on real USB traffic we need to find a way to generate such traffic. A GPS receiver, described in one of the previous articles, is a good candidate. Once powered and enumerated, it will be constantly sending serial data to Arduino. Load the pl2303_gps sketch from examples/pl2303 directory of USB Host library, compile it, load, connect the GPS unit and open the terminal window. If all is well, you should see something like this:

Start
PL Init
Addr:1
NC:1
Conf:1
PL configured       f&ÆÆÆæSFFÖ
,232,28,06,49,2&æFÆf          $GPVTG,,T,,M,,N,,K,N*2C
$GPGGA,012011.148,,,,,0,00,,,M,0.0,M,,0000*58
$GPGLL,,,,,012011.148,V,N*74
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,012011.148,V,,,,,,,261012,,,N*45
 
...

After repeating this step with WiFi shield attached and pin 10 and 4 initialization code added, the USB Host shield testing is complete. Now it is time to run two devices concurrently and the second device is going to be a WiFi module. But before starting chopping the code I want to say a couple of words about SPI initialization.

On AVR micros, SPI initialization consists of 2 steps: first step is to set direction to interface pins and second to select one of 4 SPI modes and clock speed. The SPI transfer is an easy operation of writing the transfer register and waiting for completion and rarely needs modification. One exception to this is USB Host SPI routines – to maximize speed multi-byte transfers are handled in a special way therefore low-level code looks strange and hard to understand. However, it still accesses SPI transfer register in a standard way and as long as the correct SS line is asserted before the transfer and deasserted right after it everything will work together just fine. I will not touch any of the SPI transfer routines, only initialization code.

I’ll start with the second step. All 3 of my devices require SPI mode 0,0 to function – this mode is most often used and likely OK for any SPI device ever used with Arduino, except maybe some obscure analog chips. All 3 will also work on maximum speed. Therefore, we only need to make this initialization step once. Since SCK, MOSI and MISO are shared we also need to initialize them once for all devices. SS pin is unique to the device so initialization of it needs to be handled by device-specific initialization routine. In this project, USB Host library will be initializing main interface and other libraries will be modified to only handle their specific SS pin.

First, let’s install WiFi library – look at the product page for the link. Go to utility directory and open the spi_drv.cpp file. In the beginning, we can see the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
 
...
 
#define DATAOUT 	11 // MOSI
#define DATAIN  	12 // MISO
#define SPICLOCK  	13 // sck
#define SLAVESELECT 10 // ss
#define SLAVEREADY 	7  // handshake pin
#define WIFILED 	9  // led on wifi shield
 
#define DELAY_100NS do { asm volatile("nop"); }while(0);
#define DELAY_SPI(X) { int ii=0; do {  asm volatile("nop"); }while(++ii<X);}
#define DELAY_TRANSFER() DELAY_SPI(10)
 
void SpiDrv::begin()
{
	  // Set direction register for SCK and MOSI pin.
	  // MISO pin automatically overrides to INPUT.
	  // When the SS pin is set as OUTPUT, it can be used as
	  // a general purpose output port (it doesn't influence
	  // SPI operations).
 
	  pinMode(SCK, OUTPUT);
	  pinMode(MOSI, OUTPUT);
	  pinMode(SS, OUTPUT);
	  pinMode(SLAVESELECT, OUTPUT);
	  pinMode(SLAVEREADY, INPUT);
	  pinMode(WIFILED, OUTPUT);
 
	  digitalWrite(SCK, LOW);
	  digitalWrite(MOSI, LOW);
	  digitalWrite(SS, HIGH);
	  digitalWrite(SLAVESELECT, HIGH);
	  digitalWrite(WIFILED, LOW);
 
#ifdef _DEBUG_
	  INIT_TRIGGER()
#endif
 
	  // Warning: if the SS pin ever becomes a LOW INPUT then SPI
	  // automatically switches to Slave, so the data direction of
	  // the SS pin MUST be kept as OUTPUT.
	  SPCR |= _BV(MSTR);
	  SPCR |= _BV(SPE);
	  //SPSR |= _BV(SPI2X);
}
 
void SpiDrv::end() {
  SPCR &= ~_BV(SPE);
}
 
...

First, let’s take a look at SpiDrv::end() function (lines 49-51). I’m not comfortable having it here – if WiFi code ever decides to use it, the SPI will be disabled and other SPI devices will stop functioning. To prevent this, I commented out the dangerous code, like this:

void SpiDrv::end() {
//  SPCR &= ~_BV(SPE);
}

Now, when SpiDrv::end() gets called, nothing happens.

Let’s now take a look at the beginning of the code fragment. Among the #define-s on lines 4-9 we can see definition of pin 9, which looks like that:

#define WIFILED 	9  // led on wifi shield

this pin is also initialized as output on line 28:

pinMode(WIFILED, OUTPUT);

This pin is not mentioned in WiFi shield docs so it is either some upcoming feature (the shield has 4 LEDs, including this one) or leftovers from previous versions. In any case, this pin is going to interfere with pin 9 on USB Host shield. I can re-route pin 9 as easily as I did SS (pin 10), however, after quick search through the code I realized that after initialization this pin is not used anywhere so I may as well disable the initialization of pin 9 with no loss of functionality. This is how the modified SpiDrv::begin() looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void SpiDrv::begin()
{
	  // Set direction register for SCK and MOSI pin.
	  // MISO pin automatically overrides to INPUT.
	  // When the SS pin is set as OUTPUT, it can be used as
	  // a general purpose output port (it doesn't influence
	  // SPI operations).
 
//	  pinMode(SCK, OUTPUT);
//	  pinMode(MOSI, OUTPUT);
	  pinMode(SS, OUTPUT);
	  pinMode(SLAVESELECT, OUTPUT);
	  pinMode(SLAVEREADY, INPUT);
//	  pinMode(WIFILED, OUTPUT);
 
//	  digitalWrite(SCK, LOW);
//	  digitalWrite(MOSI, LOW);
	  digitalWrite(SS, HIGH);
	  digitalWrite(SLAVESELECT, HIGH);
//	  digitalWrite(WIFILED, LOW);
 
#ifdef _DEBUG_
	  INIT_TRIGGER()
#endif
 
	  // Warning: if the SS pin ever becomes a LOW INPUT then SPI
	  // automatically switches to Slave, so the data direction of
	  // the SS pin MUST be kept as OUTPUT.
//	  SPCR |= _BV(MSTR);
//	  SPCR |= _BV(SPE);
	  //SPSR |= _BV(SPI2X);
}

As can be seen, I commented out everything except initialization of pins unique to WiFi. Technically, the only lines which I must prevent from execution are the ones modifying WIFILED, the others are commented out just in case. I also commented out #define WIFILED 9 // led on wifi shield (line 9 of the previous listing) in case I made a mistake and the pin is actually used somewhere in the code. In this case I won’t miss it since a code won’t compile.

This completes the WiFi code modifications and it’s now time to start testing. As before, I need a way to generate some data initiated from WiFi module to see if it is capable of working concurrently with USB Host. The first example on the product page provides necessary functionality – I can poll the shield continuously to get a list of networks and I don’t need to bother with association with an access point which could take some time. At this point my modified pl2303_gps sketch looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/* USB Host to PL2303-based USB GPS unit interface */
/* Navibee GM720 receiver - Sirf Star III */
/* USB support */
#include <avrpins.h>
#include <max3421e.h>
#include <usbhost.h>
#include <usb_ch9.h>
#include <Usb.h>
#include <usbhub.h>
#include <avr/pgmspace.h>
#include <address.h>
/* CDC support */
#include <cdcacm.h>
#include <cdcprolific.h>
/* Debug support */
#include <printhex.h>
#include <message.h>
#include <hexdump.h>
#include <parsetools.h>
 
#include <WiFi.h>
 
 
class PLAsyncOper : public CDCAsyncOper
{
public:
    virtual uint8_t OnInit(ACM *pacm);
};
 
uint8_t PLAsyncOper::OnInit(ACM *pacm)
{
    uint8_t rcode;
 
    // Set DTR = 1
    rcode = pacm->SetControlLineState(1);
 
    if (rcode)
    {
        ErrorMessage<uint8_t>(PSTR("SetControlLineState"), rcode);
        return rcode;
    }
 
    LINE_CODING lc;
    lc.dwDTERate  = 4800;   //default serial speed of GPS unit  
    lc.bCharFormat  = 0;
    lc.bParityType  = 0;
    lc.bDataBits  = 8;  
 
    rcode = pacm->SetLineCoding(&lc);
 
    if (rcode)
        ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode);
 
    return rcode;
}
 
USB     Usb;
USBHub     Hub(&Usb);
PLAsyncOper  AsyncOper;
PL2303       Pl(&Usb, &AsyncOper);
uint32_t read_delay;
uint32_t wifi_delay;
 
#define READ_DELAY 100
#define WIFI_DELAY 1000
 
void setup()
{
  Serial.begin( 115200 );
 
  pinMode(4, OUTPUT);
  digitalWrite(4, HIGH);
 
  Serial.println("Start");
 
  if (Usb.Init() == -1)
      Serial.println("OSCOKIRQ failed to assert");
 
  delay( 200 ); 
}
 
void loop()
{
uint8_t rcode;
uint8_t  buf[64];    //serial buffer equals Max.packet size of bulk-IN endpoint           
uint16_t rcvd = 64;   
 
  Usb.Task();
 
    if( Pl.isReady()) {  
       /* reading the GPS */
       if( read_delay < millis() ){
       read_delay += READ_DELAY;  
       rcode = Pl.RcvData(&rcvd, buf);
        if ( rcode && rcode != hrNAK )
           ErrorMessage<uint8_t>(PSTR("Ret"), rcode);            
            if( rcvd ) { //more than zero bytes received
              for( uint16_t i=0; i < rcvd; i++ ) {
                  Serial.print((char)buf[i]); //printing on the screen
              }//for( uint16_t i=0; i < rcvd; i++...              
            }//if( rcvd
       }//if( read_delay > millis()...            
    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING..
 
  if( wifi_delay < millis() ) {
 
    wifi_delay += WIFI_DELAY;
 
    listNetworks();
 
  }
 
}
 
void listNetworks() {
  // scan for nearby networks:
  Serial.println("** Scan Networks **");
  byte numSsid = WiFi.scanNetworks();
 
  // print the list of networks seen:
  Serial.print("number of available networks:");
  Serial.println(numSsid);
 
  // print the network number and name for each network found:
  for (int thisNet = 0; thisNet<numSsid; thisNet++) {
    Serial.print(thisNet);
    Serial.print(") ");
    Serial.print(WiFi.SSID(thisNet));
    Serial.print("\tSignal: ");
    Serial.print(WiFi.RSSI(thisNet));
    Serial.print(" dBm");
    Serial.print("\tEncryption: ");
    Serial.println(WiFi.encryptionType(thisNet));
  }
}//void listNetworks()...

I made the following modifications: included WiFi.h header on line 22, listNetworks() in the loop(), plus I copied the listNetworks() verbatim from Arduino WiFi shield product page. I also removed pin 10 initialization from the setup() but left pin 4 init in place since SD card is not functional yet. The sketch above is complete – it can be pasted from this page into IDE window and compiled. The terminal output looks like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Start
** Scan Networks **
number of available networks:2
0) louisville   Signal: -56 dBm Encryption: 5
1) VisionBuild  Signal: -75 dBm Encryption: 4
** Scan Networks **
number of available networks:2
0) louisville   Signal: -72 dBm Encryption: 5
1) VisionBuild  Signal: -76 dBm Encryption: 4
** Scan Networks **
number of available networks:3
0)      Signal: -54 dBm Encryption: 4
1) louisville   Signal: -54 dBm Encryption: 5
2) VisionBuild  Signal: -77 dBm Encryption: 4
** Scan Networks **
number of available networks:3
0)      Signal: -54 dBm Encryption: 4
1) louisville   Signal: -54 dBm Encryption: 5
2) VisionBuild  Signal: -77 dBm Encryption: 4
** Scan Networks **
number of available networks:2
0) louisville   Signal: -54 dBm Encryption: 5
1) VisionBuild  Signal: -76 dBm Encryption: 4
PL Init
Addr:1
NC:1
Conf:1
PL configured
$GPGGA,1705ñ$GPGGA,170517.928,,,,,0,00,,,M,0.0,M,,0000*50
$GPGL** Scan Networks **
number of available networks:3
0) louisville   Signal: -68 dBm Encryption: 5
1)      Signal: -70 dBm Encryption: 4
2) VisionBuild  Signal: -77 dBm Encryption: 4
L,,,,,170517.928,V,N*7C
$GPGSA,A,1,,,,,,,,,,,,,,,*1E
$GPRMC,17** Scan Networks **
number of available networks:3
0) louisville   Signal: -56 dBm Encryption: 5
1)      Signal: -70 dBm Encryption: 4
2) VisionBuild  Signal: -75 dBm Encryption: 4
0517.928,V,,,,,,,261012,,,N*4D
$GPVTG,,T,,M,,N,,K,N*2C

It can be seen that the sketch starts and performs 5 network scans. After that (on line 24), the GPS receiver gets initialized and starts sending data (line 29). From this point on, 2 devices are working concurrently.

Two devices are now working and all what is left is an SD card. An SD library is a part of a standard distro so it’s already in a source tree. The file which needs to be modified is SD/utility/Sd2Card.cpp and the analysis is similar to WiFi initialization above. The function inside a file is called Sd2Card::init() and my modifications are marked with //om-.

uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
  errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
  chipSelectPin_ = chipSelectPin;
  // 16-bit init start time allows over a minute
  uint16_t t0 = (uint16_t)millis();
  uint32_t arg;
 
  // set pin modes
  pinMode(chipSelectPin_, OUTPUT);
  chipSelectHigh();
  //om-pinMode(SPI_MISO_PIN, INPUT);
  //om-pinMode(SPI_MOSI_PIN, OUTPUT);
  //om-pinMode(SPI_SCK_PIN, OUTPUT);
 
#ifndef SOFTWARE_SPI
  // SS must be in output mode even it is not chip select
  pinMode(SS_PIN, OUTPUT);
  digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin
  // Enable SPI, Master, clock rate f_osc/128
  //om-SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
  // clear double speed
  //om-SPSR &= ~(1 << SPI2X);
#endif  // SOFTWARE_SPI
 
  // must supply min of 74 clock cycles with CS high.
  for (uint8_t i = 0; i < 10; i++) spiSend(0XFF);
 
  chipSelectLow();
 
...

The test for this device is to simply open a file on the card and write some data to it. Later, the card can be removed from the shield and read on a PC. The modified sketch follows.

/* USB Host to PL2303-based USB GPS unit interface */
/* Navibee GM720 receiver - Sirf Star III */
/* USB support */
#include <avrpins.h>
#include <max3421e.h>
#include <usbhost.h>
#include <usb_ch9.h>
#include <Usb.h>
#include <usbhub.h>
#include <avr/pgmspace.h>
#include <address.h>
/* CDC support */
#include <cdcacm.h>
#include <cdcprolific.h>
/* Debug support */
#include <printhex.h>
#include <message.h>
#include <hexdump.h>
#include <parsetools.h>
 
#include <WiFi.h>
#include <SD.h>
 
 
class PLAsyncOper : public CDCAsyncOper
{
public:
    virtual uint8_t OnInit(ACM *pacm);
};
 
uint8_t PLAsyncOper::OnInit(ACM *pacm)
{
    uint8_t rcode;
 
    // Set DTR = 1
    rcode = pacm->SetControlLineState(1);
 
    if (rcode)
    {
        ErrorMessage<uint8_t>(PSTR("SetControlLineState"), rcode);
        return rcode;
    }
 
    LINE_CODING lc;
    lc.dwDTERate  = 4800;   //default serial speed of GPS unit  
    lc.bCharFormat  = 0;
    lc.bParityType  = 0;
    lc.bDataBits  = 8;  
 
    rcode = pacm->SetLineCoding(&lc);
 
    if (rcode)
        ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode);
 
    return rcode;
}
 
USB     Usb;
USBHub     Hub(&Usb);
PLAsyncOper  AsyncOper;
PL2303       Pl(&Usb, &AsyncOper);
uint32_t read_delay;
uint32_t wifi_delay;
 
#define READ_DELAY 100
#define WIFI_DELAY 1000
 
void setup()
{
  Serial.begin( 115200 );
  Serial.println("Start");
 
  if (Usb.Init() == -1)
      Serial.println("OSCOKIRQ failed to assert");
 
  // see if the card is present and can be initialized:
  if (!SD.begin(4)) {
    Serial.println("Card failed, or not present");
  }
  else {
    Serial.println("card initialized.");
  }    
 
  delay( 200 ); 
}
 
void loop()
{
uint8_t rcode;
uint8_t  buf[64];    //serial buffer equals Max.packet size of bulk-IN endpoint           
uint16_t rcvd = 64;   
 
  Usb.Task();
 
    if( Pl.isReady()) {  
       /* reading the GPS */
       if( read_delay < millis() ){
       read_delay += READ_DELAY;  
       rcode = Pl.RcvData(&rcvd, buf);
        if ( rcode && rcode != hrNAK )
           ErrorMessage<uint8_t>(PSTR("Ret"), rcode);            
            if( rcvd ) { //more than zero bytes received
              for( uint16_t i=0; i < rcvd; i++ ) {
                  Serial.print((char)buf[i]); //printing on the screen
              }//for( uint16_t i=0; i < rcvd; i++...              
            }//if( rcvd
       }//if( read_delay > millis()...            
    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING..
 
  if( wifi_delay < millis() ) {
 
    wifi_delay += WIFI_DELAY;
 
    listNetworks();
 
  }
 
}
 
void listNetworks() {
 
File dataFile = SD.open("datalog.txt", FILE_WRITE);
 
  // scan for nearby networks:
  Serial.println("** Scan Networks **");
  byte numSsid = WiFi.scanNetworks();
 
  // print the list of networks seen:
  Serial.print("number of available networks:");
  Serial.println(numSsid);
 
  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(numSsid);
    dataFile.close();
  }  
  // if the file isn't open, pop up an error:
  else {
    Serial.println("error opening datalog.txt");
  } 
 
  // print the network number and name for each network found:
  for (int thisNet = 0; thisNet<numSsid; thisNet++) {
    Serial.print(thisNet);
    Serial.print(") ");
    Serial.print(WiFi.SSID(thisNet));
    Serial.print("\tSignal: ");
    Serial.print(WiFi.RSSI(thisNet));
    Serial.print(" dBm");
    Serial.print("\tEncryption: ");
    Serial.println(WiFi.encryptionType(thisNet));
  }
}//void listNetworks()...

Here I added card initialization in the setup() and a file operation in listNetworks(). The terminal output did not change. This sketch can be pasted into IDE and compiled. Insert empty SD card into WiFi shield and let the sketch run for a while, then remove power and move SD card to a PC. You should see DATALOG.TXT file. Open this file in a text editor and you should see data similar to this:

1
2
3
3
2
2
1
4
5
5
3
2
2
2
2
3
3
2
 
...

This is number of networks discovered during each scan, one per line. I can now see that all 3 devices are working simultaneously, therefore, the hardware/software mods are correct and this setup can be used for something more practical, like a portable battery-operated wardriving rig. On a side note, the last sketch compiles to 32192 bytes on my machine so it definitely requires more than standard-sized Arduino to run.

It is now Friday afternoon – I wish everybody a nice weekend! If you have questions, please ask.

Oleg.

No related posts.

9 comments to Running multiple slave devices on Arduino SPI bus

  • Martin

    Nice work!
    I am really sorry that i only have an UNO! Would be really sweet to have enough memory to implement this.

    //Martin

  • Jerric Lyns John

    Hi,
    I had bought a Rover Robot (http://www.robotshop.com/dfrobotshop-rover-tracked-robot-basic-kit-15.html) along with an arduino WiFi Shield.

    Now my problem is that since the PCB V2.0 of the rover robot is hooked to pins 5, 6, 7, 8 for permanent (its built in motor controller) and since the arduino wifi shield uses the same pin 7 for handshake between them, there is an overlapping situation and the shield doesnt work.

    The RobotShop technical team had suggested to reconfigure the wifi pin.

    I connected the free pin 2 on the PCB V2.0 to the bend pin 7 of the WiFi shield.The problem was solved.

    But now whenever in my arduino sketch i give control over to the motor controller and later wishes to executes a code specific to the WiFi shield, it doesnt work, ie, the motor controller still remains in control

    I believe there is a problem.
    I would really appreciate it if you could demo a tutorial on this , I really need help

  • MKRC

    Is there a Motor controller shield that you could suggest that work without issues when stacked with the USB Host Shield? I would like to use a USB Host Shield to allow me to use a PS3 controller to control my next project but also need to power and control two motors and two or three servos.

    • I’m not aware of any. They all seem to be based on Arduino PWM outputs, some of them interfering with SPI. You may want to use Mega as your MCU board, its SPI is not conflicting with any of the “standard” pins.

  • Ryan F

    Do you have any suggestions for doing something similar with the Arduino Mega ADK and the WiFi shield? I am trying to use the built-in host USB port on the ADK to talk to a barcode scanner and then transmit that data through wifi. So far I have both devices working separately and using parts from this guide have jumped the SS pin(10) on the wifi shield to pin(8) and changed the code in spi_drv.cpp to reflect it. That allowed the USB host to work and scan barcodes while the wifi shield was connected (though not in use) which it would not do before that. Any suggestions would be greatly appreciated.

  • handika

    i try your intruction for change pin ss from pin 10 to pin 8 and than i try to conecting barcode scanner use usb host sheid with your source code form the previous article. the result in terminal windows is OSC did not start.

    in usb.h i change to
    typedef MAX3421e MAX3421E; // Official Arduinos (UNO, Duemilanove, Mega, 2560, Leonardo etc.)

    why the result OSC did not start?
    is there a fault with my writing?

  • ZotDitzMyo

    Thanks for a great writeup! I am currently looking to use an SPI stepper Driver (ST L6470) with a microcontroller that could do ethernet as well. Since the usual way to get ethernet on the Arduino is also through SPI, that means sharing the same bus, or going for a native AT32, ATSAM or STM32 arm chip even. I would like to stick to Arduino because of all the time it saves though.
    Do you think using both devices on the same bus will lead to conflicts/dropped messages etc??

    thanks for everything!

Leave a Reply

  

  

  



You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">