Project in progress
Arduino NB-IoT with SIM7020 and T-Mobile

Arduino NB-IoT with SIM7020 and T-Mobile

Demonstration program on how to drive a SIM7020 NB-IoT modem, then set up a dashboard using T-Mobile's network and AllThingsTalk API.

  • 4,234 views
  • 3 comments
  • 12 respects

Components and supplies

Apps and online services

About this project

Summary

This demo is an example of how to connect an Arduino MKR and a NB-IoT breakout board (SIM7020E) via a service provider (T-Mobile) to a cloud API (AllThingsTalk). Demo is very basic and shows the minimum requirements to send data over to the cloud.

Arduino MKR Setup

This setup is very simple. The Arduino MKR uses the serial port to talk to the SIM7020E modem. Connect power (3V in this case!), Gnd, RX/TX and DTR/PWR to the Arduino.

Check the SIM7020 HW guide and SIM7020 AT command guide at the following website: simcom.com.

Info: PWRKEY (PowerPin) has to cycle through a high-low-high state after Vdd is powered up, DTR (SleepPin) is kept low - is used to wake up the modem from sleep in case you need this.

Sketch Code

The sketch makes use of some functions for sending AT command scripts to the SIM7020. The #include file contains the scripts (const Array *Char) and you can extend them if your like. The scripts command returns a status value : 1 = OK, 0 = ERROR, -1 = TIMEOUT. It's up to the programmer to act on these status. There is also a transparent mode, where you can send AT command manually via the Arduino serial monitor. For this demo code, the following sequence is done:

  • Initiate serial ports : Serial1 = standard Arduino serial on 13/14.
  • Power sequence the Sim7020 via PWR-Pin
  • Read Imei and other Module details.
  • Connect to the T-Mobile network (ID 20416)
  • Setup NB-IoT and access point ( cdp.iot.t-mobile.nl\ cq 172.27.131.100)
  • Open UDP socket to T-Mobile's CDP on port 15683
  • Loop: Send 2 RandomInteger values every minute

Two integers are analog values *100, represented by 8 hexadecimal characters. In this example case, they are random (noise) numbers, read from the unconnected A0 and A1 port.

REMARK: In this code example there is no anticipation build in for errors, i.e. retry or re-configure the socket. It's straightforward.

T-Mobile Setup

First, you need a service provider that supports NB-IoT in your region. This could be T-Mobile as it serves early access to this LTE sideband technology, and has a connected device platform (CDP) setup with an API or online interface, interesting forum, and a startup guide that is all focused on early adopters and startups. Do the following steps:

  • Get a T-Mobile demo SIM card online from one of its supporting partners
  • Sign up: make an account for UDP data
  • Register the Imei of your hardware modem device (Imei: use Simcom AT command AT+GSN or copy it from the label).
    REMARK: If you get an error message registering your Imei, try another browser! The support of the T-Mobile website has some limitations it seems.
  • Set up the callBack URL to forward your data to ATT (https://api.allthingstalk.io/proxy/network/tmobilenl)

If you like, you can follow also the T-Mobile examples for Beeceptor (simple end-point) of PostMan (API interfacing for managing your devices and subscriptions).

AllThingsTalk (ATT) Setup

Setup an account on AllThingsTalk. See pictures below on the setup. In your environment, you have a standard playground (or your create a new ground). In the ground, add a device by selecting "Your Own NB-IoT Device," give it a name, then select the provider (T-Mobile), and register the iMei info.

This initiates a device with empty assets. Assets are the sensors/actuator properties of the device, reflecting in the data that is sent over as the payload. Now create two assets as a sensor (as we send over two integer data), with a number as profile (we implement the numbers later), and save.

Our Arduino device will send over payload data as two integers, represented by 4 byte values binary data <sensor1high, sensor1low, sensor2high, sensor2low> ),

i.e. AT+CSOSEND=0, 8, "01A3021C" -> integer1: 01A3x, integer2 021Cx

Under Settings -> Payload Formats, we can define how ATT needs to implement this payload data (see picture below).

Use the ABCL to convert custom binary data to your representation:

{
 "sense": [
   {
     "asset": "Analog01",
     "value": {
       "byte": 0,
       "bytelength": 2,
       "type": "integer",
       "calculation": "val / 100"
     }
   },
   {
     "asset": "Analog02",
     "value": {
       "byte": 2,
       "bytelength": 2,
       "type": "integer",
       "calculation": "val / 100"
     }
   }
 ]
}

Here we define the 4 byte payload to be split out over the two 2-byte analog asset values, including the divide by 100 to make it 2-decimal values.

In the example above : integer1: 01A3x = 419d = 4.19 as represented analog value.

Last but not least in the ATT Dashboard: to keep your historical data, turn on Settings -> DataStorage

Send Data

