Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to receive payload data? #156

Closed
choppr opened this issue Nov 15, 2022 · 14 comments
Closed

How to receive payload data? #156

choppr opened this issue Nov 15, 2022 · 14 comments

Comments

@choppr
Copy link

choppr commented Nov 15, 2022

Hello, I am connecting my kotlin app to an ESP8266 Coap server. I have defined my message like so:

private val message = Message.Udp(
    type = Udp.Type.Confirmable,
    code = Message.Code.Method.GET,
    id = 0x0,
    token = 0x0,
    options = listOf(Message.Option.UriPath("192.168.4.1"), Message.Option.UriPort(5683), Message.Option.LocationPath("/SpO2")),
    payload = byteArrayOf()
)

Then I have a scheduled timer task sending the encoded message periodically and am trying to decode:

val encoded = message.encode()
val decoded = encoded.decode<Udp>()
Log.d("CoAP:  ", decoded.payload.toString())

However I keep receiving garbage data. "[B@91f6bff, [B@b82ddcc, [B@e14c815, [B@90dc72a, [B@208c91b" etc...

Is there a way to verify I have the right URI path?

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

What type of data are you expecting in the CoAP response? Doing a toString on ByteArray is rarely helpful (unfortunately) as you've seen (with the [B@91f6bff, [B@b82ddcc, ...).

