AdafruitSDEP

All communication between the Arduino user code (your sketch) and the lower level WiFi stack from Broadcom happens over SDEP commands.

SDEP stands for 'Simple Data Exchange Protocol', an in house protocol we use in a number of our products.

This is similar to the way you would talk to an external I2C or SPI sensor via a set of pre-defined registers defined in the sensor datasheet.  You send specifically formatted data to known registers (or addresses), and sometimes you get data back in a known format (depending on the command).  

SDEP is the simple data exchange protocol that we use for the command and response messages between the user code and the lower level Feather Lib that contains the WICED WiFi stack.

Normally you won't need to deal with SDEP commands yourself since these are hidden in the AdafruitFeather, AdafruitHTTP, etc., helper classes, but a specialized helper classed name AdafruitSDEP is available to send SDEP commands yourself and get the response data back if the need should ever arise to talk directly to the WICED stack yourself.

AdafruitSDEP API

// Send a simple SDEP command (1 parameter value or less)
bool sdep   (uint16_t  cmd_id       ,
             uint16_t  param_len    , void const* p_param,
             uint16_t* p_result_len , void* p_result);

// Send a complex SDEP command (multiple parameter values)
bool sdep_n (uint16_t  cmd_id       ,
             uint8_t   para_count   , sdep_cmd_para_t const* para_arr,
             uint16_t* p_result_len , void* p_result);

// SDEP error handling functions
err_t       errno       (void);
char const* errstr      (void);
char const* cmdstr      (uint16_t cmd_id);
void        err_actions (bool print, bool halt);

Constructor

AdafruitFeather inherits from AdafruitSDEP, meaning that you don't need to instantiate AdafruitSDEP directly yourself.  Simply call the functions described below from your AdafruitFeather class instance, which is normally available as 'Feather', so 'Feather.sdep(...)', 'Feather.sdep_n(...)', 'Feather.errno()', etc.

Functions

The following functions and parameters are present in AdafruitSDEP:

sdep

This function sends an SDEP command with up to one parameter (or no parameters if NULL is provided in the 'p_param' field or 'param_len' is set to 0).

Function Prototype:

bool sdep(uint16_t  cmd_id       ,
          uint16_t  param_len    , void const* p_param,
          uint16_t* p_result_len , void* p_result)

Parameters:

  • cmd_id: The 16-bit SDEP command ID
  • param_len: The length of the p_param field containing the parameter data. Set this to '0' if no parameter is provided.
  • p_param: A pointer to the parameter value to pass into the SDEP command handler. Set this to NULL if no parameter is provided.
  • p_result_len: A pointer to the 16-bit value where the response length will be written by the SDEP command handler
  • p_result: A pointer to where the response data should be written by the SDEP command handler

Return Value:

'true' if the function executed properly, otherwise 'false' if an error occured (check .errno or .errstr for details).

Examples

The simplest possible example of using this function can be seen below.

No parameter data is sent to the SDEP command, we don't check any response data (there is none from SDEP_CMD_FACTORYRESET anyway), and we don't even check if 'sdep' returned false to indicate that there was an error executing the command:

void AdafruitFeather::factoryReset(void)
{
  sdep(SDEP_CMD_FACTORYRESET, 0, NULL, NULL, NULL);
}

A more complex example of sending a simple SDEP command with this function can be seen below, where we flush the contents of the TCP buffer.

'_tcp_handle' is an internal 32-bit value (so 4 bytes), and we pass a pointer to the value to the SDEP command handler (notice the '&' symbol before the name saying that we should pass the address in memory for '_tcp_handle').

No response data is read back, so the last two parameters are set to NULL.

void AdafruitTCP::flush()
{
  if ( _tcp_handle == 0 ) return;

  // flush write
  sdep(SDEP_CMD_TCP_FLUSH, 4, &_tcp_handle, NULL, NULL);
}

This last example checks if any TCP data is available in the buffer, and the command will set the 'result' variable to a non-zero value if any data is available.

Since we know the size of the results variable, we don't need to read back the length of the response data, and we can insert NULL for 'p_result_len':

int AdafruitTCP::available()
{
  if ( _tcp_handle == 0 ) return 0;

  uint32_t result = 0;
  sdep(SDEP_CMD_TCP_AVAILABLE, 4, &_tcp_handle, NULL, &result);

  return result;
}

sdep_n

This function sends an SDEP command with an array of parameter values, using a dedicated parameter array typedef called sdep_cmd_para_t.

Function Prototype:

bool sdep_n(uint16_t  cmd_id       ,
            uint8_t   para_count   , sdep_cmd_para_t const* para_arr,
            uint16_t* p_result_len , void* p_result)

Parameters:

  • cmd_id: The 16-bit SDEP command ID
  • para_count: The number of parameters in para_arr
  • para_arr: An array of sdep_cmd_para_t values, consisting of a 16-bit length value and a pointer to the actual parameter data
  • p_results_len: A pointer to the 16-bit value where the response length will be written by the SDEP command handler
  • p_result: A pointer to where the response data should be written by the SDEP command handler

