@@ -101,6 +101,26 @@ const auto QUECTEL_NCP_MAX_MUXER_FRAME_SIZE = 1509;
101101const auto QUECTEL_NCP_KEEPALIVE_PERIOD = 5000 ; // milliseconds
102102const auto QUECTEL_NCP_KEEPALIVE_MAX_MISSED = 5 ;
103103
104+ // Bandmask same for BG95-M1 ~ BG95-M6 & BG95-MF
105+ // Note: BG95-M1 does not support NB mode
106+ // BG95-M4 not supported (bands are different as well)
107+ const uint64_t QUECTEL_NCP_BANDMASK_CATM1_1_64_BG95 = 0xF0E189F ; // Bands 1,2,3,4,5,8,12,13,18,19,20,25,26,27,28 [all default enabled]
108+ const uint64_t QUECTEL_NCP_BANDMASK_CATM1_65_128_BG95 = 0x100002 ; // Band 66,85 enabled
109+ const uint64_t QUECTEL_NCP_BANDMASK_CATNB_1_64_BG95 = 0x90E189F ; // Bands 1,2,3,4,5,8,12,13,18,19,20,25,26,27,28 [all default enabled]
110+ const uint64_t QUECTEL_NCP_BANDMASK_CATNB_65_128_BG95 = 0x100002 ; // Band 66,85 enabled
111+
112+ // BG96-MC
113+ const uint64_t QUECTEL_NCP_BANDMASK_CATM1_1_64_BG96_MC = 0xF8E189F ; // Bands 1,2,3,4,5,8,12,13,18,19,20,25,26,28,39
114+ const uint64_t QUECTEL_NCP_BANDMASK_CATNB_1_64_BG96_MC = 0xF8E189F ; // Bands 1,2,3,4,5,8,12,19,19,20,25,26,28,39
115+
116+ // EG91-E/EX (LTE Cat-1)
117+ const uint64_t QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_E_EX = 0x1010008D ; // Bands 1,3,7,8,20,28
118+ // const uint64_t QUECTEL_NCP_BANDMASK_CAT1_65_128_EG91_E_EX = 0x0; // No bands in 65-128 range
119+
120+ // EG91-NAX (LTE Cat-1)
121+ const uint64_t QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_NA_NAX = 0x1836 ; // Bands 2,4,5,12,13
122+ // const uint64_t QUECTEL_NCP_BANDMASK_CAT1_65_128_EG91_NA_NAX = 0x0; // No bands in 65-128 range
123+
104124// FIXME: for now using a very large buffer
105125const auto QUECTEL_NCP_AT_CHANNEL_RX_BUFFER_SIZE = 4096 ;
106126const auto QUECTEL_NCP_PPP_CHANNEL_RX_BUFFER_SIZE = 256 ;
@@ -1022,6 +1042,109 @@ int QuectelNcpClient::checkNetConfForImsi() {
10221042 return SYSTEM_ERROR_TIMEOUT;
10231043}
10241044
1045+ int QuectelNcpClient::setupBands () {
1046+ uint64_t envPreferredBands[2 ] = {};
1047+ uint64_t envForbiddenBands[2 ] = {};
1048+ getEnvBands (" PARTICLE_CELLULAR_PREFERRED_BANDS" , envPreferredBands);
1049+ getEnvBands (" PARTICLE_CELLULAR_FORBIDDEN_BANDS" , envForbiddenBands);
1050+ bool useEnvBands = (envPreferredBands[0 ] || envPreferredBands[1 ] || envForbiddenBands[0 ] || envForbiddenBands[1 ]);
1051+
1052+ // At least one set of bands is set, ensure the rest have default values if not specified
1053+ if (useEnvBands) {
1054+ if (envPreferredBands[0 ]) {
1055+ if (isQuecBG95xDevice ()) {
1056+ envPreferredBands[0 ] = envPreferredBands[0 ] & QUECTEL_NCP_BANDMASK_CATM1_1_64_BG95;
1057+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_BG96) {
1058+ envPreferredBands[0 ] = envPreferredBands[0 ] & QUECTEL_NCP_BANDMASK_CATM1_1_64_BG96_MC;
1059+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_NAX || ncpId () == PLATFORM_NCP_QUECTEL_EG91_NA) {
1060+ envPreferredBands[0 ] = envPreferredBands[0 ] & QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_NA_NAX;
1061+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_E || ncpId () == PLATFORM_NCP_QUECTEL_EG91_EX) {
1062+ envPreferredBands[0 ] = envPreferredBands[0 ] & QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_E_EX;
1063+ }
1064+ } else {
1065+ if (isQuecBG95xDevice ()) {
1066+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CATM1_1_64_BG95;
1067+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_BG96) {
1068+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CATM1_1_64_BG96_MC;
1069+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_NAX || ncpId () == PLATFORM_NCP_QUECTEL_EG91_NA) {
1070+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_NA_NAX;
1071+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_E || ncpId () == PLATFORM_NCP_QUECTEL_EG91_EX) {
1072+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_E_EX;
1073+ }
1074+ }
1075+ if (envPreferredBands[1 ]) {
1076+ if (isQuecBG95xDevice ()) {
1077+ envPreferredBands[1 ] = envPreferredBands[1 ] & QUECTEL_NCP_BANDMASK_CATM1_65_128_BG95;
1078+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_BG96) {
1079+ envPreferredBands[1 ] = 0 ;
1080+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_NAX || ncpId () == PLATFORM_NCP_QUECTEL_EG91_NA) {
1081+ envPreferredBands[1 ] = 0 ;
1082+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_E || ncpId () == PLATFORM_NCP_QUECTEL_EG91_EX) {
1083+ envPreferredBands[1 ] = 0 ;
1084+ }
1085+ } else {
1086+ if (isQuecBG95xDevice ()) {
1087+ envPreferredBands[1 ] = QUECTEL_NCP_BANDMASK_CATM1_65_128_BG95;
1088+ }
1089+ // These are effectively zero already, save some bytes
1090+ // else if (ncpId() == PLATFORM_NCP_QUECTEL_BG96) {
1091+ // envPreferredBands[1] = 0;
1092+ // } else if (ncpId() == PLATFORM_NCP_QUECTEL_EG91_NAX || ncpId() == PLATFORM_NCP_QUECTEL_EG91_NA) {
1093+ // envPreferredBands[1] = 0;
1094+ // } else if (ncpId() == PLATFORM_NCP_QUECTEL_EG91_E || ncpId() == PLATFORM_NCP_QUECTEL_EG91_EX) {
1095+ // envPreferredBands[1] = 0;
1096+ // }
1097+ }
1098+ envPreferredBands[0 ] = envPreferredBands[0 ] & ~envForbiddenBands[0 ];
1099+ envPreferredBands[1 ] = envPreferredBands[1 ] & ~envForbiddenBands[1 ];
1100+ } else {
1101+ // Simplify and reuse these variables with default bandmask if env var not set
1102+ if (isQuecBG95xDevice ()) {
1103+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CATM1_1_64_BG95;
1104+ envPreferredBands[1 ] = QUECTEL_NCP_BANDMASK_CATM1_65_128_BG95;
1105+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_BG96) {
1106+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CATM1_1_64_BG96_MC;
1107+ // envPreferredBands[1] = 0; // This is effectively zero already, save some bytes
1108+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_NAX || ncpId () == PLATFORM_NCP_QUECTEL_EG91_NA) {
1109+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_NA_NAX;
1110+ // envPreferredBands[1] = 0; // This is effectively zero already, save some bytes
1111+ } else if (ncpId () == PLATFORM_NCP_QUECTEL_EG91_E || ncpId () == PLATFORM_NCP_QUECTEL_EG91_EX) {
1112+ envPreferredBands[0 ] = QUECTEL_NCP_BANDMASK_CAT1_1_64_EG91_E_EX;
1113+ // envPreferredBands[1] = 0; // This is effectively zero already, save some bytes
1114+ }
1115+ }
1116+ // LOG(INFO, "ENV BANDS: [65-128]=0x%llx, [1-64]=0x%016llx", envPreferredBands[1], envPreferredBands[0]);
1117+
1118+ // Log bandmask, and change bandmask if necessary
1119+ auto respBand = parser_.sendCommand (" AT+QCFG=\" band\" " );
1120+ uint64_t uint64LTEbandsCurrent[2 ] = {};
1121+ char qbandGSM_Str[4 +1 ] = {};
1122+ char qbandLTE_Str[32 +1 ] = {};
1123+ char qbandCatNB_Str[32 +1 ] = {};
1124+ // BG95_M5: +QCFG: "band",0xf,0x100002000000000f0e189f,0x10004200000000090e189f <GSM>,<CAT-M1-1-128>,<CAT-NB-1-128>
1125+ auto retBand = CHECK_PARSER (respBand.scanf (" +QCFG: \" band\" ,0x%4[^,],0x%32[^,],0x%32[^,]" , qbandGSM_Str, qbandLTE_Str, qbandCatNB_Str));
1126+ LOG (INFO, " %s,%s,%s" , qbandGSM_Str, qbandLTE_Str, qbandCatNB_Str);
1127+ CHECK_PARSER_OK (respBand.readResult ());
1128+
1129+ if (retBand == 3 ) {
1130+ if (isQuecBG95xDevice ()) {
1131+ hexString128toUint64Array (qbandLTE_Str, uint64LTEbandsCurrent);
1132+ // LOG(INFO, "[65-128]=0x%llx, [1-64]=0x%016llx", uint64LTEbandsCurrent[1], uint64LTEbandsCurrent[0]);
1133+
1134+ if (uint64LTEbandsCurrent[0 ] != envPreferredBands[0 ] ||
1135+ uint64LTEbandsCurrent[1 ] != envPreferredBands[1 ]) {
1136+ sprintf (qbandLTE_Str, " %llx%016llx" , envPreferredBands[1 ], envPreferredBands[0 ]);
1137+ // Apply band changes immediately, no reboot required. Modem may need to disconnect to apply the change though.
1138+ CHECK_PARSER_OK (setModuleFunctionality (CellularFunctionality::AIRPLANE, true /* check */ ));
1139+ CHECK_PARSER_OK (parser_.execCommand (" AT+QCFG=\" band\" ,%s,%s,%s,1" , qbandGSM_Str, qbandLTE_Str, qbandCatNB_Str));
1140+ CHECK_PARSER_OK (setModuleFunctionality (CellularFunctionality::FULL, false /* check */ ));
1141+ }
1142+ }
1143+ }
1144+
1145+ return SYSTEM_ERROR_NONE;
1146+ }
1147+
10251148int QuectelNcpClient::selectSimCard () {
10261149 // Using numeric CME ERROR codes
10271150 // int r = CHECK_PARSER(parser_.execCommand("AT+CMEE=2"));
@@ -1148,6 +1271,7 @@ int QuectelNcpClient::initReady(ModemState state) {
11481271 setPolicymanServiceMode (CellularPolicymanServiceMode::FULL_SERVICE, true /* check */ );
11491272 }
11501273
1274+ auto runtimeBaudrate = QUECTEL_NCP_DEFAULT_SERIAL_BAUDRATE;
11511275 if (state != ModemState::MuxerAtChannel) {
11521276 // Cold Boot only, Warm Boot will skip the following block...
11531277
@@ -1167,7 +1291,7 @@ int QuectelNcpClient::initReady(ModemState state) {
11671291 CHECK_PARSER_OK (parser_.execCommand (" AT+IFC=2,2" ));
11681292 CHECK (waitAtResponse (10000 ));
11691293 }
1170- auto runtimeBaudrate = getRuntimeBaudrate ();
1294+ runtimeBaudrate = getRuntimeBaudrate ();
11711295 CHECK (changeBaudRate (runtimeBaudrate));
11721296 // Check that the modem is responsive at the new baudrate
11731297 skipAll (serial_.get (), 1000 );
@@ -1196,7 +1320,18 @@ int QuectelNcpClient::initReady(ModemState state) {
11961320 if (isQuecCat1Device ()) {
11971321 CHECK_PARSER (parser_.execCommand (" AT+QDSIM=0" ));
11981322 }
1323+ }
11991324
1325+ if (isQuecBG95xDevice () ||
1326+ ncpId () == PLATFORM_NCP_QUECTEL_BG96 ||
1327+ ncpId () == PLATFORM_NCP_QUECTEL_EG91_NAX ||
1328+ ncpId () == PLATFORM_NCP_QUECTEL_EG91_NA ||
1329+ ncpId () == PLATFORM_NCP_QUECTEL_EG91_E ||
1330+ ncpId () == PLATFORM_NCP_QUECTEL_EG91_EX) {
1331+ CHECK (setupBands ());
1332+ }
1333+
1334+ if (state != ModemState::MuxerAtChannel) {
12001335 // Send AT+CMUX and initialize multiplexer
12011336 int portspeed;
12021337 switch (runtimeBaudrate) {
@@ -1211,9 +1346,10 @@ int QuectelNcpClient::initReady(ModemState state) {
12111346 default :
12121347 return SYSTEM_ERROR_INVALID_ARGUMENT;
12131348 }
1349+
12141350 // XXX: AT+CMUX=? says portspeed value range is (1-7), but 8 is required for it to work on BG95-M5
12151351 r = CHECK_PARSER (parser_.execCommand (" AT+CMUX=0,0,%d,%u,,,,," , portspeed, QUECTEL_NCP_MAX_MUXER_FRAME_SIZE));
1216- CHECK_TRUE (r == AtResponse::OK, SYSTEM_ERROR_UNKNOWN );
1352+ CHECK_TRUE (r == AtResponse::OK, SYSTEM_ERROR_AT_NOT_OK );
12171353
12181354 // Initialize muxer
12191355 CHECK (initMuxer ());
@@ -1446,7 +1582,7 @@ int QuectelNcpClient::setPolicymanServiceMode(CellularPolicymanServiceMode mode,
14461582int QuectelNcpClient::configureApn (const CellularNetworkConfig& conf) {
14471583 // IMPORTANT: Set modem full functionality!
14481584 // Otherwise we won't be able to query ICCID/IMSI
1449- CHECK_PARSER_OK (parser_. execCommand ( " AT+CFUN=1,0 " ));
1585+ CHECK_PARSER_OK (setModuleFunctionality (CellularFunctionality::FULL, true /* check */ ));
14501586
14511587 netConf_ = conf;
14521588 if (!netConf_.isValid ()) {
@@ -1522,7 +1658,7 @@ int QuectelNcpClient::configureApn(const CellularNetworkConfig& conf) {
15221658 // setApn, cgdcontIpVal, cgdcontApnVal, cgdcontFmt, netConf_.hasApn(),
15231659 // netConf_.apn(), strncmp(cgdcontApnVal, netConf_.hasApn() ? netConf_.apn() : "", sizeof(cgdcontApnVal)) != 0);
15241660 if (setApn) {
1525- CHECK_PARSER_OK (parser_. execCommand ( " AT+CFUN=4,0 " )); // airplane mode
1661+ CHECK_PARSER_OK (setModuleFunctionality (CellularFunctionality::AIRPLANE, true /* check */ ));
15261662 // XXX: we've seen CGDCONT fail on cold boot, retrying here a few times
15271663 for (int i = 0 ; i < CGDCONT_ATTEMPTS; i++) {
15281664 // FIXME: for now IPv4 context only
@@ -1545,9 +1681,10 @@ int QuectelNcpClient::configureApn(const CellularNetworkConfig& conf) {
15451681int QuectelNcpClient::registerNet () {
15461682 int r = 0 ;
15471683 // Set modem full functionality
1548- r = CHECK_PARSER (parser_. execCommand ( " AT+CFUN=1,0 " ));
1684+ r = CHECK_PARSER (setModuleFunctionality (CellularFunctionality::FULL, true /* check */ ));
15491685 CHECK_TRUE (r == AtResponse::OK, SYSTEM_ERROR_UNKNOWN);
15501686
1687+
15511688 resetRegistrationState ();
15521689
15531690 if (isQuecCat1Device () || ncpId () == PLATFORM_NCP_QUECTEL_BG95_M5) {
0 commit comments