Customized IoTerop IOWA client on RPi4 + SIM7070G

In this Guide you will learn how to set up the SIMCom SIM7070G on a Raspberry Pi 4 and how to build a custom object from IoTerop's IOWA LWM2M stack which will deliver the CPU temperatur from the RPi4.


No security layer!

Please keep in mind that we are using the free evaluation version of the IOWA LwM2M SDK on GitHub.
It does not include a security layer, only the full IOWA SDK does

This Guide is divided into the following 6 main steps:

Before we start you should know that it might be useful to start with our UDP guide for the SIM7070G if you have no experience with the device or our network yet. You can find the documentation here.

Useful links

1 Connect the SIM7070G with the Raspberry Pi 4

You can connect the modem directly with the GPIO interface of the Raspberry Pi.


But before you are able to access the device from your RPi you need to initialize IO and install some software from SIMCom. To do so just execute the following commands.

sudo apt-get update
sudo pip install RPi.GPIO
sudo apt-get install python-serial
wget -P ~/Documents/
cd ~/Documents
tar -xzf SIM7070G_Cat_M_NB_IoT_HAT_Code.tar.gz
sh ~/Documents/SIM7070G_Cat_M_NB_IoT_HAT_Code/RaspberryPi/
sudo pip install RPi.GPIO
sudo apt-get install python-serial

Afterwards you can enable UART and hardware connection in raspi-config.

sudo rapsi-config

Choose Interfacing Options -> Serial, close login shell -> open the hardware serial port.


Now you should be able to access the SIM7070G with your RPi4.

2 Set up the SIMCom 7070G for PPP connection

An easy way to setup a Point-to-Point Protocol connection is wvdial.
Open your wvdial.config file with the following command.

sudo nano /etc/wvdial.conf

Copy the following content and paste it into your wvdial.config file.


Network Operator

Be sure that the network operator matches your location (country).
You can find a list of the supported countries with its PLMN IDs here

[Dialer sim7070g]

Carrier Check = off
Stupid Mode = on

# Your modem device
Modem = /dev/ttyS0

# Set Baud rate
Baud = 115200

Init = ATZ

#Set band to LTE-M

# Set APN
Init2 = AT+CGDCONT=1,"IP","scs.telekom.tma.iot"
Init3 = AT+CNCFG=0,1,"scs.telekom.tma.iot"

#LTE-M with Telekom Deutschland
Init4= AT+COPS=0,2,"26201",8

#dial with *99# 
Phone = *99#

# These often suffice, but your ISP might require different details. They'r
# often dummy details used for all users on the ISP, frequently the ISP's
# name, but some ISP's do require you to use a real username and password.
Username = dummy
Password = dummy

After you have saved your file you are able to get your connection. Dial with the following command:

wvdial sim7070g

If dialing was successful you can add your route:

sudo ip route add dev ppp0



If everything has worked so far, you are now able to access our network with your RPi SIMCom setup via a PPP connection.

3 Register and subscribe your device

Before you can start working with the IoTerop LwM2M client you need to register your device to our IoT Creators Portal. Furthermore it is helpful to register an endpoint URL and do a lifecycle subscription on your device.

3.1 Register your device

Usually you would use your device IMEI but in our case the IoTerop client is the "device". So let's name it IoTeropCustomClient_1. The _1 is not necessary but the easiest way when it comes to changing the code later.
To add your device to the IoT Creators Portal, use the following Northbound API:

HeaderContent-Type: application/json,
Accept: application/json,
Authorization: Basic <YOUR BASE64 STRING>
"serialNumber": "IoTeropCustomClient_1",
"groupName": "<YOUR GROUP NAME>",
"protocol": "LWM2M",
"additionalParams": {},
"address": "",
"identifier": "IoTeropCustomClient_1"

You can find the required information (Group name & login credentials) in your IoT Creators account.


For the authentication you need to encode your credentials to base64 format. Therefore you can use this website. Just enter USERNAME:PASSWORD and copy your generated string. Replace <YOUR BASE64 STRING> with the generated string from the website.

3.2 Register an Application URL

