/ firmware / src / bin / walter-iot.cpp
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  }