CC3000 with adafruit library, non-blocking?

Status
Not open for further replies.

christoph

Well-known member
Hi all,

I'm having some issues with the CC3000. When I send a http request and immediately after that use a different SPI device, the CC3000 interrupt that occurs later interferes with my other device drivers and my application locks up (this is how I interpret the errors I get).

I can work around that by simply finishing all CC3000 operations before starting anything else, but that's not what I want. I'd like to send a request, do something else, and check the request result later.

Is there a way to use the adafruit library in a non-blocking way or are there library alternatives?

Regards

Christoph

Edit: Also, if there are any other modules comparable to the CC3000 (example code/library available, SPI or I2C interface) you recommend, I'll try those.
 
Last edited:
Wouldn't that require writing a new SPI part for the library? The adafruit library seems to use the SPI in an ISR, when it shouldn't. Making the SPI unavailable would not change a lot. Or am I totally misinterpreting your point?

Regards

Christoph
 
I've written several lengthy explanations about SPI transactions on the Arduino Developers Mailing List.

At the risk of being terse, rather than duplicate that work, I'm going to refer you to that already-written material. It's very long read, with a number of not-so-well-informed comments, but also a lot of good input and testing from Matthijs and Cristian.

https://groups.google.com/a/arduino.cc/d/msg/developers/TuZLfjeZjDI/6deNjw3Y9m4J

My general feeling is this issue has already been discussed far too much. A lot of very good work has already been done. Recently Cristian gave some guidance on what he'll accept into Arduino, so this message is more-or-less the final word.

https://groups.google.com/a/arduino.cc/d/msg/developers/TuZLfjeZjDI/Hm7UFtWxm2UJ

Today I'm working on a fix for the pin 33 troubles, and also support for activating the chip lock bit. I still have a pretty good list of desired features for 1.20... many of which may get pushed out to 1.21. My intention is to work on the SPI transaction stuff after the pin 33 issue is fixed, since it really is important.

I'm still participating on these forum discussions, but on the matter of SPI transactions, it's been discussed and planned very throughly. Cristian has outlined when Arduino will accept, so the best use of my time now is actually working on the code.
 
Paul, this was not meant to be about SPI transactions as such. Maybe my question was not phrased well enough to make that clear. I'll try to re-write it. Let's take this snippet from the WebClient example:

Code:
Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
if (www.connected()) {
  www.fastrprint(F("GET "));
  www.fastrprint(WEBPAGE);
  www.fastrprint(F(" HTTP/1.1\r\n"));
  www.fastrprint(F("Host: ")); www.fastrprint(WEBSITE); www.fastrprint(F("\r\n"));
  www.fastrprint(F("\r\n"));
  www.println();
} else {
  Serial.println(F("Connection failed"));    
  return;
}

Serial.println(F("-------------------------------------"));
  
/* Read data until either the connection is closed, or the idle timeout is reached. */ 
unsigned long lastRead = millis();
while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {
  while (www.available()) {
    char c = www.read();
    Serial.print(c);
    lastRead = millis();
  }
}
www.close();

The code sends a http request and waits for a response. That takes about one or two seconds. All SPI transactions are blocking and may stay so. I would like to do the following instead:

Code:
Adafruit_CC3000_Client www = cc3000.connectTCP(ip, 80);
if (www.connected()) {
  www.fastrprint(F("GET "));
  www.fastrprint(WEBPAGE);
  www.fastrprint(F(" HTTP/1.1\r\n"));
  www.fastrprint(F("Host: ")); www.fastrprint(WEBSITE); www.fastrprint(F("\r\n"));
  www.fastrprint(F("\r\n"));
  www.println();
} else {
  Serial.println(F("Connection failed"));    
  return;
}

-----> Use the SPI for something else here <-----
  
/* Read data until either the connection is closed, or the idle timeout is reached. */ 
unsigned long lastRead = millis();
while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {
  while (www.available()) {
    char c = www.read();
    Serial.print(c);
    lastRead = millis();
  }
}
www.close();

The problem is that the CC3000 driver uses the SPI in an ISR that would mess with any code that uses the SPI in between. My question was about alternative APIs that use the SPI as it is (blocking), but don't try to use the SPI at a random point in time when I don't have any control over what is happening. I hav found this in the meatime: https://code.google.com/p/cc3000-non-blocking-lib/ but I don't know and didn't find any information about how to use it. It seems to do just what I want: Move SPI transfers from the ISR into the main loop.

Does anyone have any experience with that non-blocking driver?

Regards