Fire up your Arduino, load the sketch, and watch the serial monitor. Once you see the following AT sequence, data is sent over:

ATZ
OK
AT+CFUN=0
+CPIN: NOT READY
OK
AT+CREG=2
OK
AT*MCGDEFCONT="IP","cdp.iot.t-mobile.nl"
OK
AT+CFUN=1
OK
AT+CBAND=8
OK
AT+COPS=1,2,"20416"              -> CONNECT TO T-MOBILE NETWORK
+CPIN: READY
+CREG: 2
+CREG: 5,"0405","00517C66",9     -> CONFIRMED 9 = NBioT
OK
AT+CGCONTRDP
OK
AT+CSQ                           -> SIGNAL QUALITY
+CSQ: 17,0
OK
AT+CSOC=1,2,1                    -> DEFINE UDS SOCKET
+CSOC: 0
OK
AT+CSOCON=0,15683,"172.27.131.100"     -> OPEN SOCKET PORT 15683
OK
AT+CSOSEND=0,8,"01A3021C"              -> DATA IS SEND, 8char, 4 bytes in HEX
OK

In the T-Mobile project dashboard, you can see the last payload sent. Project -> Details -> Devices-List

In the ATT dashboard you should see data coming in under 'life data' of your device:

Under debug, you can see the payload data and the parsing. If parsing goes wrong, you can see it here.

To Be Done

  • Actuator data send to device
  • MQTT forwarding

Code

SIM7020zero.inoC/C++
sim7020 Demo Sketch
#include "SIM7020_script.h"
//
// Sim7020E demo to AllThingsTalk Cloud via Nbiot T-Mobile
// March 2019 V 1.00
// J.Vos - Voske
//
// See also Hackster.io for explanation
//
//
// Pinning to Module : 
// MKR pin13 = RX = PB23_S5_RX => SerCom5 PAD3
// MKR pin14 = TX = PB22_S5_TX => SerCom5 PAD2
// MKR Pin6 = SLEEP
// MKR Pin7= PWR

#define TIMEOUT 200        // AT-command timeout in 100ms  -> 200=20sec
#define COMMANDMAX 1024
const int SleepPin = 6;       
const int PowerPin = 7;
char SimError[] = "**Error ";
char SimTimeout[] = "**Timeout ";
char MyImei[32];
char MyIp[16];
char MyMfr[16];
char MyCicc[24];
char MyModel[16];
int TimeOut=TIMEOUT;

char simresult[COMMANDMAX];  // AT command result buffer

void setup(){
pinMode(SleepPin,OUTPUT);
pinMode(PowerPin,OUTPUT);
digitalWrite(SleepPin,LOW); // let DTR in pull-UP
digitalWrite(PowerPin,HIGH); // Keep PowerKey high

int t=10;  //Initialize serial and wait for port to open, max 10 second waiting
  Serial.begin(921600UL);
  while (!Serial) {
    ; delay(1000);
    if ( (t--)== 0 ) break;
  }
  
Serial.print("SIM Demo starts.");
Powercycle();   // run power cycle
Serial1.begin(115200); // RX/TX serial connect SIM7020

}


void loop() {
delay(1000);
//SerialCheck();

if (readimei() !=1) transparantmode();
if (readmfr() !=1) transparantmode();
if (readcicc() !=1) transparantmode();
if (readmodel() !=1) transparantmode();
//Serial.print("\n** Imei:");Serial.println(MyImei);
//Serial.print("** cICC:");Serial.println(MyCicc);
//Serial.print("** Mfr: ");Serial.println(MyMfr);
//Serial.print("** Model: ");Serial.println(MyModel);
delay(2000);


if  (runscript(NBstart)!=1 ) transparantmode();
delay(1000);
if  (runscript(NBopensocket)!=1 ) transparantmode();
delay(1000);
while (1) 
{
  sendpayload2int( (int) analogRead(A0), (int) analogRead(A1) );
  delay(60000); // wait 1 MINUTE //
}

}


