NB-IOT Prototyping Board

In this set of tutorials we to go though taking the NB-IOT board out of the box and using it to transfer data (in the first case an RFID tag sensor) to a server and then to a simple website. Then click on a button in the website and have the  NB-IOT board turn on an LED. Full duplex communication.

The way we are going to do this is first get the board to use a public server - in this case Hive MQTT, then we will use MQTT explorer a free program to receive and send messages. We will then replace HIVE MQTT with our own server running on AWS - after that is working we will replace MQTT explorer with our own website. Next lets send the GPS data and display it on a website, and last we add security! and some sets for writing better code to handle the AT commands

  1. Overview
  2. Setting Up the Arduino side
  3. Setting up the cloud side
  4. Hardware Details

Overview

The Board is based around the Arduino platform and the Quectel BG96 IOT module. The BG96 Module is a LTE modem. The BG96 module supports multiple cellular technologies, including LTE Cat-M1, NB-IoT, and EGPR. The module also includes a GPS. It is well suited for IOT applications as it can transmit small amounts of data over long ranges using only small battery consumption.

Setting Up the Arduino IDE

Setting up the Arduino IDE is very simple, The board works the same as any Arduino Nano.
From Tools - Board - Arduino AVR Boards select Arduino Nano.

The Serial to USB chip is the ch340g and may require and extra driver to be installed.

Testing the Arduino Is all Setup

Plug the Arduino into you computer using the USB cable and load the following code.

void setup() {
 Serial.begin(9600); // open the serial port at 9600 bps:
}

void loop() {
  delay(1000);            
  Serial.println("Hello NB-IOT World");  
}

This should simple Display "Hello NB-IOT World" to the Serial monitor. If this is the case we are ready to start.

Getting Started with the BG96

The BG96 communicates with the micro controller over a serial connection. The micro controller sends commands called AT commands in ASCII the BG96 replies also in ASCII. The default BAUD rate for the BG96 is 115200. It is connected on pins D9 and D10 of the Arduino

Turning the BG96 on

First thing to do Is turn the chip on, this is done simply by pulling Power (on pin 8) high High for 100ms and then again low.

#define BG96_PWR 8

void setup() {
  pinMode(BG96_PWR, OUTPUT); // Set the BG96 PowerPin as an output
  digitalWrite(BG96_PWR, HIGH); //Pull the pin high 
  delay(100);            // wait 100 ms
  digitalWrite(BG96_PWR, LOW);  // Pull Low
  delay(100);  
}

Hello BG96!

With the BG96 turned on we are ready to send our first AT command to check everything is working
The simplest command is simply "AT" the modem will respond with AT OK. This just means the modem is on and ready.

To send a Serial command to the modem an easy way is to use the SoftwareSerial library included in Arduino. Receive line is pin 9 and the transmit line is pin 10.  

The below code sends the command "AT" to the modem then waits 1 second to allow to the modem to respond it then loops though the BG96Serial buffer to see and reads each character one by one and prints them to the Serial (i.e your computer).

*This code is very blocking and for more complex tasks is not applicable but as a simple test to show AT commands working it will suffice

1#include <SoftwareSerial.h>
2#define BG96_PWR 8
3SoftwareSerial BG96Serial(9, 10); // RX, TX
4
5void setup() {
6  Serial.begin(9600); // open the serial port at 9600 bps:  
7  BG96Serial.begin(115200); // the default Baud rate of the BG96 is 115200 - this however can be changed
8  pinMode(BG96_PWR, OUTPUT); // Set the BG96 PowerPin as an output
9  digitalWrite(BG96_PWR, HIGH); //Pull the pin high 
10  delay(100);            // wait 100 ms
11  digitalWrite(BG96_PWR, LOW);  // Pull Low
12  delay(2000);
13}
14
15void loop() {
16  delay(1000);            // Wait a second to avoid spamming the chip
17  BG96Serial.println("AT"); // Send the command AT to the chip
18  delay(1000); // wait a certain amount of time for the chip to anwser back
19  while (BG96Serial.available()){ // If something is in the BGSerial buffer read each charcter until the buffer is empty
20    Serial.write(BG96Serial.read()); // write the anwser out to the Serial terminal 
21  }
22}

Serial Pass though

