OpenThread CoAP server example?

829 views
Skip to first unread message

Stuart Longland

unread,
Jun 2, 2017, 2:03:40 AM6/2/17
to openthre...@googlegroups.com
Hi all,

I'm in the process of learning about OpenThread with the view of
building a small embedded metering/control device with it. The hardware
we have is built around the TI CC2538SF53 microcontroller, and I'm
coding it using the GNU ARM gcc tools (specifically, GNU Tools for ARM
Embedded Processors 6-2017-q1-update) on Linux.

I have two CC2538EM+SmartRF06 board set-ups, sitting about 1m apart,
both of which are plugged into USB ports on my laptop. One will run the
NCP firmware, the other the CLI firmware hacked to also support CoAP.

My aim at the moment is to try and measure the light sensor and blink
the on-board LEDs using CoAP calls.

The best resource I've seen on doing this has been Nordic
Semiconductor's example code, which has given me an idea on how I am
supposed to call the CoAP-related functions, but it seems there are some
gaps in my knowledge as I have not gotten things working.

The branch I'm working on is this:
https://github.com/vrtsystems/openthread/tree/feature/WH-288-cc2538dk-support

The earlier commits in this branch try to add support for the CC2538DK's
on-board hardware, using some code from Zolertia's Firefly branch.
Basically my intent here was to separate SoC support code from the
actual surrounding board hardware so that the CC2538DK, Zolertia
Firefly, our own CC2538-based device, and anyone else's devices, can be
supported.

I'm open to suggestions on how to achieve this.

The actual CoAP action happens here:
https://github.com/vrtsystems/openthread/blob/feature/WH-288-cc2538dk-support/examples/apps/cli/coap.c

With this code, I can build a binary with:
> $ ./bootstrap && make -f examples/Makefile-cc2538 BOARD=cc2538dk COAP=1

and I get four binaries, two CLI ones (a MTD and FTD version), and two
NCP ones (again, a MTD and FTD version). I'm not sure what the
difference is between the FTD and MTD versions are, the documentation
for the CC2538[1] makes no mention of these two versions or what I
should be using. So far the FTD version is what I've been using.

Flashing ot-cli-ftd to one board and ot-ncp-ftd to the other, I can get
both up on a network, and have `wpantund` link my laptop in. From my
laptop, I can ping the device running `ot-cli-ftd`.

Great. Now I pull out Firefox and use the Copper extension to try and
talk to the device. Right off the bat, I can click Ping, and get a
response almost immediately (61 msec). There are supposed to be two
endpoints: `/leds` and `/als`.

Doing a `GET` of either of those, hangs. The microcontroller is still
responsive, but it doesn't seem to reply.

`tcpdump` reports the following when I try a `GET /leds`…
> 15:58:22.072173 IP6 fdde:ad00:beef:0:ded9:f9f3:b3e7:5641.57450 > fdde:ad00:beef:0:530:dfc9:acd2:c9ac.5683: UDP, length 11
> 0x0000: 6007 b59f 0013 1140 fdde ad00 beef 0000
> 0x0010: ded9 f9f3 b3e7 5641 fdde ad00 beef 0000
> 0x0020: 0530 dfc9 acd2 c9ac e06a 1633 0013 64b1
> 0x0030: 4001 c2d6 b46c 6564 73c1 02

… `wpantund` reports:
> wpantund[32003]: TunnelIPv6Interface::on_link_state_changed() UP=1 RUNNING=1
> wpantund[32003]: TunnelIPv6Interface::on_link_state_changed() UP=1 RUNNING=1
> wpantund[32003]: [->NCP] IPv6 len:59 type:17(cksum 0x64b1) [SECURE]
> wpantund[32003]: to(remote):[fdde:ad00:beef:0:530:dfc9:acd2:c9ac]:5683
> wpantund[32003]: from(local):[fdde:ad00:beef:0:ded9:f9f3:b3e7:5641]:57450
> wpantund[32003]: ↳ 8003723B006007B59F00131140FDDEAD00BEEF0000DED9F9F3B3E75641FDDEAD00BEEF00000530DFC9ACD2C9ACE06A1633001364B14001C2D6B46C656473C102
> wpantund[32003]: Tickle...
> wpantund[32003]: [->NCP] CMD_NOOP tid:6
> wpantund[32003]: ↳ 8600
> wpantund[32003]: [NCP->] CMD_PROP_VALUE_IS(PROP_LAST_STATUS) tid:6
> wpantund[32003]: [-NCP-]: Last status (STATUS_OK, 0)