Might be helpful for you to either print the hex representation (can use Tuulbox's toHexString extension function, found in the encoding artifact) or, if you know it should be a string message, could use Kotlin's built in decodeToString().

For example:

val decoded = encoded.decode<Udp>()
Log.d("CoAP: ", decoded.payload.toHexString()) // or: decoded.payload.decodeToString()

As for the options to send, depends on what your peripheral is expecting.
I haven't used LocationPath, so I can't speak to that (although a quick look at the RFC makes me think that Location* is for responses and Uri* are for requests?).

The UriPath should indicate each component of the path (without /s), so you have multiple UriPath options to indicate levels deep of a directory structure.

For example:

192.168.4.1:5683/hello/world

Might be constructed via the following options:

import com.juul.koap.Message.Option.UriHost
import com.juul.koap.Message.Option.UriPath
import com.juul.koap.Message.Option.UriPort

listOf(UriHost("192.168.4.1"), UriPort(5683), UriPath("hello"), UriPath("world"))

@choppr
Copy link
Author

choppr commented Nov 15, 2022

Awesome! Thanks so much for you response. The data from my CoAP server is string data converted from a float using snprintf.
So I have updated my code based on your suggestions!

private val message = Message.Udp(
    type = Udp.Type.Confirmable,
    code = Message.Code.Method.GET,
    id = 0x0,
    token = 0x0,
    options = listOf(UriPath("192.168.4.1"), UriPort(5683), UriPath("SpO2")),
    payload = byteArrayOf()
)

And decoding as such:

val encoded = message.encode()
val decoded = encoded.decode<Udp>()
Log.d("CoAP:  ", decoded.payload.decodeToString())

I believe my URI path is ok because I used the Copper Chrome extension to make GET requests to my SpO2 resource and I am able to read the payload.
My problem now is that no payload data is being recorded in my Log on the mobile app. Is there an example of how to perform resource Discovery with KoAP? Perhaps I am using the wrong URI

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

Is there an example of how to perform resource Discovery with KoAP?

We haven't needed resource discovery in our projects, so didn't implement any kind of support in KoAP.

Copper Chrome extension to make GET requests

Any chance you can grab the raw data (preferable in hex notation) that the extension is sending (where you said it is working)? I could help you reverse engineer the request to determine how to shape the request using KoAP.

@choppr
Copy link
Author

choppr commented Nov 15, 2022

Unfortunately I do not have access to the hex data, but I can provide screenshot with as much data possible.
I'm not sure if this matters or not but the resources on my CoAP server are configured to blocked responses. In my response handler, I just increment my float data by 1.0 and verify it in the payload.

Coap

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

Hmm...is there a particular reason why you're using UriPath for the 192.168.4.1?

According to the screenshot 192.168.4.1 appears to be a different type (purely based on the icon) than SpO2. Guessing you should have UriHost("192.168.4.1") vs. your UriPath("192.168.4.1")?

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

Being that your payload is expected to be a float, I think you'll want something like:

val value = ByteBuffer.wrap(payload).getFloat()

Note, the above example is JVM only, if you want to read a float in common code you'll have to find a pure Kotlin mechanism of converting a ByteArray to Float.

@choppr
Copy link
Author

choppr commented Nov 15, 2022

🤦 I tried to put the full path like so UriPath(192.168.4.1:5683/SpO2) before and didn't update it after. Thanks so much for that catch. My data should still be string data because I am echoing the payload through serial COM on my ESP8266 and I am reading it correctly.
I still have the same results with no payload data after your fixes :(

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

same results with no payload data

Can you try logging either the payload.size or the hex notation of the payload (just to verify you're getting data).
It is possible that the data you're getting back wouldn't render as a string representation (decodeToString()), making it look empty when it is not.

Just trying to confirm if your payload has any data or not.

I would think (hope) that your peripheral would respond with a failure CoAP response if you have the host/path incorrect.
Have you tried println(response) (where response is the decoded CoAP response, using KoAP, you get from the peripheral)?

Alternatively, if you can share the full raw (in hex notation) requests that you're sending (i.e. provide the bytes that you encoded to using KoAP) and responses that you're receiving (i.e. the bytes you're getting back before decoding w/ KoAP) then I can help debug further.

@choppr
Copy link
Author

choppr commented Nov 15, 2022

Here is my log

val encoded = message.encode()
Log.d("CoAP: ","Encoded messaged for transmission: " + encoded.toHexString())
val decoded = encoded.decode<Udp>()
Log.d("CoAP:  ", "Decoded Payload size: " + decoded.payload.size.toString())
Log.d("CoAP:  ", "Decoded Payload result: " + decoded.payload.decodeToString())
Log.d("CoAP:  ", "Decoded: " + decoded.encode().toHexString())

Encoded messaged for transmission: 400100003B3139322E3136382E342E314216334453704F32
Decoded Payload size: 0
Decoded Payload result:
Decoded: 400100003B3139322E3136382E342E314216334453704F32

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

Is encoded the variable that you populate with the response from the remote peripheral/server?

The bytes you provided for both the request and "response" are identical, indicating that perhaps encoded isn't being populated with the response (and rather just decoding the request)?

@choppr
Copy link
Author

choppr commented Nov 15, 2022

From my understanding, the Message class method 'encode' will transmit the CoAP message and update the payload from the response message within the same Message class. Do I need to call another method to receive the response?

I set my CoAP server log to the most verbose settings and I do not see any negative response, only the creating and binding of the CoAP server socket

@twyatt
Copy link
Member

twyatt commented Nov 15, 2022

KoAP is just an encoder/decoder, it doesn't have a transport layer; it is expected that the library consumer transmits the data.

So, you'll essentially use KoAP to encode a CoAP message (as a ByteArray). Then you'll need to transmit the ByteArray, and when you get a response (as a ByteArray), then you'll use the decode function in KoAP.

For example:

val coapRequest = Message.Udp(..)
val requestBytes = coapRequest.encode()

// hypothetical `transport` that sends bytes over-the-wire/air
val responseBytes = transport.send(requestBytes)

val coapResponse = responseBytes.decode<Udp>()
println(coapResponse)

@choppr
Copy link
Author

choppr commented Nov 15, 2022

Oh my god! So sorry for wasting your time....

Are there any future plans to be the first kotlin written CoAP client/server?

@choppr choppr closed this as completed Nov 15, 2022
@twyatt
Copy link
Member

twyatt commented Nov 16, 2022

No worries. 😄

Are there any future plans to be the first kotlin written CoAP client/server?

Unfortunately, no. Just too many other priorities to work on, and we only needed the encoding/decoding side of things.
Would be cool to see someone open source a library that sits on top of KoAP and provides the client/server, but we don't have any plans to take that on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants