Skip to content

Connect a sensor to a mobile application with UART or I²C

This application note explains how to simply realize an HMI and a connection to the Cloud for a sensor. A CO2 sensor is used as an example medium.

Connect electrically to the sensor

Several possibilities exist depending on the type of sensor:

Serial components/modules are often addressable via an I²C, SPI or serial link. In these different cases, the easiest way is to write a few lines of Java that will retrieve the data and save it in 'InTap' variables, i.e. variables stored in the TapNLink module:

flowchart LR
    A2(Extension port) <--> |I2C/SPI/UART...| B[Sensor];
    subgraph mod1 [TapNLink]
    A1(Java) <--> A2
    A1 <--> A3(InTap Variables)
    end
    style A1 fill:#22f,stroke:#0,color:#fff
    style A2 fill:#22f,stroke:#0,color:#fff
    style B fill:#1f1,stroke:#0,color:#0
    style mod1 fill:#ddd,stroke:#0,color:#000

Sensor equipped with a microcontroller

If the sensor is equipped with a microcontroller accessible from the module, there is no real need to copy the data into the 'InTap' variables. It is possible to directly access the microcontroller variables via the 'target' communication channels: SWD, S3P, CAN, ...

flowchart LR
    A2(lwM2M) <--> |SWD/S3P/CAN/Modbus/4-20mA/...| B1[MCU];
    subgraph B [Sensor]
    B1(MCU) <--> B2(Physical sensor)
    end
    subgraph sens1 [TapNLink]
    A2
    end

    style A2 fill:#22f,stroke:#0,color:#fff
    style B fill:#1f1,stroke:#0,color:#0
    style B1 fill:#5C5,stroke:#0,color:#0
    style B2 fill:#5C5,stroke:#0,color:#0
    style sens1 fill:#ddd,stroke:#0,color:#000

Sensor accessible by a fieldbus (Modbus...)

Finally, if the sensor itself is a finished product and you wish to access its data via an available 'fieldbus' interface, you can use a 'Tapioca' to do this:

flowchart LR
    A2(lwM2M) <--> |CAN/Modbus...| B1[Modbus interface];
    subgraph B [Sensor]
    B1[Modbus interface] <--> B2(Physical sensor)
    end
    subgraph sens1 [Tapioca]
    A2
    end

    style A2 fill:#22f,stroke:#0,color:#fff
    style B fill:#1f1,stroke:#0,color:#0
    style sens1 fill:#ddd,stroke:#0,color:#000
    style B1 fill:#5C5,stroke:#0,color:#0
    style B2 fill:#5C5,stroke:#0,color:#0

Collect and process data

In what follows, we will only deal with the first model: a sensor whose data can be read by a serial link. More precisely, we will consider as an example the CO2 sensor "SCD41" from Sensirion. This part has an I²C port at address 0x62. It provides in addition to the level of CO2 (ppm) the temperature and the humidity of the air.

We first connect 4 wires between the sensor and the TapNLink extension port:

- Vcc (red) on PA1,
- Gnd (black) on PA12,
- SDA (green) on PB2,
- SCL on PA11.

Modules connection

Create a new project

In the main menu of IoTize Studio, the command 'File | New' opens the project creation wizard. We can then specify:

- The name of the project: 'SensorTest',

- The type of 'Tap': here 'TapNLink NFC+BLE+WiFi' but any other model of TapNLink with JVM would be suitable,

- and select a new directory which will contain all of our files.

Modules connection

You don't have to specify an ELF/CSV file, and you can launch the creation with these only parameters.

Configure the project

Accessing to the sensor will be done by I²C from Java. We therefore validate the use of the JVM: "Use Internal JVM" => Yes

The fact of having validated the use of the JVM automatically created a Java file that could be compiled from a template. We can add the creation of the I2C port: "I2C" => Used

JVM configuration

As access to Java will only be from Java, there is no 'target protocol'.

Tap configuration

Create the variables

This sensor can provide us with three physical data: CO2 level, ambient temperature and humidity level. We can therefore in the Main bundle create three 'InTap' volatile variables that we rename:

 - var_CO2ppm

 - var_temperature

 - var_RH

Create variables

For these three variables:

 - we indicate that they are 'float' values (instead of 'integer'),

 - you can indicate an 'alias' ('CO2', 'Temperature', 'Humidity') and specify their unit ('ppm', '°C' and '%').

 - finally, add these variables in the Java file (popup then 'Add to Java'):

Change to float

Java Editing