By comparison, if I just do a ping, I get this:

> wpantund[32003]: [->NCP] IPv6 len:52 type:17(cksum 0xb370) [SECURE]
> wpantund[32003]: to(remote):[fdde:ad00:beef:0:530:dfc9:acd2:c9ac]:5683
> wpantund[32003]: from(local):[fdde:ad00:beef:0:ded9:f9f3:b3e7:5641]:46418
> wpantund[32003]: ↳ 800372340060066CBC000C1140FDDEAD00BEEF0000DED9F9F3B3E75641FDDEAD00BEEF00000530DFC9ACD2C9ACB5521633000CB37040002ED1
> wpantund[32003]: [NCP->] CMD_PROP_VALUE_IS(PROP_STREAM_NET) tid:0
> wpantund[32003]: [NCP->] IPv6 len:52 type:17(cksum 0x8370) [SECURE]
> wpantund[32003]: to(local):[fdde:ad00:beef:0:ded9:f9f3:b3e7:5641]:46418
> wpantund[32003]: from(remote):[fdde:ad00:beef:0:530:dfc9:acd2:c9ac]:5683

> 16:00:28.314615 IP6 fdde:ad00:beef:0:ded9:f9f3:b3e7:5641.46418 > fdde:ad00:beef:0:530:dfc9:acd2:c9ac.5683: UDP, length 4
> 0x0000: 6006 6cbc 000c 1140 fdde ad00 beef 0000
> 0x0010: ded9 f9f3 b3e7 5641 fdde ad00 beef 0000
> 0x0020: 0530 dfc9 acd2 c9ac b552 1633 000c b370
> 0x0030: 4000 2ed1
> 16:00:28.344467 IP6 fdde:ad00:beef:0:530:dfc9:acd2:c9ac.5683 > fdde:ad00:beef:0:ded9:f9f3:b3e7:5641.46418: UDP, length 4
> 0x0000: 6000 0000 000c 1140 fdde ad00 beef 0000
> 0x0010: 0530 dfc9 acd2 c9ac fdde ad00 beef 0000
> 0x0020: ded9 f9f3 b3e7 5641 1633 b552 000c 8370
> 0x0030: 7000 2ed1

So it isn't firewalling or routing, the two *can* talk… just the CoAP
server decides to get the 'umph and refuses to talk, thus clearly I have
missed something.

Does anyone have any ideas?

1.
https://github.com/openthread/openthread/tree/master/examples/platforms/cc2538

Regards,
--
_ ___ Stuart Longland - Systems Engineer
\ /|_) | T: +61 7 3535 9619
\/ | \ | 38b Douglas Street F: +61 7 3535 9699
SYSTEMS Milton QLD 4064 http://www.vrt.com.au

Jonathan Hui

unread,
Jun 2, 2017, 1:30:19 PM6/2/17
to Stuart Longland, openthre...@googlegroups.com
Hi Stuart,

Responses below:

On Thu, Jun 1, 2017 at 11:03 PM Stuart Longland <stu...@vrt.com.au> wrote:

I'm in the process of learning about OpenThread with the view of
building a small embedded metering/control device with it.  The hardware
we have is built around the TI CC2538SF53 microcontroller, and I'm
coding it using the GNU ARM gcc tools (specifically, GNU Tools for ARM
Embedded Processors 6-2017-q1-update) on Linux.

