Skip to content

Commit 160b1f6

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

2 files changed

Lines changed: 100 additions & 30 deletions

File tree

app/src/sm_at_coap.c

Lines changed: 56 additions & 18 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);
@@ -707,13 +741,13 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
707741
* ...,17,"3c" → Accept: application/cbor (1 byte 0x3c)
708742
* ...,6,"00",17,"3c" → Observe + Accept
709743
*/
710-
if (param_count > 8 && ((param_count - 8) & 1U)) {
744+
if (param_count > 9 && ((param_count - 9) & 1U)) {
711745
LOG_ERR("Odd number of option params; each opt_num must be paired "
712746
"with an opt_val");
713747
ret = -EINVAL;
714748
goto cleanup_req;
715749
}
716-
for (uint32_t i = 8; (i + 1) < param_count; i += 2) {
750+
for (uint32_t i = 9; (i + 1) < param_count; i += 2) {
717751
const char *val_str;
718752
size_t val_len;
719753
size_t decoded_len;
@@ -786,8 +820,8 @@ STATIC int handle_at_coap_req(enum at_parser_cmd_type cmd_type, struct at_parser
786820

787821
case AT_PARSER_CMD_TYPE_TEST:
788822
rsp_send("\r\n#XCOAPCREQ: <handle>,<path>,<method>"
789-
"[,<auto_reception>[,<confirmable>[,<content_format>"
790-
"[,<payload_len>[,<opt_num>,<opt_val>...]]]]]\r\n");
823+
"[,<auto_reception>[,<hex_format>[,<confirmable>[,<content_format>"
824+
"[,<payload_len>[,<opt_num>,<opt_val>...]]]]]]]\r\n");
791825
ret = 0;
792826
break;
793827

@@ -936,7 +970,11 @@ STATIC int handle_at_coap_data(enum at_parser_cmd_type cmd_type, struct at_parse
936970
rsp_send("\r\n#XCOAPCDATA: %d,%d,%d\r\n", handle, (int)req->bytes_sent,
937971
(int)send_len);
938972
req->bytes_sent += send_len;
939-
data_send(req->pipe, req->rx_buf, send_len);
973+
if (req->hex_rx) {
974+
coap_data_send_hex(req->pipe, req->rx_buf, send_len);
975+
} else {
976+
data_send(req->pipe, req->rx_buf, send_len);
977+
}
940978
req->rx_buf_filled -= send_len;
941979

942980
if (req->rx_buf_filled > 0) {

doc/app/at_coap.rst

Lines changed: 44 additions & 12 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

@@ -127,14 +139,17 @@ Unsolicited notification
127139

128140
#XCOAPCDATA: <handle>,<offset>,<length>
129141

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

132146
* The ``<handle>`` parameter is an integer.
133147
It identifies the socket.
134148
* The ``<offset>`` parameter is an integer.
135149
It contains the number of response bytes already delivered before this block.
136150
* The ``<length>`` parameter is an integer.
137-
It contains the number of response bytes in this block.
151+
It contains the raw byte count of the response data in this block.
152+
In hex mode (``<hex_format>=1``), the payload following the header line is ``2×<length>`` ASCII hex characters.
138153

139154
``#XCOAPCHEAD`` is emitted in manual mode for each received response block.
140155

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

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

168185
.. note::
169186

@@ -174,7 +191,7 @@ The notification line is terminated with ``\r\n`` and the raw response bytes fol
174191
Examples
175192
~~~~~~~~
176193

177-
CoAP GET (automatic mode):
194+
CoAP GET (automatic mode, binary):
178195

179196
::
180197

@@ -197,6 +214,18 @@ CoAP GET (automatic mode):
197214
AT#XCLOSE=0
198215
OK
199216

217+
CoAP GET (automatic mode, hex-encoded response, ``hex_format=1``):
218+
219+
::
220+
221+
AT#XCOAPCREQ=0,"/sensors/temperature",1,1,1
222+
OK
223+
224+
#XCOAPCDATA: 0,0,12
225+
32332e352043656c73697573
226+
227+
#XCOAPCSTAT: 0,69,12
228+
200229
CoAP GET (manual mode):
201230

202231
::
@@ -217,7 +246,7 @@ CoAP POST with JSON payload (``content_format=50`` = ``application/json``):
217246

218247
::
219248

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

229258
::
230259

231-
AT#XCOAPCREQ=0,"proxy",1,1,0,0,0,35,"coap://remote-host/resource"
260+
AT#XCOAPCREQ=0,"proxy",1,1,0,0,0,0,35,"coap://remote-host/resource"
232261
OK
233262

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

241270
::
242271

243-
AT#XCOAPCREQ=0,"/obs",1,1,1,0,0,6,"0x00",17,"0x3c"
272+
AT#XCOAPCREQ=0,"/obs",1,1,0,1,0,0,6,"0x00",17,"0x3c"
244273
OK
245274

246275
#XCOAPCDATA: 0,0,18
@@ -265,15 +294,15 @@ Response syntax
265294

266295
::
267296

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

270299
Example
271300
~~~~~~~
272301

273302
::
274303

275304
AT#XCOAPCREQ=?
276-
#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>...]]]]]]
277306
OK
278307

279308
CoAP data pull #XCOAPCDATA
@@ -308,9 +337,12 @@ When a block is available
308337
::
309338

310339
#XCOAPCDATA: <handle>,<offset>,<length>
311-
<length bytes of raw response data>
340+
<data>
312341
OK
313342

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

0 commit comments

Comments
 (0)