@@ -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+
153169static 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 */
582603SM_AT_CMD_CUSTOM (xcoapreq , "AT#XCOAPCREQ" , handle_at_coap_req );
583604STATIC 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 ) {
0 commit comments