Awesome!  Thanks for sharing.
A Full Thread Device (FTD) is one that implements Thread Router capabilities.  A Minimal Thread Device (MTD) is one that can only operate as a Thread End Device and does not route/forward messages for other Thread devices.  An MTD requires less code and memory, allowing the use of smaller/cheaper processors.  The Thread Specification has all the details.
 
Flashing ot-cli-ftd to one board and ot-ncp-ftd to the other, I can get
both up on a network, and have `wpantund` link my laptop in.  From my
laptop, I can ping the device running `ot-cli-ftd`.

Great.  Now I pull out Firefox and use the Copper extension to try and
talk to the device.  Right off the bat, I can click Ping, and get a
response almost immediately (61 msec).  There are supposed to be two
endpoints: `/leds` and `/als`.

Doing a `GET` of either of those, hangs.  The microcontroller is still
responsive, but it doesn't seem to reply.

Looking at your code, it seems that some of your error-code checks are not correct.  One example appears on L177 of your code.

    result = otMessageAppend(replyMessage,
            "Hello World!", 12);

    if (result != OT_ERROR_NONE)
    {
        /* All good, now send it */
        result = otCoapSendResponse(
                handlerContext->aInstance, replyMessage,
                aMessageInfo);
        }
        ...
 
I believe you want result == OT_ERROR_NONE in the if-statement above.

--
Jonathan Hui
--
You received this message because you are subscribed to the Google Groups "openthread-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openthread-use...@googlegroups.com.
To post to this group, send email to openthre...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/openthread-users/a6b16943-d9fb-24a1-b662-0538cd09936c%40vrt.com.au.
For more options, visit https://groups.google.com/d/optout.

Stuart Longland

unread,
Jun 3, 2017, 3:20:56 AM6/3/17
to Jonathan Hui, openthre...@googlegroups.com
On 03/06/17 03:30, Jonathan Hui wrote:
> Looking at your code, it seems that some of your error-code checks are
> not correct. One example appears on L177 of your code
> <https://github.com/vrtsystems/openthread/blob/feature/WH-288-cc2538dk-support/examples/apps/cli/coap.c#L177>.
>
> result = otMessageAppend(replyMessage,
> "Hello World!", 12);
>
> if (result != OT_ERROR_NONE)
> {
> /* All good, now send it */
> result = otCoapSendResponse(
> handlerContext->aInstance, replyMessage,
> aMessageInfo);
> }
> ...
>
> I believe you want result == OT_ERROR_NONE in the if-statement above.

Ahh right, a problem exists between keyboard and chair. I'll try
reversing the IF statement condition and see how that goes, but that
could well be it.

That example is heavily dependent on the hardware in the CC2538DK and
I'm thinking it'd be good to abstract away LEDs and the light sensor
(since I see the Firefly has one too, a I²C part).

If there's an interface I should be using to expose this hardware, I
should really try to use that so the examples can be "hardware
independent" and aid in explaining how it all should work.

Are there any plans to introduce common interfaces for LEDs and sensors
like the Linux kernel does?

Stuart Longland

unread,
Jun 4, 2017, 9:49:38 PM6/4/17
to openthre...@googlegroups.com
On 03/06/17 17:20, Stuart Longland wrote:
>> I believe you want result == OT_ERROR_NONE in the if-statement above.
> Ahh right, a problem exists between keyboard and chair. I'll try
> reversing the IF statement condition and see how that goes, but that
> could well be it.

Well, that got me a lot further, but still not there yet. Seems
otCoapSendResponse returns OT_ERROR_PARSE. I have just done a rebase
off master branch (at commit ce8ca59c7a92ef2df0073c7ac53d741bbfe58b7b).

