6868#include "modmachine.h"
6969#include "mpirq.h"
7070
71+ #include "str_utils.h"
72+
7173/******************************************************************************
7274 DEFINE TYPES
7375 ******************************************************************************/
8385
8486#define SQNS_SW_FULL_BAND_SUPPORT 41000
8587#define SQNS_SW_5_8_BAND_SUPPORT 39000
88+
89+ // PSM Power saving mode
90+ // PERIOD, aka Requested Periodic TAU (T3412), in GPRS Timer 3 format
91+ #define PSM_PERIOD_2S 0b011
92+ #define PSM_PERIOD_30S 0b100
93+ #define PSM_PERIOD_1M 0b101
94+ #define PSM_PERIOD_10M 0b000
95+ #define PSM_PERIOD_1H 0b001
96+ #define PSM_PERIOD_10H 0b010
97+ #define PSM_PERIOD_320H 0b110
98+ #define PSM_PERIOD_DISABLED 0b111
99+ // ACTIVE, aka Requested Active Time (T3324), in GPRS Timer 2 format
100+ #define PSM_ACTIVE_2S 0b000
101+ #define PSM_ACTIVE_1M 0b001
102+ #define PSM_ACTIVE_6M 0b010
103+ #define PSM_ACTIVE_DISABLED 0b111
104+
86105/******************************************************************************
87106 DECLARE PRIVATE DATA
88107 ******************************************************************************/
@@ -408,7 +427,8 @@ static void TASK_LTE_UPGRADE(void *pvParameters){
408427// Micro Python bindings; LTE class
409428
410429static mp_obj_t lte_init_helper (lte_obj_t * self , const mp_arg_val_t * args ) {
411- char at_cmd [LTE_AT_CMD_SIZE_MAX - 4 ];
430+ const size_t at_cmd_len = LTE_AT_CMD_SIZE_MAX - 4 ;
431+ char at_cmd [at_cmd_len ];
412432 lte_modem_conn_state_t modem_state ;
413433
414434 if (lte_obj .init ) {
@@ -426,24 +446,23 @@ static mp_obj_t lte_init_helper(lte_obj_t *self, const mp_arg_val_t *args) {
426446 MP_THREAD_GIL_ENTER ();
427447 if (E_LTE_MODEM_DISCONNECTED == lteppp_modem_state ()) {
428448 xSemaphoreGive (xLTE_modem_Conn_Sem );
429- nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Couldn't connect to Modem! " ));
449+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Couldn't connect to Modem (modem_state=disconnected) " ));
430450 }
431451 break ;
432452 case E_LTE_MODEM_CONNECTING :
433453 // Block till modem is connected
434454 xSemaphoreTake (xLTE_modem_Conn_Sem , portMAX_DELAY );
435455 if (E_LTE_MODEM_DISCONNECTED == lteppp_modem_state ()) {
436456 xSemaphoreGive (xLTE_modem_Conn_Sem );
437- nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Couldn't connect to Modem! " ));
457+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Couldn't connect to Modem (modem_state=connecting) " ));
438458 }
439459 break ;
440460 case E_LTE_MODEM_CONNECTED :
441461 //continue
442462 break ;
443463 default :
444- nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Couldn't connect to Modem! " ));
464+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Couldn't connect to Modem (modem_state=default) " ));
445465 break ;
446-
447466 }
448467 lte_obj .cid = args [1 ].u_int ;
449468
@@ -490,16 +509,102 @@ static mp_obj_t lte_init_helper(lte_obj_t *self, const mp_arg_val_t *args) {
490509 lteppp_set_state (E_LTE_IDLE );
491510 mod_network_register_nic (& lte_obj );
492511 lte_obj .init = true;
512+
513+ // configure PSM
514+ u8_t psm_period_value = args [4 ].u_int ;
515+ u8_t psm_period_unit = args [5 ].u_int ;
516+ u8_t psm_active_value = args [6 ].u_int ;
517+ u8_t psm_active_unit = args [7 ].u_int ;
518+ if ( psm_period_unit != PSM_PERIOD_DISABLED && psm_active_unit != PSM_ACTIVE_DISABLED ) {
519+ u8_t psm_period = ( psm_period_unit << 5 ) | psm_period_value ;
520+ u8_t psm_active = ( psm_active_unit << 5 ) | psm_active_value ;
521+ char p [9 ];
522+ char a [9 ];
523+ sprint_binary_u8 (p , psm_period );
524+ sprint_binary_u8 (a , psm_active );
525+ snprintf (at_cmd , at_cmd_len , "AT+CPSMS=1,,,\"%s\",\"%s\"" , p , a );
526+ lte_push_at_command (at_cmd , LTE_RX_TIMEOUT_MAX_MS );
527+ }
528+
493529 xSemaphoreGive (xLTE_modem_Conn_Sem );
494530 return mp_const_none ;
495531}
496532
533+ STATIC mp_obj_t lte_psm (mp_uint_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
534+ lte_check_init ();
535+ lte_check_inppp ();
536+ mp_obj_t tuple [5 ];
537+ static const qstr psm_info_fields [] = {
538+ MP_QSTR_enabled ,
539+ MP_QSTR_period_value ,
540+ MP_QSTR_period_unit ,
541+ MP_QSTR_active_value ,
542+ MP_QSTR_active_unit ,
543+ };
544+
545+ lte_push_at_command ("AT+CPSMS?" , LTE_RX_TIMEOUT_MAX_MS );
546+ const char * resp = modlte_rsp .data ;
547+ char * pos ;
548+ if ( ( pos = strstr (resp , "+CPSMS: " ) ) ) {
549+ // decode the resp:
550+ // +CPSMS: <mode>,[<Requested_Periodic-RAU>],[<Requested_GPRS-READYtimer>],[<Requested_Periodic-TAU>],[<Requested_Active-Time>]
551+
552+ // go to <mode>
553+ pos += strlen_const ("+CPSMS: " );
554+ tuple [0 ] = mp_obj_new_bool (* pos == '1' );
555+
556+ // go to <Requested_Periodic-RAU>
557+ pos += strlen_const ("1," );
558+
559+ // find <Requested_GPRS-READYtimer>
560+ pos = strstr (pos , "," );
561+ pos ++ ;
562+
563+ // find <Requested_Periodic-TAU>
564+ pos = strstr (pos , "," );
565+ pos ++ ; // ,
566+ pos ++ ; // "
567+
568+ // get three digit TAU unit
569+ char * oldpos = pos ;
570+ tuple [2 ] = mp_obj_new_int_from_str_len ( (const char * * ) & pos , 3 , false, 2 );
571+ assert ( pos == oldpos + 3 ); // mp_obj_new_int_from_str_len is supposed to consume exactly 3 characters
572+
573+ // get five digit TAU value
574+ tuple [1 ] = mp_obj_new_int_from_str_len ( (const char * * ) & pos , 5 , false, 2 );
575+
576+ // find <Requested_Active-Time>
577+ pos = strstr (pos , "," );
578+ pos ++ ; // ,
579+ pos ++ ; // "
580+
581+ // get three digit ActiveTime unit
582+ oldpos = pos ;
583+ tuple [4 ] = mp_obj_new_int_from_str_len ( (const char * * ) & pos , 3 , false, 2 );
584+ assert ( pos == oldpos + 3 ); // mp_obj_new_int_from_str_len is supposed to consume exactly 3 characters
585+
586+ // get five digit ActiveTime value
587+ tuple [3 ] = mp_obj_new_int_from_str_len ( (const char * * ) & pos , 5 , false, 2 );
588+
589+ } else {
590+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Failed to read PSM setting" ));
591+ }
592+
593+ return mp_obj_new_attrtuple (psm_info_fields , 5 , tuple );
594+ }
595+ STATIC MP_DEFINE_CONST_FUN_OBJ_KW (lte_psm_obj , 1 , lte_psm );
596+
597+
497598static const mp_arg_t lte_init_args [] = {
498599 { MP_QSTR_id , MP_ARG_INT , {.u_int = 0 } },
499600 { MP_QSTR_carrier , MP_ARG_KW_ONLY | MP_ARG_OBJ , {.u_obj = mp_const_none } },
500601 { MP_QSTR_cid , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 1 } },
501602 { MP_QSTR_legacyattach , MP_ARG_KW_ONLY | MP_ARG_BOOL , {.u_bool = true} },
502603 { MP_QSTR_debug , MP_ARG_KW_ONLY | MP_ARG_BOOL , {.u_bool = false} },
604+ { MP_QSTR_psm_period_value , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
605+ { MP_QSTR_psm_period_unit , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = PSM_PERIOD_DISABLED } },
606+ { MP_QSTR_psm_active_value , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 0 } },
607+ { MP_QSTR_psm_active_unit , MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = PSM_ACTIVE_DISABLED } },
503608};
504609
505610static mp_obj_t lte_make_new (const mp_obj_type_t * type , mp_uint_t n_args , mp_uint_t n_kw , const mp_obj_t * all_args ) {
@@ -522,6 +627,30 @@ static mp_obj_t lte_make_new(const mp_obj_type_t *type, mp_uint_t n_args, mp_uin
522627 nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , mpexception_os_resource_not_avaliable ));
523628 }
524629 }
630+
631+ // check psm args
632+ u8_t psm_period_value = args [5 ].u_int ;
633+ u8_t psm_period_unit = args [6 ].u_int ;
634+ u8_t psm_active_value = args [7 ].u_int ;
635+ u8_t psm_active_unit = args [8 ].u_int ;
636+ if (psm_period_unit > 7 )
637+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Invalid psm_period_unit" ));
638+ if (psm_period_value > 31 )
639+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Invalid psm_period_value" ));
640+ switch (psm_active_unit ){
641+ case PSM_ACTIVE_2S :
642+ case PSM_ACTIVE_1M :
643+ case PSM_ACTIVE_6M :
644+ case PSM_ACTIVE_DISABLED :
645+ // ok, nothing to do
646+ break ;
647+ default :
648+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Invalid psm_active_unit" ));
649+ break ;
650+ }
651+ if (psm_active_value > 31 )
652+ nlr_raise (mp_obj_new_exception_msg (& mp_type_OSError , "Invalid psm_active_value" ));
653+
525654 // start the peripheral
526655 lte_init_helper (self , & args [1 ]);
527656 return (mp_obj_t )self ;
@@ -531,8 +660,7 @@ STATIC mp_obj_t lte_init(mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *k
531660 // parse args
532661 mp_arg_val_t args [MP_ARRAY_SIZE (lte_init_args ) - 1 ];
533662 mp_arg_parse_all (n_args - 1 , pos_args + 1 , kw_args , MP_ARRAY_SIZE (args ), & lte_init_args [1 ], args );
534- if (args [3 ].u_bool )
535- lte_debug = true;
663+ lte_debug = args [3 ].u_bool ;
536664 return lte_init_helper (pos_args [0 ], args );
537665}
538666STATIC MP_DEFINE_CONST_FUN_OBJ_KW (lte_init_obj , 1 , lte_init );
@@ -1407,6 +1535,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(lte_events_obj, lte_events);
14071535STATIC const mp_map_elem_t lte_locals_dict_table [] = {
14081536 { MP_OBJ_NEW_QSTR (MP_QSTR_init ), (mp_obj_t )& lte_init_obj },
14091537 { MP_OBJ_NEW_QSTR (MP_QSTR_deinit ), (mp_obj_t )& lte_deinit_obj },
1538+ { MP_OBJ_NEW_QSTR (MP_QSTR_psm ), (mp_obj_t )& lte_psm_obj },
14101539 { MP_OBJ_NEW_QSTR (MP_QSTR_attach ), (mp_obj_t )& lte_attach_obj },
14111540 { MP_OBJ_NEW_QSTR (MP_QSTR_dettach ), (mp_obj_t )& lte_detach_obj },
14121541 { MP_OBJ_NEW_QSTR (MP_QSTR_detach ), (mp_obj_t )& lte_detach_obj }, /* backward compatibility for dettach method FIXME */
@@ -1435,6 +1564,20 @@ STATIC const mp_map_elem_t lte_locals_dict_table[] = {
14351564 { MP_OBJ_NEW_QSTR (MP_QSTR_IP ), MP_OBJ_NEW_QSTR (MP_QSTR_IP ) },
14361565 { MP_OBJ_NEW_QSTR (MP_QSTR_IPV4V6 ), MP_OBJ_NEW_QSTR (MP_QSTR_IPV4V6 ) },
14371566 { MP_OBJ_NEW_QSTR (MP_QSTR_EVENT_COVERAGE_LOSS ), MP_OBJ_NEW_SMALL_INT (LTE_TRIGGER_SIG_LOST ) },
1567+ // PSM Power Saving Mode
1568+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_2S ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_2S ) },
1569+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_30S ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_30S ) },
1570+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_1M ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_1M ) },
1571+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_10M ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_10M ) },
1572+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_1H ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_1H ) },
1573+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_10H ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_10H ) },
1574+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_320H ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_320H ) },
1575+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_PERIOD_DISABLED ), MP_OBJ_NEW_SMALL_INT (PSM_PERIOD_DISABLED ) },
1576+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_ACTIVE_2S ), MP_OBJ_NEW_SMALL_INT (PSM_ACTIVE_2S ) },
1577+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_ACTIVE_1M ), MP_OBJ_NEW_SMALL_INT (PSM_ACTIVE_1M ) },
1578+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_ACTIVE_6M ), MP_OBJ_NEW_SMALL_INT (PSM_ACTIVE_6M ) },
1579+ { MP_OBJ_NEW_QSTR (MP_QSTR_PSM_ACTIVE_DISABLED ), MP_OBJ_NEW_SMALL_INT (PSM_ACTIVE_DISABLED ) },
1580+
14381581};
14391582STATIC MP_DEFINE_CONST_DICT (lte_locals_dict , lte_locals_dict_table );
14401583
0 commit comments