A super useful small program when dealing with modems and AT commands is a Serial pass though, this simply reads the reads what comes in on the Serial port and pushes to the modem and vice versa -allowing the user to directly write AT commands to the modem.

1#include <SoftwareSerial.h>
2#define BG96_PWR 8
3SoftwareSerial BG96Serial(9, 10); // RX, TX
4
5void setup() {
6  Serial.begin(9600); // open the serial port at 9600 bps:  
7  BG96Serial.begin(115200);
8  pinMode(BG96_PWR, OUTPUT); // Set the BG96 PowerPin as an output
9  digitalWrite(BG96_PWR, HIGH); //Pull the pin high 
10  delay(100);            // wait 100 ms
11  digitalWrite(BG96_PWR, LOW);  // Pull Low
12  delay(2000);
13}
14
15void loop() {
16  if (Serial.available()) {// If anything comes in Serial (USB), send it out to the modem
17    BG96Serial.write(Serial.read());
18  }
19  if (BG96Serial.available()) { // if anything arrives from the modem send it to BG96
20   Serial.write(BG96Serial.read());
21  }
22}

very important is to set the Carriage return in the Serial monitor. The BG96 needs AT commands to be terminated in two bytes \r\n or 0x0D 0x0A. The carriage return adds these bytes to the message when enter is pressed on the keyboard.

Lets Connect already!

Ok lets Connect to the internet and start sending some messages!!

The easiest was to do this is to use a Library to handle all the AT commands for us, feel free to that section but for the brave lets connect ourselves directly using the AT commands

The first command we can use is the AT+COPS=? command, this is going to scan the surrounding area and return all the cell towers within reach. this might take several minutes to scan the area before returning the answer

+COPS: (1,"Telekom.de","TFG","26201",8) , ......... a typical response will look something like this probably with multiple operators. In this case the module has found a Telekom tower, TFG is the short name of the operator, 26201 operator code for telekom, 8 means the tower is available to be connected to.

Make sure you have a sim card Inserted, If your sim card has a pinyou need to unlock it
for a pin number of 1234 use this at command
AT+CPIN="1234"


Sending a text message

A nice way to test if everything is working is to send a text message. Its pretty easy to do.

1#include <SoftwareSerial.h>
2#define BG96_PWR 8
3SoftwareSerial BG96Serial(9, 10); // RX, TX
4
5void setup() {
6  Serial.begin(9600); // open the serial port at 9600 bps:  
7  BG96Serial.begin(115200);
8  pinMode(BG96_PWR, OUTPUT); // Set the BG96 PowerPin as an output
9  digitalWrite(BG96_PWR, HIGH); //Pull the pin high 
10  delay(100);            // wait 100 ms
11  digitalWrite(BG96_PWR, LOW);  // Pull Low
12  delay(2000);
13}
14
15void loop() {
16  // Your code here
17  BG96Serial.println("AT+CMGF=1");
18  delay(1000);
19  // Example: Sending an AT command followed by CTRL+Z
20  BG96Serial.println("AT+CMGS=\"+123456789\""); // Replace +123456789 with the recipient's phone number
21  delay(1000); // Wait for the command to be sent
22  BG96Serial.println("Hello, this is a test message."); // Replace with your SMS message
23  delay(1000); // Wait for the message to be sent
24  BG96Serial.write(26); // Send CTRL+Z character
25  delay(1000); // Wait for the CTRL+Z character to be sent
26
27  // Your code here
28  
29  delay(5000); // Example delay before sending another message (5 seconds)
30}
31


CTRL+Z - this is the substitute character in ASCII (26 in decimal or 0x1A in hex)  one annoying this about using the Arduino as a pass though is you can not send the CTRL+Z character though the serial port. To send CTRL+Z we can just get the Arduino to check for a particle other character (in this case the *) and replace it with CTRL+Z

#include <SoftwareSerial.h>
#define BG96_PWR 8
SoftwareSerial BG96Serial(9, 10); // RX, TX

void setup() {
  Serial.begin(9600); // open the serial port at 9600 bps:  
  BG96Serial.begin(115200);
  pinMode(BG96_PWR, OUTPUT); // Set the BG96 PowerPin as an output
  digitalWrite(BG96_PWR, HIGH); //Pull the pin high 
  delay(100);            // wait 100 ms
  digitalWrite(BG96_PWR, LOW);  // Pull Low
  delay(2000);
}

