Thanks for the ping @kennethlimcp — and good question @ibebbs.
- TCP packet from cloud to core depends on length of variable name
- 1–7 characters: TCP payload is 18 bytes
- 8–12 characters: TCP payload is 34 bytes
- TCP ACK
- 18-byte TCP packet from core to cloud with variable value
- TCP ACK
Add all the various framing overhead, and if packets get dropped then things get repeated at the TCP level.
All the Detail
There’s an ASCII chart in the CoAP spec that I often go to as a reminder of the format. The first four bytes (version, type, token length, code, message ID) are always present; the minimum message size in CoAP is 4 bytes.
Every message is encrypted with AES-128-CBC, which means that the ciphertext length will always be a multiple of 128 bits, which is 16 bytes.
Additionally, the first 2 bytes of every message is the length of the rest of the encrypted message.
- A plaintext of 1–15 bytes will get encrypted to a single 16-byte block and prefixed with hex
0010 to say that the length of the ciphertext is 16. That’s 18 total bytes.
- A plaintext of 16–31 bytes will get encrypted to two 16-byte blocks, a total of 32 bytes, and prefixed with
0020 to say the length of the ciphertext is 32. That’s 34 total bytes.
This ultimately means that most messages on the wire in the Spark protocol are 18 bytes. They get longer when long strings are involved, and they’re always a multiple of 16, plus 2: 18, 34, 50, 66…
These are the TCP payload lengths, so then there’s framing overhead and a TCP ACK each time in the happy case or some repeated messages if things get dropped.
You can see in
received_message that a variable request from the cloud to the core is a confirmable
GET request with the first path option set to
There is a single-byte token that is used to correlate the request with the response.
The name of a variable may be 1–12 characters long.
Here’s an example of the shortest possible variable request CoAP message in xxd format:
0000000: 4101 8877 66b1 7601 76 A..wf.v.v
4101 means this is a confirmable GET request with a single-byte token. The message ID is hex
8877 and the token is hex
66. The next
b176 makes this a variable request because the first Uri-Path option is
v. The name of the variable is as short as possible, a single character:
v; that’s the final Uri-Path option
Now here’s an example of the longest possible variable request message, where the name of the variable is 12 characters long.
0000000: 4101 beef cab1 760c 6177 6573 6f6d 6573 A.....v.awesomes
0000010: 6175 6365 auce
4101 is the same, indicating a confirmable GET request with a single-byte token. The message ID is
beef, and the token is
ca. The next two bytes are still the same Uri-Path option of
The remaining 13 bytes are the second Uri-Path option, representing the variable name. The first byte
0c means the option value length is 12 bytes. The final 12 bytes are the variable name:
If the name of the variable is 7 characters long, then the whole CoAP message is 15 bytes long, the maximum plaintext that fits in a single AES-128-CBC block. If the variable name is 8 characters or longer, we need a second encrypted block.
Variable Value Response
The current value of the variable is returned to the server in what the CoAP specification refers to as a “piggy-backed response”. There’s no separate CoAP ACK; the ACK contains the response.
Side note: We do this with variables because retrieving them is fast. However, when calling
Spark.functions we send an empty CoAP ACK, followed by a separate response since we don’t know how long the user code may take to execute.
variable_value function with the final parameter type
double shows exactly what the response looks like.
- 4-byte header
- 1-byte token
- 1-byte payload marker
- 8-byte payload of double-precision floating point value
That totals 14 bytes which gets AES encrypted into a single 16-byte block, plus the 2-byte length prefix, giving an actual encrypted TCP payload of 18 bytes.