...
Register your application with the OpenWSN stack
This is the only step that requires you to edit anything outside of those two main application files. Goto your openwsnopendefs.h file and add a UNIQUE identifier to the "//component identifiers" enum structure (see below).
Code Block | ||
---|---|---|
| ||
//component identifiers enum { COMPONENT_NULL = 0x00, //cross-layers COMPONENT_IDMANAGER = 0x01, COMPONENT_OPENQUEUE = 0x02, . . . //App applications COMPONENT_OHLONEC6T = 0x1e0x1a, COMPONENT_HELICEXAMPLE = 0x1b, //--YOUR APP IDENTIFIER--// = 0x1f, . . COMPONENT_IMU UECHO = 0x200x23, COMPONENT_RLEDSUINJECT = 0x210x24, COMPONENT_RREG RRT = 0x22, COMPONENT_RWELLKNOWN = 0x230x25, COMPONENT_RTSECURITY = 0x240x26, COMPONENT_REX = 0x25, //--YOUR APP IDENTIFIER--// . . . }; |
We also need to go to openwsn.c
, and add rex_init()
into our main initialization function (rex_init()
is described below):
Code Block | ||
---|---|---|
| ||
openwsn.c void openwsn_init() { // drivers openserial}; |
We also need to go to openapps.c, and add cexample_init() into our main initialization function (cexample_init()is described below):
Code Block | ||
---|---|---|
| ||
openwsn.c . . . #include "cleds.h" #include "cexample.h" // add header file #include "cstorm.h" . . . void openapps_init() { // CoAP c6t_init(); cinfo_init(); cexample_init(); // add initial function cleds__init(); cstorm_init(); cwellknown_init(); rrt_init(); . . . rex_init(); //initialize your app here rleds_init(); rreg_init(); . . . } |
Basic application Structure
...
}
|
Basic application Structure
The core of our app lives in cexample.c
, while rexcexample.h
only holds prototyping information. Taking a closer look at rexcexample.c
, we notice some basic include statements. No matter what your app does, you will likely need most of these:
Code Block | ||
---|---|---|
| ||
#include "openwsnopendefs.h" #include "cexample.h" #include //basic OpenWSN information"opencoap.h" #include "opentimers.h" #include "rexopenqueue.h" #include "opencoappacketfunctions.h" //CoAP features #include "opentimersopenserial.h" //Timer support #include "openqueueopenrandom.h" //OpenWSN packet formats #include "packetfunctionsscheduler.h" #include "openserialADC_Channel.h" //Support for serial interface in case you want to debug #include "scheduler.h" //The ability to push your application tasks to the OS sheduler #include "ADC_Channel.h" //ADC functionality |
Basic application Structure
The application defines some basic properties: How long is our timer period? How large will our packets be? What is the short name of our application when others access it via CoAP?
No Format |
---|
/// inter-packet period (in ms) #define REXPERIOD 5000 //5 seconds #define PAYLOADLEN 62#include "IEEE802154E.h" |
Basic application Structure
The application defines some basic properties: How long is our timer period? How large will our packets be? What is the short name of our application when others access it via CoAP?
No Format |
---|
/// inter-packet period (in ms)
#define CEXAMPLEPERIOD 10000
#define PAYLOADLEN 40
const uint8_t cexample_path0[] = "ex";
|
We now need to create an cexample_init()
function, which will be called when OpenWSN first starts. This function will initialize the variables that describe the "cexample" app to CoAP. Furthermore, we point to two callback functions: One is called when receives a packet from CoAP, and one is called when cexample is done sending a packet to CoAP. We will not use these functions in this example, but they may be useful to you in the future. Consider them like higher-level interrupts that alert your application of network traffic over IPv6. We then register the app with CoAP, and start a timer (describe below).
Code Block | ||
---|---|---|
| ||
void cexample_init() { // prepare the resource descriptor for the /ex path cexample_vars.desc.path0len //bytes const= uint8_t rex_path0[] = "rex"; //application name on the internet |
We now need to create an rex_init()
function, which will be called when OpenWSN first starts. This function will initialize the variables that describe the "rex" app to CoAP. Furthermore, we point to two callback functions: One is called when receives a packet from CoAP, and one is called when rex is done sending a packet to CoAP. We will not use these functions in this example, but they may be useful to you in the future. Consider them like higher-level interrupts that alert your application of network traffic over IPv6. We then register the app with CoAP, and start a timer (describe below).
Code Block | ||
---|---|---|
| ||
void rex_init() {sizeof(cexample_path0)-1; cexample_vars.desc.path0val = (uint8_t*)(&cexample_path0); cexample_vars.desc.path1len = 0; cexample_vars.desc.path1val = //NULL; prepare the resource descriptor for the /rex path cexample_vars.desc.componentID rex = COMPONENT_CEXAMPLE; cexample_vars.desc.path0lendiscoverable = sizeof(rex_path0)-1TRUE; rexcexample_vars.desc.path0val callbackRx = (uint8_t*)(&rex_path0)&cexample_receive; rexcexample_vars.desc.path1lencallbackSendDone = &cexample_sendDone; = 0; rex_vars.desc.path1valopencoap_register(&cexample_vars.desc); cexample_vars.timerId = NULL;opentimers_start(CEXAMPLEPERIOD, rex_vars.desc.componentID = COMPONENT_REX; rex_vars.desc.callbackRx = &rex_receive; rex_vars.desc.callbackSendDone = &rex_sendDone; //Register and start timer now opencoap_register(&rex_vars.desc); rex_vars.timerId = opentimers_start(REXPERIODTIMER_PERIODIC,TIME_MS, TIMER_PERIODIC, rex_timer_cb); } |
...
cexample_timer_cb);
} |
Starting a periodic timer
In the above lines, our app starts an openTimer, which is a higher-level timer module you can use to execute single or periodic tasks within OpenWSN. The code above tells openTimers to call the function rex
cexample_timer_cb(), once every
...
CEXAMPLEPERIOD milliseconds. We set the timer type to TIMER_PERIODIC, to tell the timer module to execute
...
cexample_timer_cb() periodically, rather than just one time.
Executing a function once the timer expires
Once every 5 seconds we will want to run the ADC, collect a readings, and send it out. When the timer fires it will call rexcexample_timer_cb():
Code Block | ||
---|---|---|
| ||
//timer fired, but we don't want to execute task in ISR mode //instead, push task to scheduler with COAP priority, and let scheduler take care of it void rexcexample_timer_cb(){ scheduler_push_task(rexcexample_task_cb,TASKPRIO_COAP); } |
...
Every micro-controller has a different means by which to sample its ADC. In the rex example, we are using the ADC on an MSP430. The code to take an ADC reading will be different for each micro controller. Please see the rex example in the OpenWSN repository for how to do it on the MSP430, or edit the code below to suit your purposes:
...
language | cpp |
---|
...
Code Block | ||
---|---|---|
| ||
void cexample_task_cb() { OpenQueueEntry_t* pkt; owerror_t outcome; uint8_t i; uint16_t x_int = 0; uint16_t sum = 0; uint16_t avg = 0; // don't run if not synch if (ieee154e_isSynch() == FALSE) return; // don't run on dagroot if (idmanager_getIsDAGroot()) { opentimers_stop(cexample_vars.timerId); return; numOptions;} //Take ADC reding here and store it in the avg value uint16_t avg = SampleADC(); //This will be custom for your platform // create a CoAP RD packet pkt = openqueue_getFreePacketBuffer(COMPONENT_CEXAMPLE); if (pkt==NULL) { openserial_printError(COMPONENT_REX,ERR_NO_FREE_PACKET_BUFFER, COMPONENT_CEXAMPLE, ERR_NO_FREE_PACKET_BUFFER, (errorparameter_t)0, 0, (errorparameter_t)0 ); openqueue_freePacketBuffer(pkt); return; } // take ownership over that packet pkt->creator = COMPONENT_REXCEXAMPLE; pkt->owner = COMPONENT_REXCEXAMPLE; // CoAP payload packetfunctions_reserveHeaderSize(pkt,PAYLOADLEN); for (i=0;i<PAYLOADLEN;i++) { pkt->payload[i] = i; } pkt->payload[0i] = (avg>>8)&0xff; pkt->payload[1] = (avg>>0)&0xff; numOptions = 0i; //} location-path option avg = packetfunctions_reserveHeaderSize(pkt,sizeof(rex_path0)-1)openrandom_get16b(); memcpy(&pkt->payload[0],&rex_path0,sizeof(rex_path0)-1); = packetfunctions_reserveHeaderSize(pkt,1)(avg>>8)&0xff; pkt->payload[01] = (COAP_OPTION_LOCATIONPATH-COAP_OPTION_CONTENTTYPE) << 4 |avg>>0)&0xff; packetfunctions_reserveHeaderSize(pkt,1); pkt->payload[0] sizeof(rex_path0)-1= COAP_PAYLOAD_MARKER; numOptions++; // content-type option packetfunctions_reserveHeaderSize(pkt,2); pkt->payload[0] = =(COAP_OPTION_NUM_CONTENTFORMAT - COAP_OPTION_NUM_CONTENTTYPEURIPATH) << 4 | 1; pkt->payload[1] = COAP_MEDTYPE_APPOCTETSTREAM; | numOptions++1; pkt->payload[1] // metadata pkt->l4_destination_port = WKPCOAP_UDPMEDTYPE_COAPAPPOCTETSTREAM; pkt->l3_destinationORsource.type = ADDR_128B// location-path option packetfunctions_reserveHeaderSize(pkt,sizeof(cexample_path0)-1); memcpy(&pkt->l3_destinationORsource.addr_128b>payload[0],&ipAddr_motesEecs,16,cexample_path0,sizeof(cexample_path0)-1); // send outcome = opencoap_send(pkt,packetfunctions_reserveHeaderSize(pkt,1); pkt->payload[0] = ((COAP_OPTION_NUM_URIPATH) << 4) | (sizeof(cexample_path0)-1); COAP_TYPE_NON, // metadata pkt->l4_destination_port = WKP_UDP_COAP; pkt->l3_destinationAdd.type = ADDR_128B; COAP_CODE_REQ_PUT,memcpy(&pkt->l3_destinationAdd.addr_128b[0],&ipAddr_motesEecs,16); // send outcome = opencoap_send( numOptionspkt, COAP_TYPE_NON, COAP_CODE_REQ_PUT, 1, &rexcexample_vars.desc ); // avoid overflowing the queue if fails if (outcome==E_FAIL) { openqueue_freePacketBuffer(pkt); } return; } |
The above function allocates an OpenWSN packet, sets its ownership properties to the REX CEXAMPLE app. It also samples the ADC and stores the ADC result in a 16bit variable. This variable is then passed into an OpenWSN packet, along with some CoAP settings. Furthermore, the application selects a destination address
"pkt->l3_destinationORsource.addr_128b0" which denotes the IPv6 address our packet is intended for. In this case the address is "ipAddr_motesEecs," which is the IPv6 address of our server (http://motes.eecs.berkeley.edu).(Note: this server is temporally down) If you are running your own CoAP server, replace this address with the address of your server. Feel free to use our server however, as it is already running and you can simply store all your sensor readings there.
Setting up OpenVisualizer to forward IPv6 CoAP packets
...