// send payload 2 integers in hex format (2x4 = 8 characters) - MIXED UP NIBBLES FOR DEMO !!! 
int sendpayload2int(int value0, int value1)
{
 char c=0;
 int t=0,n=0;
 simresult[0]=0;
 Serial1.flush();
 Serial1.print("AT+CSOSEND=0,8,\"");
 Serial1.print( ((byte) value0)&0x0F,HEX);          // setup every nibble (4bits) individual, as print-HEX formatting does not support leading Zero
 Serial1.print( ((byte) value0>>4)&0x0F,HEX);       // mixing up lower and higher nibbles to create bigger random number
 Serial1.print( ((byte) value0>>8)&0x0F,HEX);
 Serial1.print( ((byte) value0>>12)&0x0F,HEX);
 Serial1.print( ((byte) value1)&0x0F,HEX);
 Serial1.print( ((byte) value1>>12)&0x0F,HEX);
 Serial1.print( ((byte) value1>>4)&0x0F,HEX);
 Serial1.print( ((byte) value1>>8)&0x0F,HEX);
 Serial1.print('\"');
 Serial1.print('\r');Serial1.print('\n');
  while ( n<=TimeOut ){
  if (Serial1.available()) {
    c=Serial1.read();
    simresult[t]=c;simresult[++t]=0;
    Serial.write(c);
    }
    else {delay(100);n++;};
    if ( (t>3) &&  (  ( (simresult[t-4]=='O')&&(simresult[t-3]=='K')&&(simresult[t-2]=='\r')&&(simresult[t-1]=='\n') )  ||  (  (simresult[t-4]=='O')&&(simresult[t-3]=='R')&&(simresult[t-2]=='\r')&&(simresult[t-1]=='\n')  )  ) )  break;
 }
 if (n>=TimeOut) {Serial.println(SimTimeout); return(-1);}
 else { if( (simresult[t-4]=='O')&&(simresult[t-3]=='R') ) {Serial.println(SimError);return(0);}
 else return(1);}
}


long int SerialCheck()
{
  int n;
  int t;
  int b=0;
  TimeOut =3;
  Serial.print("\n** Serial1 Test:");
  Serial1.end(); delay(1000);
  for (t=0;t<BAUDRATES;++t){
    Serial.print("\nBd=");Serial.println(Baudrates[t]);
    Serial1.begin((long int) Baudrates[t]);
    delay(1000);
    if (runscript(NBat)!=-1) b=t; // send AT command if baudrate works, then remeber baudrate
    for (n=0;n<24;++n) transparantmode();delay(200); // read responses 
    Serial1.end(); delay(1000);
   }
     Serial.print("\n** Serial1 Test Max Bd=");Serial.print((long int) Baudrates[b]);
Serial1.begin((long int) Baudrates[b]);
TimeOut=TIMEOUT;
return((long int) Baudrates[b]); // return highest speed
}


// run script -> array of strings ending with 0-string , result 1=ok, 0= error -1=timeout
int runscript(const char** scrpt){
int t=0;
int s=1;
//Serial.println("\n** SimScript:");
while ( strlen( scrpt[t]) > 1){
   s = writecommand(scrpt[t]);
   delay(1000);
   if ( s==1 ) ++t;
   else break;
   }  
return(s);
}


// write one commandline to Serial, result 1=ok, 0= error -1=timeout
int writecommand(const char* cmd){
  char c=0;
  int t=0,n=0;
  simresult[0]=0;
 Serial1.flush();
 Serial1.print(cmd);Serial1.print('\r');Serial1.print('\n');
 while ( n<=TimeOut ){
  if (Serial1.available()) {
    c=Serial1.read();
    simresult[t]=c;simresult[++t]=0;
    Serial.write(c);
    }
    else {delay(100);n++;};
    if ( (t>3) &&  (  ( (simresult[t-4]=='O')&&(simresult[t-3]=='K')&&(simresult[t-2]=='\r')&&(simresult[t-1]=='\n') )  ||  (  (simresult[t-4]=='O')&&(simresult[t-3]=='R')&&(simresult[t-2]=='\r')&&(simresult[t-1]=='\n')  )  ) )  break;
 }
 if (n>=TimeOut) {Serial.println(SimTimeout); return(-1);}
 else { if( (simresult[t-4]=='O')&&(simresult[t-3]=='R') ) {Serial.println(SimError);return(0);}
 else return(1);}
}



// transparant mode
void transparantmode() {
 char c1=0,c2=0;
  Serial.println("*Transparant AT-mode - type *X to eXit -");
while(1)
 {
  if (Serial.available()) {      // If anything comes in Serial (USB),
    c2=c1;
    c1=Serial.read();
    if ( (c1=='X') && (c2=='*') ) break;
    else
    Serial1.write(c1);   // read it and send it out Serial1 
  }

  if (Serial1.available()) {     // If anything comes in Serial1 
    Serial.write(Serial1.read());   // read it and send it out Serial (USB)
  }
 }
 Serial1.write(c1);
 while ( Serial.available() ) {c1=Serial.read(); Serial1.write(c1);} // flush USB write buffer

}


void Powercycle()
{
Serial.print("\n**PowerCycle.");
digitalWrite(PowerPin,HIGH);
Serial.print(".high");
delay(2000);
digitalWrite(PowerPin,LOW);
Serial.print("..low");
delay(2000);
digitalWrite(PowerPin,HIGH);
Serial.print("..high\n");
//digitalWrite(SleepPin,HIGH);
delay(2000);
}


void Poweroff()
{
Serial.print("\n**Power..");
digitalWrite(PowerPin,LOW); // let DTR pull up
delay(3000);
Serial.print(".off");
}