void loop() {
  if (Serial.available()) {// If anything comes in Serial (USB), send it out to the modem
  	    char incomingByte = Serial.read();
    // Check if the incoming byte is an asterisk '*'
    if (incomingByte == '*') {
      BG96Serial.write(26); // replaces the * with CTRL+Z
    }else {
    BG96Serial.write(incomingByte);
    }
    
  }
  if (BG96Serial.available()) { // if anything arrives from the modem send it to BG96
   Serial.write(BG96Serial.read());
  }
}

Connect to the Internet

Ok lets connect onto the internet! The following AT commands are used to get us an IP address and connected onto the internet ready to send and receive information from a server

  1. AT+CMGF=1
  2. AT+CREG=1
  3. AT+CGATT=1
  4. AT+CGDCONT=1,"IP","<APN>"
  5. AT+QIACT=1
  6. AT+QIACT?

Ok lets make some sense of the nonsense

2. AT+CREG=1 - this set the network registration to automatic, the means the sim module is going to log on to the most available network it can see.

3. AT+CGATT=1 - you are instructing the BG96 module to attach to the Packet Domain Service, which is necessary for data transmission over the cellular network, this is the  GPRS (General Packet Radio Service). We now connecting to the data network required for the internet

4. AT+CGDCONT=1,"IP","<APN>" - this configures the PDP context, so the first number is which PDP context you wish to configure (I am not sure why anyone would need more then 1 but the option is there) the second is the protocol we are going to use in this case the IP (internet protocol) the third is the APN of the sim card, for example vodafone APN is "web.vodafone.de"

5. AT+QIACT=1 - this just activates the PDP context that was configured in the last step

6. AT+QIACT? just queries the stat of the PDP context, this is not needed to connect just to see if we are connected or not

Sending An MQTT Message

Before we have set up our own server lets use a free MQTT server from hivemq

  1. AT+QMTOPEN=0,"broker.hivemq.com",1883
  2. AT+QMTCONN=0,"clientExample"
  3. AT+QMTPUB=0,0,0,0,"steve"

Lets open a connection to the Hivemq server, what we need to do first is is open a connection

AT+QMTOPEN= first number is the client ID , the BG96 can hold multiple connections simultaneously
The second para is the broker address inside speech marks.
the third para is the port number

AT+QMTOPEN=0,"broker.hivemq.com",1883
OK

+QMTOPEN: 0,0

Once the port is open you will need to connect a client quickly, as the Hive mq server will close this connection

AT+QMTPUB=0,0,0,0,"steve"  -
first number is client ID,
Second number Integer type. Message identifier of packet. Range: 0–65535. The value is 0 only when =0.
third number Integer type. The QoS level at which the client wants to publish the messages.
     0 At most once
     1 At least once
     2 Exactly once
fourth number Integer type. Whether or not the server retains the message after it has been delivered to the current subscribers.
    0 The server does not retain the message after it has been delivered to the current subscribers
    1 The server retains the message after it has been delivered to the current subscribers
The last string is the topic your going to send the message to, so in this case sending to topic called steve

>This is test data, hello MQTT.
after reciving the > character you can typing your message, to send the message you need to send the byte 26 (CTRL+Z)

Connecting to a cloud

Now lets replace the Hive MQ cloud with our own.

To start we are going to use Mosquitto a MQTT broker. We are going to host this on AWS on a EC2 Instance - any cloud provider can be used in this case we are simple using AWS

Setting up a VM on AWS

Login to AWS / create an account

Then click on EC2

Pick a Ubuntu - T2 Micro (this is on the free tier)

For simplicity and testing lets allow traffic from all IP addresses and All types of traffic obviously this is the opposite of a secure system and is just to test

Download the PKK cert for putty

If you click on Instances in the EC2 dashboard you can see the VM.

Inside the Instance click on the securty tab

We going to change the Security settings To allow all traffic (this is just a test!)

Lets allow all traffic from any IP4 address

Lets connect to the Console of the instance we just started

Click connect one more time and you should now have a console screen

Ok Now we are all logged into AWS its time to set up the Mosquito Server.