We can then modify slightly the Java code to:

  • initialize the sensor in the constructor,

  • read the data from the sensor and copy the values into the 'InTap variables'.

At the end, we get the following Java file:

SensorTest.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
/**  =================     TAPNLINK JVM     ==================   **\
    File:  SensorTest.java                                         
           Created from JVM/template/TnL.java                       
    Date:  15/12/2022                            
    Tag:   {$tag_TnL} Automatic management from Studio.
           Do not remove manually the tags (e.g. lines containing 
           the "{$...}" string of characters).
\**  ==========================================================   **/
import com.iotize.jvm.*;       // System classes (Variables, MQTT...)
import com.iotize.jvm.hal.*;   // HAL drivers (Pins,...)

public class SensorTest implements MainClass {
    public TapNLinkSys system; // To access to the lwM2M Engine {$_cls}
    public I2c  instanceI2C  = new I2c(50000); // {$H_I2C}
    public static final byte   DEFAULT_I2C_ADDRESS     = 0x62;  //for Sensirion SC41 sensor

    // First the variables declarations {$var_dec}
    public static final short ID_VAR_RH = 3;  //{$D:3}
    public static final short ID_VAR_TEMPERATURE = 2;  //{$D:2}
    public static final short ID_VAR_CO2PPM = 1;  //{$D:1}

    // Variables registration {$var_reg}
    public TapNLinkVarFloat var_RH      = new TapNLinkVarFloat( ID_VAR_RH,  "var_RH", 0); //{$R:3} 
    public TapNLinkVarFloat var_temperature     = new TapNLinkVarFloat( ID_VAR_TEMPERATURE,  "var_temperature", 0); //{$R:2} 
    public TapNLinkVarFloat var_CO2ppm  = new TapNLinkVarFloat( ID_VAR_CO2PPM,  "var_CO2ppm", 0); //{$R:1} 

    public SensorTest ( ) {
        //{$Constructor}
        byte [] buf = { (byte) 0x21, (byte) 0xB1 }; 
        instanceI2C.writeBytes(DEFAULT_I2C_ADDRESS, buf, 2);
        MainClass.startTimer(5000);      // Start timer
    }

    public void onEvent(Object event) {     //Called by MainClass.timer overflow
    //{$onEvent_body}
        byte [] buf = { (byte) 0xEC?, (byte) 0x05,  //9 bytes to contain the answer... 
        (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0, (byte)0x0 } ;        
        instanceI2C.writeBytes(DEFAULT_I2C_ADDRESS, buf, 2);    //Send the command
        instanceI2C.readBytes(DEFAULT_I2C_ADDRESS, buf, 9); //Then read the current values
        var_CO2ppm.setValue( (float)(buf[0] *256 + buf[1]));        //Extract CO2 ppm from bytes 0-1
        var_temperature.setValue( (float) (-45) 
        + (175 * (float)(buf[3] *256 + buf[4])) / 65536 );      //Extract temperature from bytes 3-4
        var_RH.setValue( (float) 100 * (float)(buf[6] *256 + buf[7]) / 65536 ); //Extract humidity from bytes 6-7
    }   
    public void onException(final int errcode, final int par1, final int par2) {
    //{$onException_body}
        system.log( "Exception " + errcode + " at PC=" + par1 + " with JSP=" + par2, 0 );
    }
    public void onCheck(final int id) {     //Not called but must be present
    }
}
/*-------------------------------*/
/* End of File : SensorTest.java */
/*-------------------------------*/

In this file:

 - on line 14 is the declaration of the I²C driver which will be used to initialize the sensor (line 30) then to access the data (lines 38 and 39),

 - in line 23-25 the declarations of the three variables which will be written in 40 - 43,

 - in line 31 the start of the timer which will launch a periodic call (every 5 seconds) to the onEvent method. As this is the only call to this method, there is no need to test the 'event' parameter as input to the function.

First test from Studio

A first test can be launched from Studio. By connecting to the prototype, we obtain a first HTML screen in Studio which gives us in real time the values of the sensor read every 5 seconds:

First test on Studio

Generate an APK

You can refer to the 'Getting Started' to follow in detail the steps of generating a mobile application. The big steps are:

- Create an account on TapCloud,

- in Studio select 'Generated App' for the type of mobile App,

- select the main files for the spashscreen and/or icon,

- for each variable, setup the type of graphical representation you wish,

- then click on the button 'Generate App'.

You will then receive an email with a link to your first APK. Below a screenshot of a very first HMI for this sensor:

Generated App

If you need to generate an iOS version, you will have to download the project, then to compile it onto a Mac.

Back to top