// read iMei and put in global variable,  returns status -1,0,1
byte readimei(){
  int t=0,n=0,s=0;
  s=runscript(NBgetimei);
  if (s!=1) return(s);
  else {
  // anlyse result in simresult buffer
  t=0;n=0; 
  while(simresult[t++] !='\n');
  while(simresult[t] !='\n') MyImei[n++]=simresult[t++]; // copy second line
  MyImei[n]=0;
  return (s);
  }
} // end function


// read Mfr and put in global variable,  returns status -1,0,1
byte readmfr(){
  int t=0,n=0,s=0;
  s=runscript(NBgetmfr);
  if (s!=1) return(s);
  else {
  // anlyse result in simresult buffer
  t=0;n=0; 
  while(simresult[t++] !='\n');
  while(simresult[t] !='\n') MyMfr[n++]=simresult[t++]; // copy second line
  MyMfr[n]=0;
  return (s);
  }
} // end function


// read CICC and put in global variable, returns status -1,0,1
byte readcicc(){
  int t=0,n=0,s=0;
  s=runscript(NBgetcicc);
  if (s!=1) return(s);
  else {
  // anlyse result in simresult buffer
  t=0;n=0; 
  while(simresult[t++] !='\n');
  while(simresult[t] !='\n') MyCicc[n++]=simresult[t++]; // copy second line
  MyCicc[n]=0;
  return (s);
  }
} // end function


// read Model and put in global variable, r returns status -1,0,1
byte readmodel(){
  int t=0,n=0,s=0;
  s=runscript(NBgetmodel);
  if (s!=1) return(s);
  else {
  // anlyse result in simresult buffer
  t=0;n=0; 
  while(simresult[t++] !='\n');
  while(simresult[t] !='\n') MyModel[n++]=simresult[t++]; // copy second line
  MyModel[n]=0;
  return (s);
  }
} // end function
Sim7020 Demo Include fileC/C++
Include file with AT-Scripts
#define BAUDRATES 15
const long int Baudrates[BAUDRATES] = {0,110,300,1200,2400,4800,9600,19200,38400,57600,115200,230400,460800,921600,3000000};

const char* NBstart[] = {
  "ATZ",
  "AT+CFUN=0",
  "AT+CREG=2",
  "AT*MCGDEFCONT=\"IP\",\"cdp.iot.t-mobile.nl\"",
  "AT+CFUN=1",
  "AT+CBAND=8",
  "AT+COPS=1,2,\"20416\"",  // sign up to T-Moble NL
  "AT+CGCONTRDP",
  "AT+CSQ",
  "\0"  // end script with 0x00
};

const char* NBopensocket[] = {
  "AT+CSOC=1,2,1",
  "AT+CSOCON=0,15683,\"172.27.131.100\"", // T-Mobile Server socket 0
  "\0"     // end script with 0x00
};

const char* NBclosesocket[] = { 
  "AT+CSODIS=0",
  "AT+CSOCL=0",
  "AT+CGACT=0,1",
  "\0"     // end script with 0x00
};

const char* NBhelloworld[] = {
  "AT+CSOSEND=0,0,\"Hello World!\"",
  "\0"     // end script with 0x00
};

const char* NBgetimei[] = {
  "AT+GSN",
  "\0"     // end script with  0x00
};

const char* NBgetmfr[] = {
  "AT+GMI",
  "\0"     // end script with  0x00
};

const char* NBgetcicc[] = {
  "AT+CCID",
  "\0"     // end script with  0x00
};

const char* NBgetmodel[] = {
  "AT+GMM",
  "\0"     // end script with  0x00
};

const char* NBdefault[] = {
  "ATZ",
  "\0"     // end script with  0x00
};

const char* NBat[] = {
  "AT",
  "\0"     // end script with  0x00
};

const char* NBss921600[] = {
  "AT+IPR=921600",
  "\0"     // end script with  0x00
};

const char* NBss0[] = {
  "AT+IPR=0",
  "\0"     // end script with  0x00
};

Schematics

Arduino - BK-SIM7020E Connections
Arduino - BK-SIM7020E Connections
Sim7020demo qez2scnack

Comments

Similar projects you might like

GSM based Home Automation

Project tutorial by Team Brink.IO

  • 20,920 views
  • 48 comments
  • 47 respects

MultiFunctional Clock

Project in progress by 3 developers

  • 16,777 views
  • 2 comments
  • 28 respects

OH HAI! on Windows 10 IoT Core

Project in progress by BuddyC

  • 12,978 views
  • 3 comments
  • 48 respects

IoT arduino ESP Garage Door opener (UD)

Project in progress by David Smerkous

  • 11,601 views
  • 6 comments
  • 17 respects
Add projectSign up / Login