New CC3K Driver Development

Hi all,

I’ve been working on a completely new driver for the CC3000 from the ground up. A test binary for the Core is available at https://github.com/wtfuzz/cc3k_demo.

There is no SmartConfig or profiles yet so the network has to be added at each boot from the serial console at 230400 baud. It provides a small shell on the USB serial port which will look like the following:

spark> help

Spark Core CC3K Driver Demo
Matt Thompson (Twitter @inet_ntoa)

Commands:
wifi up <none|wpa|wpa2> <Network SSID> <password> - Connect to wireless LAN
wifi down - Disconnect from wireless LAN
ifconfig - Show interface IP configuration
ping <hostname|ip address> - Send ICMP ping messages
info - Get info
dfu - Enter DFU Mode (reflash)
spark> wifi up wpa2 MySSID MyPassword
spark> ifconfig
IP Address: 10.78.100.176
Netmask: 255.255.255.0
Default Gateway: 10.78.100.1
DHCP Server: 10.78.100.1
DNS Server: 10.78.100.1

I’ve only tested with WPA2, but open and WPA should work. I don’t have WEP support added yet. To connect to the network:

wifi up wpa2 YourSSID YourPassword

After a few seconds, the blue D7 LED should light up after it has connected to the network. You should now see the IP address information with the ifconfig shell command:

spark> ifconfig
IP Address: 10.78.100.176
Netmask: 255.255.255.0
Default Gateway: 10.78.100.1
DHCP Server: 10.78.100.1
DNS Server: 10.78.100.1

Information about the driver state and statistics can be shown with the ‘info’ command:

spark> info
-----=====[ CC3K Driver Info ]====-----
Uptime (ms): 1557914
INT Enabled: 1
IRQ Preempt: 0
Current Command: 4104
Select Pending: 1
CS Asserted: 0
INT Pin: 1
SPI Busy: 0
SPI Unhandled: 2
Buffers available: 6
WLAN Status: 3
State: 4
Last State: 4
Int State: 2
Unhandled State: 0
Commands TX: 41730
Events RX: 62013
DATA RX: 20204
DATA Bytes RX: 30420538
Unsolicited Events RX: 80
DMA Interrupts: 165676
HW Interrupts: 103743
Unhandled Interrupts: 0

There is a UDP server socket running in the firmware, so people can use something like netcat to send data to the core with the intention of seeing if there is improved stability in other environments. My test environment is on a very busy wifi network consisting of 3 access points (Airport Extreme, and two Airport express extenders)

The driver source code is available at https://github.com/wtfuzz/cc3k

The test binary which can be installed on a Core with DFU is available at https://github.com/wtfuzz/cc3k_demo

6 Likes

what’s better than the standard spark one?

do you have static ip working? or ping?

wifi N and WEP seemed to be the problems with the spark early on.

isn’t profiles automatic as netapp_dhcp() writes to eeprom?

It's all designed to be non-blocking, and so far has shown drastic stability improvements. That being said, the API will be vastly different than the standard TI supplied driver (the current driver in the Spark firmware) so there isn't support for Spark cloud or Wiring Sockets.

Static IP is not yet implemented but is fairly trivial to add. Currently only DHCP is used to get a lease. The firmware of the CC3000 chip handles ping responses without driver intervention.

Profiles are automatic dependent on the connection policy IOCTL. The profiles have to be added with SET_PROFILE IOCTL SPI commands. I don't have profile support implemented in the driver at all at the moment which is why manual SSID has to be specified each time the core boots. I'm focused on driver stability right now and don't want to mess with EEPROM, as it could also interfere with the stock Spark firmware if a user reverts from the test firmware back to the Spark firmware .. playing it safe :slight_smile: netapp_dhcp() is a function of the stock TI driver which doesn't exist in cc3k.

wow you’re doing a bare metal cc3000 driver, not using the TI SDK?

Yep, it's completely different from the TI driver, based mostly by piecing together commands from the wiki documentation http://processors.wiki.ti.com/index.php/CC3000_HCI_CMND_messages

The model of the driver is very different essentially implementing an integrated event loop, abstracting all of the "BSD-style" socket calls into a socket manager. The descriptors and state of the sockets are managed within the driver internally allowing for non-blocking behavior by calling the select command on all used socket descriptors and responding to those if necessary on each event loop iteration. The driver will also return from the loop immediately after sending a single command like socket creation, connect, bind etc without having to busy wait for a response from the chip like the standard TI driver does. The logic is based around state machines managed by the chip and SPI interrupt handlers.

