Skip to content

Commit 51d5856

Browse files
committed
add hex format
Signed-off-by: Juha Ylinen <juha.ylinen@nordicsemi.no>
1 parent 244bdeb commit 51d5856

2 files changed

Lines changed: 121 additions & 39 deletions

File tree

app/src/sm_at_coap.c

Lines changed: 68 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ struct coap_request {
9595
* each subsequent block request to the host's pull rate.
9696
*/
9797
bool manual_rx; /* true: manual pull mode enabled */
98+
bool hex_rx; /* true: deliver response payload as ASCII hex string */
9899
uint8_t *rx_buf; /* Block-sized buffer (CONFIG_COAP_CLIENT_BLOCK_SIZE) */
99100
size_t rx_buf_filled; /* Bytes currently in rx_buf[] */
100101
struct k_sem rx_consumed; /* Given by AT#XCOAPCDATA; taken by coap_callback */
@@ -150,6 +151,21 @@ static void coap_send_status(struct coap_request *req)
150151
(int)req->bytes_sent);
151152
}
152153

154+
static void coap_data_send_hex(struct modem_pipe *pipe, const uint8_t *buf, size_t len)
155+
{
156+
char hex_buf[257];
157+
size_t chunk = (sizeof(hex_buf) - 1) / 2;
158+
size_t done = 0;
159+
160+
while (done < len) {
161+
size_t n = MIN(chunk, len - done);
162+
size_t sz = bin2hex(buf + done, n, hex_buf, sizeof(hex_buf));
163+
164+
data_send(pipe, hex_buf, sz);
165+
done += n;
166+
}
167+
}
168+
153169
static void coap_send_data(struct coap_request *req, const uint8_t *payload, size_t payload_len)
154170
{
155171
if (!payload || payload_len == 0) {
@@ -159,7 +175,11 @@ static void coap_send_data(struct coap_request *req, const uint8_t *payload, siz
159175
urc_send_to(req->pipe, "\r\n#XCOAPCDATA: %d,%d,%d\r\n", req->nrf_handle,
160176
(int)req->bytes_sent, (int)payload_len);
161177
req->bytes_sent += payload_len;
162-
data_send(req->pipe, payload, payload_len);
178+
if (req->hex_rx) {
179+
coap_data_send_hex(req->pipe, payload, payload_len);
180+
} else {
181+
data_send(req->pipe, payload, payload_len);
182+
}
163183
}
164184

165185
/* Notify host that a response block is ready to pull in manual receive mode. */
@@ -543,16 +563,16 @@ static int coap_datamode_callback(uint8_t op, const uint8_t *data, int len, uint
543563
}
544564

545565
/**
546-
* AT#XCOAPCREQ=<handle>,<path>,<method>[,<auto_reception>[,<confirmable>
547-
* [,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]
566+
* AT#XCOAPCREQ=<handle>,<path>,<method>[,<auto_reception>[,<hex_format>
567+
* [,<confirmable>[,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]
548568
* AT#XCOAPCREQ=?
549569
*
550570
* Send a CoAP request over the AT socket identified by <handle>.
551571
* The socket must already be created with AT#XSOCKET and connected with AT#XCONNECT.
552572
* Returns OK immediately; the response is delivered asynchronously.
553573
*
554574
* Auto receive mode (auto_reception=1, default):
555-
* #XCOAPCDATA: <handle>,<received_bytes>,<len>\r\n<raw bytes>
575+
* #XCOAPCDATA: <handle>,<received_bytes>,<len>\r\n<raw bytes or hex string>
556576
* #XCOAPCSTAT: <handle>,<status>,<total_bytes>
557577
*
558578
* Manual receive mode (auto_reception=0):
@@ -564,20 +584,21 @@ static int coap_datamode_callback(uint8_t op, const uint8_t *data, int len, uint
564584
* <path>: URI path string (quoted)
565585
* <method>: 1=GET, 2=POST, 3=PUT, 4=DELETE, 5=FETCH, 6=PATCH, 7=iPATCH
566586
* <auto_reception>: 1=automatic (default), 0=manual; host pulls body via AT#XCOAPCDATA.
587+
* <hex_format>: 0=binary (default), 1=hex string (each byte as two ASCII hex chars).
567588
* <confirmable>: 0=NON-confirmable (default), 1=CON-confirmable
568589
* <content_format>: CoAP content format integer (default 0 = text/plain)
569590
* <payload_len>: Optional payload length in bytes; if > 0, the command returns OK
570591
* and the device enters data mode to receive the raw payload bytes.
571592
* <opt_num>: CoAP option number (decimal integer, e.g. 35 for Proxy-Uri).
572593
* Must be followed by <opt_val>. Zero or more pairs may be appended.
573-
* <opt_val>: Option value (quoted string). Decoded as hex bytes when the string
574-
* is non-empty, even-length, and all hex digits (e.g. "3c" -> 0x3c);
594+
* <opt_val>: Option value (quoted string). A value prefixed with "0x" or "0X"
595+
* is decoded as raw hex bytes (e.g. "0x3c" -> 0x3c);
575596
* otherwise used verbatim as a UTF-8 string.
576597
*
577598
* Examples:
578599
* AT#XCOAPCREQ=0,"/path",1
579-
* AT#XCOAPCREQ=0,"proxy",1,1,1,0,0,35,"https://host/path"
580-
* AT#XCOAPCREQ=0,"/obs",1,1,1,0,0,6,"00",17,"3c"
600+
* AT#XCOAPCREQ=0,"proxy",1,1,0,1,0,0,35,"https://host/path"
601+
* AT#XCOAPCREQ=0,"/obs",1,1,0,1,0,0,6,"0x00",17,"0x3c"
581602
*/
582603
SM_AT_CMD_CUSTOM(xcoapreq, "AT#XCOAPCREQ", handle_at_coap_req);
583604
STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser *parser,
@@ -592,6 +613,7 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
592613
const char *path;
593614
size_t path_len;
594615
int auto_reception = 1;
616+
int hex_format = 0;
595617
int confirmable = 0;
596618
int content_format = COAP_CONTENT_FORMAT_TEXT_PLAIN;
597619
int payload_len = 0;
@@ -641,25 +663,36 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
641663
}
642664

643665
if (param_count > 5) {
644-
ret = at_parser_num_get(parser, 5, &confirmable);
666+
ret = at_parser_num_get(parser, 5, &hex_format);
645667
if (ret) {
646668
return ret;
647669
}
648-
if (confirmable != 0 && confirmable != 1) {
649-
LOG_ERR("Invalid confirmable value: %d", confirmable);
670+
if (hex_format != 0 && hex_format != 1) {
671+
LOG_ERR("Invalid hex_format value: %d", hex_format);
650672
return -EINVAL;
651673
}
652674
}
653675

654676
if (param_count > 6) {
655-
ret = at_parser_num_get(parser, 6, &content_format);
677+
ret = at_parser_num_get(parser, 6, &confirmable);
656678
if (ret) {
657679
return ret;
658680
}
681+
if (confirmable != 0 && confirmable != 1) {
682+
LOG_ERR("Invalid confirmable value: %d", confirmable);
683+
return -EINVAL;
684+
}
659685
}
660686

661687
if (param_count > 7) {
662-
ret = at_parser_num_get(parser, 7, &payload_len);
688+
ret = at_parser_num_get(parser, 7, &content_format);
689+
if (ret) {
690+
return ret;
691+
}
692+
}
693+
694+
if (param_count > 8) {
695+
ret = at_parser_num_get(parser, 8, &payload_len);
663696
if (ret) {
664697
return ret;
665698
}
@@ -690,6 +723,7 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
690723
memcpy(req->path, path, path_len);
691724
req->path[path_len] = '\0';
692725
req->manual_rx = !auto_reception;
726+
req->hex_rx = (bool)hex_format;
693727

694728
if (req->manual_rx) {
695729
req->rx_buf = malloc(CONFIG_COAP_CLIENT_BLOCK_SIZE);
@@ -699,21 +733,25 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
699733
}
700734
}
701735

702-
/* Parse option pairs (<opt_num>, <opt_val>) starting at param 8.
703-
* <opt_val> is decoded as hex bytes when it is a non-empty, even-length
704-
* all-hex-digits string; otherwise it is used verbatim as a UTF-8 string.
736+
/* Parse option pairs (<opt_num>, <opt_val>) starting at param 9.
737+
* Param 8 is <payload_len>; it must be present (explicitly 0) when options
738+
* follow. Omitting <payload_len> shifts every option one position left, which
739+
* always produces an odd option-param count and is caught below.
740+
*
741+
* <opt_val> is decoded as raw bytes when prefixed with "0x"/"0X"
742+
* (e.g. "0x3c" → 1 byte 0x3c); otherwise used verbatim as a UTF-8 string.
705743
* Examples:
706-
* ...,35,"https://host/path" Proxy-Uri (option 35, text value)
707-
* ...,17,"3c" Accept: application/cbor (1 byte 0x3c)
708-
* ...,6,"00",17,"3c" Observe + Accept
744+
* ...,0,0,35,"coap://host/path" payload_len=0, Proxy-Uri (text)
745+
* ...,0,0,17,"0x3c" payload_len=0, Accept: application/cbor
746+
* ...,0,0,6,"0x00",17,"0x3c" payload_len=0, Observe + Accept
709747
*/
710-
if (param_count > 8 && ((param_count - 8) & 1U)) {
711-
LOG_ERR("Odd number of option params; each opt_num must be paired "
712-
"with an opt_val");
748+
if (param_count > 9 && ((param_count - 9) & 1U)) {
749+
LOG_ERR("Odd number of option params after payload_len "
750+
"(missing payload_len=0 before options, or unpaired opt_num/opt_val)");
713751
ret = -EINVAL;
714752
goto cleanup_req;
715753
}
716-
for (uint32_t i = 8; (i + 1) < param_count; i += 2) {
754+
for (uint32_t i = 9; (i + 1) < param_count; i += 2) {
717755
const char *val_str;
718756
size_t val_len;
719757
size_t decoded_len;
@@ -786,8 +824,8 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
786824

787825
case AT_PARSER_CMD_TYPE_TEST:
788826
rsp_send("\r\n#XCOAPCREQ: <handle>,<path>,<method>"
789-
"[,<auto_reception>[,<confirmable>[,<content_format>"
790-
"[,<payload_len>[,<opt_num>,<opt_val>...]]]]]\r\n");
827+
"[,<auto_reception>[,<hex_format>[,<confirmable>[,<content_format>"
828+
"[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]]\r\n");
791829
ret = 0;
792830
break;
793831

@@ -936,7 +974,11 @@ STATIC int handle_at_coap_data(enum at_parser_cmd_type cmd_type, struct at_parse
936974
rsp_send("\r\n#XCOAPCDATA: %d,%d,%d\r\n", handle, (int)req->bytes_sent,
937975
(int)send_len);
938976
req->bytes_sent += send_len;
939-
data_send(req->pipe, req->rx_buf, send_len);
977+
if (req->hex_rx) {
978+
coap_data_send_hex(req->pipe, req->rx_buf, send_len);
979+
} else {
980+
data_send(req->pipe, req->rx_buf, send_len);
981+
}
940982
req->rx_buf_filled -= send_len;
941983

942984
if (req->rx_buf_filled > 0) {

doc/app/at_coap.rst

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Syntax
3838

3939
::
4040

41-
AT#XCOAPCREQ=<handle>,<path>,<method>[,<auto_reception>[,<confirmable>[,<content_format>[,<payload_len>[,<opt_num_1>,<opt_val_1>[,<opt_num_2>,<opt_val_2>[...]]]]]]]
41+
AT#XCOAPCREQ=<handle>,<path>,<method>[,<auto_reception>[,<hex_format>[,<confirmable>[,<content_format>[,<payload_len>[,<opt_num_1>,<opt_val_1>[,<opt_num_2>,<opt_val_2>[...]]]]]]]]
4242

4343
* The ``<handle>`` parameter is an integer.
4444
It identifies the connected socket handle returned by ``AT#XSOCKET`` and connected by ``AT#XCONNECT``.
@@ -66,6 +66,18 @@ Syntax
6666
The firmware buffers each response block and emits ``#XCOAPCHEAD``.
6767
The host must then retrieve the block using ``AT#XCOAPCDATA``.
6868

69+
* The ``<hex_format>`` parameter is an optional integer.
70+
It controls the encoding used to deliver response payload bytes to the host.
71+
It can accept the following values:
72+
73+
* ``0`` - Binary (default).
74+
Response payload bytes are forwarded to the host as raw binary.
75+
* ``1`` - Hex string.
76+
Response payload bytes are encoded as a lower-case ASCII hex string before delivery.
77+
Each raw byte becomes two hex characters.
78+
The ``<length>`` field in ``#XCOAPCDATA`` URCs and responses always reports the raw byte count.
79+
The actual data transmitted to the host is ``2×<length>`` ASCII hex characters.
80+
6981
* The ``<confirmable>`` parameter is an optional integer.
7082
It can accept the following values:
7183

@@ -82,8 +94,15 @@ Syntax
8294
* ``60`` - application/cbor.
8395

8496
* The ``<payload_len>`` parameter is an optional integer.
85-
When ``0`` or omitted, no request payload is sent and the command returns ``OK`` immediately.
97+
When ``0`` or omitted and no option pairs follow, no request payload is sent and the command returns ``OK`` immediately.
8698
A positive integer specifies the total payload length in bytes; the command then returns ``OK`` and enters data mode.
99+
100+
.. note::
101+
102+
When option pairs follow, ``<payload_len>`` must be written explicitly.
103+
Use ``0`` if no payload is needed.
104+
Omitting it shifts the option pairs one position left, which always causes the command to return ``ERROR``.
105+
87106
The host must send exactly this many bytes as the request payload.
88107
Data mode exits and ``#XDATAMODE: 0`` is reported when all bytes have been consumed.
89108
Data mode is the only mechanism for supplying the payload — there is no inline parameter alternative.
@@ -98,6 +117,7 @@ Syntax
98117
Each subsequent block is sent as more data arrives, so the server exchange runs concurrently with the host's serial upload.
99118

100119
* The ``<opt_num_X>,<opt_val_X>`` parameters are optional CoAP option pairs.
120+
When present, ``<payload_len>`` must also be present as the positional separator before the first pair.
101121
``<opt_num_X>`` is a decimal integer option number.
102122
``<opt_val_X>`` is a quoted string.
103123
A value prefixed with ``0x`` or ``0X`` is decoded as raw hex bytes (for example ``"0x3c"`` → byte ``0x3c``).
@@ -127,14 +147,17 @@ Unsolicited notification
127147

128148
#XCOAPCDATA: <handle>,<offset>,<length>
129149

130-
The notification line is terminated with ``\r\n`` and the raw response bytes follow immediately with no additional separator.
150+
The notification line is terminated with ``\r\n`` and the response data follows immediately with no additional separator.
151+
In binary mode (default), the data is ``<length>`` raw bytes.
152+
In hex mode (``<hex_format>=1``), the data is ``2×<length>`` lower-case ASCII hex characters.
131153

132154
* The ``<handle>`` parameter is an integer.
133155
It identifies the socket.
134156
* The ``<offset>`` parameter is an integer.
135157
It contains the number of response bytes already delivered before this block.
136158
* The ``<length>`` parameter is an integer.
137-
It contains the number of response bytes in this block.
159+
It contains the raw byte count of the response data in this block.
160+
In hex mode (``<hex_format>=1``), the payload following the header line is ``2×<length>`` ASCII hex characters.
138161

139162
``#XCOAPCHEAD`` is emitted in manual mode for each received response block.
140163

@@ -147,7 +170,8 @@ The notification line is terminated with ``\r\n`` and the raw response bytes fol
147170
* The ``<code>`` parameter is an integer.
148171
It contains the CoAP response code for this block.
149172
* The ``<block_len>`` parameter is an integer.
150-
It contains the number of bytes in the buffered block.
173+
It contains the raw byte count of the buffered block.
174+
In hex mode (``<hex_format>=1``), ``AT#XCOAPCDATA`` will deliver ``2×<block_len>`` ASCII hex characters.
151175
The host must call ``AT#XCOAPCDATA`` to retrieve this block before the next one arrives.
152176

153177
``#XCOAPCSTAT`` is emitted when the request completes, fails, or is cancelled
@@ -163,7 +187,8 @@ The notification line is terminated with ``\r\n`` and the raw response bytes fol
163187
CoAP response codes are encoded as ``(class << 5) | detail``.
164188
For example, ``2.05 Content`` is encoded as ``69`` and ``2.04 Changed`` as ``68``.
165189
* The ``<total_bytes>`` parameter is an integer.
166-
It contains the total number of response payload bytes delivered to the host.
190+
It contains the total number of raw response payload bytes received.
191+
In hex mode (``<hex_format>=1``), this is the raw byte count, not the number of hex characters delivered.
167192

168193
.. note::
169194

@@ -174,7 +199,7 @@ The notification line is terminated with ``\r\n`` and the raw response bytes fol
174199
Examples
175200
~~~~~~~~
176201

177-
CoAP GET (automatic mode):
202+
CoAP GET (automatic mode, binary):
178203

179204
::
180205

@@ -197,6 +222,18 @@ CoAP GET (automatic mode):
197222
AT#XCLOSE=0
198223
OK
199224

225+
CoAP GET (automatic mode, hex-encoded response, ``hex_format=1``):
226+
227+
::
228+
229+
AT#XCOAPCREQ=0,"/sensors/temperature",1,1,1
230+
OK
231+
232+
#XCOAPCDATA: 0,0,12
233+
32332e352043656c73697573
234+
235+
#XCOAPCSTAT: 0,69,12
236+
200237
CoAP GET (manual mode):
201238

202239
::
@@ -217,7 +254,7 @@ CoAP POST with JSON payload (``content_format=50`` = ``application/json``):
217254

218255
::
219256

220-
AT#XCOAPCREQ=0,"/data",2,1,0,50,13
257+
AT#XCOAPCREQ=0,"/data",2,1,0,0,50,13
221258
OK
222259
{"value": 42}
223260
#XDATAMODE: 0
@@ -228,7 +265,7 @@ CoAP GET via proxy with Proxy-Uri option (option 35, value is a plain URI string
228265

229266
::
230267

231-
AT#XCOAPCREQ=0,"proxy",1,1,0,0,0,35,"coap://remote-host/resource"
268+
AT#XCOAPCREQ=0,"proxy",1,1,0,0,0,0,35,"coap://remote-host/resource"
232269
OK
233270

234271
#XCOAPCDATA: 0,0,128
@@ -240,7 +277,7 @@ CoAP GET with Observe and Accept options (option 6 = Observe register ``"0x00"``
240277

241278
::
242279

243-
AT#XCOAPCREQ=0,"/obs",1,1,1,0,0,6,"0x00",17,"0x3c"
280+
AT#XCOAPCREQ=0,"/obs",1,1,0,1,0,0,6,"0x00",17,"0x3c"
244281
OK
245282

246283
#XCOAPCDATA: 0,0,18
@@ -265,15 +302,15 @@ Response syntax
265302

266303
::
267304

268-
#XCOAPCREQ: <handle>,<path>,<method>[,<auto_reception>[,<confirmable>[,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]
305+
#XCOAPCREQ: <handle>,<path>,<method>[,<auto_reception>[,<hex_format>[,<confirmable>[,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]
269306

270307
Example
271308
~~~~~~~
272309

273310
::
274311

275312
AT#XCOAPCREQ=?
276-
#XCOAPCREQ: <handle>,<path>,<method>[,<auto_reception>[,<confirmable>[,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]
313+
#XCOAPCREQ: <handle>,<path>,<method>[,<auto_reception>[,<hex_format>[,<confirmable>[,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]
277314
OK
278315

279316
CoAP data pull #XCOAPCDATA
@@ -308,9 +345,12 @@ When a block is available
308345
::
309346

310347
#XCOAPCDATA: <handle>,<offset>,<length>
311-
<length bytes of raw response data>
348+
<data>
312349
OK
313350

351+
In binary mode (default), ``<data>`` is ``<length>`` raw bytes.
352+
In hex mode (``<hex_format>=1``), ``<data>`` is ``2×<length>`` lower-case ASCII hex characters.
353+
314354
The ``#XCOAPCDATA:`` line is terminated with ``\r\n``.
315355
``<length>`` raw response bytes follow immediately with no additional separator.
316356
``OK`` follows on its own line after the response bytes.

0 commit comments

Comments
 (0)