Christoph
 
The problem is that the CC3000 driver uses the SPI in an ISR that would mess with any code that uses the SPI in between.

This is exactly the problem SPI transactions are meant to solve. This, and differing SPI settings, which is also an issue with CC3000.
 
Differing SPI settings are ok, I found a workaround for those. Regarding the rest: I...I just don't get it. The bottom line seems to be that the current driver needs me to block for however long it wants, so I guess I need to adapt to that.

Nonetheless, having a better API for the SPI is surely a huge benefit and effort, and I'm looking forward to that.
 
Christoph, if you're still watching this thread, I'm testing CC3000 together with other SPI devices.

I see code fragments in reply #5, but not a complete program. I'm going to try to fill in the gaps...

If you'll post a complete program that shows the problem you were seeing, I'll test with your code (rather than stuff I make up for the rest). That would give the best odds I'll truly solve the exact problem you encountered.
 
I'm building a test which will write a file to a SD card with random data. Then it'll run this example and repetitively read the file and compare against the known data, while it waits for the web server to respond.

Hopefully that's a good test? If you had some other SPI device in mind to use while using the CC3000 in non-blocking mode, please let me know?
 
Hi Paul,

thanks for helping. Your suggestion sounds just like what I'd like to do. One other device I have in mind is an OLED display (ssd1351), but that should not make a difference. johnnyfp is using a modified version of the Energia library which I'm planning to test, but not before the weekend as I'm currently on vacation.

I'll also try to write a minimum working example when I'm back.

Regards

Christoph
 
I'm happy to report the SPI transactions are working great.

I modified the CC3000 example to repetitively read a 3000 byte file from the SD card while waiting for the CC3000. Without SPI transactions, it crashes on Teensy 2.0 within just a few iterations. On Teensy 3.1, it and can't ever access the card, because CC3000 doesn't save/restore SPI settings for non-AVR chips. With SPI transactions, it works perfectly.

Here's the code that waits for the CC3000 incoming data, while calling read_sd_card(). I'll attach the full code to this message.

Code:
        /* Read data until either the connection is closed, or the idle timeout is reached. */
        unsigned long lastRead = millis();
        while (www.connected() && (millis() - lastRead < IDLE_TIMEOUT_MS)) {

                // while waiting for the Wifi to return data, read the SD card
                read_sd_card();

                while (www.available()) {
                        char c = www.read();
                        Serial.print(c);
                        lastRead = millis();
                }
        }
        www.close();

At startup, 3000 bytes of random data are written to a file and kept in memory. The read_sd_card() function reads the file and checks 3000 bytes are the same as originally written. The full code is attached below. (you must edit to add your SSID and Wifi password if secure)

Here are the libraries, patched for SPI transactions.

https://github.com/PaulStoffregen/SPI
https://github.com/PaulStoffregen/Adafruit_CC3000
https://github.com/PaulStoffregen/SD

Earlier in this thread, I probably didn't communicate the time scale of SPI transactions well. A SPI transaction is NOT the entire 1+ seconds needed for the CC3000 to fetch a web page. It's only a very brief time, while a library uses the SPI port. Usually each SPI transaction corresponds to a single assert of a chip select. Ethernet uses transactions as the socket level, so several small transfers are done per transaction, but the time is still very small.

In this case, the SD library is doing transactions that involve single sector reads or writes, or small transfers to check the card status. The CC3000 interrupt is only masked for very short times, but long enough that it can't interfere with proper operation of the SD card, or (hopefully) any other SPI-based library that's using SPI.beginTransaction() and SPI.endTransaction().
 

Attachments

  • cc3000_sd.ino
    6.2 KB · Views: 340
Hi Paul,

Just wanted to reiterate my appreciation for your work, both here as well as with the Arduiono collective!
 
Cristian Maglie just agreed on the Arduino Developers Mail List that it's time to send this as a pull request for Arduino (which I'll do tomorrow). I'm also going to put together a 1.20-rc2 Teensyduino installer.

Here's a photo the hardware I used for the test in #11.

cc3k_sd_test.jpg

I'm really glad these strange SPI problems are finally going to be solved, not just on Teensy, but eventually for all Arduino compatible boards. Nobody deserves to put so much work into a project and ultimately have it turn out flaky because of the platform.
 
Paul, there's a small error in your cc3000_sd.ino