> Breakpoint 1, ledsReplyHandler (handlerContext=handlerContext@entry=0x200001f8 <instanceContext>,
> aHeader=aHeader@entry=0x20005368 <stack+3552>, aMessageInfo=aMessageInfo@entry=0x20005464 <stack+3804>)
> at /home/stuartl/vrt/projects/widesky/hub/openthread/examples/../examples/apps/cli/coap.c:322
> 322 result = otCoapSendResponse(
> (gdb) s
> otCoapSendResponse (aInstance=0x20000978 <sInstanceRaw>, aMessage=aMessage@entry=0x20000e00 <sInstanceRaw+1160>,
> aMessageInfo=aMessageInfo@entry=0x20005464 <stack+3804>)
> at /home/stuartl/vrt/projects/widesky/hub/openthread/examples/../src/core/api/coap_api.cpp:180
> 180 *static_cast<Message *>(aMessage), *static_cast<const Ip6::MessageInfo *>(aMessageInfo));
> (gdb) finish
> Run till exit from #0 otCoapSendResponse (aInstance=0x20000978 <sInstanceRaw>,
> aMessage=aMessage@entry=0x20000e00 <sInstanceRaw+1160>, aMessageInfo=aMessageInfo@entry=0x20005464 <stack+3804>)
> at /home/stuartl/vrt/projects/widesky/hub/openthread/examples/../src/core/api/coap_api.cpp:180
> ledsReplyHandler (handlerContext=handlerContext@entry=0x200001f8 <instanceContext>, aHeader=aHeader@entry=0x20005368 <stack+3552>,
> aMessageInfo=aMessageInfo@entry=0x20005464 <stack+3804>)
> at /home/stuartl/vrt/projects/widesky/hub/openthread/examples/../examples/apps/cli/coap.c:328
> 328 if (result != OT_ERROR_NONE)
> Value returned is $1 = OT_ERROR_PARSE

There appears to be a range of conditions that can produce this result,
and I haven't nailed down the cause as yet. All I can gather is that it
is not happy with the message being given to it.

Jonathan Hui

unread,
Jun 5, 2017, 1:43:41 PM6/5/17
to Stuart Longland, openthre...@googlegroups.com
Hi Stuart,

Great to hear you're making progress.  It's not obvious to me what is causing the `OT_ERROR_PARSE` error.  I did a simple test between two nodes in simulation using the `coap get` cli command on the `leds` resource and was able to successfully receive a response.  The `OT_ERROR_PARSE` error should come from the `CoapHeader` class.  Can you dig in there to see what is causing that error?

--
Jonathan Hui


--
You received this message because you are subscribed to the Google Groups "openthread-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openthread-use...@googlegroups.com.
To post to this group, send email to openthre...@googlegroups.com.

Stuart Longland

unread,
Jun 6, 2017, 8:38:28 PM6/6/17
to openthre...@googlegroups.com
Hi Jonathan,
On 06/06/17 03:43, Jonathan Hui wrote:
> Great to hear you're making progress. It's not obvious to me what is
> causing the `OT_ERROR_PARSE` error. I did a simple test between two
> nodes in simulation using the `coap get` cli command on the `leds`
> resource and was able to successfully receive a response. The
> `OT_ERROR_PARSE` error should come from the `CoapHeader` class. Can you
> dig in there to see what is causing that error?

Well, my big problem is I am unable to set a breakpoint on
`VerifyOrExit`, nor can I see where it is defined so I've had to resort
to peppering the code with logging statements.

`printf` didn't work, but I realised I could print things to the console
using `otPlatLog` (from openthread/platform/logging.h).

Attached is my unholy hack to see what on earth is going on, as
single-stepping all this proved to be a royal pain.

>> ::FromMessage: length (12) >= kTokenOffset (4)
>
> ::FromMessage: GetVersion (1)
>
> ::FromMessage: tokenLength (0) <= kMaxTokenLength (8)
>
> ::FromMessage: optionLength (4) <= length (7)
>
> ::FromMessage: length (2) > 0
>
> ::FromMessage: FAIL mFirstOptionOffset = 4
>
> ledsReplyHandler: replying to message
>
> ledsReplyHandler: response=f len=1
>
> ledsReplyHandler: send
>
> ::FromMessage: length (6) >= kTokenOffset (4)
>
> ::FromMessage: GetVersion (1)
>
> ::FromMessage: tokenLength (0) <= kMaxTokenLength (8)
>
> ::FromMessage: optionLength (6) <= length (1)
>
> ::FromMessage: FAIL mFirstOptionOffset = 4
>
> ledsReplyHandler: FAIL


