Skip to content

Commit 38a9fec

Browse files
committed
add hex format
Signed-off-by: Juha Ylinen <juha.ylinen@nordicsemi.no>
1 parent 5706665 commit 38a9fec

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
@@ -93,6 +93,7 @@ struct coap_request {
9393
* each subsequent block request to the host's pull rate.
9494
*/
9595
bool manual_rx; /* true: manual pull mode enabled */
96+
bool hex_rx; /* true: deliver response payload as ASCII hex string */
9697
uint8_t *rx_buf; /* Block-sized buffer (CONFIG_COAP_CLIENT_BLOCK_SIZE) */
9798
size_t rx_buf_filled; /* Bytes currently in rx_buf[] */
9899
struct k_sem rx_consumed; /* Given by AT#XCOAPCDATA; taken by coap_callback */
@@ -144,6 +145,21 @@ static void coap_send_status(struct coap_request *req)
144145
(int)req->bytes_sent);
145146
}
146147

148+
static void coap_data_send_hex(struct modem_pipe *pipe, const uint8_t *buf, size_t len)
149+
{
150+
char hex_buf[257];
151+
size_t chunk = (sizeof(hex_buf) - 1) / 2;
152+
size_t done = 0;
153+
154+
while (done < len) {
155+
size_t n = MIN(chunk, len - done);
156+
size_t sz = bin2hex(buf + done, n, hex_buf, sizeof(hex_buf));
157+
158+
data_send(pipe, hex_buf, sz);
159+
done += n;
160+
}
161+
}
162+
147163
static void coap_send_data(struct coap_request *req, const uint8_t *payload, size_t payload_len)
148164
{
149165
if (!payload || payload_len == 0) {
@@ -153,7 +169,11 @@ static void coap_send_data(struct coap_request *req, const uint8_t *payload, siz
153169
urc_send_to(req->pipe, "\r\n#XCOAPCDATA: %d,%d,%d\r\n", req->fd,
154170
(int)req->bytes_sent, (int)payload_len);
155171
req->bytes_sent += payload_len;
156-
data_send(req->pipe, payload, payload_len);
172+
if (req->hex_rx) {
173+
coap_data_send_hex(req->pipe, payload, payload_len);
174+
} else {
175+
data_send(req->pipe, payload, payload_len);
176+
}
157177
}
158178

159179
/* Notify host that a response block is ready to pull in manual receive mode. */
@@ -537,16 +557,16 @@ static int coap_datamode_callback(uint8_t op, const uint8_t *data, int len, uint
537557
}
538558

539559
/**
540-
* AT#XCOAPCREQ=<handle>,<path>,<method>[,<auto_reception>[,<confirmable>
541-
* [,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]
560+
* AT#XCOAPCREQ=<handle>,<path>,<method>[,<auto_reception>[,<hex_format>
561+
* [,<confirmable>[,<content_format>[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]
542562
* AT#XCOAPCREQ=?
543563
*
544564
* Send a CoAP request over the AT socket identified by <handle>.
545565
* The socket must already be created with AT#XSOCKET and connected with AT#XCONNECT.
546566
* Returns OK immediately; the response is delivered asynchronously.
547567
*
548568
* Auto receive mode (auto_reception=1, default):
549-
* #XCOAPCDATA: <handle>,<received_bytes>,<len>\r\n<raw bytes>
569+
* #XCOAPCDATA: <handle>,<received_bytes>,<len>\r\n<raw bytes or hex string>
550570
* #XCOAPCSTAT: <handle>,<status>,<total_bytes>
551571
*
552572
* Manual receive mode (auto_reception=0):
@@ -558,20 +578,21 @@ static int coap_datamode_callback(uint8_t op, const uint8_t *data, int len, uint
558578
* <path>: URI path string (quoted)
559579
* <method>: 1=GET, 2=POST, 3=PUT, 4=DELETE, 5=FETCH, 6=PATCH, 7=iPATCH
560580
* <auto_reception>: 1=automatic (default), 0=manual; host pulls body via AT#XCOAPCDATA.
581+
* <hex_format>: 0=binary (default), 1=hex string (each byte as two ASCII hex chars).
561582
* <confirmable>: 0=NON-confirmable (default), 1=CON-confirmable
562583
* <content_format>: CoAP content format integer (default 0 = text/plain)
563584
* <payload_len>: Optional payload length in bytes; if > 0, the command returns OK
564585
* and the device enters data mode to receive the raw payload bytes.
565586
* <opt_num>: CoAP option number (decimal integer, e.g. 35 for Proxy-Uri).
566587
* Must be followed by <opt_val>. Zero or more pairs may be appended.
567-
* <opt_val>: Option value (quoted string). Decoded as hex bytes when the string
568-
* is non-empty, even-length, and all hex digits (e.g. "3c" -> 0x3c);
588+
* <opt_val>: Option value (quoted string). A value prefixed with "0x" or "0X"
589+
* is decoded as raw hex bytes (e.g. "0x3c" -> 0x3c);
569590
* otherwise used verbatim as a UTF-8 string.
570591
*
571592
* Examples:
572593
* AT#XCOAPCREQ=0,"/path",1
573-
* AT#XCOAPCREQ=0,"proxy",1,1,1,0,0,35,"https://host/path"
574-
* AT#XCOAPCREQ=0,"/obs",1,1,1,0,0,6,"00",17,"3c"
594+
* AT#XCOAPCREQ=0,"proxy",1,1,0,1,0,0,35,"https://host/path"
595+
* AT#XCOAPCREQ=0,"/obs",1,1,0,1,0,0,6,"0x00",17,"0x3c"
575596
*/
576597
SM_AT_CMD_CUSTOM(xcoapreq, "AT#XCOAPCREQ", handle_at_coap_req);
577598
STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser *parser,
@@ -586,6 +607,7 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
586607
const char *path;
587608
size_t path_len;
588609
int auto_reception = 1;
610+
int hex_format = 0;
589611
int confirmable = 0;
590612
int content_format = COAP_CONTENT_FORMAT_TEXT_PLAIN;
591613
int payload_len = 0;
@@ -635,25 +657,36 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
635657
}
636658

637659
if (param_count > 5) {
638-
ret = at_parser_num_get(parser, 5, &confirmable);
660+
ret = at_parser_num_get(parser, 5, &hex_format);
639661
if (ret) {
640662
return ret;
641663
}
642-
if (confirmable != 0 && confirmable != 1) {
643-
LOG_ERR("Invalid confirmable value: %d", confirmable);
664+
if (hex_format != 0 && hex_format != 1) {
665+
LOG_ERR("Invalid hex_format value: %d", hex_format);
644666
return -EINVAL;
645667
}
646668
}
647669

648670
if (param_count > 6) {
649-
ret = at_parser_num_get(parser, 6, &content_format);
671+
ret = at_parser_num_get(parser, 6, &confirmable);
650672
if (ret) {
651673
return ret;
652674
}
675+
if (confirmable != 0 && confirmable != 1) {
676+
LOG_ERR("Invalid confirmable value: %d", confirmable);
677+
return -EINVAL;
678+
}
653679
}
654680

655681
if (param_count > 7) {
656-
ret = at_parser_num_get(parser, 7, &payload_len);
682+
ret = at_parser_num_get(parser, 7, &content_format);
683+
if (ret) {
684+
return ret;
685+
}
686+
}
687+
688+
if (param_count > 8) {
689+
ret = at_parser_num_get(parser, 8, &payload_len);
657690
if (ret) {
658691
return ret;
659692
}
@@ -677,6 +710,7 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
677710
memcpy(req->path, path, path_len);
678711
req->path[path_len] = '\0';
679712
req->manual_rx = !auto_reception;
713+
req->hex_rx = (bool)hex_format;
680714

681715
if (req->manual_rx) {
682716
req->rx_buf = malloc(CONFIG_COAP_CLIENT_BLOCK_SIZE);
@@ -686,21 +720,25 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
686720
}
687721
}
688722

689-
/* Parse option pairs (<opt_num>, <opt_val>) starting at param 8.
690-
* <opt_val> is decoded as hex bytes when it is a non-empty, even-length
691-
* all-hex-digits string; otherwise it is used verbatim as a UTF-8 string.
723+
/* Parse option pairs (<opt_num>, <opt_val>) starting at param 9.
724+
* Param 8 is <payload_len>; it must be present (explicitly 0) when options
725+
* follow. Omitting <payload_len> shifts every option one position left, which
726+
* always produces an odd option-param count and is caught below.
727+
*
728+
* <opt_val> is decoded as raw bytes when prefixed with "0x"/"0X"
729+
* (e.g. "0x3c" → 1 byte 0x3c); otherwise used verbatim as a UTF-8 string.
692730
* Examples:
693-
* ...,35,"https://host/path" Proxy-Uri (option 35, text value)
694-
* ...,17,"3c" Accept: application/cbor (1 byte 0x3c)
695-
* ...,6,"00",17,"3c" Observe + Accept
731+
* ...,0,0,35,"coap://host/path" payload_len=0, Proxy-Uri (text)
732+
* ...,0,0,17,"0x3c" payload_len=0, Accept: application/cbor
733+
* ...,0,0,6,"0x00",17,"0x3c" payload_len=0, Observe + Accept
696734
*/
697-
if (param_count > 8 && ((param_count - 8) & 1U)) {
698-
LOG_ERR("Odd number of option params; each opt_num must be paired "
699-
"with an opt_val");
735+
if (param_count > 9 && ((param_count - 9) & 1U)) {
736+
LOG_ERR("Odd number of option params after payload_len "
737+
"(missing payload_len=0 before options, or unpaired opt_num/opt_val)");
700738
ret = -EINVAL;
701739
goto cleanup_req;
702740
}
703-
for (uint32_t i = 8; (i + 1) < param_count; i += 2) {
741+
for (uint32_t i = 9; (i + 1) < param_count; i += 2) {
704742
const char *val_str;
705743
size_t val_len;
706744
size_t decoded_len;
@@ -773,8 +811,8 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
773811

774812
case AT_PARSER_CMD_TYPE_TEST:
775813
rsp_send("\r\n#XCOAPCREQ: <handle>,<path>,<method>"
776-
"[,<auto_reception>[,<confirmable>[,<content_format>"
777-
"[,<payload_len>[,<opt_num>,<opt_val>...]]]]]\r\n");
814+
"[,<auto_reception>[,<hex_format>[,<confirmable>[,<content_format>"
815+
"[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]]\r\n");
778816
ret = 0;
779817
break;
780818

@@ -923,7 +961,11 @@ STATIC int handle_at_coap_data(enum at_parser_cmd_type cmd_type, struct at_parse
923961
rsp_send("\r\n#XCOAPCDATA: %d,%d,%d\r\n", handle, (int)req->bytes_sent,
924962
(int)send_len);
925963
req->bytes_sent += send_len;
926-
data_send(req->pipe, req->rx_buf, send_len);
964+
if (req->hex_rx) {
965+
coap_data_send_hex(req->pipe, req->rx_buf, send_len);
966+
} else {
967+
data_send(req->pipe, req->rx_buf, send_len);
968+
}
927969
req->rx_buf_filled -= send_len;
928970

929971
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)