I recommend to do a lifecycle subscription on your device so you can see if everything works fine. The easiest way to do that is beeceptor. Just enter an endpoint name (e.g. and copy the generated endpoint.

1455 1452

Use this URL to replace the <YOUR APPLICATION URL> in the following API. You also need the string from your credentials, which you have already used in the registration API before.

HeaderAccept : application/json
Content-Type : application/json
"headers" :{"authorization" : "<YOUR BASE64 STRING>"},

3.3 Lifecycle Subscription

I also recommend to do a lifecycle subscription so you can see all your device activities. To do that, you can use the following API with your group name:

HeaderAccept : application/json
Content-Type : application/json
"deletionPolicy": 0,
"groupName": "<YOUR GROUPNAME>",
"events": ["registration", "deregistration", "update", "expiration"],
"subscriptionType": "lifecycleEvents"

3.4 Resource Subscription

Subscribe to the resource of our measured CPU temperature.

HeaderAccept : application/json
Content-Type : application/json
"criteria": {"serialNumbers": ["IoTeropCustomClient_1"]},
"deletionPolicy": 0,
"groupName": "<YOUR GROUPNAME>",
"resources": [ {"resourcePath":"3303/0/5700"}, {"resourcePath":"3/0/0"}],
"subscriptionType": "resources"

4 Edit the IoTerop IOWA Custom Object Dynamic Client

Let us start with downloading the public version of IoTerop's IOWA LwM2M stack from GitHub.
Now you can do the changes as in the following screenshots (detailed description below the images).

Before we will start editing the main file we will need to do two little changes in the header file.
As mentioned we will use the custom object dynamic client, so please go to the samples/04-custom_object_dynamic folder and open the sample_object.h file.

4.1 sample_object.h



  • 1: Change SAMPLE_OBJECT_ID from 3200 to 3303

  • 2: Lower the SAMPLE_RES_COUNT from 3 to 1

  • 3: Definition:


  • 4: Replace the content of "SAMPLE_RES_DESCRIPTION{...}
    with the following line:
  • 5: Replace the content of "typedef struct{...}sample_obkect_values_t"
    with the following line:
float floatValue;

4.2 sample_object.c

Afterwards we have to adjust the sample_object.c file.
Open it and replace the cases of the switch case with the following case.



case 5700:
  if (operation == IOWA_DM_READ)
    dataP[i].value.asFloat = objectValuesP->floatValue;
  else if (operation == IOWA_DM_WRITE)
    objectValuesP->floatValue = dataP[i].value.asFloat;

4.3 main.c

Now we can finally edit the main.c file without getting errors. Just open it and do the following changes:

Modification 1-2


main.c modification 1 & 2

  • 1: Replace the IP address from IoTerop's test server with the one from IoT Creators
    IoT Creators LwM2M Server:

  • 2: Set the ID for the endpoint name to 1 instead of generating it from your computer.
    This is the part that I mentioned at the beginning. It is responsible for the _1 of our device name. The rest (IoTeropCustomClient) will be defined in the build progress.

Modification 3


main.c modification 3

  • 3: Add the following function to read the CPU temperature:
float read_cpu_temperature()
FILE *temperatureFile;
float T;
temperatureFile = fopen ("/sys/class/thermal/thermal_zone0/temp", "r");
if (temperatureFile == NULL)
    printf("Cannot read CPU Temperatur!")
fscanf (temperatureFile, "%f", &T);
T /= 1000; //to transfer in degrees
printf ("CPU temperature = %6.3f C.\n", T);
fclose (temperatureFile);
return T;

Modification 4-6


main.c modification 4-6

  • 4: Set value
// Delete:
objectValues.booleanValue = true;
objectValues.integerValue = 10;
objectValues.stringValue = 0;

objectValues.booleanValue = read_cpu_temperature();
  • 5: Replace loop content 1
objectValues.booleanValue = !objectValues.booleanValue;
objectValues.integerValue = objectValues.integerValue + 1;

objectValues.floatValue = read_cpu_temperature();
  • 6: Replace loop content 2
iowa_client_object_resource_changed(iowaH, SAMPLE_OBJECT_ID, 0, SAMPLE_OBJECT_BOOLEAN_RES_ID);
iowa_client_object_resource_changed(iowaH, SAMPLE_OBJECT_ID, 0, SAMPLE_OBJECT_INTEGER_RES_ID);

iowa_client_object_resource_changed(iowaH, SAMPLE_OBJECT_ID, 0, SAMPLE_OBJECT_FLOAT_RES_ID);

Save the files and that's it. Now you are ready to build your custom client.

5 Build and run the client

We will build our custom object dynamic just like in the documentation from IoTerop:

Create the build folder in the IOWA repository:

~/IOWA$ mkdir build

Go to your new folder:

~/IOWA$ cd build

Launch cmake in debug mode and define the endpoint name. This is the part where we define the rest of our device name -> IoTeropCustomClient

~/IOWA/build$ cmake -DCMAKE_BUILD_TYPE=Debug -DIOWA_DEV_NAME="IoTeropCustomClient"

Build the client and the server:

~/IOWA/build$ make -j 4

After the build progress is completed we can go to the 04-custom_object_dynamic folder:

~/IOWA/build$ cd samples/04-custom_object_dynamic

Now we can run the client:

~/IOWA/build/samples/04-custom_object_dynamic$ ./custom_object_dynamic_client

6 Read Request

While the custom client is running you can use the following API with your string of your credentials to do a read request on the temperature ressource (3303/0/7500).

HeaderContent-Type : application/json
Authorization : Basic <YOUR BASE64 STRING>


Check your Beeceptor!

After executing you should be able to see the event in your Beeceptor.