Quite why `Header::fromHeader` gets called twice, I'm not sure, but both
times, it fails. The first time, it fails here:

> if (mHeader.mBytes[mHeaderLength] == 0xff)
> {
> mHeaderLength += sizeof(uint8_t);
> length -= sizeof(uint8_t);
> // RFC7252: The presence of a marker followed by a zero-length payload MUST be processed
> // as a message format error.
> otPlatLog(OT_LOG_LEVEL_INFO, OT_LOG_REGION_COAP,
> "::FromMessage: length (%d) > 0\r\n",
> length);
> VerifyOrExit(length > 0, error = OT_ERROR_PARSE);
> ExitNow(error = OT_ERROR_NONE);
> }

then the second time around (after my `ledsReplyHandler` is called), it
fails here:

>
> otPlatLog(OT_LOG_LEVEL_INFO, OT_LOG_REGION_COAP,
> "::FromMessage: optionLength (%d) <= length (%d)\r\n",
> optionLength, length);
> VerifyOrExit(optionLength <= length, error = OT_ERROR_PARSE);

`wpantund` sees this traffic:

> wpantund[15381]: [->NCP] IPv6 len:60 type:17(cksum 0xe604) [SECURE]
> wpantund[15381]: to(remote):[fdde:ad00:beef:0:e166:4e34:ffb3:47f4]:5683
> wpantund[15381]: from(local):[fdde:ad00:beef:0:8378:3baf:621e:b567]:33757
> wpantund[15381]: ↳ 8003723C00600ED7D700141140FDDEAD00BEEF000083783BAF621EB567FDDEAD00BEEF0000E1664E34FFB347F483DD16330014E60440025EE8B46C656473FF3166
> wpantund[15381]: Tickle...
> wpantund[15381]: [->NCP] CMD_NOOP tid:5
> wpantund[15381]: ↳ 8500
> wpantund[15381]: [NCP->] CMD_PROP_VALUE_IS(PROP_LAST_STATUS) tid:5
> wpantund[15381]: [-NCP-]: Last status (STATUS_OK, 0)

So… it would seem there's something funny with the option length, which
I did not set. Should I have set this to something?
loghack.diff

Stuart Longland

unread,
Jun 6, 2017, 10:53:29 PM6/6/17
to openthre...@googlegroups.com
On 07/06/17 10:38, Stuart Longland wrote:
> Hi Jonathan,

> So… it would seem there's something funny with the option length, which
> I did not set. Should I have set this to something?
>

Found it.

After a grep through the sources, and much head scratching, I stumbled
on some code in openthread/src/cli/cli_coap.cpp:
> otCoapHeaderInit(&responseHeader, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode);
> otCoapHeaderSetMessageId(&responseHeader, otCoapHeaderGetMessageId(aHeader));
> otCoapHeaderSetToken(&responseHeader, otCoapHeaderGetToken(aHeader), otCoapHeaderGetTokenLength(aHeader));
>
> if (otCoapHeaderGetCode(aHeader) == OT_COAP_CODE_GET)
> {
> otCoapHeaderSetPayloadMarker(&responseHeader); ««««
> }

So I updated my code with the following:

>
> otCoapHeaderInit(
> &replyHeader,
> OT_COAP_TYPE_ACKNOWLEDGMENT,
> OT_COAP_CODE_CONTENT
> );
>
> otCoapHeaderSetToken(&replyHeader,
> otCoapHeaderGetToken(aHeader),
> otCoapHeaderGetTokenLength(aHeader)
> );
>
> otCoapHeaderSetMessageId(
> &replyHeader,
> otCoapHeaderGetMessageId(aHeader)
> );
>
> otCoapHeaderSetPayloadMarker(&replyHeader);
>
> replyMessage = otCoapNewMessage(
> handlerContext->aInstance, &replyHeader
> );