2 Likes

I’d love to see this benchmarked against the TI driver. Way to go!

1 Like

@wtfuzz, i will be happy to assist you in any way i can.

Let me know if you need more resources to get this going. I can bring this up to the Spark team and i’m sure they will be happy to hear about your progress. :wink:

The initial post is updated. I’ve added a link to a repo with a test binary that can be flashed with DFU.

It would be awesome to get any willing participants to flash that firmware, make sure they can connect to their wifi network using the serial console shell and doing UDP data transfer tests for now to see what kind of stability people see. If the driver hangs up, the console won’t hang since the driver is non-blocking, so if anything does hang up if you could run ‘info’ at the shell and paste the state that would greatly assist in debugging.

I will flush out some more features of the demo firmware, and get the whole source tree up in github as well so people can modify and try custom network code once the data transfer portions are fully flushed out in the driver.

1 Like

Hey @wtfuzz, i will be happy to flash that binary file. How can i do the UDP data transfer test?

Sorry but i have never messed with TCP/UDP in my entire life. :smiley:

I’m using Coolterm on mac but the device seems to be echoing back stuff i type. :smiley:

spark> ..[8G.[Kw.[9G.[8G.[Kwi.[10G.[8G.[Kwif.[11G.[8G.[Kwifi.[12G.[8G.[Kwifi .[13G.[8G.[Kwifi u.[14G.[8G.[Kwifi up.[15G.[8G.[Kwifi up .[16G.[8G.[Kwifi up w.[17G.[8G.[Kwifi up wp.[18G.[8G.[Kwifi up wpa.[19G.[8G.[Kwifi up wpa .[20G.[8G.[Kwifi up wpa T.[21G.[8G.[Kwifi up wpa To.[22G.[8G.[Kwifi up wpa Tom.[23G.[8G.[Kwifi up wpa Toma.[24G.[8G.[Kwifi up wpa Tomat.[25G.[8G.[Kwifi up wpa Tomato.[26G.[8G.[Kwifi up wpa Tomato2.[27G.[8G.[Kwifi up wpa Tomato24.[28G
Invalid arguments

Once you get the IP address of the core using ‘ifconfig’, I pipe /dev/zero or /dev/random to netcat to flood the socket with data.

In this case the firmware is listening on UDP port 5000, so we can throw traffic at it with:

cat /dev/zero | nc -u <Your core IP address> 5000

You won’t see anything from netcat while it’s streaming data, but you can see the stats being updated on the core firmware with the ‘info’ command at the console:

spark> info
-----=====[ CC3K Driver Info ]====-----
Uptime (ms): 2302813
INT Enabled: 1
IRQ Preempt: 3
Current Command: 0
Select Pending: 0
CS Asserted: 0
INT Pin: 1
SPI Busy: 0
SPI Unhandled: 2
Buffers available: 6
WLAN Status: 3
State: 5
Last State: 3
Int State: 4
Unhandled State: 0
Commands TX: 100782
Events RX: 150420
DATA RX: 49524
DATA Bytes RX: 74540882
Unsolicited Events RX: 117
DMA Interrupts: 401502
HW Interrupts: 251199
Unhandled Interrupts: 0

In this case, I’ve received about 50k packets and 75MB

2 Likes

Hmmm I'm using VT102 for a readline-style shell interface.

Can you try using screen from Terminal:

screen /dev/tty.usbserialXXXX 230400

To exit the screen session, press Ctrl-A then k and y to confirm

Or, I suppose if Coolterm has terminal emulation options like vt102 or ansi that should work as well (The [8G.[Kw etc are control characters for cursor movement that aren't being interpreted by Coolterm)

cool! i got screen working. Now, will i be able to connect using open network?

I will test this tonight when i get home as the school network doesn’t allow the core to connect.

Going to let it run for a couple of days and paste the log for your usage. :wink:

Excellent, the more long term testing the better :smile:

For now, the core is acting as a simple UDP server, so you’ll have to use the netcat command above to stream data from a computer to the core. In an upcoming version I’ll have the ability to create sockets from the shell to receive data.

Once you get it on a wifi network and get the IP address, just plug the IP into the netcat command on a mac or linux machine and let it stream away :smile:

1 Like

You rock @wtfuzz!

Let me know if you need more resources. Just @ping me or drop me a PM anytime. :wink:

Do you mean no encryption? I haven't tested that, but it should work using a dummy key

wifi up none MySSID nokey

The reason for the 'nokey' is the command parser FSM (written in Ragel) expects something to be in the key field.

1 Like

I tested but it is not connecting to my phone Hotspot. Let me use my home router tonight to see how it goes. :wink:

@wtfuzz - this is a great effort. I’d be very interested to see how it handles UDP since there are issues there with the current driver.

As you know, calls to the CC3000 driver are present throughout the code, making automated testing and refactoring difficult. I’ll be building an abstraction layer to better separate the wiring code from the hardware, making it possible to plug in your driver implementation. We can then compare yours and TI’s implementation directly.

1 Like

The Wiring code should fit nicely with the cc3k model, but a HAL layer between Wiring and cc3k may be somewhat difficult as it relies on select() for example in the available() method. I see the Wiring API being the abstraction layer between application and hardware, so maybe a different Wiring implementation is all that's needed?

One problem will be figuring how to make available() work in Wiring. We'll either need to create per-socket buffers or buffers in the wiring code to take the data from the driver callback and buffer it. I can explain this further, but basically right now there is a pair of MTU+overhead sized buffers in the driver, so the data must be consumed when data is ready to be serviced otherwise the next loop iteration can clobber the buffer.

Here's an example of using a callback based socket with the driver. This is what the test firmware binary is doing at the moment (receiving UDP data, not doing anything with it in the callback atm). The cc3k_socket_* calls don't actually do anything with the hardware when they're called, they just setup internal socket state. Once they're added to the socket manager it will start communicating with the hardware in the background to set the sockets up in the state machine (https://github.com/wtfuzz/cc3k/blob/master/src/cc3k_socket.c#L180)

// cc3k driver context
static cc3k_t driver;

// Socket context structure
static cc3k_socket_t udp;

void udp_data_received(cc3k_t *driver, cc3k_socket_t *socket, uint8_t *data, uint16_t data_length, cc3k_sockaddr_t *from)
{
  // UDP Data received
}

void setup_sockets()
{
  // Socket Address for our UDP socket
  cc3k_sockaddr_t udp_sa;

  // Setup the socket address for bind
  udp_sa.family = AF_INET;
  udp_sa.port = 0x8813; // Port 5000, big endian
  udp_sa.addr = 0; // Bind to all

  // Initialize a datagram socket
  cc3k_socket_init(&udp, SOCK_DGRAM);

  // Bind to the socket
  cc3k_socket_bind(&udp, &udp_sa);

  // Set the receive callback
  udp.receive_callback = udp_data_received;
  
  // Add the socket to the socket manager in the cc3k driver
  cc3k_socket_add(&driver, &udp);
}
1 Like

It should also be possible to write a blocking compatibility layer on the driver to the BSDish calls to make it work with core-communication-lib, probably requiring a few driver modifications to keep the driver from automatically reading socket data if a socket becomes readable.

For example select() could be implemented to walk the sockets registered with the socket manager and look for any sockets marked readable. A read() call could then follow and busy wait on the socket states, while the layer calls the driver loops to continue hardware communication.

I still need to work out some details on data flow (transmission) and buffering, so if anyone has any thoughts on how the model could work that would be great. I toyed with the idea of a global ring more like a traditional NIC, but another option could be some per-socket buffers with the expense of more RAM. The ring would allow sockets to share a single transmit buffer, but larger writes could quickly fill it causing subsequent socket writes to fail. Another option might be more of a scatter-gather approach, passing pointers to buffers directly into a ring of descriptors which would come with the risk of stack variables being passed into the driver and later popped and smashed.

While we could replace the whole of wiring, that’s quite a bit of code and most of it doesn’t touch the hardware. The point of the HAL would be to only rewrite those parts that has to change with new drivers.

Having the callback push the data to a socket-specific buffer is a possible approach. But what happens if the client code doesn’t read the data in time? Is there a way to signal to the driver that the data cannot be delivered?

For UDP this isn’t an issue since it’s a best effort protocol with no guarantees so the data can be discarded, but for TCP, shouldn’t the driver/callback hook into the flow control so that packets can be flagged as non-delivered?