diff --git a/microcoap/Makefile b/microcoap/Makefile new file mode 100644 index 0000000000..75b7527f88 --- /dev/null +++ b/microcoap/Makefile @@ -0,0 +1,32 @@ +# name of your application +APPLICATION = microcoap-example + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# if you try to compile this for anything but the boards specified, it will break. +# This application has not been verified to work with any other boards-- proceed with caution. +BOARD_WHITELIST := native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../../RIOT + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +CFLAGS += -DRIOT -DMICROCOAP_DEBUG + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +USEPKG=microcoap + +USEMODULE += config +USEMODULE += uart0 + +USEMODULE += nativenet + +USEMODULE += sixlowpan +USEMODULE += udp + +include $(RIOTBASE)/Makefile.include diff --git a/microcoap/README.md b/microcoap/README.md new file mode 100644 index 0000000000..ceab8b9ba5 --- /dev/null +++ b/microcoap/README.md @@ -0,0 +1,146 @@ +Microcoap example +============ + +This is a small microcoap example application. It provides a server which only +answers GET requests to the resource /foo/bar. + +## Setup + +You can use [marz](https://github.com/sgso/marz) to tunnel CoAP messages into the RIOT native thread. This is a bit tricky, so maybe this walkthrough will help: + +0. Build this application. +1. Install the copper plugin in your Firefox browser. +2. Run `sudo apt-get install bridge-utils` +3. In your RIOT directury, run + + ./cpu/native/tapsetup.sh create 2 + +This will set up two tap devices connected by a bridge. our RIOT application and +marz will each listen at one of these devices, and communicate over the bridge. + +3. Open two terminal windows. + +**In window #1**, start the microcoap-example application: + + cd applications/microcoap + sudo ./bin/native/microcoap-example.elf tap1 -i 1 + +*Make sure to bind it to ``tap1``, since marz will be bound to ``tap0`!* +``-i 1`` forces your RIOT instance to match its id to the one specified in marz.config. You should **only** specify this for the **one** RIOT that marz tunnels to. This is sufficient for this example; if you need help running more than one RIOT with marz, please contact the author of this example. + +You should see output similar to this. + + RIOT native uart0 initialized. + RIOT native interrupts/signals initialized. + LED_GREEN_OFF + LED_RED_ON + RIOT native board initialized. + RIOT native hardware initialization complete. + + kernel_init(): This is RIOT! (Version: 400e-microcoap) + kernel_init(): jumping into first task... + UART0 thread started. + uart0_init() [OK] + Initializing transport layer protocol: udp + Starting example microcoap server... + initializing 6LoWPAN... + initializing receive socket... + Ready to receive requests. + + Welcome to RIOT + + > + +**In window #2**, first install Python development headers by running + + sudo apt-get install python-dev + +Afterwards you can install and run marz: + + pip install --user Twisted && + pip install --user bidict && + git clone https://github.com/sgso/marz && + cd marz && + ./setup.sh + + ./marz.py + +You should see output similar to this. + + WARNING: No route found for IPv6 destination :: (no default route?) + Listening on UDP ports: [5683, 2222] + Listening on tap interface tap0 with MAC address 9a:80:a3:fc:93:18 + +## Testing + +The "Copper" firefox plugin is a convenient way to test CoAP endpoints. In the absence of a GUI you can also use Python to send test requests. + +### Using python(3) + +First, make sure Python 3 is installed, clone `aiocoap` into a directory of your choice and then change into it: + + git clone git@github.com:chrysn/aiocoap.git && + cd aiocoap + +Open the `clientGET.py` file and change the line that reads + + request.set_request_uri() + +to + + request.set_request_uri('coap://[::1]/foo/bar') + +Then run `clientGET.py`, which should print the following: + + $ ./clientGET.py + Result: 2.05 Content + b'1337' + + +### Using the Firefox Copper plugin + +Open Firefox and enter + + coap://[::1]:5683/foo/bar + +Into the browser window. Then, click the big gren ``GET`` button. This should +trigger a GET request to our microcoap-example application. Shortly after you've +clicked GET, **window #2** should read + + make new connection + [UDP6 5683] Received 14 data bytes from ('::1', 54685): Relaying through 54685 to RiotEndpoint(hwaddr=1, ipv6='fe80::ff:fe00:1', port=5683) + [TAP] Received 12 data bytes on port 54685: Relaying through 5683 to IP6Endpoint(ipv6='::1', port=54685) + +**window #1** should supply you with detailed information about the received +request and the reply our microcoap-example is sending: + + > Received packet: 40 01 0B EC B3 66 6F 6F 03 62 61 72 C1 02 + content: + Header: + ver 0x01 + t 0x01 + tkl 0x00 + code 0x01 + id 0x0BEC + Options: + 0x0B [ 66 6F 6F ] + 0x0B [ 62 61 72 ] + 0x17 [ 02 ] + Payload: + Sending packet: 60 45 0B EC C2 00 00 FF 31 33 33 37 + content: + Header: + ver 0x01 + t 0x01 + tkl 0x00 + code 0x45 + id 0x0BEC + Options: + 0x0C [ 00 00 ] + Payload: 31 33 33 37 + +And finally, the big grey ``Payload`` box in your Firefox window should read: + + 1337 + +If this all works, you're good to go! :) diff --git a/microcoap/endpoints.c b/microcoap/endpoints.c new file mode 100644 index 0000000000..ddf95700a6 --- /dev/null +++ b/microcoap/endpoints.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief microcoap example server endpoints + * + * @author Lotte Steenbrink + * + * @} + */ + +#include +#include +#include +#include "coap.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define MAX_RESPONSE_LEN 1500 +static uint8_t response[MAX_RESPONSE_LEN] = ""; + +static const coap_endpoint_path_t path = {2, {"foo", "bar"}}; + +void create_response_payload(const uint8_t *buffer) +{ + char *response = "1337"; + memcpy((void*)buffer, response, strlen(response)); +} + +/* The handler which handles the path /foo/bar */ +static int handle_get_response(coap_rw_buffer_t *scratch, const coap_packet_t *inpkt, coap_packet_t *outpkt, uint8_t id_hi, uint8_t id_lo) +{ + DEBUG("[endpoints] %s()\n", __func__); + create_response_payload(response); + /* NOTE: COAP_RSPCODE_CONTENT only works in a packet answering a GET. */ + return coap_make_response(scratch, outpkt, response, strlen((char*)response), + id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN); +} + +const coap_endpoint_t endpoints[] = +{ + {COAP_METHOD_GET, handle_get_response, &path, "ct=0"}, + {(coap_method_t)0, NULL, NULL, NULL} /* marks the end of the endpoints array */ +}; diff --git a/microcoap/main.c b/microcoap/main.c new file mode 100644 index 0000000000..782dc11ca6 --- /dev/null +++ b/microcoap/main.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief microcoap example server + * + * @author Lotte Steenbrink + * + * @} + */ + +#include + +#include "udp.h" +#include "net_help.h" +#include "net_if.h" +#include "periph/cpuid.h" +#include "board_uart0.h" +#include "thread.h" +#include "posix_io.h" +#include +#include "hashes.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +#define PORT 5683 +#define BUFSZ 128 + +#define RCV_MSG_Q_SIZE (64) + +static void *_microcoap_server_thread(void *arg); + +msg_t msg_q[RCV_MSG_Q_SIZE]; +char _rcv_stack_buf[KERNEL_CONF_STACKSIZE_MAIN]; + +static ipv6_addr_t prefix; +int sock_rcv, if_id; +sockaddr6_t sa_rcv; +uint8_t buf[BUFSZ]; +uint8_t scratch_raw[BUFSZ]; +coap_rw_buffer_t scratch_buf = {scratch_raw, sizeof(scratch_raw)}; + +static void _init_tlayer(void); +static uint16_t get_hw_addr(void); + +int main(void) +{ + + DEBUG("Starting example microcoap server...\n"); + + _init_tlayer(); + thread_create(_rcv_stack_buf, KERNEL_CONF_STACKSIZE_MAIN, PRIORITY_MAIN, CREATE_STACKTEST, _microcoap_server_thread, NULL ,"_microcoap_server_thread"); + + DEBUG("Ready to receive requests.\n"); + + return 0; +} + +static uint16_t get_hw_addr(void) +{ + return sysconfig.id; +} + +/* init transport layer & routing stuff*/ +static void _init_tlayer(void) +{ + msg_init_queue(msg_q, RCV_MSG_Q_SIZE); + + net_if_set_hardware_address(0, get_hw_addr()); + DEBUG("set hawddr to: %d\n", get_hw_addr()); + + printf("initializing 6LoWPAN...\n"); + + ipv6_addr_init(&prefix, 0xABCD, 0xEF12, 0, 0, 0, 0, 0, 0); + if_id = 0; /* having more than one interface isn't supported anyway */ + + sixlowpan_lowpan_init_interface(if_id); +} + +static void *_microcoap_server_thread(void *arg) +{ + (void)arg; /* make the compiler shut up about unused variables */ + + printf("initializing receive socket...\n"); + + sa_rcv = (sockaddr6_t) { .sin6_family = AF_INET6, + .sin6_port = HTONS(PORT) }; + + sock_rcv = socket_base_socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); + + if (-1 == socket_base_bind(sock_rcv, &sa_rcv, sizeof(sa_rcv))) { + printf("Error: bind to receive socket failed!\n"); + socket_base_close(sock_rcv); + } + + printf("Ready to receive requests.\n"); + + while(1) + { + int n, rc; + socklen_t len = sizeof(sa_rcv); + coap_packet_t pkt; + + n = socket_base_recvfrom(sock_rcv, buf, sizeof(buf), 0, &sa_rcv, &len); + printf("Received packet: "); + coap_dump(buf, n, true); + printf("\n"); + + if (0 != (rc = coap_parse(&pkt, buf, n))) + printf("Bad packet rc=%d\n", rc); + else + { + size_t rsplen = sizeof(buf); + coap_packet_t rsppkt; + printf("content:\n"); + coap_dumpPacket(&pkt); + coap_handle_req(&scratch_buf, &pkt, &rsppkt); + + if (0 != (rc = coap_build(buf, &rsplen, &rsppkt))) + printf("coap_build failed rc=%d\n", rc); + else + { + printf("Sending packet: "); + coap_dump(buf, rsplen, true); + printf("\n"); + printf("content:\n"); + coap_dumpPacket(&rsppkt); + socket_base_sendto(sock_rcv, buf, rsplen, 0, &sa_rcv, sizeof(sa_rcv)); + } + } + } + + return NULL; +}