I now get a reply back. The problem was I wasn't *telling* it that
there was a payload, so it assumed there wasn't one. I needed to set
that marker so that it would know this response message had a payload
attached.

*That* is why I was getting the "parse error". When I send 'tf' (toggle
bitmask 0xf) in a POST, I see all four LEDs toggle, I am also able to
send '0x' (x = hex bitmask) to turn off specific LEDs, or '1x' to turn
them on.

I'll clean up the debug statements, get the other endpoints working,
maybe see if I can get the discovery working in Copper, then we can see
about what is needed to beat this into the right shape as an example
application.
coap-success.png

Jonathan Hui

unread,
Jun 7, 2017, 12:06:12 PM6/7/17
to Stuart Longland, openthread-users
Hi Stuart,

Great to hear you got things working!  I agree that the current API regarding CoAP message formation (especially the payload marker) could be improved.  We'll add improving the API to the backlog.

Thanks.

--
Jonathan Hui


--
You received this message because you are subscribed to the Google Groups "openthread-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to openthread-users+unsubscribe@googlegroups.com.
To post to this group, send email to openthread-users@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/openthread-users/a960f5f9-3ee7-4600-df4f-3d48365fca20%40vrt.com.au.

Stuart Longland

unread,
Jun 7, 2017, 9:11:19 PM6/7/17
to openthre...@googlegroups.com
On 08/06/17 02:06, Jonathan Hui wrote:
> Great to hear you got things working! I agree that the current API
> regarding CoAP message formation (especially the payload marker) could
> be improved. We'll add improving the API to the backlog.

I've just submitted a little pull request related to this:
https://github.com/openthread/openthread/pull/1889/commits/52cddd3a4f4127dc22e64b65fd9392dc09aa79d8

With that, I'm able to "discover" the endpoints using the Copper
extension in Firefox. Just got to get the CLA signed at this end. :-)

Campbell Wray

unread,
Jun 12, 2017, 10:54:43 PM6/12/17
to openthread-users
Stuart, 

What you are trying to do is of quite a bit of interest to me, I am using the same hardware and also trying to get light sensor values from the RF06, I think my only difference is that I am using an external CoAP server written in Java as it provides better power efficiency on the boards and it is a bit more flexible. 

Did you have any luck with the vrtsystems repository in terms of reading the light sensor values, is there some form of API or anything like that for reading the values?

Cheers,
Campbell.

Stuart Longland

unread,
Jun 12, 2017, 11:04:43 PM6/12/17
to openthre...@googlegroups.com
Hi Campbell,
On 13/06/17 12:54, Campbell Wray wrote:
> What you are trying to do is of quite a bit of interest to me, I am
> using the same hardware and also trying to get light sensor values from
> the RF06, I think my only difference is that I am using an external CoAP
> server written in Java as it provides better power efficiency on the
> boards and it is a bit more flexible.

Ahh okay, running Java on the boards themselves? Can't say I've tried that.

> Did you have any luck with the vrtsystems repository in terms of reading
> the light sensor values, is there some form of API or anything like that
> for reading the values?

Not at this stage, I seem to get *a* value, but not anything that
changes with light level. Quite possibly because I haven't turned
something on, probably the clock. (The GPIO powering the sensor *is*
on, to my knowledge, unless it's inverted logic, but I'd have to grab a
multimeter to confirm that.)

The code I'm using actually borrows from the Zolertia FireFly
repository, which I cherry-picked bits out of. One of the parts was the
ADC. It allegedly works there. There's also I²C which is of interest
to us since we've got a large number of bits hanging off of it.

In our application, we aren't actually using the ADCs, so I haven't
pursued it further, and have instead, focussed my attention on the
mechanics of over-the-air updates. This morning, I managed to get the
SHA-256 crypto engine working, and was beginning to analyse the PKA with
the intent of implementing RSA signature verification (to verify SHA-256
hashes).

If I get some time after that, I might re-visit it, but there's no
priority to do so.
Reply all
Reply to author
Forward
0 new messages