walter-iot.cpp
1 #include <Arduino.h> 2 #include <WalterModem.h> 3 4 //------------------------------------------ 5 // Configuration 6 //------------------------------------------ 7 #define CELLULAR_APN "m2minternet.apn" 8 9 //------------------------------------------ 10 // Global state 11 //------------------------------------------ 12 WalterModem modem; 13 WalterModemRsp rsp = {}; 14 volatile bool is_gnss_fix_received = false; 15 WMGNSSFixEvent last_fix = {}; 16 17 //------------------------------------------ 18 // GNSS event handler 19 //------------------------------------------ 20 void on_gnss_event(WMGNSSEventType type, const WMGNSSEventData *data, void *args) { 21 (void)args; 22 if (type == WALTER_MODEM_GNSS_EVENT_FIX) { 23 last_fix = data->gnssfix; 24 is_gnss_fix_received = true; 25 } 26 } 27 28 //------------------------------------------ 29 // LTE connection 30 //------------------------------------------ 31 bool lte_connect() { 32 Serial.println("[lte] setting opstate NO_RF..."); 33 if (!modem.setOpState(WALTER_MODEM_OPSTATE_NO_RF)) { 34 Serial.println("[lte] failed to set NO_RF"); 35 return false; 36 } 37 38 Serial.printf("[lte] defining PDP context with APN: %s\n", CELLULAR_APN); 39 if (!modem.definePDPContext(1, CELLULAR_APN)) { 40 Serial.println("[lte] failed to define PDP context"); 41 return false; 42 } 43 44 Serial.println("[lte] setting opstate FULL..."); 45 if (!modem.setOpState(WALTER_MODEM_OPSTATE_FULL)) { 46 Serial.println("[lte] failed to set FULL"); 47 return false; 48 } 49 50 Serial.println("[lte] setting automatic network selection..."); 51 if (!modem.setNetworkSelectionMode(WALTER_MODEM_NETWORK_SEL_MODE_AUTOMATIC)) { 52 Serial.println("[lte] failed to set network selection"); 53 return false; 54 } 55 56 Serial.print("[lte] waiting for registration"); 57 for (int i = 0; i < 60; i++) { 58 WalterModemNetworkRegState state = modem.getNetworkRegState(); 59 if (state == WALTER_MODEM_NETWORK_REG_REGISTERED_HOME || 60 state == WALTER_MODEM_NETWORK_REG_REGISTERED_ROAMING) { 61 Serial.println(" registered!"); 62 return true; 63 } 64 Serial.print("."); 65 delay(1000); 66 } 67 68 Serial.println(" timeout"); 69 return false; 70 } 71 72 //------------------------------------------ 73 // Print modem + SIM info 74 //------------------------------------------ 75 void print_modem_info() { 76 if (modem.getIdentity(&rsp)) { 77 Serial.printf("[modem] IMEI: %s\n", rsp.data.identity.imei); 78 } 79 80 if (modem.getSIMState(&rsp)) { 81 Serial.printf("[sim] state: %d\n", rsp.data.simState); 82 } 83 84 if (modem.getSIMCardID(&rsp)) { 85 Serial.printf("[sim] ICCID: %s\n", rsp.data.simCardID.iccid); 86 } 87 88 if (modem.getRAT(&rsp)) { 89 const char *rat_name = "unknown"; 90 switch (rsp.data.rat) { 91 case WALTER_MODEM_RAT_LTEM: rat_name = "LTE-M"; break; 92 case WALTER_MODEM_RAT_NBIOT: rat_name = "NB-IoT"; break; 93 case WALTER_MODEM_RAT_AUTO: rat_name = "auto"; break; 94 default: break; 95 } 96 Serial.printf("[modem] RAT: %s\n", rat_name); 97 } 98 } 99 100 //------------------------------------------ 101 // Print cell info 102 //------------------------------------------ 103 void print_cell_info() { 104 if (modem.getCellInformation(WALTER_MODEM_SQNMONI_REPORTS_SERVING_CELL, &rsp)) { 105 Serial.printf("[cell] operator: %s\n", rsp.data.cellInformation.netName); 106 Serial.printf("[cell] RSRP: %.1f dBm\n", rsp.data.cellInformation.rsrp); 107 Serial.printf("[cell] RSRQ: %.1f dB\n", rsp.data.cellInformation.rsrq); 108 Serial.printf("[cell] RSSI: %.1f dBm\n", rsp.data.cellInformation.rssi); 109 Serial.printf("[cell] CID: %lu\n", (unsigned long)rsp.data.cellInformation.cid); 110 Serial.printf("[cell] TAC: %u\n", rsp.data.cellInformation.tac); 111 Serial.printf("[cell] PCI: %u\n", rsp.data.cellInformation.pci); 112 Serial.printf("[cell] EARFCN: %u\n", rsp.data.cellInformation.earfcn); 113 } 114 } 115 116 //------------------------------------------ 117 // Sync GNSS clock from network 118 //------------------------------------------ 119 void sync_gnss_clock() { 120 Serial.println("[gnss] checking clock..."); 121 122 if (modem.gnssGetUTCTime(&rsp)) { 123 if (rsp.data.clock.epochTime > 0) { 124 Serial.printf("[gnss] clock set: %llu\n", rsp.data.clock.epochTime); 125 return; 126 } 127 } 128 129 Serial.println("[gnss] clock not set — will use cold start"); 130 } 131 132 //------------------------------------------ 133 // Update GNSS assistance data 134 //------------------------------------------ 135 void update_gnss_assistance() { 136 Serial.println("[gnss] checking assistance data..."); 137 138 if (modem.gnssGetAssistanceStatus(&rsp)) { 139 const char *names[] = {"almanac", "realtime_ephemeris", "predicted_ephemeris"}; 140 for (int i = 0; i < WALTER_MODEM_GNSS_ASSISTANCE_TYPE_COUNT; i++) { 141 WMGNSSAssistance &a = rsp.data.gnssAssistance[i]; 142 Serial.printf("[gnss] %s: available=%d, expires_in=%ds\n", 143 names[i], a.available, a.timeToExpire); 144 } 145 } 146 147 Serial.println("[gnss] updating realtime ephemeris..."); 148 if (modem.gnssUpdateAssistance(WALTER_MODEM_GNSS_ASSISTANCE_TYPE_REALTIME_EPHEMERIS)) { 149 Serial.println("[gnss] assistance updated"); 150 } else { 151 Serial.println("[gnss] assistance update failed (non-fatal)"); 152 } 153 } 154 155 //------------------------------------------ 156 // Get GNSS fix 157 //------------------------------------------ 158 bool get_gnss_fix() { 159 Serial.println("[gnss] disconnecting LTE for GNSS..."); 160 modem.setOpState(WALTER_MODEM_OPSTATE_MINIMUM); 161 delay(500); 162 163 Serial.println("[gnss] configuring..."); 164 modem.gnssConfig( 165 WALTER_MODEM_GNSS_SENS_MODE_HIGH, 166 WALTER_MODEM_GNSS_ACQ_MODE_COLD_WARM_START, 167 WALTER_MODEM_GNSS_LOC_MODE_ON_DEVICE_LOCATION 168 ); 169 170 Serial.println("[gnss] requesting fix..."); 171 is_gnss_fix_received = false; 172 modem.gnssPerformAction(WALTER_MODEM_GNSS_ACTION_GET_SINGLE_FIX); 173 174 Serial.print("[gnss] waiting for fix"); 175 for (int i = 0; i < 120; i++) { 176 if (is_gnss_fix_received) { 177 Serial.println(" got it!"); 178 return true; 179 } 180 Serial.print("."); 181 delay(1000); 182 } 183 184 Serial.println(" timeout (no fix in 120s)"); 185 return false; 186 } 187 188 //------------------------------------------ 189 // Print GNSS fix 190 //------------------------------------------ 191 void print_fix() { 192 Serial.println("\n========== GNSS FIX =========="); 193 bool is_valid = (last_fix.satCount > 0 && last_fix.estimatedConfidence > 0); 194 Serial.printf(" Status: %s\n", is_valid ? "VALID FIX" : "NO FIX"); 195 Serial.printf(" Latitude: %.6f\n", last_fix.latitude); 196 Serial.printf(" Longitude: %.6f\n", last_fix.longitude); 197 Serial.printf(" Height: %.1f m\n", last_fix.height); 198 Serial.printf(" Confidence: %.1f m\n", last_fix.estimatedConfidence); 199 Serial.printf(" Satellites: %d\n", last_fix.satCount); 200 Serial.printf(" Time to fix: %lu ms\n", (unsigned long)last_fix.timeToFix); 201 Serial.printf(" Timestamp: %lld\n", last_fix.timestamp); 202 203 if (last_fix.satCount > 0) { 204 Serial.println(" Satellites:"); 205 for (uint8_t i = 0; i < last_fix.satCount && i < 10; i++) { 206 Serial.printf(" #%d: signal=%d dB/Hz\n", 207 last_fix.sats[i].satNo, last_fix.sats[i].signalStrength); 208 } 209 } 210 Serial.println("==============================\n"); 211 } 212 213 //------------------------------------------ 214 // Main 215 //------------------------------------------ 216 void setup() { 217 Serial.begin(115200); 218 delay(2000); 219 Serial.println("\n[walter] starting..."); 220 221 if (!modem.begin(&Serial2)) { 222 Serial.println("[walter] modem init failed — rebooting"); 223 delay(3000); 224 ESP.restart(); 225 } 226 Serial.println("[walter] modem initialized"); 227 228 modem.setGNSSEventHandler(on_gnss_event, NULL); 229 230 print_modem_info(); 231 232 if (!lte_connect()) { 233 Serial.println("[walter] LTE failed — trying GNSS without assistance"); 234 } else { 235 print_cell_info(); 236 sync_gnss_clock(); 237 update_gnss_assistance(); 238 } 239 240 if (get_gnss_fix()) { 241 print_fix(); 242 } else { 243 Serial.println("[gnss] no fix obtained"); 244 } 245 246 Serial.println("[walter] done."); 247 } 248 249 void loop() { 250 delay(10000); 251 }