Each entry in para_arr has the following structure:

typedef struct {
  uint16_t    len;
  void const* p_value;
} sdep_cmd_para_t;

Return Value:

'true' if the function executed properly, otherwise 'false' if an error occured (check .errno or .errstr for details).

Examples

The example below uses the SDEP_CMD_WIFI_PROFILE_ADD command to store the connection details to non-volatile memory. 

This is a blocking command that only returns when the procedure succeeeds or fails. As such, we will ignore any return data from the command other than a possible SDEP error code. As such, p_results_len and p_result are both set to NULL here:

bool AdafruitFeather::addProfile(char* ssid)
{
  sdep_cmd_para_t para_arr[] =
  {
      { .len = strlen(ssid), .p_value = ssid },
  };
  uint8_t para_count = sizeof(para_arr)/sizeof(sdep_cmd_para_t);

  return sdep_n(SDEP_CMD_WIFI_PROFILE_ADD, para_count, para_arr,
                NULL, NULL);
}

A more complex example is shown below where we read the SDEP response, and a pointer to certain parameter values is also used (noticed the '&' character below some parameter values). The use of pointers is necessary when passing large or complex parameters to the SDEP command handler.

In this particular example we use SDEP_CMD_TCP_READ but we also want to read the response data.

int AdafruitTCP::read(uint8_t* buf, size_t size)
{
  if ( _tcp_handle == 0 ) return 0;

  uint16_t size16 = (uint16_t) size;
  sdep_cmd_para_t para_arr[] =
  {
      { .len = 4, .p_value = &_tcp_handle },
      { .len = 2, .p_value = &size16      },
      { .len = 4, .p_value = &_timeout    },
  };
  uint8_t para_count = sizeof(para_arr)/sizeof(sdep_cmd_para_t);

  uint16_t readlen = size16;
  VERIFY_RETURN( sdep_n(SDEP_CMD_TCP_READ, para_count, para_arr, &readlen, buf), 0);

  _bytesRead += readlen;
  return readlen;
}

We pass in three parameters to SDEP_CMD_TCP_READ:

  • The TCP handle (_tcp_handle)
  • The number of bytes we want to read (size16)
  • The timeout before returning an error (_timeout)

The command will then return the data that was read back, populating the buf and size16 fields. The 'size16' field will contain the numbers of bytes written to 'buf' so that we can compare the numbers of bytes requested with the number of bytes actually read out.

The VERIFY macro in the example above is simply a helper to check the response from sdep_n, and it will return '0' if an error was encountered.

Error Handling Functions

The following functions are defined to work with any SDEP errors generated by the system:

err_t errno (void)

If sdep or sdep_n returned false as a return value, if means the SDEP command failed.  To determine the error message, you can read the results from .errno() immediately after the .sdep or .sdep_n command, which will give you a 16-bit (uint16_t) error code.

char const* errstr(void)

To provide further details on the value returned in errno you can also call .errstr() which will return a char array containing the internam enum name for the last error code.

Unfortunately, for copyright reasons we're not able to release the Broadcom WICED WiFi stack source, but seeing the string associated with your errno provides an excellent indicator of what went wrong executing the SDEP command.

char const* cmdstr (uint16_t cmd_id)

Returns the name of the command associated with the specified SDEP command ID.

Parameters:

  • cmd_id: The 16-bit SDEP command ID to lookup (based on .errno, for example)

Returns: A string representing the name of the SDEP command associated with 'cmd_id'.

void err_actions (bool print, bool halt)

This function allows you to enable various optional 'actions' that should be automatically taken when an SDEP error occurs.  By default all actions are disabled.

Parameters

  • print: If set to true, any SDEP error will be displayed in the Serial Monitor via Serial.print, including both the .errstr and .errno values. This can help keep your code clean and make it easier to switch between debug and release mode.
  • halt: If set to true, the code will stop executing and wait in a 'while(1)' loop as soon as an SDEP error is encountered.

Returns: Nothing

Error Handling Example

The following example shows an example of how you can use the .errno and .errstr functions to handle the last SDEP error generated by the system:

// Attempt to connect to the AP
if ( Feather.connect("SSID", "PASSWORD", ENC_TYPE_AUTO ) )
{
  int8_t rssi = Feather.RSSI();
  uint32_t ipAddress = Feather.localIP();
  // Do something now that you are connected to the AP!
}
else
{
  // Display the error message
  err_t err = Feather.errno();
  Serial.println("Connection Error:");
  switch (err)
  {
    case ERROR_WWD_ACCESS_POINT_NOT_FOUND:
      // SSID wasn't found when scanning for APs
      Serial.println("Invalid SSID");
      break;
    case ERROR_WWD_INVALID_KEY:
      // Invalid SSID passkey
      Serial.println("Invalid Password");
      break;
    default:
      // The most likely cause of errors at this point is that
      // you are just out of the device/AP operating range
      Serial.print(Feather.errno());
      Serial.print(":");
      Serial.println(Feather.errstr());
      break;
  }    
}
Last updated on Mar 04, 2016 Published on Mar 23, 2016