Lines 59 and 60 use hard-coded pin numbers, but the pins have been defined before. These defines should be used here, so
Code:
// lines 59...60:
pinMode(4, INPUT_PULLUP);  // SD Card
pinMode(10, INPUT_PULLUP); // CC3000
should be
Code:
pinMode(SD_CHIP_SELECT, INPUT_PULLUP);  // SD Card
pinMode(ADAFRUIT_CC3000_CS, INPUT_PULLUP); // CC3000
instead.

You probably didn't notice this as resetting a pin to input mode in setup() usually doesn't have any real effect when the hardware came from reset before.

After correctly setting the pin numbers, the output of your sketch doesn't show any errors on my hardware, which also includes an adafruit ssd1351 (carrying the SD card) and other stuff. There are lots of "SD ok :)" messages and a message block from the CC3000 in regular intervals.

I'll now try (!) to port my existing project to 1.20 RC2 and see if interleaved display, SD and CC3000 works for me. Might take some time, though...

Regards

Christoph
 
Hi folks,

Just tried out a CC3000 with the Adafruit lib for Teensy last night. Mine has the 1.26 firmware Buildtest got me as far as trying to connect to my wifi network last night, but couldn't do it. Messed around with things- the second (stock, commented) below wouldn't work, the second came back with a connect, but checkConnected would always return 0 afterwards

if(cc3000.connectSecure(WLAN_SSID, WLAN_PASS,3))
// if (cc3000.connectToAP(WLAN_SSID, WLAN_PASS, 3,1))

On a lark, tried it with an Arduino uno tonight and Adafruit's library. Everything works fine. Any ideas? Stuff I could try? Thanks for any and all. I will probably go back and double check my connections, but it was definitely talking to the cc3000 to have gotten that far.
 
Impressive work Paul.
I just found this thread after 3 days of trying to figure out why my CC3000 Shield with SD card is not working properly. I'm working in a project where I have a server listening to clients and once a client connects needs to send a large file from the SD card. My setup uses an Arduino DUE, does your code works with the DUE you think ?
 
I browsed through adafruit cc3000 library code.
I see the library uses pin interrupt, yet the code blocks waiting for interrupt to complete. :0
 

hi Paul ,

first of all thanks sooo much for your work , im having the SD.h corrupted file problem in a very critical project at my work and i wasn't able to understand why till i read your threads
i use Ethernet 2 shield for both Ethernet and SD read write , also i user external interrupts a lot
my application is an interrupt based production counter that saves the count to SD file and sends it to a server by Ethernet , i have some questions please

1- i downloaded your SD library will it work with the ethernet 2 library with no problem or should be more adjustments to the ethernet 2 lib code
2- will the external interrupt cause problems too or is it just the 2 spi devices
3- does loosing electricity while writing a file causes a corrupt file too
4- i just knew about Teensy is it 100% compatible with arduino code and libraries and does it support UTFT library

thanks in advance ,

thats my code
Code:
#include <Ethernet2.h>
#include <EthernetClient.h>
#include <SPI.h>
#include <SD.h>
#include <TimerOne.h>
#include <avr/wdt.h>
//************************************************************************************
//************************************************************************************
// GLOBAL VARIABLES
const int chipSelect = 4;// SD

File dataFile;//FILE ENTITY
File dataFile2;//FILE ENTITY

