Демонстрационная прошивка отладочного комплекта МТС NB-IoT
main.c
См. документацию.
1 
171 #include "stm32l1xx.h"
172 #include "board_support_api.h"
173 #include "menu_handlers.h"
174 
181 #define DO_NOT_GO_STOP 0
182 
185 #define ALWAYS_ENTER_SETUP 0
186 
190 #define USE_OLD_MODULE_INIT 0
191 
192 #ifndef __NO_SYSTEM_INIT
193 #error This firmware must be compiled with __NO_SYSTEM_INIT defined at project level.
194 #endif
195 
210 #if USE_OLD_MODULE_INIT == 1
211 #warning Firmware compiled with the old module initialization method used
212 uint8_t SARA_init(uint8_t use_NIDD,uint8_t *NIDD_APN)
213 {
214  uint8_t k;
215  uint16_t resp_len;
216  uint32_t boot_start,boot_end;
217  uint8_t module_OK;
218  uint8_t buffer[500];
219  uint8_t error;
220  uint8_t needs_reconfig;
221 
222  module_OK=0;
223 
224  printf("Starting SARA-R410M...\r\n");
226  printf("Waiting module to boot...\r\n");
227 
228  boot_start = get_uptime_ms();
229  /*
230  Module should be configured to transmit the greeting text "BOOT OK" as soon as it has booted.
231  This is done with AT+CSGT=1,"BOOT OK" command.
232  */
233  resp_len = AT_ReadReponseBuffer(NULL,0,NULL,NULL,NULL,5*CYCLES_PER_1SEC);
234  boot_end = get_uptime_ms();
235 
236  if (resp_len==0)
237  {
238  /* The loop above has finished by timeout, so the module is either already running, or is faulty by some reason. */
239  printf("No startup message from the module, checking if it is already running.\r\n");
240 
241  k=0;
242  while ((resp_len==0) && (k<3))
243  {
244  printf("Attempt %d...\r\n",k+1);
245  AT_SendCommand("AT\r\n");
246  resp_len = AT_ReadReponseBuffer(NULL,0,NULL,NULL,NULL,CYCLES_PER_1SEC/2);
247  k++;
248  }
249 
250  if (resp_len==0)
251  {
252  printf("Module failed to start. Radio communication unavailable.\r\n");
253  }
254  else
255  {
256  printf("Module was already running, OK.\r\n");
257 
258  module_OK=1;
259  }
260  }
261  else
262  {
263  printf("Module has booted in %d ms.\r\n",boot_end - boot_start);
264  module_OK=1;
265  }
266 
267  /* Configuring the module for IP or NIDD */
268  if (module_OK)
269  {
270  needs_reconfig=1;
271 
272  AT_SendCommand("AT+CGDCONT?\r\n");
273  AT_ReadReponseBuffer(buffer,500,NULL,NULL,&error,3*CYCLES_PER_1SEC);
274 
275  if (error)
276  {
277  printf("Unable to analyze and set profile, the RF module does not respond.\r\n");
278 
279  return 0;
280  }
281 
282  if (use_NIDD==1)
283  {
284  if ((NIDD_APN==NULL) || (NIDD_APN[0]=='\0'))
285  {
286  printf("NIDD APN value is wrong; the function will not be configured.\r\n");
287  needs_reconfig=0;
288  }
289  else
290  {
291  if (strstr(buffer,"+CGDCONT: 1,\"NONIP\"")==NULL)
292  {
293  printf("Current context is set for IP, but settings call for NIDD, fixing...\r\n");
294  snprintf(buffer,500,"AT+CGDCONT=1,\"NONIP\",\"%s\"\r\n",NIDD_APN);
295  }
296  else
297  {
298  printf("NIDD mode used.\r\n");
299  needs_reconfig=0;
300  }
301  }
302  }
303  else
304  {
305  if (strstr(buffer,"+CGDCONT: 1,\"IPV4V6\"")==NULL)
306  {
307  printf("Current context is set for NIDD method, but settings call for IP, fixing...\r\n");
308  snprintf(buffer,500,"AT+CGDCONT=1,\"IPV4V6\"\r\n");
309  }
310  else
311  {
312  printf("IP mode used.\r\n");
313  needs_reconfig=0;
314  }
315  }
316 
317  if (needs_reconfig)
318  {
319  AT_SendCommand(buffer);
320  AT_ReadReponseBuffer(buffer,500,NULL,NULL,&error,3*CYCLES_PER_1SEC);
321 
322  if (error!=0)
323  {
324  printf("Unable to configure module.\r\n");
325 
326  return 0;
327  }
328 
329  printf("Applying settings (needs module reboot)...\r\n");
330  AT_SendCommand("AT+CFUN=15\r\n");
331  AT_ReadReponseBuffer(NULL,0,NULL,NULL,&error,CYCLES_PER_1SEC);
332 
333  if (error)
334  {
335  printf("Unable to reboot module.\r\n");
336 
337  return 0;
338  }
339 
340  /* The module should be configured to output "BOOT OK" after boot, see appropriate AT command. */
341  AT_ReadReponseBuffer(NULL,0,NULL,NULL,&error,5*CYCLES_PER_1SEC);
342  }
343  }
344 
345  return 1;
346 }
347 #else
348 uint8_t SARA_init(uint8_t use_NIDD,uint8_t *NIDD_APN)
349 {
350  uint8_t k;
351  uint16_t resp_len;
352  uint32_t boot_start,boot_end;
353  uint32_t time;
354  uint8_t module_OK;
355  uint8_t buffer[500];
356  uint8_t error;
357  uint8_t needs_reconfig;
358 
359  module_OK = 0;
360  resp_len = 0;
361 
362  if (GPIOA->IDR & PIN_MASK(NB_VINT_PA))
363  {
364  printf("V_INT is detected to be inactive, RF module is not switched on.\r\nStarting SARA-R410M...\r\n");
365  __AT_FSM_rearm();
367  printf("Waiting module to boot...\r\n");
368 
369  boot_start = get_uptime_ms();
370  time = boot_start;
371 
372  while ((GPIOA->IDR & PIN_MASK(NB_VINT_PA)) && ((time - boot_start)<5000))
373  {
374  delay_ms(100);
375  time = get_uptime_ms();
376  }
377 
378  if ((time - boot_start)>7000)
379  {
380  printf("Unable to switch the RF module. This might be a hardware problem.\r\n");
381 
382  return 0;
383  }
384  else
385  {
386  printf("V_INT detected after %d ms.\r\n",time - boot_start);
387  }
388 
389  /*
390  Module should be configured to transmit the greeting text "BOOT OK" as soon as it has booted.
391  This is done with AT+CSGT=1,"BOOT OK" command.
392  */
393  resp_len = AT_ReadReponseBuffer(NULL,0,NULL,NULL,NULL,5*CYCLES_PER_1SEC);
394  boot_end = get_uptime_ms();
395 
396  if (resp_len == 0)
397  {
398  printf("No startup message from the module.\r\n");
399  }
400 
401  printf("The module has booted in %d second(s).\r\n",boot_end - boot_start);
402  }
403  else
404  {
405  printf("V_INT is present, the RF module is already powered ON.\r\n");
406  }
407 
408  printf("Ensuring that the module responds to AT commands...\r\n");
409 
410  k=0;
411  resp_len = 0;
412  while ((resp_len==0) && (k<3))
413  {
414  printf("Attempt %d...\r\n",k+1);
415  AT_SendCommand("AT\r\n");
416  resp_len = AT_ReadReponseBuffer(NULL,0,NULL,NULL,NULL,CYCLES_PER_1SEC);
417  k++;
418  }
419 
420  if (resp_len==0)
421  {
422  printf("The module does not respond.\r\n");
423  }
424  else
425  {
426  printf("Module OK.\r\n");
427 
428  module_OK=1;
429  }
430 
431  /* Configuring the module for IP or NIDD */
432  if (module_OK)
433  {
434  needs_reconfig=1;
435 
436  printf("Reading module configuration...\r\n");
437 
438  k=0;
439  do
440  {
441  k++;
442  printf("Attempt %d...\r\n",k);
443  AT_SendCommand("AT+CGDCONT?\r\n");
444  AT_ReadReponseBuffer(buffer,500,NULL,NULL,&error,5*CYCLES_PER_1SEC);
445 
446  if (error != 0)
447  {
448  delay_ms(3000);
449  }
450  }
451  while ((error != 0) && (k<3));
452 
453  if (error)
454  {
455  printf("Unable to analyze and set profile, try this later.\r\n");
456 
457  return 0;
458  }
459 
460  if (use_NIDD==1)
461  {
462  if ((NIDD_APN==NULL) || (NIDD_APN[0]=='\0'))
463  {
464  printf("NIDD APN value is wrong; the function will not be configured.\r\n");
465  needs_reconfig=0;
466  }
467  else
468  {
469  if (strstr(buffer,"+CGDCONT: 1,\"NONIP\"")==NULL)
470  {
471  printf("Current context is set for IP, but settings call for NIDD, fixing...\r\n");
472  snprintf(buffer,500,"AT+CGDCONT=1,\"NONIP\",\"%s\"\r\n",NIDD_APN);
473  }
474  else
475  {
476  printf("NIDD mode used.\r\n");
477  needs_reconfig=0;
478  }
479  }
480  }
481  else
482  {
483  if (strstr(buffer,"+CGDCONT: 1,\"IPV4V6\"")==NULL)
484  {
485  printf("Current context is set for NIDD method, but settings call for IP, fixing...\r\n");
486  snprintf(buffer,500,"AT+CGDCONT=1,\"IPV4V6\"\r\n");
487  }
488  else
489  {
490  printf("IP mode used.\r\n");
491  needs_reconfig=0;
492  }
493  }
494 
495  if (needs_reconfig)
496  {
497  AT_SendCommand(buffer);
498  AT_ReadReponseBuffer(buffer,500,NULL,NULL,&error,3*CYCLES_PER_1SEC);
499 
500  if (error!=0)
501  {
502  printf("Unable to configure module.\r\n");
503 
504  return 0;
505  }
506 
507  printf("Applying settings (needs module reboot)...\r\n");
508  AT_SendCommand("AT+CFUN=15\r\n");
509  AT_ReadReponseBuffer(NULL,0,NULL,NULL,&error,CYCLES_PER_1SEC);
510 
511  if (error)
512  {
513  printf("Unable to reboot module.\r\n");
514 
515  return 0;
516  }
517 
518  /* The module should be configured to output "BOOT OK" after boot, see appropriate AT command. */
519  AT_ReadReponseBuffer(NULL,0,NULL,NULL,&error,5*CYCLES_PER_1SEC);
520  }
521  }
522 
523  return 1;
524 }
525 #endif
526 
536 uint8_t NMEA_to_LatLon(uint8_t *NMEA_string_in,double *dd_lat_out,double *dd_lon_out)
537 {
538  uint8_t *NMEA_pointers[10];
539  uint8_t ptr_n;
540  uint8_t tmp_str[5];
541  double lat_deg,lat_min;
542  double lon_deg,lon_min;
543 
544  NMEA_pointers[0] = strtok(NMEA_string_in,",");
545 
546  if (NMEA_pointers[0]==NULL)
547  {
548  //Some weird string
549  return 0;
550  }
551 
552  if (strcmp(NMEA_pointers[0],"$GNGLL")!=0)
553  {
554  //Wrong NMEA message
555  return 0;
556  }
557 
558  ptr_n=0;
559  while ((NMEA_pointers[ptr_n]!=NULL) && (ptr_n<10))
560  {
561  ptr_n++;
562  NMEA_pointers[ptr_n] = strtok(NULL,",");
563 
564  if ((NMEA_pointers[ptr_n]==NULL) && (ptr_n<7))
565  {
566  //String lacks fields
567  return 0;
568  }
569  }
570 
571  if (ptr_n>9)
572  {
573  //Incorrect string
574  return 0;
575  }
576 
577  if (NMEA_pointers[6][0]!='A')
578  {
579  //Position data are not fixed
580  return 0;
581  }
582 
583  if (strlen(NMEA_pointers[1])<4)
584  {
585  return 0;
586  }
587 
588  if (strlen(NMEA_pointers[3])<4)
589  {
590  return 0;
591  }
592 
593  //Thank God, the NMEA format is fixed and leading zeros are always inserted, so it is safe to parse it the simple way.
594 
595  //Extracting latitude degrees
596  tmp_str[0] = NMEA_pointers[1][0];
597  tmp_str[1] = NMEA_pointers[1][1];
598  tmp_str[2] = 0;
599  lat_deg = atof(tmp_str);
600  //Extracting latitude minutes
601  lat_min = atof(&(NMEA_pointers[1][2]));
602 
603  //Extracting longitude degrees
604  tmp_str[0] = NMEA_pointers[3][0];
605  tmp_str[1] = NMEA_pointers[3][1];
606  tmp_str[2] = NMEA_pointers[3][2];
607  tmp_str[3] = 0;
608  lon_deg = atof(tmp_str);
609  //Extracting longitude minutes
610  lon_min = atof(&(NMEA_pointers[3][3]));
611 
612  if (dd_lat_out!=NULL)
613  {
614  if (NMEA_pointers[2][0]=='N')
615  {
616  (*dd_lat_out) = lat_deg + (lat_min/60.0);
617  }
618  else
619  {
620  (*dd_lat_out) = -(lat_deg + (lat_min/60.0));
621  }
622  }
623 
624  if (dd_lon_out!=NULL)
625  {
626  if (NMEA_pointers[4][0]=='E')
627  {
628  (*dd_lon_out) = lon_deg + (lon_min/60.0);
629  }
630  else
631  {
632  (*dd_lon_out) = -(lon_deg + (lon_min/60.0));
633  }
634  }
635 
636  return 1;
637 }
638 
639 #define MAX_JSON_LENGTH 200
640 #define MAX_COAP_MSG_LEN (MAX_JSON_LENGTH + MAX_URI_LENGTH + 50)
641 
662 uint8_t transmit_telemetry(uint8_t *target_IP,uint8_t *target_url,uint16_t target_port,uint8_t *ICCID_string,uint8_t use_NIDD,uint8_t private_gnss)
663 {
664  int16_t Tamb;
665  int16_t accel_X,accel_Y,accel_Z;
666  int16_t RSSI;
667  static uint8_t json_string[MAX_JSON_LENGTH]; /* Making long strings static to offload the stack */
668  volatile uint16_t CoAP_msg_len;
669  static uint8_t CoAP_msg_buffer[MAX_COAP_MSG_LEN];
670  uint8_t socket_ID;
671  uint16_t rx_data_length;
672  uint8_t rx_socket_ID;
673  uint32_t wait_counter;
674  CoAP_header_info_t hdr_info;
675  uint32_t resp_wait_start,resp_wait_end;
676  volatile uint16_t k;
677  uint8_t gnss_result;
678  uint8_t gnss_data_conversion_result;
679  uint8_t gnss_string[80] = {"No GNSS data"};
680  double latitude;
681  double longitude;
682  uint8_t GNSS_data_valid;
683  static uint8_t NIDD_response_data[520];
684  uint16_t NIDD_response_length;
685 
686  //Palace square in St. Petersburg by default.
687  latitude = 59.9397655;
688  longitude = 30.3132998;
689 
690  if (use_NIDD!=0)
691  {
692  printf("Using NIDD method.\r\n");
693  execute_AT_command("AT+CRTDCP=1\r\n");
694  }
695  else
696  {
697  printf("Using IP method.\r\n");
698 
699  if ((target_IP[0]==0) || (target_url[0]==0))
700  {
701  printf("Unable to transmit telemetry.\r\nPlease check that IP and URL are properly set and try again.\r\n");
702 
703  return 0;
704  }
705 
706  printf("Sending data to %s:%d%s\r\n",target_IP,target_port,target_url);
707  }
708 
709  GNSS_data_valid=1;
710  gnss_result = GNSS_ReadLocationData(gnss_string,80);
711  gnss_data_conversion_result=0;
712 
713  if (gnss_result==0)
714  {
715  printf("Unable to read GNSS data, the shield is not fitted or does not function properly.\r\n");
716  GNSS_data_valid=0;
717  }
718  else
719  {
720  gnss_data_conversion_result = NMEA_to_LatLon(gnss_string,&latitude,&longitude);
721 
722  if (gnss_data_conversion_result==0)
723  {
724  printf("Unable to convert GNSS data to decimal degrees format. GNSS shield is not fitted, GNSS fix is not completed or some other error occurred.\r\n");
725  GNSS_data_valid=0;
726  }
727 
728  if (private_gnss)
729  {
730  printf("GNSS logging is set to private mode; actual data will not be transmitted to the server.\r\n");
731  printf("Actual GNSS location: latitude = %.6f, longitude = %.6f valid = %d\r\n",latitude,longitude,GNSS_data_valid);
732  latitude = 59.931839;
733  longitude = 30.243405;
734  printf("Location data will be replaced to: latitude = %.6f, longitude = %.6f\r\n",latitude,longitude);
735  }
736  }
737 
738 #if USE_DS1721 == 1
739  Tamb = DS1721_ReadTemperature(LOCAL_TSENSOR_I2C_ADDR);
740 #else
742 #endif
743  LIS3DH_ReadXYZ(LOCAL_ACCEL_I2C_ADDR,&accel_X,&accel_Y,&accel_Z);
744  RSSI = AT_GetRSSI(CYCLES_PER_1SEC);
745 
746  snprintf(json_string,MAX_JSON_LENGTH,
747  "{'interface':'telemetry', 'ICCID':'%s', 'Tamb_degC':%d, 'aX':%d, 'aY':%d, 'aZ':%d, 'RSSI_dBm':%d, 'latitude':%.6f, 'longitude':%.6f, 'GNSS_data_valid':%d}",
748  ICCID_string,
749  Tamb,
750  accel_X,
751  accel_Y,
752  accel_Z,
753  RSSI,
754  latitude,
755  longitude,
756  GNSS_data_valid);
757 
758  printf("Telemetry:\r\n%s\r\n(%d bytes)\r\n",json_string,strlen(json_string));
759 
760  if (use_NIDD!=0)
761  {
762  if (AT_SendStringUsingNIDD(json_string,CYCLES_PER_1SEC)!=0)
763  {
764  printf("Message successfully sent.\r\n");
765 
766  wait_counter = 0;
767  NIDD_response_length = 0;
768  while ((NIDD_response_length == 0) && (wait_counter<CYCLES_PER_1SEC))
769  {
770  NIDD_response_length = AT_CheckNIDDReceived(NIDD_response_data);
771  wait_counter++;
772  }
773 
774  if (NIDD_response_length != 0)
775  {
776  printf("NIDD response received: %s\r\n",NIDD_response_data);
777  }
778  else
779  {
780  printf("No NIDD response.\r\n");
781  }
782  }
783  else
784  {
785  printf("Failed to send message.\r\n");
786  }
787  }
788  else
789  {
790  CoAP_msg_len = CoAP_assemble_request(COAP_METHOD_POST,CoAP_msg_buffer,MAX_COAP_MSG_LEN,target_url,json_string,strlen(json_string));
791 
792  printf("Raw CoAP data (%d bytes):\r\n",CoAP_msg_len);
793  for (k=0; k<CoAP_msg_len; k++)
794  {
795  if (CoAP_msg_buffer[k]<0x10)
796  {
797  printf("0%X",CoAP_msg_buffer[k]);
798  }
799  else
800  {
801  printf("%X",CoAP_msg_buffer[k]);
802  }
803  }
804  printf("\r\n");
805 
806  socket_ID = AT_CreateUDPSocket(CYCLES_PER_1SEC);
807 
808  if (socket_ID==INVALID_SOCKET_ID)
809  {
810  printf("Unable to create UDP socket.\r\n");
811 
812  return 0;
813  }
814 
815  if (AT_SendUDPData(socket_ID,target_IP,target_port,CoAP_msg_buffer,CoAP_msg_len,3*CYCLES_PER_1SEC)!=AT_NO_ERROR)
816  {
817  printf("Failed to send data over IP.\r\n");
818  }
819 
820  resp_wait_start = get_uptime_ms();
821  wait_counter=0;
822  rx_socket_ID = INVALID_SOCKET_ID;
823  rx_data_length=0;
824  while ((rx_socket_ID!=socket_ID) && (wait_counter<CYCLES_PER_1SEC))
825  {
826  AT_CheckUDPReceived(&rx_socket_ID,&rx_data_length);
827  wait_counter++;
828  }
829  resp_wait_end = get_uptime_ms();
830 
831  if ((rx_socket_ID!=socket_ID) || (rx_data_length==0))
832  {
833  printf("No answer from server.\r\n");
834  }
835  else
836  {
837  if (rx_data_length<MAX_COAP_MSG_LEN)
838  {
839  AT_ReadUDPData(rx_socket_ID,rx_data_length,NULL,NULL,CoAP_msg_buffer,3*CYCLES_PER_1SEC);
840 
841  CoAP_parse_message_header(CoAP_msg_buffer,&hdr_info);
842 
843  printf("Server response code: %d.%02d, response time = %d ms.\r\n",hdr_info.code_class,hdr_info.code_detail,resp_wait_end - resp_wait_start);
844 
845  printf("Server response dump:\r\n");
846  for (k=0; k<rx_data_length; k++)
847  {
848  if (CoAP_msg_buffer[k]<0x10)
849  {
850  printf("0%X",CoAP_msg_buffer[k]);
851  }
852  else
853  {
854  printf("%X",CoAP_msg_buffer[k]);
855  }
856  }
857  printf("\r\n");
858  }
859  else
860  {
861  printf("Server response is too large (%d bytes).\r\n",rx_data_length);
862  }
863  }
864 
865  if (!AT_CloseUDPSocket(socket_ID,CYCLES_PER_1SEC))
866  {
867  printf("Unable to close UDP socket.\r\n");
868  }
869  }
870 
871  return 1;
872 }
873 
877 void service_menu(void)
878 {
879  uint8_t user_choice;
880  device_setup_data_t settings;
881  uint16_t k;
882  uint8_t user_input[80];
883 
884  printf("\r\n*** Welcome to MTS NB-IoT Development Kit service menu ***\r\nFirmware version: %s\r\n\r\n",FIRMWARE_VERSION);
885 
886  printf("Current settings found in EEPROM:\r\n\r\n");
887 
888  recall_device_settings(&settings);
889 
890  if (settings.target_server_IP[0]=='\0')
891  {
892  printf("Warning: target server IP not set.\r\n");
893  }
894  else
895  {
896  printf("Target IP:\t\t%s\r\n",settings.target_server_IP);
897  }
898 
899  printf("Target port:\t\t%d\r\n",settings.target_server_port);
900 
901  if ((!is_alphanumeric(settings.target_URL[1])) || (settings.target_URL[0]=='\0'))
902  {
903  printf("Warning: target URL not set.\r\n");
904  }
905  else
906  {
907  printf("Target URL:\t\t%s\r\n",settings.target_URL);
908  }
909 
910  if ((!is_alphanumeric(settings.NIDD_APN_name[1])) || (settings.NIDD_APN_name[0]=='\0'))
911  {
912  printf("Warning: APN name not set.\r\n");
913  }
914  else
915  {
916  printf("NIDD APN:\t\t%s\r\n",settings.NIDD_APN_name);
917  }
918 
919  printf("Use NIDD for telemetry:\t%d\r\n",settings.use_NIDD);
920 
921  printf("Board mode on startup:\t");
922 
924  {
925  printf("logger\r\n");
926  }
927  else
928  {
929  printf("service menu\r\n");
930  }
931 
932  printf("Telemetry interval\r\n(in logger mode):\t%d ms\r\n",settings.telemetry_interval_ms);
933  printf("GNSS privacy mode:\t%d\r\n",settings.gnss_privacy_mode);
934 
935  printf("\r\nType in a function number from a list below and press enter.\r\n\r\n");
936 
937  k=0;
938  while ((menu_items[k].menu_item_handler!=NULL) && (menu_items[k].item_string_description[0]!='\0'))
939  {
940  if (k==0)
941  {
942  printf("Target server setup:\r\n");
943  }
944  if (k==4)
945  {
946  printf("System functions:\r\n");
947  }
948  printf("\t%d\t- %s\r\n",k+1,menu_items[k].item_string_description);
949  k++;
950  }
951 
952  printf("> ");
953  scanf("%s",user_input);
954  apply_backspace(user_input,80);
955  user_choice = atoi(user_input);
956 
957  user_choice--; //Array numbering is zero-based, but menu items are numbered starting from one not to confuse the user
958 
959  printf("\r\n--------------------------------------------------------------------------------\r\n");
960 
961  if (user_choice>=k)
962  {
963  printf("Unknown function - %d",user_input+1);
964  }
965  else
966  {
967  menu_items[user_choice].menu_item_handler(&settings);
968  }
969 
970  printf("\r\n--------------------------------------------------------------------------------\r\n");
971 
972  printf("Press Enter to return to service menu.\r\n");
973  getchar();
974  getchar();
975  printf("\r\n");
976 }
977 
981 void main(void)
982 {
983  device_setup_data_t eep_settings;
984  /*
985  The board uses soldered-in SIM-chip, so it's obvious that ICCID can not change while the board is powered.
986  It is a good idea to read it just once at startup and then use cached value to avoid pointless requests to the RF module.
987  */
988  uint8_t ICCID_cache_string[100];
989 
990  init_board();
991 
993  printf("Accelerometer initialized.\r\n");
994 
995 #if USE_DS1721 == 1
996  DS1721_StartConversion(LOCAL_TSENSOR_I2C_ADDR);
997  printf("Temperature sensor initialized.\r\n");
998 #else
999  printf("Temperature sensor always ready.\r\n");
1000 #endif
1001  printf("Board initialization complete.\r\n");
1002 
1003  recall_device_settings(&eep_settings);
1004 
1005  if ((eep_settings.reserved[0]!=SETTINGS_SIGNATURE_0) || (eep_settings.reserved[1]!=SETTINGS_SIGNATURE_1) || (eep_settings.reserved[2]!=SETTINGS_SIGNATURE_2))
1006  {
1007  printf("The board is all-new or the settings in EEPROM are severely damaged or non-existent.\r\nPerforming factory setup.\r\n");
1008  perform_initial_setup(&eep_settings);
1009  }
1010 
1011  if ((eep_settings.telemetry_interval_ms<1000) || (eep_settings.telemetry_interval_ms>16000))
1012  {
1013  printf("Saved telemetry interval is incorrect. Messages will be sent with 1000 ms timeout.\r\n");
1014  eep_settings.telemetry_interval_ms = 1000;
1015  }
1016 
1017  if ((eep_settings.target_URL[0]!='/') || (!(GPIOC->IDR & PIN_MASK(USER_SW_PC))) || (eep_settings.logger_mode_on_startup!=LOGGER_MODE_VALUE) || ALWAYS_ENTER_SETUP)
1018  {
1019  printf("(!) The device will enter service menu.\r\n");
1020 
1021  if (SARA_init(eep_settings.use_NIDD,eep_settings.NIDD_APN_name)==0)
1022  {
1023  printf("Unable to initialize the RF module. Please check its configuration manually.\r\n");
1024  }
1025  else
1026  {
1027  printf("Switching off the use of PSM...\r\n");
1028 
1029  if (!AT_SwitchPSM(0,3*CYCLES_PER_1SEC))
1030  {
1031  printf("Could not disable the use of PSM;\r\ncheck that the module has started up and not in PSM mode already, and try to do this manually.\r\n");
1032  }
1033  }
1034 
1035  while (1)
1036  {
1037  service_menu();
1038  }
1039  }
1040  else
1041  {
1042  if (SARA_init(eep_settings.use_NIDD,eep_settings.NIDD_APN_name)==0)
1043  {
1044  printf("Unable to initialize the RF module, rebooting in menu mode...\r\n");
1045 
1046  eep_settings.logger_mode_on_startup = 0;
1047  store_device_settings(&eep_settings);
1048 
1049  NVIC_SystemReset();
1050  }
1051 
1052  while (AT_IsRegistered(CYCLES_PER_1SEC)==0)
1053  {
1054  /*
1055  Notice: this is SUPPOSED TO HANG if the module is unable to register, because if it is not registered in a network,
1056  continuing has no sense.
1057  */
1058  printf("Waiting for the module to register in a network...\r");
1059  delay_ms(50);
1060  }
1061 
1062  printf("\r\n");
1063 
1064  AT_ReadICCID(ICCID_cache_string,CYCLES_PER_1SEC);
1065 
1066  #if DO_NOT_GO_STOP == 0
1067  AWU_Init(eep_settings.telemetry_interval_ms);
1068 
1069  if (eep_settings.telemetry_interval_ms>6000)
1070  {
1071  printf("Enabling the use of PSM...");
1072  if (!AT_SwitchPSM(1,3*CYCLES_PER_1SEC))
1073  {
1074  printf("Could not enable the use of PSM; something is wrong, the result will be unpredictable.\r\n");
1075  }
1076  }
1077  else
1078  {
1079  printf("Telemetry interval is lower than 6 sec, so the module will not be able to go into PSM; the use of PSM will be disabled.\r\n");
1080  if (!AT_SwitchPSM(0,3*CYCLES_PER_1SEC)==0)
1081  {
1082  printf("Could not disable PSM; something is wrong, the result will be unpredictable.\r\n");
1083  }
1084  }
1085  #else
1086  printf("(i) Notice: this firmware was compiled with power saving features disabled (debug mode).\r\n");
1087  #endif
1088 
1089  while (1)
1090  {
1091  switch_LED(1);
1092  #if DO_NOT_GO_STOP == 0
1093  if (eep_settings.telemetry_interval_ms>6000)
1094  {
1095  /*
1096  The module is most likely in PSM mode now (see docs for more about it), so we need to wake it up.
1097  */
1099  /*
1100  When greeting message is set to "BOOT OK", this call will return on "OK", as designed.
1101  If the message is not set, it will return after five seconds or so, which is sufficient for the module to start up.
1102  This is effectively a small hack to determine when the module is ready.
1103  */
1104  AT_ReadReponseBuffer(NULL,0,NULL,NULL,NULL,5*CYCLES_PER_1SEC);
1105  }
1106  #endif
1107  transmit_telemetry(eep_settings.target_server_IP,eep_settings.target_URL,eep_settings.target_server_port,ICCID_cache_string,eep_settings.use_NIDD,eep_settings.gnss_privacy_mode);
1108  switch_LED(0);
1109  #if DO_NOT_GO_STOP == 0
1110  AWU_GoStop();
1111  #else
1112  delay_ms(eep_settings.telemetry_interval_ms);
1113  #warning Firmware compiled without power-saving features.
1114  #endif
1115  }
1116  }
1117 }
1118 
CYCLES_PER_1SEC
volatile uint32_t CYCLES_PER_1SEC
Калиброванное значение: количество итераций пустого цикла за 1 секунду при текущей тактовой частоте....
Definition: board_support_api.c:11
SETTINGS_SIGNATURE_0
#define SETTINGS_SIGNATURE_0
См. описание поля reserved структуры device_setup_data_t.
Definition: board_support_api.h:90
transmit_telemetry
uint8_t transmit_telemetry(uint8_t *target_IP, uint8_t *target_url, uint16_t target_port, uint8_t *ICCID_string, uint8_t use_NIDD, uint8_t private_gnss)
Передача телеметрии
Definition: main.c:662
main
void main(void)
Точка входа приложения.
Definition: main.c:981
device_setup_data_t::logger_mode_on_startup
uint8_t logger_mode_on_startup
Definition: board_support_api.h:120
SETTINGS_SIGNATURE_2
#define SETTINGS_SIGNATURE_2
См. описание поля reserved структуры device_setup_data_t.
Definition: board_support_api.h:94
device_setup_data_t::use_NIDD
uint8_t use_NIDD
Флаг использования технологии NIDD. Если записана единица, передача будет выполняться через NIDD,...
Definition: board_support_api.h:112
CoAP_header_info_t
Definition: coap_essentials.h:102
LIS3DH_ReadXYZ
void LIS3DH_ReadXYZ(uint8_t i2c_addr, int16_t *X, int16_t *Y, int16_t *Z)
Прочесть значения ускорений по осям
Definition: lis3dh_driver.c:67
PIN_MASK
#define PIN_MASK(pin_no)
Преобразует номер бита в битовую маску
Definition: port_macros.h:31
AT_ReadReponseBuffer
uint16_t AT_ReadReponseBuffer(uint8_t *buffer_out, uint16_t buffer_length, uint8_t *is_overflowed, uint8_t *overrun_detected, uint8_t *error_status, uint32_t max_wait_time)
Чтение ответа модуля.
Definition: at_cmd_support.c:243
get_uptime_ms
uint32_t get_uptime_ms(void)
Возвращает время с момента начала работы прошивки
Definition: board_support_api.c:83
AT_CheckNIDDReceived
uint16_t AT_CheckNIDDReceived(uint8_t *data_out)
Проверка наличия NIDD-данных и, при их наличии, чтение принятой строки
Definition: at_cmd_support.c:498
AWU_Init
void AWU_Init(uint16_t wakeup_timeout_ms)
Функция выполняет настройку блока периодического пробуждения (AWU) для работы с режимом пониженного э...
Definition: awu_api.c:35
device_setup_data_t::target_server_IP
uint8_t target_server_IP[48]
IP/IPv6-адрес сервера для передачи телеметрии через IP/IPv6.
Definition: board_support_api.h:101
AT_ReadICCID
uint8_t AT_ReadICCID(uint8_t *ICCID, uint32_t timeout)
Чтение ICCID SIM-карты/чипа
Definition: at_cmd_support.c:683
device_setup_data_t::gnss_privacy_mode
uint8_t gnss_privacy_mode
Если переменная отлична от нуля, в пакет телеметрии не будут включаться истинные данные,...
Definition: board_support_api.h:122
AT_CheckUDPReceived
uint8_t AT_CheckUDPReceived(uint8_t *socket_id, uint16_t *packet_length)
Позволяет проверить, есть ли в буфере радиомодуля входящий UDP-пакет.
Definition: at_cmd_support.c:441
perform_initial_setup
void perform_initial_setup(device_setup_data_t *settings)
Функция, выполняющая заводскую настройку платы и радиомодуля.
Definition: menu_handlers.c:602
LIS3DH_FULLSCALE_2g
#define LIS3DH_FULLSCALE_2g
Уровень ускорения, соответствующий наибольшему значению по осям
Definition: lis3dh_driver.h:46
is_alphanumeric
uint8_t is_alphanumeric(uint8_t ch)
Вспомогательная функция. Проверяет принадлежность символа к буквам либо цифрам.
Definition: menu_handlers.c:46
service_menu
void service_menu(void)
Функция, реализующая сервисное меню. См. описание сервисного меню на главной странице.
Definition: main.c:877
CoAP_header_info_t::code_class
uint8_t code_class
Definition: coap_essentials.h:111
device_setup_data_t
Структура, хранящая настройки работы основного демонстрационного приложения
Definition: board_support_api.h:99
LOCAL_ACCEL_I2C_ADDR
#define LOCAL_ACCEL_I2C_ADDR
Адрес акселерометра LIS3DH, установленного на плате
Definition: board_support_api.h:54
INVALID_SOCKET_ID
#define INVALID_SOCKET_ID
См. AT_CreateUDPSocket()
Definition: at_cmd_support.h:38
SETTINGS_SIGNATURE_1
#define SETTINGS_SIGNATURE_1
См. описание поля reserved структуры device_setup_data_t.
Definition: board_support_api.h:92
execute_AT_command
uint8_t execute_AT_command(const uint8_t *atcmd)
Вспомогательная функция. Выполняет AT-команду
Definition: menu_handlers.c:16
TMP75_ReadTemperatureCentigrade
int8_t TMP75_ReadTemperatureCentigrade(uint8_t device_addr)
Читает значение регистра температуры из датчика TMP75.
Definition: tmp75_driver.c:9
AT_ReadUDPData
at_udp_error_t AT_ReadUDPData(uint8_t socket_id, uint16_t read_length, uint8_t *source_IP, uint16_t *source_port, uint8_t *data_out, uint32_t max_wait_time)
Чтение данных из указанного UDP-сокета
Definition: at_cmd_support.c:555
AT_SendUDPData
at_udp_error_t AT_SendUDPData(uint8_t socket_id, uint8_t *target_IP_string, uint16_t target_port, uint8_t *data, uint16_t data_length, uint32_t max_wait_time)
Посылает пакет UDP по указанному адресу.
Definition: at_cmd_support.c:390
AT_SendStringUsingNIDD
uint8_t AT_SendStringUsingNIDD(uint8_t *str, uint32_t timeout)
Пересылает данные с помощью технологии NIDD.
Definition: at_cmd_support.c:763
CoAP_assemble_request
#define CoAP_assemble_request(request_method, buffer, buffer_length, url_string, data, data_length)
Препроцессорная замена для обеспечения совместимости с кодом, использующим старую версию функции,...
Definition: coap_essentials.h:202
device_setup_data_t::target_server_port
uint16_t target_server_port
Номер порта на сервере для передачи телеметрии через IP.
Definition: board_support_api.h:110
recall_device_settings
void recall_device_settings(device_setup_data_t *data)
Загрузка настроек устройства из области EEPROM.
Definition: board_support_api.c:164
device_setup_data_t::NIDD_APN_name
uint8_t NIDD_APN_name[64]
Имя APN для NIDD.
Definition: board_support_api.h:105
init_board
void init_board(void)
Функция выполняет настройку тактирования и инициализацию периферии, используемой демонстрационным при...
Definition: board_support_api.c:207
LOCAL_TSENSOR_I2C_ADDR
#define LOCAL_TSENSOR_I2C_ADDR
Адрес термодатчика TMP75, установленного на плате
Definition: board_support_api.h:52
delay_ms
void delay_ms(uint32_t N)
Приостанавливает выполнение программы на заданное время. Значение задержки задается в миллисекундах.
Definition: board_support_api.c:88
SARA_init
uint8_t SARA_init(uint8_t use_NIDD, uint8_t *NIDD_APN)
Инициализация радиомодуля
Definition: main.c:348
ALWAYS_ENTER_SETUP
#define ALWAYS_ENTER_SETUP
Definition: main.c:185
AT_SwitchPSM
uint8_t AT_SwitchPSM(uint8_t state, uint32_t timeout)
Включить или выключить использование модулем режима PSM.
Definition: at_cmd_support.c:834
GNSS_ReadLocationData
uint8_t GNSS_ReadLocationData(uint8_t *str_out, uint16_t max_length)
Прочесть данные о местоположении, полученные от GNSS-приемника
Definition: gnss_support.c:80
LOGGER_MODE_VALUE
#define LOGGER_MODE_VALUE
См. описание device_setup_data_t.
Definition: board_support_api.h:87
SARA_R410_PWR_ON_pulse
void SARA_R410_PWR_ON_pulse(void)
Генерирует импульс низкого уровня продолжительностью примерно 500 мс на линии PWR_ON радиомодуля.
Definition: board_support_api.c:129
AT_IsRegistered
uint8_t AT_IsRegistered(uint32_t timeout)
Проверка регистрации модуля в сети LTE.
Definition: at_cmd_support.c:797
AT_CreateUDPSocket
uint8_t AT_CreateUDPSocket(uint32_t max_wait_time)
Создает UDP-сокет средствами радиомодуля.
Definition: at_cmd_support.c:324
NMEA_to_LatLon
uint8_t NMEA_to_LatLon(uint8_t *NMEA_string_in, double *dd_lat_out, double *dd_lon_out)
Преобразует данные в формате NMEA (с широтой и долготой в градусах и минутах) в градусы с дробной час...
Definition: main.c:536
switch_LED
void switch_LED(uint8_t state)
Включает или выключает светодиод на плате.
Definition: board_support_api.c:117
device_setup_data_t::target_URL
uint8_t target_URL[128]
URL на сервере для передачи телеметрии через IP.
Definition: board_support_api.h:103
store_device_settings
void store_device_settings(device_setup_data_t *data)
Сохранение настроек устройства в область EEPROM.
Definition: board_support_api.c:158
AT_NO_ERROR
Definition: at_cmd_support.h:50
AT_SendCommand
void AT_SendCommand(const uint8_t *cmd)
Посылает AT-команду в модуль.
Definition: at_cmd_support.c:318
board_support_api.h
CoAP_parse_message_header
void CoAP_parse_message_header(uint8_t *hdr, CoAP_header_info_t *out)
Выполняет разбор заголовка CoAP-сообщения с извлечением информации в структуру типа CoAP_header_info_...
Definition: coap_essentials.c:12
AT_GetRSSI
int16_t AT_GetRSSI(uint32_t timeout)
Получение мощности принимаемого сигнала в dBm.
Definition: at_cmd_support.c:623
AT_CloseUDPSocket
uint8_t AT_CloseUDPSocket(uint8_t socket_id, uint32_t max_wait_time)
Закрывает ранее созданный сокет UDP.
Definition: at_cmd_support.c:365
AWU_GoStop
void AWU_GoStop(void)
Функция останавливает выполнение программы и переводит контроллер в режим Stop. Выход из режима Stop ...
Definition: awu_api.c:80
device_setup_data_t::telemetry_interval_ms
uint32_t telemetry_interval_ms
Интервал передачи телеметрии в миллисекундах
Definition: board_support_api.h:108
menu_handlers.h
CoAP_header_info_t::code_detail
uint8_t code_detail
Definition: coap_essentials.h:113
menu_item_descriptor_t::menu_item_handler
menu_handler_t menu_item_handler
Указатель на функцию, ассоциированную с пунктом меню
Definition: menu_handlers.h:34
menu_items
const menu_item_descriptor_t menu_items[]
Массив, сопоставляющий указатели на функции, реализующие опции меню настроек, и их описания
Definition: menu_handlers.c:903
device_setup_data_t::reserved
uint8_t reserved[3]
Definition: board_support_api.h:128
apply_backspace
void apply_backspace(uint8_t *str, uint16_t max_length)
Модифицирует строку с учетом символов backspace.
Definition: board_support_api.c:170
LIS3DH_EnableXYZ
void LIS3DH_EnableXYZ(uint8_t i2c_addr, uint8_t data_rate, uint8_t fullscale, uint8_t hi_res)
Сконфигурировать параметры работы акселерометра
Definition: lis3dh_driver.c:28