@@ -114,6 +114,7 @@ struct modem_cellular_data {
114114 uint8_t iccid [MODEM_CELLULAR_DATA_ICCID_LEN ];
115115 uint8_t manufacturer [MODEM_CELLULAR_DATA_MANUFACTURER_LEN ];
116116 uint8_t fw_version [MODEM_CELLULAR_DATA_FW_VERSION_LEN ];
117+ struct modem_cellular_gnss gnss_data ;
117118
118119 /* PPP */
119120 struct modem_ppp * ppp ;
@@ -130,6 +131,8 @@ struct modem_cellular_data {
130131 uint8_t event_buf [8 ];
131132 struct ring_buf event_rb ;
132133 struct k_mutex event_rb_lock ;
134+
135+ struct modem_cell_profile s_profile ;
133136};
134137
135138struct modem_cellular_config {
@@ -146,6 +149,68 @@ struct modem_cellular_config {
146149 const struct modem_chat_script * periodic_chat_script ;
147150};
148151
152+
153+
154+
155+ static void modem_cellular_apply_profile (struct modem_cellular_data * data );
156+
157+
158+
159+
160+ /* Hook débil para el backend PPP, si se usa auth */
161+ __attribute__((weak ))
162+ void modem_cellular_ppp_set_auth (const char * user , const char * pass )
163+ {
164+ ARG_UNUSED (user );
165+ ARG_UNUSED (pass );
166+ }
167+
168+ int modem_cellular_profile_set (const struct device * dev , struct modem_cell_profile * in )
169+ {
170+ if (!dev || !in ) {
171+ return - EINVAL ;
172+ }
173+ struct modem_cellular_data * data = (struct modem_cellular_data * )dev -> data ;
174+
175+
176+ memcpy (data -> s_profile .apn , in -> apn , sizeof (data -> s_profile .apn ));
177+ data -> s_profile .apn [sizeof (data -> s_profile .apn ) - 1 ] = '\0' ;
178+
179+ memcpy (data -> s_profile .user , in -> user , sizeof (data -> s_profile .user ));
180+ data -> s_profile .user [sizeof (data -> s_profile .user ) - 1 ] = '\0' ;
181+
182+ memcpy (data -> s_profile .pass , in -> pass , sizeof (data -> s_profile .pass ));
183+ data -> s_profile .pass [sizeof (data -> s_profile .pass ) - 1 ] = '\0' ;
184+
185+ data -> s_profile .use_auth = in -> use_auth ;
186+
187+ return 0 ;
188+ }
189+
190+ int modem_cellular_profile_get (const struct device * dev , struct modem_cell_profile * out )
191+ {
192+ if (!dev || !out ) {
193+ return - EINVAL ;
194+ }
195+ struct modem_cellular_data * data = (struct modem_cellular_data * )dev -> data ;
196+
197+ memcpy (out , & data -> s_profile , sizeof (struct modem_cell_profile ));
198+ // *out = data->s_profile; /* copia estructurada directa */
199+
200+ return 0 ;
201+ }
202+
203+ /* API de conveniencia para leer GNSS desde main */
204+ int modem_cellular_get_gnss (const struct device * dev , struct modem_cellular_gnss * out )
205+ {
206+ if (!dev || !out ) {
207+ return - EINVAL ;
208+ }
209+ struct modem_cellular_data * data = (struct modem_cellular_data * )dev -> data ;
210+ * out = data -> gnss_data ;
211+ return data -> gnss_data .valid ? 0 : - ENODATA ;
212+ }
213+
149214static const char * modem_cellular_state_str (enum modem_cellular_state state )
150215{
151216 switch (state ) {
@@ -293,6 +358,114 @@ static void modem_cellular_chat_callback_handler(struct modem_chat *chat,
293358 }
294359}
295360
361+ static double nmea_degmin_to_decimal (const char * ddmmHemi )
362+ {
363+ size_t n = strlen (ddmmHemi );
364+ if (n < 3 ) {
365+ return 0.0 ;
366+ }
367+
368+ char hemi = ddmmHemi [n - 1 ]; // Last character: N/S/E/W
369+ char buf [16 ];
370+ if (n - 1 >= sizeof (buf )) {
371+ return 0.0 ;
372+ }
373+
374+ strncpy (buf , ddmmHemi , n - 1 );
375+ buf [n - 1 ] = '\0' ;
376+
377+ // Determine number of degree digits (2 for lat, 3 for lon)
378+ int deg_digits = (n >= 10 ) ? 3 : 2 ;
379+ char d [4 ] = {0 };
380+ memcpy (d , buf , deg_digits );
381+
382+ double deg = atof (d );
383+ double min = atof (buf + deg_digits );
384+ double dec = deg + (min / 60.0 );
385+
386+ if (hemi == 'S' || hemi == 'W' ) {
387+ dec = - dec ;
388+ }
389+
390+ return dec ;
391+ }
392+
393+ static void modem_cellular_chat_on_gnss_error (struct modem_chat * chat , void * user_data )
394+ {
395+ struct modem_cellular_data * data = (struct modem_cellular_data * )user_data ;
396+ struct modem_cellular_gnss * gnss = & data -> gnss_data ;
397+
398+ gnss -> valid = false;
399+ LOG_WRN ("GNSS: error en respuesta" );
400+ }
401+
402+ static void modem_cellular_chat_on_gnss (struct modem_chat * chat , char * * argv , uint16_t argc , void * user_data )
403+ {
404+ struct modem_cellular_data * data = (struct modem_cellular_data * )user_data ;
405+ struct modem_cellular_gnss * gnss = & data -> gnss_data ;
406+
407+ // Expected: +QGPSLOC: <UTC>,<lat>,<lon>,<HDOP>,<alt>,<fix>,<COG>,<spkm>,<spkn>,<date>,<nsat>
408+ if (argc < 12 ) {
409+ LOG_WRN ("GNSS: respuesta muy corta (%d campos)" , argc );
410+ gnss -> valid = false;
411+ return ;
412+ }
413+
414+ LOG_INF ("GNSS raw message (%d campos):" , argc );
415+ for (int i = 0 ; i < argc ; i ++ ) {
416+ LOG_INF (" argv[%d] = '%s'" , i , argv [i ]);
417+ }
418+
419+ // --- Parse fields ---
420+ memset (gnss , 0 , sizeof (* gnss ));
421+
422+ // UTC time (hhmmss)
423+ strncpy (gnss -> time_utc , argv [1 ], sizeof (gnss -> time_utc ) - 1 );
424+ gnss -> time_utc [sizeof (gnss -> time_utc ) - 1 ] = '\0' ;
425+
426+ // Latitude and Longitude with hemisphere
427+ gnss -> lat_deg = nmea_degmin_to_decimal (argv [2 ]);
428+ gnss -> lon_deg = nmea_degmin_to_decimal (argv [3 ]);
429+
430+ // Precision, altitude, and fix type
431+ gnss -> hdop = atof (argv [4 ]);
432+ gnss -> alt_m = atof (argv [5 ]);
433+ gnss -> fix = atoi (argv [6 ]);
434+
435+ // Course over ground and speed
436+ gnss -> cog_deg = atof (argv [7 ]);
437+ gnss -> spd_kmh = atof (argv [8 ]);
438+ gnss -> spd_knt = atof (argv [9 ]);
439+
440+ // Date (ddmmyy)
441+ strncpy (gnss -> date_utc , argv [10 ], sizeof (gnss -> date_utc ) - 1 );
442+ gnss -> date_utc [sizeof (gnss -> date_utc ) - 1 ] = '\0' ;
443+
444+ // Number of satellites
445+ gnss -> nsat = atoi (argv [11 ]);
446+
447+ // Determine validity
448+ gnss -> valid = (gnss -> fix > 0 && gnss -> lat_deg != 0.0 && gnss -> lon_deg != 0.0 );
449+
450+ //PRINT ALL ARRAY ITEMS FOR DEBUGGING
451+ LOG_INF ("GNSS raw message (%d campos):" , argc );
452+ for (int i = 0 ; i < argc ; i ++ ) {
453+ LOG_INF (" argv[%d] = '%s'" , i , argv [i ]);
454+ }
455+
456+ LOG_INF ("GNSS fix: UTC=%s, date=%s, lat=%.6f, lon=%.6f, alt=%.2f m, fix=%d, sat=%d, hdop=%.2f, cog=%.2f°, spd=%.2f km/h" ,
457+ gnss -> time_utc ,
458+ gnss -> date_utc ,
459+ gnss -> lat_deg ,
460+ gnss -> lon_deg ,
461+ gnss -> alt_m ,
462+ gnss -> fix ,
463+ gnss -> nsat ,
464+ gnss -> hdop ,
465+ gnss -> cog_deg ,
466+ gnss -> spd_kmh );
467+ }
468+
296469static void modem_cellular_chat_on_imei (struct modem_chat * chat , char * * argv , uint16_t argc ,
297470 void * user_data )
298471{
@@ -434,6 +607,10 @@ MODEM_CHAT_MATCHES_DEFINE(allow_match,
434607 MODEM_CHAT_MATCH ("OK" , "" , NULL ),
435608 MODEM_CHAT_MATCH ("ERROR" , "" , NULL ));
436609
610+ MODEM_CHAT_MATCHES_DEFINE (qgpsloc_match ,
611+ MODEM_CHAT_MATCH ("+QGPSLOC:" , "," , modem_cellular_chat_on_gnss ),
612+ MODEM_CHAT_MATCH ("+CME ERROR" , "" , modem_cellular_chat_on_gnss_error ));
613+
437614MODEM_CHAT_MATCH_DEFINE (imei_match , "" , "" , modem_cellular_chat_on_imei );
438615MODEM_CHAT_MATCH_DEFINE (cgmm_match , "" , "" , modem_cellular_chat_on_cgmm );
439616MODEM_CHAT_MATCH_DEFINE (csq_match , "+CSQ: " , "," , modem_cellular_chat_on_csq );
@@ -852,6 +1029,10 @@ static void modem_cellular_run_dial_script_event_handler(struct modem_cellular_d
8521029 switch (evt ) {
8531030 case MODEM_CELLULAR_EVENT_TIMEOUT :
8541031 modem_chat_attach (& data -> chat , data -> dlci1_pipe );
1032+
1033+ // NEW: Apply profile settings (APN, auth) before dialing
1034+ modem_cellular_apply_profile (data );
1035+
8551036 modem_chat_run_script_async (& data -> chat , config -> dial_chat_script );
8561037 break ;
8571038
@@ -1462,6 +1643,7 @@ const static struct cellular_driver_api modem_cellular_api = {
14621643 .get_signal = modem_cellular_get_signal ,
14631644 .get_modem_info = modem_cellular_get_modem_info ,
14641645 .get_registration_status = modem_cellular_get_registration_status ,
1646+ //.get_gnss = modem_cellular_get_gnss,
14651647};
14661648
14671649#ifdef CONFIG_PM_DEVICE
@@ -1587,6 +1769,48 @@ static int modem_cellular_init(const struct device *dev)
15871769 return 0 ;
15881770}
15891771
1772+ /* static void modem_cellular_apply_profile(struct modem_cellular_data *data)
1773+ {
1774+ ARG_UNUSED(data);
1775+
1776+ if (data->s_profile.use_auth && data->s_profile.user[0] && data->s_profile.pass[0]) {
1777+ modem_cellular_ppp_set_auth(data->s_profile.user, data->s_profile.pass);
1778+ }
1779+
1780+ if (!data->s_profile.apn[0]) {
1781+ return;
1782+ }
1783+
1784+ char cmd[64];
1785+ snprintk(cmd, sizeof(cmd), "AT+CGDCONT=1,\"IP\",\"%s\"", data->s_profile.apn); */
1786+
1787+
1788+
1789+ /* Enviar y consumir OK, pero ignorar errores para no bloquear el dial */ /* Usa tu infraestructura
1790+ de chat; normalmente
1791+ tienes 'data->chat' y
1792+ 'ok_match' */
1793+ // #if defined(MODEM_CHAT_HAS_SEND_SYNC)
1794+ // (void)modem_chat_send_sync(&data->chat, cmd, &ok_match, K_SECONDS(2), data);
1795+ // #else
1796+ // MODEM_CHAT_SCRIPT_CMDS_DEFINE(_cgdcont_script,
1797+ // MODEM_CHAT_SCRIPT_CMD_RESP_DYNAMIC(cmd, ok_match),
1798+ // MODEM_CHAT_SCRIPT_CMD_RESP("", ok_match));
1799+
1800+ /* Copiar la string dinámica al primer comando */
1801+ // ((struct modem_chat_script_chat *)&_cgdcont_script_cmds[0]) = cmd;
1802+
1803+ /* static const struct modem_chat_script _cgdcont_script = {
1804+ .script_cmds = _cgdcont_script_cmds,
1805+ .abort_matches = NULL,
1806+ .callback = NULL,
1807+ .timeout = 3,
1808+ };
1809+ */
1810+ //(void)modem_chat_script_run(&data->chat, &_cgdcont_script);
1811+ // #endif
1812+ // }
1813+
15901814/*
15911815 * Every modem uses two custom scripts to initialize the modem and dial out.
15921816 *
@@ -1601,6 +1825,53 @@ static int modem_cellular_init(const struct device *dev)
16011825 * dial out and put the DLCI channel into data mode.
16021826 */
16031827
1828+
1829+
1830+
1831+
1832+ /* Aplica el perfil PDP/APN de runtime antes del dial.
1833+ * - Si hay auth y backend lo soporta, la pasa via hook débil.
1834+ * - Envía AT+CGDCONT con APN dinámico y consume OK.
1835+ * - Errores se ignoran a propósito para no bloquear el dial.
1836+ */
1837+ static void modem_cellular_apply_profile (struct modem_cellular_data * data )
1838+ {
1839+ ARG_UNUSED (data );
1840+
1841+ /* 1) PPP auth (opcional) */
1842+ if (data -> s_profile .use_auth && data -> s_profile .user [0 ] && data -> s_profile .pass [0 ]) {
1843+ modem_cellular_ppp_set_auth (data -> s_profile .user , data -> s_profile .pass );
1844+ }
1845+
1846+ /* 2) APN dinámico */
1847+ if (!data -> s_profile .apn [0 ]) {
1848+ /* Sin APN => no tocamos CGDCONT */
1849+ return ;
1850+ }
1851+
1852+ /* Construir AT+CGDCONT=1,"IP","<apn>" */
1853+ char cmd [128 ];
1854+ snprintk (cmd , sizeof (cmd ), "AT+CGDCONT=1,\"IP\",\"%s\"" , data -> s_profile .apn );
1855+
1856+ /* 3) Script de un paso: enviar cmd y esperar OK (Zephyr 2.7 API) */
1857+ struct modem_chat_script_chat step ;
1858+
1859+ modem_chat_script_chat_init (& step );
1860+ modem_chat_script_chat_set_request (& step , cmd );
1861+ modem_chat_script_chat_set_response_matches (& step , & ok_match , 1 );
1862+
1863+ struct modem_chat_script script ;
1864+
1865+ modem_chat_script_init (& script );
1866+ modem_chat_script_set_script_chats (& script , & step , 1 );
1867+ /* opcional: timeout corto para no demorar el dial si el módem no responde
1868+ // modem_chat_script_set_timeout(&script, 3);
1869+ */
1870+
1871+ /* 4) Ejecutar e ignorar retorno para no bloquear el flujo */
1872+ (void )modem_chat_run_script (& data -> chat , & script );
1873+ }
1874+
16041875#if DT_HAS_COMPAT_STATUS_OKAY (quectel_bg95 )
16051876MODEM_CHAT_SCRIPT_CMDS_DEFINE (quectel_bg95_init_chat_script_cmds ,
16061877 MODEM_CHAT_SCRIPT_CMD_RESP ("ATE0" , ok_match ),
@@ -1652,7 +1923,8 @@ MODEM_CHAT_SCRIPT_DEFINE(quectel_bg95_periodic_chat_script,
16521923
16531924#if DT_HAS_COMPAT_STATUS_OKAY (quectel_eg25_g )
16541925MODEM_CHAT_SCRIPT_CMDS_DEFINE (
1655- quectel_eg25_g_init_chat_script_cmds , MODEM_CHAT_SCRIPT_CMD_RESP ("ATE0" , ok_match ),
1926+ quectel_eg25_g_init_chat_script_cmds ,
1927+ MODEM_CHAT_SCRIPT_CMD_RESP ("ATE0" , ok_match ),
16561928 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CFUN=4" , ok_match ),
16571929 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CMEE=1" , ok_match ),
16581930 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CREG=1" , ok_match ),
@@ -1671,17 +1943,25 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE(
16711943 MODEM_CHAT_SCRIPT_CMD_RESP ("" , ok_match ),
16721944 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CIMI" , cimi_match ),
16731945 MODEM_CHAT_SCRIPT_CMD_RESP ("" , ok_match ),
1946+
1947+ // GNSS on cmd
1948+ MODEM_CHAT_SCRIPT_CMD_RESP ("AT+QGPS=1" , ok_match ),
1949+
16741950 MODEM_CHAT_SCRIPT_CMD_RESP_NONE ("AT+CMUX=0,0,5,127,10,3,30,10,2" , 100 ));
16751951
1952+
16761953MODEM_CHAT_SCRIPT_DEFINE (quectel_eg25_g_init_chat_script , quectel_eg25_g_init_chat_script_cmds ,
16771954 abort_matches , modem_cellular_chat_callback_handler , 10 );
16781955
16791956MODEM_CHAT_SCRIPT_CMDS_DEFINE (quectel_eg25_g_dial_chat_script_cmds ,
16801957 MODEM_CHAT_SCRIPT_CMD_RESP_MULT ("AT+CGACT=0,1" , allow_match ),
1681- MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CGDCONT=1,\"IP\","
1682- "\"" CONFIG_MODEM_CELLULAR_APN "\"" ,
1683- ok_match ),
1958+ // MODEM_CHAT_SCRIPT_CMD_RESP("AT+CGDCONT=1,\"IP\","
1959+ // "\""CONFIG_MODEM_CELLULAR_APN"\"",
1960+ // ok_match),
16841961 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CFUN=1" , ok_match ),
1962+
1963+ MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CGDCONT=1" , ok_match ),
1964+
16851965 MODEM_CHAT_SCRIPT_CMD_RESP_NONE ("ATD*99***1#" , 0 ), );
16861966
16871967MODEM_CHAT_SCRIPT_DEFINE (quectel_eg25_g_dial_chat_script , quectel_eg25_g_dial_chat_script_cmds ,
@@ -1691,11 +1971,21 @@ MODEM_CHAT_SCRIPT_CMDS_DEFINE(quectel_eg25_g_periodic_chat_script_cmds,
16911971 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CREG?" , ok_match ),
16921972 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CEREG?" , ok_match ),
16931973 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CGREG?" , ok_match ),
1974+
1975+ // GNSS on cmd
1976+ /* GNSS: pide localización y decodifica la línea +QGPSLOC: ... */
1977+ MODEM_CHAT_SCRIPT_CMD_RESP ("AT+QGPSLOC?" , qgpsloc_match ),
1978+
1979+ MODEM_CHAT_SCRIPT_CMD_RESP ("" , ok_match ),
16941980 MODEM_CHAT_SCRIPT_CMD_RESP ("AT+CSQ" , csq_match ));
1981+
16951982
16961983MODEM_CHAT_SCRIPT_DEFINE (quectel_eg25_g_periodic_chat_script ,
16971984 quectel_eg25_g_periodic_chat_script_cmds , abort_matches ,
16981985 modem_cellular_chat_callback_handler , 4 );
1986+
1987+
1988+
16991989#endif
17001990
17011991#if DT_HAS_COMPAT_STATUS_OKAY (zephyr_gsm_ppp )
0 commit comments