volatile unsigned long int total;//Counter accumelator
char b, R, N, X, Y, Z, x, p; // k //status flag to stop count while stop , connection trials count ,
//status send & reset send, reet debouncer , count debouncer , repeat send status
int port=5045;
byte mac[] = {0x90, 0xA2, 0xDA, 0x10, 0x76, 0x7C};
byte ip[] = {10, 10, 10, 45}; // unit for machine 21 counting ip =41
byte server[] = {10, 10, 10, 250}; // server ip
EthernetClient client;
//************************************************************************************
//************************************************************************************
void setup() {
  char w, c;
   wdt_disable();  

   p=0;

  pinMode (2, INPUT); // status
  pinMode (8, OUTPUT); // reset out
  pinMode (9, INPUT); // reset in
  pinMode (3, INPUT); // count
  pinMode(10, OUTPUT);//CS for SD
  pinMode (7, OUTPUT); //Simulator o/p
  
  //----------------------------------------------------------------
  c = 0;
  while (c < 20)
  { 
    if (SD.begin(chipSelect))
    {
      c = 120;
   }
    else
    {
      delay(5);
      c++;
    }
  }
  //-------------------------------------------------
  Ethernet.begin(mac, ip);
  c = 0;
  while (c < 20)
  {
    c++;
    client.connect(server, port);
    if (client.connected()) 
    {
      c = 120;
    }
    else
    {
    client.stop();
    delay(5);
    }
  }
     //-------------------------------------------------
  w = 0;
  dataFile2 = SD.open("total.txt");
  if (dataFile2)
  {
    w = 1;
    getit();
  }
  else {
    dataFile2 = SD.open("total1.txt");
  }
  if (dataFile2)
  {
    if (w == 0) {
      getit();
    }
  }
 
  //-------------------------------------------------
  Timer1.initialize(2000000L); // set a timer 2 seconds
  Timer1.attachInterrupt( Repeats ); // attach the service routine here
  Status1();
}
//************************************************************************************
//************************************************************************************
void loop() {
  char f, a;
   attachInterrupt (digitalPinToInterrupt (3), Counting, RISING);  // attach interrupt handler
//----------------------------------------------------------------
  f = digitalRead(2);
  if (f != X)
  {
    Status1();
    X = f;
  }
//----------------------------------------------------------------
//----------------------------------------------------------------
  a = digitalRead(9);
  if (a != Z)
  {
    Z = a;
    if (digitalRead(9) == 1)
    {
      delay(5);
      if (digitalRead(9) == 1)
      {
        reset1();
      }
    }
  }
  //----------------------------------------------------------------
  if (R == 1)
  { // those are the functions which where in the Timer ISR R is a flag of time
    sendether();
    p++;
    if (p>=28){
    p=0;
    }
    writetotal();
    R = 0;
  }
}
//************************************************************************************
//************************************************************************************
void Counting()
{ 
  
  static unsigned long last_interrupt_time = 0;
 unsigned long interrupt_time = millis();
 // If interrupts come faster than 200ms, assume it's a bounce and ignore
 if (interrupt_time - last_interrupt_time > 150) 
 {
   if (digitalRead(2) == 1)
  {
    total++;
  }
 }
 last_interrupt_time = interrupt_time;
}
  

//************************************************************************************
//************************************************************************************
void reset1()
{
  
  b = 2;
  digitalWrite(8 , HIGH);
  delay(1000);
  digitalWrite(8 , LOW);
  delay(500);

  sendether();
  total = 0;
  writetotal();
 
}
//************************************************************************************
//************************************************************************************
void Status1()
{
  b = 1;
  if (digitalRead(2) == 1)
  {
    sendether();
  }
  else
  {
    sendether();
  }
}
//************************************************************************************
//************************************************************************************
void Repeats()
{
  R = 1;
 // k++;
}
//************************************************************************************
//************************************************************************************
void sendether()
{
  char c;
 c = 0;
  while (c < 5)
  {
    c++;
    client.connect(server, port);
    if (client.connected()) 
    {
      c = 10;
    }
    else
    {
    client.stop();
    
    }
  }
 
  // if coming from reset
  if (b == 2)
  {
    client.println(total);
    client.println(F("shift"));
    client.println(total);
    client.println(total);
    client.println(total);
    b = 0;
  }
  // if coming from repeat
  if (b == 0)
  {
    client.println(total);
    x++;
    if (x == 5)
    {
      if (digitalRead(2) == 1) {
        client.println(F("run"));
      }
      else if (digitalRead(2) == 0) {
        client.println(F("stop"));
      }
      x = 0;
    }
  }
  // if coming from status
  else if (b == 1)
  {
    if (digitalRead(2) == 1)
    { 
      client.println(F("run"));
      client.println(F("run"));
      b = 0;
    }
    else if (digitalRead(2) == 0)
    { 
      client.println(F("stop"));
      client.println(F("stop"));
      b = 0;
     }
  }
}
//************************************************************************************
//************************************************************************************
void writetotal()
{
  char dataString2[25];
  sprintf(dataString2, "%u", total);
  SD.remove("total.txt");
  dataFile2 = SD.open("total.txt", FILE_WRITE);
  if (dataFile2)
  {
    dataFile2.println(dataString2);
    dataFile2.close();
  }
  SD.remove("total1.txt");
  dataFile2 = SD.open("total1.txt", FILE_WRITE);
  
  if (dataFile2)
  {
    dataFile2.println(dataString2);
    dataFile2.close();
  }
}
//************************************************************************************
//************************************************************************************
void getit()
{
  char dataString1[25];
  char q = 0;
  while (dataFile2.available())
  {
    dataString1[q++] = dataFile2.read();
    dataString1[q] = '\0';
  }
   // close the file:
  dataFile2.close();
}
//************************************************************************************
//************************************************************************************
 
Status
Not open for further replies.
Back
Top