We going to use Docker for this. We going to download a Mosquito container and run it on our server.

1)Install Docker - follow the instructions on the Docker website
https://docs.docker.com/engine/install/ubuntu/

2) install mosquito - from the eclipse website
https://hub.docker.com/_/eclipse-mosquitto

Running mosquitto

Before we can run the docker files we need to make a configuration file on the AWS instance and then copy that into the Docker container. The file we need to create is called mosquitto.conf and it needs to live at /etc/mosquitto/mosquitto.conf on the AWS machine (it can actually be placed anywhere but for this tutorial)

mosquitto.conf Is the configuration file for the mosquitto broker, here we can set many many option for this tutorial we will keep it as basic as possible we just going to allow both MQTT and websockets


Below is the file that needs to be created and placed at /etc/mosquitto/mosquitto.conf

allow_anonymous true

# MQTT Default listener
listener 1883 0.0.0.0

# MQTT over WebSockets
listener 9001 0.0.0.0
protocol websockets

Lets Run the Docker  

Use the command below

sudo docker run -it -p 1883:1883 -p 9001:9001 -v /etc/mosquitto/mosquitto.conf:/mosquitto/config/mosquitto.conf eclipse-mosquitto:2

Lets connect a client

Lets connect a test client to test if the server is up and running before we connect the NB-IOT board. MQTT explorer is a free to download simple tool

Under host we want to add the IP Address and turn any encryption off (THIS IS JUST A TEST ).


Under the advanced TAB we need to add all the Topics we want to subscribe to.Lets add a topic called steve.

We should be able now to publish a message to the steve topic

Lets connect a website

Now that we have a Broker set up and working lets Connect a website to it. We cant directly connect Via MQTT thus we are going to use websockets.

Below is the HTML and JS for a very simple website - the website has one button and when clicked publishes "Hello WebSockets" to the server.

client = new Paho.MQTT.Client("3.70.224.190", Number(9001), "/ws", "12"); In this line we need to change the IP address to the IP address of the VM from AWS

On line 16 is the function for what happens when receiving a message on the subscribed topic - currently it just prints to the console.

1<html>
2   <head>
3      <title>JavaScript MQTT WebSocket Example</title>
4	  <script src="https://cdnjs.cloudflare.com/ajax/libs/paho-mqtt/1.0.1/mqttws31.js" type="text/javascript">
5	 </script>
6	 <script type = "text/javascript" language = "javascript">
7		// Create a client instance: Broker, Port, Websocket Path, Client ID
8client = new Paho.MQTT.Client("3.70.224.190", Number(9001), "/ws", "12");
9
10// set callback handlers
11client.onConnectionLost = function (responseObject) {
12    console.log("Connection Lost: "+responseObject.errorMessage);
13}
14
15client.onMessageArrived = function (message) {
16  console.log("Message Arrived: "+message.payloadString);
17}
18
19// Called when the connection is made
20function onConnect(){
21	console.log("Connected!");
22	var message = new Paho.MQTT.Message("Message Payload");
23    message.destinationName = "steve";
24    message.qos = 0;
25
26client.send(message);
27client.subscribe("steve");
28}
29
30function PostMessage(){
31	var message = new Paho.MQTT.Message("Hello Websockets");
32    message.destinationName = "steve";
33    message.qos = 0;
34
35client.send(message);
36}
37
38console.log("connecting!");
39// Connect the client, providing an onConnect callback
40client.connect({
41	onSuccess: onConnect
42});
43client.onMessageArrived = function (message) {
44  console.log("Message Arrived: " + message.payloadString);
45}
46
47</script>
48   </head>
49     <body>
50		<button id="publish" onclick="PostMessage()">Hello Websockets</button>
51	 </body>
52	 </html>

Publish from the NB-IOT board to the website

Its finally time to Publish from our NB-IOT board to a website.

In the sending a MQTT message section you can read the AT commands to send a MQTT message, replace the HiveMQ server address with the IP address of your AWS Sever

AT+QMTOPEN=0,"3.70.224.190",1883

Set UpHello BG96ConnectAWS Set UpMQTT SetUpConnect A clientHTML & JS setupDocumentsSchematicBG96 AT commandsBG96 Hardware design