diff --git a/examples/rest-example/Makefile b/examples/rest-example/Makefile index 53414be6d..6ce29e628 100644 --- a/examples/rest-example/Makefile +++ b/examples/rest-example/Makefile @@ -7,7 +7,7 @@ CONTIKI=../.. WITH_UIP6=1 UIP_CONF_IPV6=1 -#WITH_COAP = 1 +WITH_COAP = 1 CFLAGS += -DPROJECT_CONF_H=1 diff --git a/examples/rest-example/README b/examples/rest-example/README index 3bce83cfc..c75d10f7f 100644 --- a/examples/rest-example/README +++ b/examples/rest-example/README @@ -1,25 +1,51 @@ -COOJA RUN: +Open a terminal and go to "examples/rest-example/" directory. -Open a terminal and go to "rest" directory. -In rest/Makefile, define WITH_COAP = 1 if you want to use COAP, rather than HTTP. -Issue following command to load and compile Rest api with COOJA. ->make rest-example-no-rpl.csc TARGET=cooja +MAIN EXAMPLE: +rest-server-example.c : A RESTful server example showing how to use the REST layer to develop server-side applications (possible to run it over either COAP or HTTP) +To use COAP as the underlying application protocol, one should define WITH_COAP = 1 in rest-example/Makefile. Otherwise, HTTP is used. +Look at the source code to see which resources are available. (check the RESOURCE macros in the code). -In another terminal, issue the following command + +To run REST examples in COOJA under Linux +-------------------------------------------- + +Accessing the server from outside: +Start COOJA and load the simulation "rest-server-example.csc" by the following command. +> make TARGET=cooja rest-server-example.csc +After loading the cooja file, in another terminal connect to the COOJA simulation using tunslip6: >make connect-router-cooja - -There are 2 nodes running the Rest code in COOJA (node 2 and 6). Their IP addresses are aaaa::0212:7402:0002:0202 and aaaa::0212:7406:0006:0606 respectively. - -TEST: -Please check the rest/rest-example.c source code to see which resources are available. (check the RESOURCE macros in the code). - +Now you need to use a COAP or HTTP client to interact with the COOJA nodes running REST code. +In this setting, two servers are available: +IP addresses are aaaa::0212:7402:0002:0202 and aaaa::0212:7403:0003:0303. COAP uses 61616, whereas HTTP uses 8080 port. HTTP Examples You can use curl as an http client to interact with the COOJA motes running REST code. -curl -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/helloworld #get helloworld plain text -curl -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/led?color=green -d mode=off -i #turn off the green led -curl -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/.well-known/core -i -curl -X POST -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/helloworld #method not allowed +>curl -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/helloworld #get helloworld plain text +>curl -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/led?color=green -d mode=off -i #turn off the green led +>curl -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/.well-known/core -i +>curl -X POST -H "User-Agent: curl" aaaa::0212:7402:0002:0202:8080/helloworld #method not allowed -COAP Examples: -You need a COAP client to be able to to interact with the COOJA motes running REST code. URIs are same with the HTTP examples. + +Accessing the server inside the sensor network: +(Note: Provided only for COAP implementation) +Start COOJA and load the simulation "coap-client-server-example.csc" by the following command. +> make TARGET=cooja coap-client-server-example.csc +coap-client-server-example.csc : Runs rest-server-example as the server (over COAP) (IP:aaaa::0212:7401:0001:0101) in one node and coap-client-example as the client (IP: aaaa::0212:7402:0002:0202) in another node. +Client periodically accesses resources of server and prints the payload. + + +To run REST server on real nodes under Linux +-------------------------------------------- + +1. Program the nodes with the rest-server-example +> make TARGET=sky rest-server-example.upload + +2. Disconnect the nodes and program one node with the RPL border router +> (cd ../ipv6/rpl-border-router && make TARGET=sky border-router.upload) + +3. Connect to the border router using tunslip6: +> make connect-router + +4. Reconnect the motes, reboot them and note their IP addresses. + +5. Remaining parts are the same with the COOJA example. (i.e. if it is a COAP Server, it is available at :61616) diff --git a/examples/rest-example/coap-client-example.c b/examples/rest-example/coap-client-example.c new file mode 100644 index 000000000..012934418 --- /dev/null +++ b/examples/rest-example/coap-client-example.c @@ -0,0 +1,122 @@ +#include +#include +#include "contiki.h" +#include "contiki-net.h" +#include "rest.h" +#include "buffer.h" + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +#define SERVER_NODE(ipaddr) uip_ip6addr(ipaddr, 0xfe80, 0, 0, 0, 0x0212, 0x7401, 0x0001, 0x0101) +#define LOCAL_PORT 61617 +#define REMOTE_PORT 61616 + +char temp[100]; +int xact_id; +static uip_ipaddr_t server_ipaddr; +static struct uip_udp_conn *client_conn; +static struct etimer et; +#define MAX_PAYLOAD_LEN 100 + +#define NUMBER_OF_URLS 3 +char* service_urls[NUMBER_OF_URLS] = {"light", ".well-known/core", "helloworld"}; + +static void +response_handler(coap_packet_t* response) +{ + uint16_t payload_len = 0; + uint8_t* payload = NULL; + payload_len = coap_get_payload(response, &payload); + + PRINTF("Response transaction id: %u", response->tid); + if (payload) { + memcpy(temp, payload, payload_len); + temp[payload_len] = 0; + PRINTF(" payload: %s\n", temp); + } +} + +static void +send_data(void) +{ + char buf[MAX_PAYLOAD_LEN]; + + if (init_buffer(COAP_DATA_BUFF_SIZE)) { + int data_size = 0; + int service_id = random_rand() % NUMBER_OF_URLS; + coap_packet_t* request = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); + init_packet(request); + + coap_set_method(request, COAP_GET); + request->tid = xact_id++; + request->type = MESSAGE_TYPE_CON; + coap_set_header_uri(request, service_urls[service_id]); + + data_size = serialize_packet(request, buf); + + PRINTF("Client sending request to:["); + PRINT6ADDR(&client_conn->ripaddr); + PRINTF("]:%u/%s\n", (uint16_t)REMOTE_PORT, service_urls[service_id]); + uip_udp_packet_send(client_conn, buf, data_size); + + delete_buffer(); + } +} + +static void +handle_incoming_data() +{ + PRINTF("Incoming packet size: %u \n", (u16_t)uip_datalen()); + if (init_buffer(COAP_DATA_BUFF_SIZE)) { + if (uip_newdata()) { + coap_packet_t* response = (coap_packet_t*)allocate_buffer(sizeof(coap_packet_t)); + parse_message(response, uip_appdata, uip_datalen()); + if (response) { + response_handler(response); + } + } + delete_buffer(); + } +} + +PROCESS(coap_client_example, "COAP Client Example"); +AUTOSTART_PROCESSES(&coap_client_example); + +PROCESS_THREAD(coap_client_example, ev, data) +{ + PROCESS_BEGIN(); + + SERVER_NODE(&server_ipaddr); + + /* new connection with server */ + client_conn = udp_new(&server_ipaddr, UIP_HTONS(REMOTE_PORT), NULL); + udp_bind(client_conn, UIP_HTONS(LOCAL_PORT)); + + PRINTF("Created a connection with the server "); + PRINT6ADDR(&client_conn->ripaddr); + PRINTF(" local/remote port %u/%u\n", + UIP_HTONS(client_conn->lport), UIP_HTONS(client_conn->rport)); + + etimer_set(&et, 5 * CLOCK_SECOND); + while(1) { + PROCESS_YIELD(); + if (etimer_expired(&et)) { + send_data(); + etimer_reset(&et); + } else if (ev == tcpip_event) { + handle_incoming_data(); + } + } + + PROCESS_END(); +} diff --git a/examples/rest-example/coap-client-server-example.csc b/examples/rest-example/coap-client-server-example.csc new file mode 100644 index 000000000..a0cc86d4d --- /dev/null +++ b/examples/rest-example/coap-client-server-example.csc @@ -0,0 +1,147 @@ + + + [CONTIKI_DIR]/tools/cooja/apps/mrm + [CONTIKI_DIR]/tools/cooja/apps/mspsim + [CONTIKI_DIR]/tools/cooja/apps/avrora + [CONTIKI_DIR]/tools/cooja/apps/serial_socket + + coap-client-server-example + -2147483648 + 123456 + 1000000 + + se.sics.cooja.radiomediums.UDGM + 50.0 + 100.0 + 1.0 + 1.0 + + + 40000 + + + se.sics.cooja.mspmote.SkyMoteType + sky1 + CoapServer + [CONTIKI_DIR]/examples/rest-example/rest-server-example.c + make rest-server-example.sky TARGET=sky + [CONTIKI_DIR]/examples/rest-example/rest-server-example.sky + se.sics.cooja.interfaces.Position + se.sics.cooja.interfaces.RimeAddress + se.sics.cooja.interfaces.IPAddress + se.sics.cooja.interfaces.Mote2MoteRelations + se.sics.cooja.interfaces.MoteAttributes + se.sics.cooja.mspmote.interfaces.MspClock + se.sics.cooja.mspmote.interfaces.MspMoteID + se.sics.cooja.mspmote.interfaces.SkyButton + se.sics.cooja.mspmote.interfaces.SkyFlash + se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem + se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.MspSerial + se.sics.cooja.mspmote.interfaces.SkyLED + se.sics.cooja.mspmote.interfaces.MspDebugOutput + se.sics.cooja.mspmote.interfaces.SkyTemperature + + + se.sics.cooja.mspmote.SkyMoteType + sky2 + CoapClient + [CONTIKI_DIR]/examples/rest-example/coap-client-example.c + make coap-client-example.sky TARGET=sky + [CONTIKI_DIR]/examples/rest-example/coap-client-example.sky + se.sics.cooja.interfaces.Position + se.sics.cooja.interfaces.RimeAddress + se.sics.cooja.interfaces.IPAddress + se.sics.cooja.interfaces.Mote2MoteRelations + se.sics.cooja.interfaces.MoteAttributes + se.sics.cooja.mspmote.interfaces.MspClock + se.sics.cooja.mspmote.interfaces.MspMoteID + se.sics.cooja.mspmote.interfaces.SkyButton + se.sics.cooja.mspmote.interfaces.SkyFlash + se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem + se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.MspSerial + se.sics.cooja.mspmote.interfaces.SkyLED + se.sics.cooja.mspmote.interfaces.MspDebugOutput + se.sics.cooja.mspmote.interfaces.SkyTemperature + + + + + se.sics.cooja.interfaces.Position + 54.5338749671737 + 36.41934631024719 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 1 + + sky1 + + + + + se.sics.cooja.interfaces.Position + 49.41583327244326 + 52.00647916206431 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 2 + + sky2 + + + + se.sics.cooja.plugins.SimControl + 318 + 2 + 172 + 0 + 0 + + + se.sics.cooja.plugins.Visualizer + + se.sics.cooja.plugins.skins.IDVisualizerSkin + se.sics.cooja.plugins.skins.AddressVisualizerSkin + 3.686023978928717 0.0 0.0 3.686023978928717 -20.14794638096936 -127.69712925102564 + + 271 + 0 + 211 + 666 + 41 + + + se.sics.cooja.plugins.LogListener + + + + 1263 + 1 + 199 + 0 + 339 + + + se.sics.cooja.plugins.TimeLine + + 0 + 1 + + + + 125 + 500.0 + + 1263 + 3 + 150 + 0 + 538 + + + diff --git a/examples/rest-example/rest-server-example.c b/examples/rest-example/rest-server-example.c new file mode 100644 index 000000000..49e477e41 --- /dev/null +++ b/examples/rest-example/rest-server-example.c @@ -0,0 +1,161 @@ +#include +#include +#include "contiki.h" +#include "contiki-net.h" +#include "rest.h" + +#include "dev/light-sensor.h" +#include "dev/battery-sensor.h" +#include "dev/sht11-sensor.h" +#include "dev/leds.h" + +#define DEBUG 1 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#define PRINT6ADDR(addr) PRINTF(" %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x ", ((u8_t *)addr)[0], ((u8_t *)addr)[1], ((u8_t *)addr)[2], ((u8_t *)addr)[3], ((u8_t *)addr)[4], ((u8_t *)addr)[5], ((u8_t *)addr)[6], ((u8_t *)addr)[7], ((u8_t *)addr)[8], ((u8_t *)addr)[9], ((u8_t *)addr)[10], ((u8_t *)addr)[11], ((u8_t *)addr)[12], ((u8_t *)addr)[13], ((u8_t *)addr)[14], ((u8_t *)addr)[15]) +#define PRINTLLADDR(lladdr) PRINTF(" %02x:%02x:%02x:%02x:%02x:%02x ",(lladdr)->addr[0], (lladdr)->addr[1], (lladdr)->addr[2], (lladdr)->addr[3],(lladdr)->addr[4], (lladdr)->addr[5]) +#else +#define PRINTF(...) +#define PRINT6ADDR(addr) +#define PRINTLLADDR(addr) +#endif + +char temp[100]; + +/* Resources are defined by RESOURCE macro, signature: resource name, the http methods it handles and its url*/ +RESOURCE(helloworld, METHOD_GET, "helloworld"); + +/* For each resource defined, there corresponds an handler method which should be defined too. + * Name of the handler method should be [resource name]_handler + * */ +void +helloworld_handler(REQUEST* request, RESPONSE* response) +{ + sprintf(temp,"Hello World!\n"); + + rest_set_header_content_type(response, TEXT_PLAIN); + rest_set_response_payload(response, temp, strlen(temp)); +} + +/*A simple actuator example, depending on the color query parameter and post variable mode, corresponding led is activated or deactivated*/ +RESOURCE(led, METHOD_POST | METHOD_PUT , "led"); + +void +led_handler(REQUEST* request, RESPONSE* response) +{ + char color[10]; + char mode[10]; + uint8_t led = 0; + int success = 1; + + if (rest_get_query_variable(request, "color", color, 10)) { + PRINTF("color %s\n", color); + + if (!strcmp(color,"red")) { + led = LEDS_RED; + } else if(!strcmp(color,"green")) { + led = LEDS_GREEN; + } else if ( !strcmp(color,"blue") ) { + led = LEDS_BLUE; + } else { + success = 0; + } + } else { + success = 0; + } + + if (success && rest_get_post_variable(request, "mode", mode, 10)) { + PRINTF("mode %s\n", mode); + + if (!strcmp(mode, "on")) { + leds_on(led); + } else if (!strcmp(mode, "off")) { + leds_off(led); + } else { + success = 0; + } + } else { + success = 0; + } + + if (!success) { + rest_set_response_status(response, BAD_REQUEST_400); + } +} + +uint16_t light_photosynthetic; +uint16_t light_solar; + +void +read_light_sensor(uint16_t* light_1, uint16_t* light_2) +{ + *light_1 = light_sensor.value(LIGHT_SENSOR_PHOTOSYNTHETIC); + *light_2 = light_sensor.value(LIGHT_SENSOR_TOTAL_SOLAR); +} + +/*A simple getter example. Returns the reading from light sensor with a simple etag*/ +RESOURCE(light, METHOD_GET, "light"); +void +light_handler(REQUEST* request, RESPONSE* response) +{ +#ifdef CONTIKI_TARGET_SKY + read_light_sensor(&light_photosynthetic, &light_solar); + sprintf(temp,"%u;%u", light_photosynthetic, light_solar); +#else /*CONTIKI_TARGET_SKY*/ + sprintf(temp,"%d.%d", 0, 0); +#endif /*CONTIKI_TARGET_SKY*/ + + char etag[4] = "ABCD"; + rest_set_header_content_type(response, TEXT_PLAIN); + rest_set_header_etag(response, etag, sizeof(etag)); + rest_set_response_payload(response, temp, strlen(temp)); +} + +/*A simple actuator example. Toggles the red led*/ +RESOURCE(toggle, METHOD_GET | METHOD_PUT | METHOD_POST, "toggle"); +void +toggle_handler(REQUEST* request, RESPONSE* response) +{ + leds_toggle(LEDS_RED); +} + +RESOURCE(discover, METHOD_GET, ".well-known/core"); +void +discover_handler(REQUEST* request, RESPONSE* response) +{ + char temp[100]; + int index = 0; + index += sprintf(temp + index, "%s\n", ";n=\"HelloWorld\""); + index += sprintf(temp + index, "%s\n", ";n=\"LedControl\""); + index += sprintf(temp + index, "%s\n", ";n=\"Light\""); + + rest_set_response_payload(response, temp, strlen(temp)); + rest_set_header_content_type(response, APPLICATION_LINK_FORMAT); +} + +PROCESS(rest_server_example, "Rest Server Example"); +AUTOSTART_PROCESSES(&rest_server_example); + +PROCESS_THREAD(rest_server_example, ev, data) +{ + PROCESS_BEGIN(); + +#ifdef WITH_COAP + PRINTF("COAP Server\n"); +#else + PRINTF("HTTP Server\n"); +#endif + + SENSORS_ACTIVATE(light_sensor); + + rest_init(); + + rest_activate_resource(&resource_helloworld); + rest_activate_resource(&resource_led); + rest_activate_resource(&resource_light); + rest_activate_resource(&resource_toggle); + rest_activate_resource(&resource_discover); + + PROCESS_END(); +} diff --git a/examples/rest-example/rest-server-example.csc b/examples/rest-example/rest-server-example.csc new file mode 100644 index 000000000..54a5260a9 --- /dev/null +++ b/examples/rest-example/rest-server-example.csc @@ -0,0 +1,185 @@ + + + [CONTIKI_DIR]/tools/cooja/apps/mrm + [CONTIKI_DIR]/tools/cooja/apps/mspsim + [CONTIKI_DIR]/tools/cooja/apps/avrora + [CONTIKI_DIR]/tools/cooja/apps/serial_socket + + REST with RPL router + -2147483648 + 123456 + 1000000 + + se.sics.cooja.radiomediums.UDGM + 50.0 + 50.0 + 1.0 + 1.0 + + + 40000 + + + se.sics.cooja.mspmote.SkyMoteType + rplroot + Sky RPL Root + [CONTIKI_DIR]/examples/ipv6/rpl-border-router/border-router.c + make border-router.sky TARGET=sky + [CONTIKI_DIR]/examples/ipv6/rpl-border-router/border-router.sky + se.sics.cooja.interfaces.Position + se.sics.cooja.interfaces.RimeAddress + se.sics.cooja.interfaces.IPAddress + se.sics.cooja.interfaces.Mote2MoteRelations + se.sics.cooja.interfaces.MoteAttributes + se.sics.cooja.mspmote.interfaces.MspClock + se.sics.cooja.mspmote.interfaces.MspMoteID + se.sics.cooja.mspmote.interfaces.SkyButton + se.sics.cooja.mspmote.interfaces.SkyFlash + se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem + se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.MspSerial + se.sics.cooja.mspmote.interfaces.SkyLED + se.sics.cooja.mspmote.interfaces.MspDebugOutput + se.sics.cooja.mspmote.interfaces.SkyTemperature + + + se.sics.cooja.mspmote.SkyMoteType + skyweb + Rest + [CONTIKI_DIR]/examples/rest-example/rest-server-example.c + make rest-server-example.sky TARGET=sky + [CONTIKI_DIR]/examples/rest-example/rest-server-example.sky + se.sics.cooja.interfaces.Position + se.sics.cooja.interfaces.RimeAddress + se.sics.cooja.interfaces.IPAddress + se.sics.cooja.interfaces.Mote2MoteRelations + se.sics.cooja.interfaces.MoteAttributes + se.sics.cooja.mspmote.interfaces.MspClock + se.sics.cooja.mspmote.interfaces.MspMoteID + se.sics.cooja.mspmote.interfaces.SkyButton + se.sics.cooja.mspmote.interfaces.SkyFlash + se.sics.cooja.mspmote.interfaces.SkyCoffeeFilesystem + se.sics.cooja.mspmote.interfaces.SkyByteRadio + se.sics.cooja.mspmote.interfaces.MspSerial + se.sics.cooja.mspmote.interfaces.SkyLED + se.sics.cooja.mspmote.interfaces.MspDebugOutput + se.sics.cooja.mspmote.interfaces.SkyTemperature + + + + + se.sics.cooja.interfaces.Position + 33.260163187353555 + 30.643217359962595 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 1 + + rplroot + + + + + se.sics.cooja.interfaces.Position + 62.239287566073514 + 34.43810269527116 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 2 + + skyweb + + + + + se.sics.cooja.interfaces.Position + 47.68359039801751 + 47.26544238238854 + 0.0 + + + se.sics.cooja.mspmote.interfaces.MspMoteID + 3 + + skyweb + + + + se.sics.cooja.plugins.SimControl + 259 + 1 + 179 + 0 + 0 + + + se.sics.cooja.plugins.Visualizer + + se.sics.cooja.plugins.skins.IDVisualizerSkin + se.sics.cooja.plugins.skins.UDGMVisualizerSkin + se.sics.cooja.plugins.skins.MoteTypeVisualizerSkin + se.sics.cooja.plugins.skins.AttributeVisualizerSkin + 10.505309204322225 0.0 0.0 10.505309204322225 -249.89475921566975 -141.01191150973983 + + 819 + 5 + 563 + 35 + 306 + + + se.sics.cooja.plugins.LogListener + + + + 762 + 0 + 326 + 36 + 296 + + + se.sics.cooja.plugins.RadioLogger + + 150 + + + 815 + 4 + 385 + 255 + 8 + + + SerialSocketServer + 0 + 422 + 3 + 74 + 1234 + 93 + + + se.sics.cooja.plugins.TimeLine + + 0 + 1 + + + + + 125 + 25.49079397896416 + + 1624 + 2 + 252 + 166 + 699 + + +