I tried to use adafruit cc3000 library with SPI transaction support.
I replaced the SPI library by Paul Stoffregen
I tried to run the webclient example.
Connection works, but when it tries to access the webpage it hangs and then timeouts.
I am using an arduino leonardo. I get the following on the serial monitor.
Hello, CC3000!
Free RAM: 1650
Initializing...
Attempting to connect to UpTrend lo
Connected!
Request DHCP
/***************************************************
This is an example for the Adafruit CC3000 Wifi Breakout & Shield
Designed specifically to work with the Adafruit WiFi products:
----> https://www.adafruit.com/products/1469
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried & Kevin Townsend for Adafruit Industries.
BSD license, all text above must be included in any redistribution
****************************************************/
/*
This example does a test of the TCP client capability:
* Initialization
* Optional: SSID scan
* AP connection
* DHCP printout
* DNS lookup
* Optional: Ping
* Connect to website and print out webpage contents
* Disconnect
SmartConfig is still beta and kind of works but is not fully vetted!
It might not work on all networks!
*/
#include <Adafruit_CC3000.h>
#include <ccspi.h>
#include <SPI.h>
#include <string.h>
#include "utility/debug.h"
// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ 3 // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT 5
#define ADAFRUIT_CC3000_CS 10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
SPI_CLOCK_DIVIDER); // you can change this clock speed
#define WLAN_SSID "UpTrend lo" // cannot be longer than 32 characters!
#define WLAN_PASS "GautamaSiddharta"
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY WLAN_SEC_WPA2
#define IDLE_TIMEOUT_MS 3000 // Amount of time to wait (in milliseconds) with no data
// received before closing the connection. If you know the server
// you're accessing is quick to respond, you can reduce this value.
// What page to grab!
#define WEBSITE "www.adafruit.com"
#define WEBPAGE "/testwifi/index.html"
/**************************************************************************/
/*!
@brief Sets up the HW and the CC3000 module (called automatically
on startup)
*/
/**************************************************************************/
uint32_t ip;
void setup(void)
{
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
delay(10000);
Serial.println(F("Hello, CC3000!\n"));
Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);
/* Initialise the module */
Serial.println(F("\nInitializing..."));
if (!cc3000.begin())
{
Serial.println(F("Couldn't begin()! Check your wiring?"));
while(1);
}
// Optional SSID scan
// listSSIDResults();
again:
Serial.print(F("\nAttempting to connect to ")); Serial.println(WLAN_SSID);
if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
Serial.println(F("Failed!"));
while(1);
}
Serial.println(F("Connected!"));
/* Wait for DHCP to complete */
Serial.println(F("Request DHCP"));
while (!cc3000.checkDHCP())
{
delay(100); // ToDo: Insert a DHCP timeout!
}
/* Display the IP address DNS, Gateway, etc. */
while (! displayConnectionDetails()) {
delay(1000);
}
ip = 0;
// Try looking up the website's IP address
Serial.print(WEBSITE); Serial.print(F(" -> "));
while (ip == 0) {
if (! cc3000.getHostByName(WEBSITE, &ip)) {
Serial.println(F("Couldn't resolve!"));
}
delay(500);
}
cc3000.printIPdotsRev(ip);
// Optional: Do a ping test on the website
/*
Serial.print(F("\n\rPinging ")); cc3000.printIPdotsRev(ip); Serial.print("...");
replies = cc3000.ping(ip, 5);
Serial.print(replies); Serial.println(F(" replies"));
*/
/* Try connecting to the website.
Note: HTTP/1.1 protocol is used to keep the server from closing the connection before all data is read.
*/
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();
Serial.println(F("-------------------------------------"));
/* You need to make sure to clean up after yourself or the CC3000 can freak out */
/* the next time your try to connect ... */
Serial.println(F("\n\nDisconnecting"));
cc3000.disconnect();
goto again;
}
void loop(void)
{
Serial.write('A');
delay(1000);
/*
char *p;
for (p="hello web\n";*p;p++) Serial.write(*p);
delay(1000);
*/
}
/**************************************************************************/
/*!
@brief Begins an SSID scan and prints out all the visible networks
*/
/**************************************************************************/
void listSSIDResults(void)
{
uint32_t index;
uint8_t valid, rssi, sec;
char ssidname[33];
if (!cc3000.startSSIDscan(&index)) {
Serial.println(F("SSID scan failed!"));
return;
}
Serial.print(F("Networks found: ")); Serial.println(index);
Serial.println(F("================================================"));
while (index) {
index--;
valid = cc3000.getNextSSID(&rssi, &sec, ssidname);
Serial.print(F("SSID Name : ")); Serial.print(ssidname);
Serial.println();
Serial.print(F("RSSI : "));
Serial.println(rssi);
Serial.print(F("Security Mode: "));
Serial.println(sec);
Serial.println();
}
Serial.println(F("================================================"));
cc3000.stopSSIDscan();
}
/**************************************************************************/
/*!
@brief Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;
if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
{
Serial.println(F("Unable to retrieve the IP Address!\r\n"));
return false;
}
else
{
Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
Serial.println();
return true;
}
}
I enasbled debug mode. I found that the program hangs in: SPI.transfer(data[loc]); called
from void SpiWriteDataSynchronous(unsigned char *data, unsigned short size) in ccspi.cpp.
To investigate this I added more debug code to ccspi.cpp
I foud that there is an SPI.endTransaction followed by SpiWriteDataSynchronous without SPI.startTransaction.
This looks to me as a bug in the Adafruit CC3000 library. Without transaction the write does not block the
application, but the data sant do not go anywhere either.
The SPI.beginTransaction() and SPI.endTransaction() calls do more or less nothing except disabling some interrupts, which is not really necessary the case in the CC3000 lib as far as I can tell from a short overview at least not for all calls.
Debugging SPI calls with a serial connection at 9600 baud is not a good idea.
Have you checked that the connection to the adafruit website is possible from your WiFi network? To me it looks like the communication with the CC3000 module is possible but that the IP connection does not happen. DNS resolving seems to happen without a problem so basic connectivity is available. Please check with a notebook and a telnet client if you can send the necessary commands manually.
Sorry, but I forgot to mention, that the program worked with the standard Arduino SPI library.
I know that trace output in any interrupt routine basically is a bad idea. I used it because it was
simple and it did not prevent the program without transaction to work.
I am using an arduino leonardo. For this board the baudrate does not mean anything. Serial
goes directly to USB.
I was running the program several times and the results were always the same: Worked
without transaction and did not with transaction. It did not matter wether debug was
on or off and the last part of the trace looked always the same.
The CC3000_DEASSERT_CS contains a SpiConfigPop. I did not check what this does because according
to my understanding CC3000_ASSERT_CS should be called before the next SPI activity. This contains
an SpiConfigPush which correctly prepares SPI.
I have more than the CC3000 chip on the SPI bus. For this reason I need transactions.
Of course, you are right that one should not use Serial in the interrupt routine. I just used it because
it just meant enable debug in debug.h.
The output from the interrupt routine looks good to me.
On the other hand, the output from the non-interrupt part shows a missing ASSERT_CS.
Without transactions the data are lost. With transactions the SpiConfigPop() has reset the SPI
and the transmit hangs. At least this is my interpretation.
I have more than the CC3000 chip on the SPI bus. For this reason I need transactions.
You don't need them but you have to keep track of the communication on the SPI bus yourself. Generally it's a bad idea to do lengthy operations in an ISR and it's quite risky to disable interrupts for longer than a few microseconds.
In your code I cannot find anything you do in an interrupt service routine. So why do you need transactions? They are not necessary because you have more than one chip on the bus and if you really want the transactions for convenience with the SPI configuration (given you have chips on the bus using different modes/speeds), at least disable the interrupt mode on the library. But if you do, you must use the same interface for all SPI calls and every access to the bus must be inside a transaction.
You are right that this program does not really require transactions. But this program is not my application
at all but only a preliminary test.
It is in agreement with the rules of this forum to use the simplest program which reproduces the
error.
Also the DEASSERT_CS, followed by a write to SPI without any previous ASSERT_CS is an indicator for
a serious bug in the Adafruit library which I would like to correct.
If you take a look at the code you might see that this isn't the case. The problem is, that Adafruit's library don't expect anything to happen during the operation of their device. They set the CS line low, activate the interrupt and wait until the interrupt happen. If you use the SPI library with transactions they activate that interrupt in the library which might result in having all interrupts blocked. This interrupt will never happen and you get unstable behavior.
Having this transaction based SPI library activated with the CC3000 library is quite risky in my opinion. If you really need it, it might be worth the time to find all issues that the CC3000 library might have with it in your special case, otherwise I strongly suggest to use the standard SPI library.
According to the trace, CS gets deasserted in the interrupt routine. I have 2 other chips on the SPI bus.
I already looked at the adafruit library, but I have to admit that I understand it only partially.
Also, I did not find any detailed specifications of the CC3000 chip.
Do you have any suggestions where to look in the library or do you know about an alternative library?
As I already wrote, you don't need transactions just because you're using other chips on the SPI bus. What are the other chips you're using? Does any of them use another SPI mode or needs another speed?
Do you have any suggestions where to look in the library or do you know about an alternative library?
My suggestion is to use the standard SPI library if you don't have good reasons not to do so. Until now you haven't written about such a reason.
One of the other chips I use is a Spirit1 from TI.
This has the following consequence: It uses a packet protocol, i.e. after I asserted the CS I have to transmit the whole packet.
Also, when the CC3000 does an interrupt there is no guarantee that the CC3000 CS is still asserted.
Transactions would solve all these problems nicely.
I would like also to mention that one day the SPI library will contain transaction support and then my application would break.
I would like to note that the transaction support in ccspi.cpp is enabled by a definition in SPI.h (in the Arduino library)
Transactions would solve all these problems nicely.
Theoretically yes, but not the way they are used in the CC3000 library. As far as I understood the library the communication with the chip is often done asynchronously, so the assertion is made in the standard code and released inside an ISR. The linked library only takes care that a complete transaction inside or outside an ISR for the same device does not interfere but there are no precautions that different devices behave the same. That could be implemented but that would mean that would mean that you loose backward compatibility and that every call must be checked if it really happened. I don't know if every device is capable of acting that way. For example, let's say you're inside a transaction with your Spirit1 and an interrupt of the CC3000 occurs. Is a reaction 0.2ms later (when the transaction with the Spirit1 finished) enough or may the CC3000 state have already changed again by then? I don't have a datasheet of the CC3000 to check such stuff. Maybe you have to change your WiFi board to one that doesn't need interrupts to work if you have also connected devices like the Spirit1.
BTW: Do you have a link to that Spirit1 from TI? I only know a Spirit1 from STMicroelectronics so I'm eager to see what TI offers.
I would like also to mention that one day the SPI library will contain transaction support and then my application would break.
That depends on how they would be implemented.
I would like to note that the transaction support in ccspi.cpp is enabled by a definition in SPI.h (in the Arduino library
No, it's triggered by a definition in your replacement library, the original library from Arduino doesn't have that definition (if you wrote about the same definition that I found).
BTW: Do you have a link to that Spirit1 from TI? I only know a Spirit1 from STMicroelectronics so I'm eager to see what TI offers.
Sorry, I am using Spirit1 from STMicroelectronics and mistyped it.
That depends on how they would be implemented.
I assumed that the implementation does define SPI_HAS_TRANSACTIONS.
No, it's triggered by a definition in your replacement library, the original library from ARDUINO doesn't have that definition (if you wrote about the same definition that I found).
An inaccuracy on my side again: I wanted to point out, that using transactions is triggered by something from the Arduino Library and not from the Adafruit CC3000 implementation.
I don't have a datasheet of the CC3000 to check SUCH stuff.
Neither did I find a datasheet on the TI homepage. I think TI assumes everybody uses their library.
Do you know about any replacment for the Adafruit library?
Do you know about any replacment for the Adafruit library?
No, I'm not aware of one. Why do you need the CC3000? Why does p.e. a WiFly module not fulfill your requirements? There you don't need interrupts and the used chip is much better documented.
If you want to stay with the CC3000, I would try to separate the IP communication with the CC3000 from the communication with the other chips. So while the CC3000 is holding the chip select, you don't do any other communication (and the other way around of course). This way you can completely eliminate the need for an SPI implementation with transactions and use the standard SPI library.