• Getting the CC3000 working with JavaScript

    Friday, August 15, 2014

    8/15/2014– Jia Huang

    When Tessel’s wifi is connected the wifi chip’s features can be accessed through Node’s net and http libraries.

    However, there are a few features of the CC3000 that do not fit within Node’s API, such as the ability to connect to a network, or exposing to a user that their network connection has been dropped.

    We recently introduced the ability to control Tessel’s onboard wifi connections from JavaScript. It’s done through another library called wifi-cc3000 that now comes bundled on Tessel, much like the tessel library. It allows for something like this:

    var wifi = require('wifi-cc3000');
    
    // connect to a network
    function connect(){
      wifi.connect({
        security: 'wpa2'
        , ssid: '#####' // network name
        , password: '#####'
        , timeout: 30 // in seconds
      });
    }
    
    // reconnect on dropped connection
    wifi.on('disconnect', function(err, data){
      console.log("disconnect emitted", err, data);
      connect(); // try to reconnect
    });
    

    A more indepth example is on Tessel’s getting started page and the full documentation is on our docs page.

    How it’s implemented

    The technical details behind the wifi-cc3000 library are similar to how we expose other C functions to JS, with the exception of having to also work with the CC3000 wifi chip.

    TI gives out a closed source firmware binary that gets loaded on to the CC3000, along with the C libraries needed to communicate with the CC3000. This provides us the socket interface and CC3000 specific functions like network connection.

    It communicates over SPI, where our Tessel MCU acts as the master and the CC3000 is the slave device. In addition to the default SPI pins (clock, data in, data out, and slave select), it also has an interrupt pin (IRQ).

    An interrupt is raised by pulling the IRQ pin low, which triggers an interrupt on our main MCU. This allows our microcontroller to give the CC3000 a set of commands (like go connect to X network), and then go do other processing until the CC3000 responds with a connected or disconnected event.

    A typical call on the CC3000 looks like this:

    1. We call a CC3000 driver function.
    2. The command gets translated into a SPI signal which is passed to the CC3000.
    3. The CC3000 does some processing.
    4. The CC3000 fires an interrupt.
    5. We handle the interrupt by calling a CC3000 processing function to the interrupt handler.

    Some calls from the CC3000 are unsolicited, such as when we drop a wifi network. In this case the CC3000 directly fires an interrupt without us initiating the call.

    The wifi-cc3000 library needs to handle both types of calls:

    1. Calls which are initiated from the user, such as .connect.
    2. Unsolicited events from the CC3000, such as a disconnected event.

    In order to make this easier, we wrote some wrapper libraries.

    Calls initiated by the user

    Calls initiated from JS are similar to any other JS -> C exposure (like our Pin API for example). It usually follows these three steps:

    1. Bind C functions to Tessel’s VM functions. We have two locations for this, one in the runtime repo, and another in the firmware repo. The bindings on runtime relate to anything needed for Node compatibility (such as the `net` lib) while the ones in firmware relate to how Tessel specifically operates (such as Pin toggling and LEDs). Since the wifi commands we’re looking at (connect, disconnect, setting timeouts) are specific to the CC3000 and not to Node, the bindings are in the firmware repo. A typical binding looks like this:
         static int l_wifi_is_busy(lua_State* L) {
           lua_pushnumber(L, hw_net_inuse() ||  
               tessel_wifi_is_connecting() ? 1 : 0);
          
           return 1;
         }
         
         
      This pushes a `1` into the global Lua context for this function’s return value if the wifi chip is in use, otherwise it pushes a `0`.
    2. Expose VM functions through a process binding to JS. All of our firmware’s hardware functions are bound to the `luaopen_hw` context. When Tessel first boots up we push in these functions into the VM.
    3. Wrap it in a JS library. We can link to a process binding in order to access the functions exposed through `hw`. This allows us to call C functions.
         self.isBusy = function(){
           return hw.wifi_is_busy() == 1 ? true : false;
         }
         

    Unsolicited calls

    Unsolicited calls are more complicated since we’re going from C -> JS.

    All JS is handled inside of the event loop, so in order to surface up a call from C we have to wrap our C function as if it were an event and then push it to the head of the event queue.

    The process is:

    1. Create a tm_event from C. In this case it’s the disconnect event.
         tm_event wifi_disconnect_event = TM_EVENT_INIT(wifi_disconnect_callback);
         
    2. On the C callback that the CC3000 triggers, also trigger the tm_event.
         void _cc3000_cb_wifi_disconnect ()
         {
             ...
             tm_event_trigger(&wifi_disconnect_event);
         }
         
    3. This pushes the event to the head of the event queue.
         void tm_event_trigger(tm_event* event) {
          tm_events_lock();
          if (event->pending == false) {
              event->pending = true;
              event->next = 0;
              if (event_queue_head) {
                  // Link it on to the end of the existing linked list
                  event_queue_tail->next = event;
                  event_queue_tail = event;
              } else {
                  event_queue_head = event_queue_tail = event;
              }
          }
          tm_events_unlock();
         }
         
    4. Push the event message on to the VM and clean up.
         void wifi_disconnect_callback(void) {
            lua_State* L = tm_lua_state;
            if (!L) return;
      
            tm_event_unref(&wifi_disconnect_event);
      
            lua_getglobal(L, "_colony_emit");
            lua_pushstring(L, "wifi_disconnect_complete");
            lua_pushnumber(L, 0);
      
            tm_checked_call(L, 2);
         }
         
    5. Listen for the hardware process in JS.
         process.once('wifi_disconnect_complete', function(err, data){
              self.emit('disconnect', err, data);
      
              callback && callback();
            });
         

    For those of you who are curious and want to see the implementation, here’s pull request #40, which has all the changes in order to get the wifi-cc3000 library functional.

    #Jia Huang #tessel #Technical Machine #cc3k #cc3000 #wifi #javascript #api

February 2017

November 2016

October 2016

September 2016

August 2016

July 2016

June 2016

April 2016

March 2016

February 2016

November 2015

September 2015

August 2015

July 2015

June 2015

May 2015

March 2015

February 2015

January 2015

December 2014

November 2014

October 2014

September 2014

August 2014

July 2014

June 2014

May 2014

April 2014

March 2014

February 2014

January 2014

December 2013

November 2013

October 2013

September 2013

August 2013

July 2013