Omron Sensor Evaluation Board 2JCIE-EV - Review

Table of contents

RoadTest: Omron Sensor Evaluation Board 2JCIE-EV

Author: moi8765

Creation date:

Evaluation Type: Development Boards & Tools

Did you receive all parts the manufacturer stated would be included in the package?: True

What other parts do you consider comparable to this product?: Omron 2JCIE-BU01, 2JCIE-BL01, Click boards, Individual sensor boards

What were the biggest problems encountered?: For someone well versed with sensors and interfacing with Arduino, it was really a breeze using this. No complaints. Really good work on the software and documentation side. I really do not know why people are making a fuss in online forums

Detailed Review:

Introduction

The Omron Sensor evaluation board 2JSCIE-IE-AR1 is a wonderful addition to the electronic hobbyist`s kitty as it brings together a multitude of interesting sensors on a single board in very suitable form factors. It comes in three variants each with a different footprint for the Arduino MKR, the Raspberry Pi and the Adafruit Feather, each of which is widely popular in the community. Now, you may argue that how does it help to have all sensors on the same board when you can buy individual modules. Very true but when the question is about reliability and ease of prototyping nothing can beat a shield that directly mounts on top of a microcontroller that can connect to the internet. Effectively, what you have is a reliable IoT sensor node providing data to the cloud. I don`t need to re-emphasise what all possibilities this could open up. We`re taking wearable devices, predictive maintenance, security and even monitoring and surveillance. Let`s see the board in action so we can better understand what all possibilities it can open up.

 

 

The Kit

The kit sent over to me had the Omron sensor evaluation board - the 2JCIE-EV01-AR1, a MEMS Temperature sensor D6T-A01 and an Arduino MKR1010 Wifi board with Wifi and BLE capabilities. As always the packaging was really generous and hats off to the efforts of the element14 team in getting the package delivered without any hassles.

The evaluation board has five different sensors on it, namely the Sensirion SHT30 Temperature/Humidity, Texas Instruments OPT3001 Ambient Light, Omron 2SMPB Barometric Pressure, STMicroelectronics LIS2DW Accelerometer and Knowles SPH0645 Microphone. Let's look at relevant details and understand their usage one at a time.

 

Out of the box, the Omron sensor evaluation board comes unsoldered for obvious reasons. So the first task for me was to get this done first because if you are like me and don`t want to dive into the datasheet to check the pin configuration as soon as you receive the package, you are much better off this way.

imageimage

After soldering and mounting on the Arduino MKR board the setup looks like this. Sleek and compact, ideal for projects where you`d like to keep the embedded sorcery away from the eyes of spectators. So let`s open the datasheet to check out in what format the sensors are sending data to the MKR board...if this is enough to demotivate you from reading the rest of the article, hold on!! We have good news!! You don`t need to do any of that. The guys at Omron have already done that for you and the sensor data is yours to exploit now.

The following Github repository contains all the necessary Arduino libraries for using the evaluation board to its optimum potential : https://github.com/omron-devhub/2jcieev01-arduino

To install libraries, download them to a folder on your system.

In the Arduino IDE, go to the Sketch tab and click on Include .zip library

image

Navigate to the path where the zip library file was downloaded and select it.

And we`re done!!

Now let`s upload the code to the Arduino MKR1010 board and check our sensors in action.

 

 

 

 

Testing

The procedure I followed for testing was to test the example code for receiving data from each sensor and either printing it on the serial monitor or plotting it. I really wanted to test it on an oscilloscope but I do not have access to it as of now. Sometime later, I would update this article with actual scope plots.

TO test the examples, fire up the Arduino IDE, connect the Arduino board with a USB B cable and head over to the File tab.

From the dropdown menu, select Examples and look for the 2JCIE library.

image

Choose the corresponding sensor to be tested, compile and upload the code.

Accelerometer (LIS2DW)

/*
 * MIT License
 * Copyright (c) 2019, 2018 - present OMRON Corporation
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
/* includes */
#include "lis2dw.h"
#include <SPI.h>


/* defines */
#define LIS2DW_FIFO_SIZE   1
#define LIS2DW_VAL_DEVICEID 0x44
#define LIS2DW_CONV(x) ((double)(x) * 4000.0 / 32767.0)


#define conv8s_s16_le(b, n) ((int16_t)b[n] | ((int16_t)b[n + 1] << 8))


#define debg(x, ...) Serial.print(x, ##__VA_ARGS__)
#define debl(x, ...) Serial.println(x, ##__VA_ARGS__)


static uint8_t ram_acc[3 * 2 * LIS2DW_FIFO_SIZE] = {0};




/* defines */
#define GPIO_LED_R_PIN 4
#define GPIO_LED_G_PIN 5
#define GPIO_LED_B_PIN 6


#define PIN_CSB     0


#define SPI_CLK_SPEED   1000000
// #define SPI_CS_HW


#if defined(SPI_CS_HW)
#define SOFT_CS_UP()
#define SOFT_CS_DOWN()
#else
#define SOFT_CS_DOWN()  digitalWrite(PIN_CSB, LOW)
#define SOFT_CS_UP()    digitalWrite(PIN_CSB, HIGH)
#endif


/* SPI functions */
void spi_setup() {
    SPI.begin();
    SPI.beginTransaction(SPISettings(SPI_CLK_SPEED, MSBFIRST, SPI_MODE0));
    #if defined(SPI_CS_HW)
    SPI.setHwCs(true);
    #else
    pinMode(PIN_CSB, OUTPUT);
    digitalWrite(PIN_CSB, HIGH);
    #endif
}


/** <!-- spi_read {{{1 --> read data from SPI-bus.
 */
void spi_read(uint8_t* tx, uint8_t tx_len, uint8_t* rx, uint8_t rx_len) {
    uint8_t rxbuf[512] = {0};
    uint16_t total_size = (uint16_t)tx_len + (uint16_t)rx_len;


    SOFT_CS_DOWN();
    for (uint16_t i = 0; i < total_size; i++) {
        rxbuf[i] = SPI.transfer(tx[i]);
    }
    SOFT_CS_UP();
    for (uint16_t i = 0; i < rx_len; i++) {
        rx[i] = rxbuf[i + tx_len];
    }
}


/** <!-- spi_write {{{1 --> write data to SPI-bus.
 */
void spi_write(uint8_t* tx, uint8_t tx_len) {
    SOFT_CS_DOWN();
    for (uint8_t i = 0; i < tx_len; i++) {
        SPI.transfer(tx[i]);
    }
    SOFT_CS_UP();
}


/** <!-- lis2dw_setup {{{1 --> setup a accerelometer sensor.
 */
void lis2dw_setup(void) {
    uint32_t retry = 100;
    uint8_t rbuf[8] = {0};
    /* Check connection */
    while ((rbuf[0] != LIS2DW_VAL_DEVICEID) && (retry > 0)) {
        lis2dw_readRegister(LIS2DW_REG_WHOAMI, rbuf, 1);
        delay(10);
        retry--;
    }
    debg("LIS2DW: WhoAmI: "); debg(rbuf[0], HEX);
    debg(", retry:");
    debl(retry);


    lis2dw_normalconfig();
}


/** <!-- lis2dw_normalconfig {{{1 --> set configuration to registers.
 */
void lis2dw_normalconfig(void) {
    uint8_t wbuf[8] = {0};


    wbuf[0] = 0x54;   // REG1: 100Hz, High-Performance
    wbuf[1] = 0x06;   // REG2:
    wbuf[2] = 0x00;   // REG3:
    wbuf[3] = 0x00;   // REG4: INT1
    wbuf[4] = 0x00;   // REG5: INT2
    wbuf[5] = 0x14;   // REG6: FS 4g


    lis2dw_writeRegister(LIS2DW_REG_CTRL1, wbuf, 6);
}


/** <!-- lis2dw_read_and_avg {{{1 --> get accerelo values from FIFO and
 * make average values.
 */
int lis2dw_read_and_avg(int16_t* accl) {
    uint8_t* accbuf = ram_acc;
    int32_t accsum[3] = {0, 0, 0};


    /* get accel data (x,y,z) x N */
    lis2dw_fifo_read(accbuf);
    for (uint8_t i = 0; i < LIS2DW_FIFO_SIZE; i++) {
        int n = i * 6;
        accsum[0] += (int32_t)conv8s_s16_le(accbuf, n + 0);
        accsum[1] += (int32_t)conv8s_s16_le(accbuf, n + 2);
        accsum[2] += (int32_t)conv8s_s16_le(accbuf, n + 4);
    }
    accl[0] = (int16_t)(accsum[0] / LIS2DW_FIFO_SIZE);
    accl[1] = (int16_t)(accsum[1] / LIS2DW_FIFO_SIZE);
    accl[2] = (int16_t)(accsum[2] / LIS2DW_FIFO_SIZE);
    return 0;
}


void lis2dw_fifo_read(uint8_t* pdata) {
    lis2dw_readRegister(LIS2DW_REG_OUT_X_L, pdata, 3*2*LIS2DW_FIFO_SIZE);
}


/** <!-- lis2dw_writeRegister {{{1 --> set registers
 */
void lis2dw_writeRegister(uint8_t reg, uint8_t* pbuf, uint8_t len) {
    uint8_t txbuf[256] = {0};


    txbuf[0] = reg & 0x7F;


    for (uint8_t i = 0; i < len; i++) {
        txbuf[i + 1] = pbuf[i];
    }
    spi_write(txbuf, (len + 1));
}


/** <!-- lis2dw_readRegister {{{1 --> get registers
 */
void lis2dw_readRegister(uint8_t reg, uint8_t* pbuf, uint8_t len) {
    uint8_t txbuf[256] = {0};


    txbuf[0] = reg | 0x80;


    spi_read(txbuf, 1, pbuf, len);
}


/** <!-- setup - accelerometer sensor {{{1 -->
 * 1. setup LED gpio.
 * 2. setup sensor
 */
void setup() {
    Serial.begin(115200);
    Serial.println("peripherals: GPIO");
    pinMode(GPIO_LED_R_PIN, OUTPUT);
    pinMode(GPIO_LED_G_PIN, OUTPUT);
    pinMode(GPIO_LED_B_PIN, OUTPUT);


    digitalWrite(GPIO_LED_R_PIN, LOW);
    digitalWrite(GPIO_LED_G_PIN, LOW);
    digitalWrite(GPIO_LED_B_PIN, LOW);


    Serial.println("peripherals: I2C");
    spi_setup();  // master


    Serial.println("sensor: accelerometer");
    lis2dw_setup();
    delay(32);
}


/** <!-- loop - accelerometer sensor {{{1 -->
 * 1. blink LEDs
 * 2. read and convert sensor.
 * 3. output results, format is: x[mg], y[mg], z[mg]
 */
void loop() {
    static bool blink = false;
    int16_t accl[3];


    blink = !blink;
    digitalWrite(GPIO_LED_R_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_G_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_B_PIN, blink ? HIGH: LOW);
    delay(900);
    int ret = lis2dw_read_and_avg(accl);
    Serial.print("sensor output:");
    Serial.print(LIS2DW_CONV(accl[0]));
    Serial.print(",");
    Serial.print(LIS2DW_CONV(accl[1]));
    Serial.print(",");
    Serial.print(LIS2DW_CONV(accl[2]));
    #if defined(OUTPUT_RAW)  // raw output
    Serial.print(","); Serial.print(accl[0]);
    Serial.print(","); Serial.print(accl[1]);
    Serial.print(","); Serial.print(accl[2]);
    #endif
    Serial.print(", return code:");
    Serial.println(ret);
}

What this piece of code does is that it reads the accelerometer in all three axes and displays it on the serial monitor

image

The serial monitor displaying accelerometer readings. in the X, Y and Z direction

If you are confused regarding the direction just look for a minuscule indication on the PCB silkscreen and you would be good to go.

An averaging filter is used in the code to get noise-free readings which are pretty stable compared to the MPU-6050 which sometimes does give unstable readings at high sampling rates. TO verify this, I plotted the MPU readings on my desktop and the LIS2DW on my laptop and could clearly see the difference in the noise immunity. The LIS2DW is a very popular sensor from STM and is finding its way into every other development board they make now.

The datasheet emphasises the low power draw and the precision which can make it useful in many applications.

image

I don`t think I can imagine any application beyond what the datasheet has mentioned. Something that I am really interested in implementing is the implementation of a human gait analysis algorithm I had developed as part of an academic project and to analyse the results. Obviously, the Arduino MKR1010 isn`t capable of handling the kind of computation we are talking about here. Interesting would be to see if we can link the sensor readings up to a model on Edge Impulse and gather intelligent insights into the body movement. Though this is a research topic as of present, if this is possible then it would mean a lot, at least to folks at Omron who have done a commendable job in incorporating this MEMS device in the design.

 

 

Barometer : Omron

The barometer reading gives an indication of the pressure which in turn indicates the height above mean sea level.

/*
 * MIT License
 * Copyright (c) 2019, 2018 - present OMRON Corporation
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
/* includes */
#include "baro_2smpb02e.h"
#include <Wire.h>


/* defines */
#define BARO_2SMPB02E_CHIP_ID     0x5C


#define GPIO_LED_R_PIN 4
#define GPIO_LED_G_PIN 5
#define GPIO_LED_B_PIN 6


/* values */
baro_2smpb02e_setting_t baro_2smpb02e_setting;


/* macros */
#define conv8s_s24_be(a, b, c) \
        (int32_t)((((uint32_t)a << 16) & 0x00FF0000) | \
                  (((uint32_t)b << 8) & 0x0000FF00) | \
                   ((uint32_t)c & 0x000000FF))


#define baro_halt(a) {Serial.println(a); while (1) {}}




/* I2C functions */
/** <!-- i2c_write_reg8 {{{1 --> I2C write function for bytes transfer.
 */
bool i2c_write_reg8(uint8_t slave_addr, uint8_t register_addr,
                    uint8_t *write_buff, uint8_t len) {
    Wire.beginTransmission(slave_addr);


    Wire.write(register_addr);
    if (len != 0) {
        for (uint8_t i = 0; i < len; i++) {
            Wire.write(write_buff[i]);
        }
    }
    Wire.endTransmission();
    return false;
}


/** <!-- i2c_read_reg8 {{{1 --> I2C read function for bytes transfer.
 */
bool i2c_read_reg8(uint8_t slave_addr, uint8_t register_addr,
                   uint8_t *read_buff, uint8_t len) {
    i2c_write_reg8(slave_addr, register_addr, NULL, 0);


    Wire.requestFrom(slave_addr, len);


    if (Wire.available() != len) {
        return true;
    }
    for (uint16_t i = 0; i < len; i++) {
        read_buff[i] = Wire.read();
    }
    return false;
}




/** <!-- baro_2smpb02e_setup {{{1 --> setup for 2SMPB-02E
 * 1. check CHIP_ID to confirm I2C connections.
 * 2. read coefficient values for compensations.
 * 3. sensor setup and start to measurements.
 */
bool baro_2smpb02e_setup(void) {
    bool result;
    uint8_t rbuf[32] = {0};
    uint8_t ex;


    // 1.
    result = i2c_read_reg8(BARO_2SMPB02E_ADDRESS,
                           BARO_2SMPB02E_REGI2C_CHIP_ID, rbuf, 1);
    if (result || rbuf[0] != BARO_2SMPB02E_CHIP_ID) {
        baro_halt("cannot find 2SMPB-02E sensor, halted...");
    }


    // 2.
    result = i2c_read_reg8(BARO_2SMPB02E_ADDRESS,
            BARO_2SMPB02E_REGI2C_COEFS, rbuf, 25);
    if (result) {
        baro_halt("failed to read 2SMPB-02E coeffients, halted...");
    }


    // pressure parameters
    ex = (rbuf[24] & 0xf0) >> 4;
    baro_2smpb02e_setting._B00 = baro_2smpb02e_conv20q4_dbl(rbuf, ex, 0);
    baro_2smpb02e_setting._BT1 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_BT1, BARO_2SMPB02E_COEFF_S_BT1, rbuf, 2);
    baro_2smpb02e_setting._BT2 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_BT2, BARO_2SMPB02E_COEFF_S_BT2, rbuf, 4);
    baro_2smpb02e_setting._BP1 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_BP1, BARO_2SMPB02E_COEFF_S_BP1, rbuf, 6);
    baro_2smpb02e_setting._B11 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_B11, BARO_2SMPB02E_COEFF_S_B11, rbuf, 8);
    baro_2smpb02e_setting._BP2 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_BP2, BARO_2SMPB02E_COEFF_S_BP2, rbuf, 10);
    baro_2smpb02e_setting._B12 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_B12, BARO_2SMPB02E_COEFF_S_B12, rbuf, 12);
    baro_2smpb02e_setting._B21 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_B21, BARO_2SMPB02E_COEFF_S_B21, rbuf, 14);
    baro_2smpb02e_setting._BP3 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_BP3, BARO_2SMPB02E_COEFF_S_BP3, rbuf, 16);


    // temperature parameters
    ex = (rbuf[24] & 0x0f);
    baro_2smpb02e_setting._A0 = baro_2smpb02e_conv20q4_dbl(rbuf, ex, 18);
    baro_2smpb02e_setting._A1 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_A1, BARO_2SMPB02E_COEFF_S_A1, rbuf, 20);
    baro_2smpb02e_setting._A2 = baro_2smpb02e_conv16_dbl(
            BARO_2SMPB02E_COEFF_A_A2, BARO_2SMPB02E_COEFF_S_A2, rbuf, 22);


    // 3. setup a sensor at 125msec sampling and 32-IIR filter.
    rbuf[0] = BARO_2SMPB02E_VAL_IOSETUP_STANDBY_0125MS;
    i2c_write_reg8(BARO_2SMPB02E_ADDRESS, BARO_2SMPB02E_REGI2C_IO_SETUP,
                   rbuf, sizeof(rbuf));


    rbuf[0] = BARO_2SMPB02E_VAL_IIR_32TIMES;
    i2c_write_reg8(BARO_2SMPB02E_ADDRESS, BARO_2SMPB02E_REGI2C_IIR,
                   rbuf, sizeof(rbuf));


    // then, start to measurements.
    result = baro_2smpb02e_trigger_measurement(
            BARO_2SMPB02E_VAL_MEASMODE_ULTRAHIGH);
    if (result) {
        baro_halt("failed to wake up 2SMPB-02E sensor, halted...");
    }
    return false;
}


/** <!-- baro_2smpb02e_conv16_dbl {{{1 --> convert bytes buffer to double.
 * bytes buffer format is a signed-16bit Big-Endian.
 */
static double baro_2smpb02e_conv16_dbl(double a, double s,
                                       uint8_t* buf, int offset) {
    uint16_t val;
    int16_t ret;


    val = (uint16_t)(
            (uint16_t)(buf[offset] << 8) | (uint16_t)buf[offset + 1]);
    if ((val & 0x8000) != 0) {
        ret = (int16_t)((int32_t)val - 0x10000);
    } else {
        ret = val;
    }
    return a + (double)ret * s / 32767.0;
}


/** <!-- baro_2smpb02e_conv20q4_dbl {{{1 --> convert bytes buffer to double.
 * bytes buffer format is signed 20Q4, from -32768.0 to 32767.9375
 *
 * ### bit field of 20Q4
 * ```
 * |19,18,17,16|15,14,13,12|11,10, 9, 8| 7, 6, 5, 4| 3, 2, 1, 0|
 * | buf[offset]           | buf[offset+1]         | ex        |
 *                                                 A
 *                                                 |
 *                                                 +-- Decimal point
 * ```
 */
static double baro_2smpb02e_conv20q4_dbl(uint8_t* buf,
                                         uint8_t ex, int offset) {
    int32_t ret;
    uint32_t val;


    val = (uint32_t)((buf[offset] << 12) | (buf[offset + 1] << 4) | ex);
    if ((val & 0x80000) != 0) {
        ret = (int32_t)val - 0x100000;
    } else {
        ret = val;
    }
    return (double)ret / 16.0;
}


/** <!-- baro_2smpb02e_trigger_measurement {{{1 --> start the sensor
 */
static bool baro_2smpb02e_trigger_measurement(uint8_t mode) {
    uint8_t wbuf[1] = {
        (uint8_t)(mode | BARO_2SMPB02E_VAL_POWERMODE_NORMAL)};


    i2c_write_reg8(BARO_2SMPB02E_ADDRESS, BARO_2SMPB02E_REGI2C_CTRL_MEAS,
                   wbuf, sizeof(wbuf));
    return false;
}


/** <!-- baro_2smpb02e_read {{{1 --> read the sensor digit and convert to
 * physical values.
 */
int baro_2smpb02e_read(uint32_t* pres, int16_t* temp,
                      uint32_t* dp, uint32_t* dt) {
    bool ret;
    uint8_t rbuf[6] = {0};
    uint32_t rawtemp, rawpres;


    ret = i2c_read_reg8(
            BARO_2SMPB02E_ADDRESS, BARO_2SMPB02E_REGI2C_PRES_TXD2,
            rbuf, sizeof(rbuf));
    if (ret) {
        return 1;
    }


    *dp = rawpres = conv8s_s24_be(rbuf[0], rbuf[1], rbuf[2]);
    *dt = rawtemp = conv8s_s24_be(rbuf[3], rbuf[4], rbuf[5]);
    return baro_2smpb02e_output_compensation(rawtemp, rawpres, pres, temp);
}


/** <!-- baro_2smpb02e_output_compensation {{{1 --> compensate sensors
 * raw output digits to [Pa] and [degC].
 */
bool baro_2smpb02e_output_compensation(uint32_t raw_temp_val,
                                       uint32_t raw_press_val,
                                       uint32_t* pres, int16_t* temp
) {
    double Tr, Po;
    double Dt, Dp;


    Dt = (int32_t)raw_temp_val - 0x800000;
    Dp = (int32_t)raw_press_val - 0x800000;


    // temperature compensation
    baro_2smpb02e_setting_t* c = &baro_2smpb02e_setting;
    Tr = c->_A0 + c->_A1 * Dt + c->_A2 * (Dt * Dt);


    // barometer compensation
    Po = c->_B00 + (c->_BT1 * Tr) + (c->_BP1 * Dp) +
         (c->_B11 * Tr * Dp) + c->_BT2 * (Tr * Tr) +
         (c->_BP2 * (Dp * Dp)) + (c->_B12 * Dp * (Tr * Tr)) +
         (c->_B21 * (Dp * Dp) * Tr) + (c->_BP3 * (Dp * Dp * Dp));


    *temp = (int16_t)(Tr / 2.56);     // x100degC
    *pres = (uint32_t)(Po * 10.0);    // x10Pa
    return false;
}




/** <!-- setup - barometer sensor {{{1 -->
 * 1. setup LED gpio.
 * 2. setup sensor
 */
void setup() {
    Serial.begin(115200);
    Serial.println("peripherals: GPIO");
    pinMode(GPIO_LED_R_PIN, OUTPUT);
    pinMode(GPIO_LED_G_PIN, OUTPUT);
    pinMode(GPIO_LED_B_PIN, OUTPUT);


    digitalWrite(GPIO_LED_R_PIN, LOW);
    digitalWrite(GPIO_LED_G_PIN, LOW);
    digitalWrite(GPIO_LED_B_PIN, LOW);


    Serial.println("peripherals: I2C");
    Wire.begin();  // master


    Serial.println("sensor: barometer");
    baro_2smpb02e_setup();
    delay(32);
}


/** <!-- loop - barometer sensor {{{1 -->
 * 1. blink LEDs
 * 2. read and convert sensor.
 * 3. output results, format is: x10[Pa], x100[degC],digit,digit
 */
void loop() {
    static bool blink = false;
    uint32_t pres, dp, dt;
    int16_t temp;


    blink = !blink;
    digitalWrite(GPIO_LED_R_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_G_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_B_PIN, blink ? HIGH: LOW);
    delay(900);
    int ret = baro_2smpb02e_read(&pres, &temp, &dp, &dt);
    Serial.print("sensor output:");
    Serial.print(pres / 10.0);
    Serial.print("[Pa], ");
    Serial.print(temp / 100.0);
    Serial.print("[degC], ");
    Serial.print(dp);
    Serial.print("[],");
    Serial.print(dt);
    Serial.print("[], retun code:");
    Serial.println(ret);
}

The column on the left gives the value of the pressure sensed in Pa. This reading was a bit on the lower side as I am testing this on a mildly cold winter night. I do not have met data to compare with this reading though my Mom`s HTC Desire smartphone`s pressure sensor readings give an indication that the value is indeed low. The two columns on the right indicate the raw values read by the sensor. The good thing is that the readings are accurate up to the first decimal place. This can be extremely useful in an application like a pressure control valve actuation system.

 

Humidity Sensor SHT30

This device from Sensirion is something probably no maker would be familiar with as we are most familiar with the DHT11 which seems to be the goto choice. However, I found the results I got with this sensor really good.

/*
 * MIT License
 * Copyright (c) 2019, 2018 - present OMRON Corporation
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
/* includes */
#include "sht30.h"
#include <Wire.h>


/* defines */
#define SHT30_ADDR  0x44
#define SHT30_STATUSMASK  0xFC1F


#define GPIO_LED_R_PIN 4
#define GPIO_LED_G_PIN 5
#define GPIO_LED_B_PIN 6


#define conv16_u8_h(a) (uint8_t)(a >> 8)
#define conv16_u8_l(a) (uint8_t)(a & 0xFF)


#define conv8s_u16_be(b, n) \
    (uint16_t)(((uint16_t)b[n] << 8) | (uint16_t)b[n + 1])
#define ap_halt(a) {Serial.println(a); while (1) {}}


/* I2C functions */
/** <!-- i2c_write_reg16 {{{1 --> I2C write function for byte transfer.
 */
bool i2c_write_reg16(uint8_t slave_addr, uint16_t register_addr,
                     uint8_t *write_buff, uint8_t len) {
    Wire.beginTransmission(slave_addr);
    Wire.write(conv16_u8_h(register_addr));
    Wire.write(conv16_u8_l(register_addr));


    if (len > 0) {
        for (uint8_t i = 0; i < len; i++) {
            Wire.write(write_buff[i]);
        }
    }
    Wire.endTransmission();
    return false;
}


/** <!-- i2c_read_reg16 {{{1 --> I2C read function for bytes transfer.
 */
bool i2c_read_reg16(uint8_t slave_addr, uint16_t register_addr,
                    uint8_t *read_buff, uint8_t len) {
    i2c_write_reg16(slave_addr, register_addr, NULL, 0);


    Wire.requestFrom(slave_addr, len);


    if (Wire.available() != len) {
        return true;
    }
    for (uint16_t i = 0; i < len; i++) {
        read_buff[i] = Wire.read();
    }
    return false;
}


/** <!-- sht30_setup {{{1 --> setup a humidity sensor.
 */
void sht30_setup() {
    sht30_reset();
    sht30_startcheck();
    sht30_measstart();
}


/** <!-- sht30_reset {{{1 --> issue software reset and wait for it.
 */
void sht30_reset(void) {
    i2c_write_reg16(SHT30_ADDR, SHT30_SOFTRESET, NULL, 0);
    delay(10);
}


/** <!-- sht30_startcheck {{{1 --> clear status and check start status.
 */
void sht30_startcheck(void) {
    uint16_t stat = 0;


    i2c_write_reg16(SHT30_ADDR, SHT30_CLEARSTATUS, NULL, 0);  // clear status
    delay(1);


    int retry = 10;
    do {
        stat = sht30_readstatus();  // check status
        delay(10);
    } while (((stat & SHT30_STATUSMASK) != 0x0000) && (retry-- > 0));


    if (((stat & SHT30_STATUSMASK) != 0x0000) || (retry == 0)) {
        ap_halt("cannot detect SHT30 working.");
    }
}


/** <!-- sht30_measstart {{{1 --> start measurement.
 */
void sht30_measstart(void) {
    i2c_write_reg16(SHT30_ADDR, SHT30_MEAS_HIGHPRD, NULL, 0);
}


/** <!-- sht30_readstatus {{{1 -->
 */
uint16_t sht30_readstatus(void) {
    bool result;
    uint8_t readbuffer[3] = {0, 0, 0};
    uint16_t stat = 0xFFFF;


    result = i2c_read_reg16(SHT30_ADDR, SHT30_READSTATUS, readbuffer, 3);
    if (!result) {
        stat = conv8s_u16_be(readbuffer, 0);
    }
    return stat;
}


/** <!-- sht30_readTempHumi {{{1 --> read raw digits and
 * convert them to physical values.
 */
int sht30_readTempHumi(int32_t* humi, int32_t* temp) {
    bool result;
    uint8_t readbuffer[6];


    result = i2c_read_reg16(SHT30_ADDR, SHT30_READ_PERIODIC, readbuffer, 6);
    if (result) {
        return 1;
    }
    if (readbuffer[2] != sht30_crc8(readbuffer, 2)) {
        return 2;  // check crc8 code failed.
    }
    if (readbuffer[5] != sht30_crc8(readbuffer + 3, 2)) {
        return 3;  // check crc8 code failed.
    }


    uint16_t ST, SRH;
    ST = conv8s_u16_be(readbuffer, 0);
    SRH = conv8s_u16_be(readbuffer, 3);


    double stemp = (double)ST * 17500.0 / 65535.0 - 4500.0;
    *temp = (int32_t)stemp;


    //  Serial.print("SRH = "); Serial.println(SRH);
    double shum = (double)SRH * 10000.0 / 65535.0;
    *humi = (int32_t)shum;
    return 0;
}


/** <!-- sht30_crc8 {{{1 --> CRC-8 formula from page 14 of SHT spec pdf
 * Test data 0xBE, 0xEF should yield 0x92.
 *
 * Initialization data 0xFF
 * Polynomial 0x31 (x8 + x5 +x4 +1)
 * Final XOR 0x00
 */
uint8_t sht30_crc8(const uint8_t *data, int len) {
    const uint8_t POLYNOMIAL(0x31);
    uint8_t crc(0xFF);


    for (int j = len; j; --j) {
        crc ^= *data++;


        for (int i = 8; i; --i) {
            crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1);
        }
    }
    return crc;
}


/** <!-- setup - humidity sensor {{{1 -->
 * 1. setup LED gpio.
 * 2. setup sensor
 */
void setup() {
    Serial.begin(115200);
    Serial.println("peripherals: GPIO");
    pinMode(GPIO_LED_R_PIN, OUTPUT);
    pinMode(GPIO_LED_G_PIN, OUTPUT);
    pinMode(GPIO_LED_B_PIN, OUTPUT);


    digitalWrite(GPIO_LED_R_PIN, LOW);
    digitalWrite(GPIO_LED_G_PIN, LOW);
    digitalWrite(GPIO_LED_B_PIN, LOW);


    Serial.println("peripherals: I2C");
    Wire.begin();  // master


    Serial.println("sensor: illuminance");
    sht30_setup();
    delay(32);
}


/** <!-- loop - humidity sensor {{{1 -->
 * 1. blink LEDs
 * 2. read and convert sensor.
 * 3. output results, format is: x100[%RH], x100[degC]
 */
void loop() {
    static bool blink = false;
    int32_t humi, temp;


    blink = !blink;
    digitalWrite(GPIO_LED_R_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_G_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_B_PIN, blink ? HIGH: LOW);
    delay(900);
    int ret = sht30_readTempHumi(&humi, &temp);
    Serial.print("sensor output:");
    Serial.print(humi / 100.0);
    Serial.print("[%RH],");
    Serial.print(temp / 100.0);
    Serial.print("[degC], return code: ");
    Serial.println(ret);
}
// vi:
 for (int j = len; j; --j) {
        crc ^= *data++;


        for (int i = 8; i; --i) {
            crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1);
        }
    }
    return crc;
}


/** <!-- setup - humidity sensor {{{1 -->
 * 1. setup LED gpio.
 * 2. setup sensor
 */
void setup() {
    Serial.begin(115200);
    Serial.println("peripherals: GPIO");
    pinMode(GPIO_LED_R_PIN, OUTPUT);
    pinMode(GPIO_LED_G_PIN, OUTPUT);
    pinMode(GPIO_LED_B_PIN, OUTPUT);


    digitalWrite(GPIO_LED_R_PIN, LOW);
    digitalWrite(GPIO_LED_G_PIN, LOW);
    digitalWrite(GPIO_LED_B_PIN, LOW);


    Serial.println("peripherals: I2C");
    Wire.begin();  // master


    Serial.println("sensor: illuminance");
    sht30_setup();
    delay(32);
}


/** <!-- loop - humidity sensor {{{1 -->
 * 1. blink LEDs
 * 2. read and convert sensor.
 * 3. output results, format is: x100[%RH], x100[degC]
 */
void loop() {
    static bool blink = false;
    int32_t humi, temp;


    blink = !blink;
    digitalWrite(GPIO_LED_R_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_G_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_B_PIN, blink ? HIGH: LOW);
    delay(900);
    int ret = sht30_readTempHumi(&humi, &temp);
    Serial.print("sensor output:");
    Serial.print(humi / 100.0);
    Serial.print("[%RH],");
    Serial.print(temp / 100.0);
    Serial.print("[degC], return code: ");
    Serial.println(ret);
}r
 blink = !blink;
    digitalWrite(GPIO_LED_R_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_G_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_B_PIN, blink ? HIGH: LOW);
    delay(900);
    int ret = sht30_readTempHumi(&humi, &temp);
    Serial.print("sensor output:");
    Serial.print(humi / 100.0);
    Serial.print("[%RH],");
    Serial.print(temp / 100.0);
    Serial.print("[degC], return code: ");
    Serial.println(ret);
}

image

As can be seen from the serial monitor, the humidity is around 51% which is what the Met department in my city claims. Not that we care a lot about humidity being a few points off, this level of accuracy has immense importance in applications like an indoor garden which I am planning on, where automatic watering needs to be done. It can also be employed in a process control system inside a factory where exact variations in moisture need to be noted. Another application I can think of right away is in cooling and ventilation systems.

 

 

Illumination sensor

I

/**
 * MIT License
 * Copyright (c) 2019, 2018 - present OMRON Corporation
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
/* includes */
#include "opt3001.h"
#include <Wire.h>


/* defines */
#define GPIO_LED_R_PIN 4
#define GPIO_LED_G_PIN 5
#define GPIO_LED_B_PIN 6


#define conv8s_u16_be(b, n) \
    (uint16_t)(((uint16_t)b[n] << 8) | (uint16_t)b[n + 1])




/* I2C functions */
/** <!-- i2c_write_reg8 {{{1 --> I2C write function for bytes transfer.
 */
bool i2c_write_reg8(uint8_t slave_addr, uint8_t register_addr,
                    uint8_t *write_buff, uint8_t len) {
    Wire.beginTransmission(slave_addr);


    Wire.write(register_addr);
    if (len != 0) {
        for (uint8_t i = 0; i < len; i++) {
            Wire.write(write_buff[i]);
        }
    }
    Wire.endTransmission();
    return false;
}


/** <!-- i2c_read_reg8 {{{1 --> I2C read function for bytes transfer.
 */
bool i2c_read_reg8(uint8_t slave_addr, uint8_t register_addr,
                      uint8_t *read_buff, uint8_t len) {
    i2c_write_reg8(slave_addr, register_addr, NULL, 0);


    Wire.requestFrom(slave_addr, len);


    if (Wire.available() != len) {
        return true;
    }
    for (uint16_t i = 0; i < len; i++) {
        read_buff[i] = Wire.read();
    }
    return false;
}


/** <!-- opt3001_setup {{{1 --> setup for OPT3001
 * 1. sensor setup and start to measurements.
 */
void opt3001_setup(void) {
    uint8_t wbuf[2];


    wbuf[0] = OPT3001_CMD_CONFIG_MSB;
    wbuf[1] = OPT3001_CMD_CONFIG_LSB;


    i2c_write_reg8(OPT3001_ADDR, OPT3001_REG_CONFIG, wbuf, sizeof(wbuf));
}


/** <!-- opt3001_read {{{1 --> read sensor digit and convert to physical values
 */
int opt3001_read(uint16_t* light) {
    bool result;
    uint8_t rbuf[2];
    uint16_t raw_data;


    result = i2c_read_reg8(OPT3001_ADDR, OPT3001_REG_CONFIG,
                           rbuf, sizeof(rbuf));
    if (result) {
        return 1;
    }
    if ((rbuf[1] & 0x80) == 0) {
        return 2;  // sensor is working...
    }


    result = i2c_read_reg8(OPT3001_ADDR, OPT3001_REG_RESULT, rbuf, sizeof(rbuf));
    if (result) {
        return 100;
    }


    raw_data = conv8s_u16_be(rbuf, 0);
    *light = (uint16_t)(opt3001_convert_lux_value_x100(raw_data) / 100);
    return 0;
}


/** <!-- opt3001_convert_lux_value_x100 {{{1 --> convert sensors
 * raw output digits to [100lx]
 */
uint32_t opt3001_convert_lux_value_x100(uint16_t value_raw) {
    uint32_t value_converted = 0;
    uint32_t exp;
    uint32_t data;


    /* Convert the value to centi-percent RH */
    exp = (value_raw >> 12) & 0x0F;
    exp = 1 << exp;
    data = value_raw & 0x0FFF;
    value_converted = (uint32_t)(exp * data);


    return value_converted;
}


/** <!-- setup - illuminance sensor {{{1 -->
 * 1. setup LED gpio.
 * 2. setup sensor
 */
void setup() {
    Serial.begin(115200);
    Serial.println("peripherals: GPIO");
    pinMode(GPIO_LED_R_PIN, OUTPUT);
    pinMode(GPIO_LED_G_PIN, OUTPUT);
    pinMode(GPIO_LED_B_PIN, OUTPUT);


    digitalWrite(GPIO_LED_R_PIN, LOW);
    digitalWrite(GPIO_LED_G_PIN, LOW);
    digitalWrite(GPIO_LED_B_PIN, LOW);


    Serial.println("peripherals: I2C");
    Wire.begin();  // master


    Serial.println("sensor: illuminance");
    opt3001_setup();
    delay(32);
}


/** <!-- loop - illuminance sensor {{{1 -->
 * 1. blink LEDs
 * 2. read and convert sensor.
 * 3. output results, format is: x10[Pa], x100[degC],digit,digit
 */
void loop() {
    static bool blink = false;
    uint16_t illm;


    blink = !blink;
    digitalWrite(GPIO_LED_R_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_G_PIN, blink ? HIGH: LOW);
    digitalWrite(GPIO_LED_B_PIN, blink ? HIGH: LOW);
    delay(900);
    int ret = opt3001_read(&illm);
    Serial.print("sensor output:");
    Serial.print(illm);
    Serial.print(", return code:");
    Serial.println(ret);
}

image

The readings were taken by first covering the sensor with a bright object and then exposing it to bright light. The readings did increase on bringing te light source closer but the relation wasn`t a square law variation high school physics teaches us. I will need to do more precise testing to determine the exact characteristics. This sensor has applications in setting where illuminance needs to be controlled like in an indoor garden.

 

Knowles Microphone SPH0645

My personal favourite and a reason why I applied for this roadtest in the first place is the SPH0645 microphone from Knowles. This is a device that is being used by smartphone manufacturers like Vivo and I really wished I could do some voice recognition or processing with it. It is a

 

#include <I2S.h>


void setup() {
  // Open serial communications and wait for port to open:
  // A baud rate of 115200 is used instead of 9600 for a faster data rate
  // on non-native USB ports
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  // start I2S at 8 kHz with 32-bits per sample
  if (!I2S.begin(I2S_PHILIPS_MODE, 8000, 32)) {
    Serial.println("Failed to initialize I2S!");
    while (1); // do nothing
  }
}


void loop() {
  // read a sample
  int sample = I2S.read();


  if (sample) {
    // if it's non-zero print value to serial
    Serial.println(sample);
  }
}

image

The reading from the I2S channel when I played a tune

image

Random noise detected by the microphone

To validate this sensor, what I did was to

 

MEMS Thermal sensor D6T-1A-01

The MEMS sensor provided to me was the D6T-1A-01. The first thing to understand is that this is a MEMS sensor unlike pyroelectric sensors which detect changes in temperature only. The difference is immediately understandable on looking at the datasheet which provides really nice visualisation for noobs. SO, for sake of simplicity I will shamelessly copy that here

imageimage

The first important thing to note here is that It has a capturing angle of 58 degrees in both the X and Y directions and a range from  5  to  50 degrees Celsius. An accuracy of 1.5 degC is good enough for applications like process control, human detection and activity tracking. I really wanted to test the 32L version of this sensor so that I could produce a thermal image processing application that I had proposed in my application. Unfortunately, I have only been sent this 1x1 IR sensor array which has limited functionality.

imageimage

image

The code to use the D6T-A1 along with the Omron evaluation board was easily available

/*
 * MIT License
 * Copyright (c) 2019, 2018 - present OMRON Corporation
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */


/* includes */
#include <Wire.h>


/* defines */
#define D6T_ADDR 0x0A  // for I2C 7bit address
#define D6T_CMD 0x4C  // for D6T-44L-06/06H, D6T-8L-09/09H, for D6T-1A-01/02


#define N_ROW 1
#define N_PIXEL 1
#define N_READ ((N_PIXEL + 1) * 2 + 1)


#define SAMPLE_TIME_0009MS 9
#define SAMPLE_TIME_0010MS 10
#define SAMPLE_TIME_0012MS 12
#define SAMPLE_TIME_0015MS 15
#define SAMPLE_TIME_0020MS 20
#define SAMPLE_TIME_0040MS 40
#define SAMPLE_TIME_0060MS 60
#define SAMPLE_TIME_0100MS 100
#define SAMPLE_TIME_0200MS 200
#define SAMPLE_TIME_0400MS 400
#define SAMPLE_TIME_0800MS 800
#define SAMPLE_TIME_1600MS 1600
#define SAMPLE_TIME_3200MS 3200


#define PARA_0009MS_1 ((uint8_t)0x90)
#define PARA_0009MS_2 ((uint8_t)0xD3)
#define PARA_0009MS_3 ((uint8_t)0x29)
#define PARA_0010MS_1 ((uint8_t)0x90)
#define PARA_0010MS_2 ((uint8_t)0xD4)
#define PARA_0010MS_3 ((uint8_t)0x3C)
#define PARA_0012MS_1 ((uint8_t)0x90)
#define PARA_0012MS_2 ((uint8_t)0xD5)
#define PARA_0012MS_3 ((uint8_t)0x3B)
#define PARA_0015MS_1 ((uint8_t)0x90)
#define PARA_0015MS_2 ((uint8_t)0xD6)
#define PARA_0015MS_3 ((uint8_t)0x32)
#define PARA_0020MS_1 ((uint8_t)0x90)
#define PARA_0020MS_2 ((uint8_t)0xD7)
#define PARA_0020MS_3 ((uint8_t)0x35)
#define PARA_0040MS_1 ((uint8_t)0x90)
#define PARA_0040MS_2 ((uint8_t)0xD8)
#define PARA_0040MS_3 ((uint8_t)0x18)
#define PARA_0060MS_1 ((uint8_t)0x90)
#define PARA_0060MS_2 ((uint8_t)0xD9)
#define PARA_0060MS_3 ((uint8_t)0x1F)
#define PARA_0100MS_1 ((uint8_t)0x90)
#define PARA_0100MS_2 ((uint8_t)0xDA)
#define PARA_0100MS_3 ((uint8_t)0x16)
#define PARA_0200MS_1 ((uint8_t)0x90)
#define PARA_0200MS_2 ((uint8_t)0xDB)
#define PARA_0200MS_3 ((uint8_t)0x11)
#define PARA_0400MS_1 ((uint8_t)0x90)
#define PARA_0400MS_2 ((uint8_t)0xDC)
#define PARA_0400MS_3 ((uint8_t)0x04)
#define PARA_0800MS_1 ((uint8_t)0x90)
#define PARA_0800MS_2 ((uint8_t)0xDD)
#define PARA_0800MS_3 ((uint8_t)0x03)
#define PARA_1600MS_1 ((uint8_t)0x90)
#define PARA_1600MS_2 ((uint8_t)0xDE)
#define PARA_1600MS_3 ((uint8_t)0x0A)
#define PARA_3200MS_1 ((uint8_t)0x90)
#define PARA_3200MS_2 ((uint8_t)0xDF)
#define PARA_3200MS_3 ((uint8_t)0x0D)


/***** Setting Parameter 1 *****/
#define comparingNumInc 16 // x samplingTime ms   (range: 1 to 39)  (example) 16 x 100 ms -> 1.6 sec
#define comparingNumDec 16  // x samplingTime ms  (range: 1 to 39)  (example) 16 x 100 ms -> 1.6 sec
#define threshHoldInc 10 //  /10 degC   (example) 10 -> 1.0 degC (temperature change > 1.0 degC -> Enable)  
#define threshHoldDec 10 //  /10 degC   (example) 10 -> 1.0 degC (temperature change > 1.0 degC -> Disable)
//bool  enablePix[8] = {true, true, true, true, true, true, true, true};
/****************************/


/***** Setting Parameter 2 *****/
#define samplingTime SAMPLE_TIME_0100MS //ms (Can select only, 9ms, 10ms, 12ms, 15ms, 20ms, 40ms, 60ms, 100ms, 200ms, 400ms, 800ms, 1600ms, 3200ms)
/****************************/


uint8_t rbuf[N_READ];
int16_t pix_data = 0;
int16_t seqData[40] = {0};
bool  occuPix = 0;
bool  occuPixFlag = false;
uint8_t  resultOccupancy = 0;
uint16_t  totalCount = 0;


/** JUDGE_occupancy: judge occupancy*/
bool judge_seatOccupancy(void) { 
  int j = 0; 
  for (j = 0; j < 39; j++){
    seqData[39 - j] = seqData[38 - j];
  }
  seqData[0] = pix_data;            
  if (totalCount <= comparingNumInc){
    totalCount++;
  }
  if (totalCount > comparingNumInc){    
    if (occuPix == false){
      if ((int16_t)(seqData[0] - seqData[comparingNumInc]) >= (int16_t)threshHoldInc){
        occuPix = true;
      }
    }
    else{   //resultOccupancy == true
      if ((int16_t)(seqData[comparingNumDec] - seqData[0]) >= (int16_t)threshHoldDec){
        occuPix = false;
      }
    }
    if (resultOccupancy == 0) {                
        if(occuPix == true){
          resultOccupancy = 1;
        }
    }
    else{
      occuPixFlag = false;
      if (occuPix == true){
        occuPixFlag = true;
      }
      if (occuPixFlag == false){
        resultOccupancy = 0;
      }
    }
  }
  return true;
}


uint8_t calc_crc(uint8_t data) {
    int index;
    uint8_t temp;
    for (index = 0; index < 8; index++) {
        temp = data;
        data <<= 1;
        if (temp & 0x80) {data ^= 0x07;}
    }
    return data;
}


/** <!-- D6T_checkPEC {{{ 1--> D6T PEC(Packet Error Check) calculation.
 * calculate the data sequence,
 * from an I2C Read client address (8bit) to thermal data end.
 */
bool D6T_checkPEC(uint8_t buf[], int n) {
    int i;
    uint8_t crc = calc_crc((D6T_ADDR << 1) | 1);  // I2C Read address (8bit)
    for (i = 0; i < n; i++) {
        crc = calc_crc(buf[i] ^ crc);
    }
    bool ret = crc != buf[n];
    if (ret) {
        Serial.print("PEC check failed:");
        Serial.print(crc, HEX);
        Serial.print("(cal) vs ");
        Serial.print(buf[n], HEX);
        Serial.println("(get)");
    }
    return ret;
}




/** <!-- conv8us_s16_le {{{1 --> convert a 16bit data from the byte stream.
 */
int16_t conv8us_s16_le(uint8_t* buf, int n) {
    int ret;
    ret = buf[n];
    ret += buf[n + 1] << 8;
    return (int16_t)ret;   // and convert negative.
}




/** <!-- setup {{{1 -->
 * 1. initialize a Serial port for output.
 * 2. initialize an I2C peripheral.
 */
void setup() {
uint8_t para[3] = {0};
switch(samplingTime){
case SAMPLE_TIME_0009MS:
para[0] = PARA_0009MS_1;
para[1] = PARA_0009MS_2;
para[2] = PARA_0009MS_3;
break;
case SAMPLE_TIME_0010MS:
para[0] = PARA_0010MS_1;
para[1] = PARA_0010MS_2;
para[2] = PARA_0010MS_3;
break;
case SAMPLE_TIME_0012MS:
para[0] = PARA_0012MS_1;
para[1] = PARA_0012MS_2;
para[2] = PARA_0012MS_3;
break;
case SAMPLE_TIME_0015MS:
para[0] = PARA_0015MS_1;
para[1] = PARA_0015MS_2;
para[2] = PARA_0015MS_3;
break;
case SAMPLE_TIME_0020MS:
para[0] = PARA_0020MS_1;
para[1] = PARA_0020MS_2;
para[2] = PARA_0020MS_3;
break;
case SAMPLE_TIME_0040MS:
para[0] = PARA_0040MS_1;
para[1] = PARA_0040MS_2;
para[2] = PARA_0040MS_3;
break;
case SAMPLE_TIME_0060MS:
para[0] = PARA_0060MS_1;
para[1] = PARA_0060MS_2;
para[2] = PARA_0060MS_3;
break;
case SAMPLE_TIME_0100MS:
para[0] = PARA_0100MS_1;
para[1] = PARA_0100MS_2;
para[2] = PARA_0100MS_3;
break;
case SAMPLE_TIME_0200MS:
para[0] = PARA_0200MS_1;
para[1] = PARA_0200MS_2;
para[2] = PARA_0200MS_3;
break;
case SAMPLE_TIME_0400MS:
para[0] = PARA_0400MS_1;
para[1] = PARA_0400MS_2;
para[2] = PARA_0400MS_3;
break;
case SAMPLE_TIME_0800MS:
para[0] = PARA_0800MS_1;
para[1] = PARA_0800MS_2;
para[2] = PARA_0800MS_3;
break;
case SAMPLE_TIME_1600MS:
para[0] = PARA_1600MS_1;
para[1] = PARA_1600MS_2;
para[2] = PARA_1600MS_3;
break;
case SAMPLE_TIME_3200MS:
para[0] = PARA_3200MS_1;
para[1] = PARA_3200MS_2;
para[2] = PARA_3200MS_3;
break;
default:
para[0] = PARA_0100MS_1;
para[1] = PARA_0100MS_2;
para[2] = PARA_0100MS_3;
break;
}

    Serial.begin(115200);  // Serial baudrate = 115200bps
    Wire.begin();  // i2c master


    Wire.beginTransmission(D6T_ADDR);  // I2C client address
    Wire.write(0x02);                  // D6T register
    Wire.write(0x00);                  // D6T register
    Wire.write(0x01);                  // D6T register
    Wire.write(0xEE);                  // D6T register
    Wire.endTransmission();            // I2C repeated start for read
    Wire.beginTransmission(D6T_ADDR);  // I2C client address
    Wire.write(0x05);                  // D6T register
    Wire.write(para[0]);                  // D6T register
    Wire.write(para[1]);                  // D6T register
    Wire.write(para[2]);                  // D6T register
    Wire.endTransmission();            // I2C repeated start for read
    Wire.beginTransmission(D6T_ADDR);  // I2C client address
    Wire.write(0x03);                  // D6T register
    Wire.write(0x00);                  // D6T register
    Wire.write(0x03);                  // D6T register
    Wire.write(0x8B);                  // D6T register
    Wire.endTransmission();            // I2C repeated start for read
    Wire.beginTransmission(D6T_ADDR);  // I2C client address
    Wire.write(0x03);                  // D6T register
    Wire.write(0x00);                  // D6T register
    Wire.write(0x07);                  // D6T register
    Wire.write(0x97);                  // D6T register
    Wire.endTransmission();            // I2C repeated start for read
    Wire.beginTransmission(D6T_ADDR);  // I2C client address
    Wire.write(0x02);                  // D6T register
    Wire.write(0x00);                  // D6T register
    Wire.write(0x00);                  // D6T register
    Wire.write(0xE9);                  // D6T register
    Wire.endTransmission();            // I2C repeated start for read

}




/** <!-- loop - Thermal sensor {{{1 -->
 * 1. read sensor.
 * 2. output results, format is: [degC]
 */
void loop() {
    int i, j;


    memset(rbuf, 0, N_READ);
    // Wire buffers are enough to read D6T-16L data (33bytes) with
    // MKR-WiFi1010 and Feather ESP32,
    // these have 256 and 128 buffers in their libraries.
    Wire.beginTransmission(D6T_ADDR);  // I2C client address
    Wire.write(D6T_CMD);               // D6T register
    Wire.endTransmission();            // I2C repeated start for read
    Wire.requestFrom(D6T_ADDR, N_READ);
    i = 0;
    while (Wire.available()) {
        rbuf[i++] = Wire.read();
    }


    if (D6T_checkPEC(rbuf, N_READ - 1)) {
        return;
    }


    // 1st data is PTAT measurement (: Proportional To Absolute Temperature)
    int16_t itemp = conv8us_s16_le(rbuf, 0);
    Serial.print("PTAT:");
    Serial.print(itemp / 10.0, 1);
    Serial.print(", Temperature:");


    // loop temperature pixels of each thrmopiles measurements
    for (i = 0, j = 2; i < N_PIXEL; i++, j += 2) {
        itemp = conv8us_s16_le(rbuf, j);
        pix_data = itemp;
        Serial.print(itemp / 10.0, 1);  // print PTAT & Temperature
        if ((i % N_ROW) == N_ROW - 1) {
            Serial.print(" [degC]");  // wrap text at ROW end.
        } else {
            Serial.print(", ");   // print delimiter
        }
    }
    judge_seatOccupancy(); //add
    Serial.print(", Occupancy:");
    Serial.println(resultOccupancy, 1);
    delay(samplingTime);
}

image

The column on the left gives the ambient temperature and the center column gives the temperature of the detected object. What the code essentially does is that it detects the number of warm objects in front of the sensor. However, the sensor failed to detect multiple objects for obvious reasons. Another thing which was very disappointing was that it could not detect the body temperature from a distance of just around 40cm. For all practical uses, this is not at all suitable, unless we are talking of a facial attendance system watchdog.

image

The minuscule sensor, I found it really hard to focus the camera on it...its just too small

 

Conclusion

The Omron Sensor Evaluation board has impressed in more ways than one. It provides a multitude of very accurate and useful sensors in a very small footprint. The cost-efficiency of such a solution is something the maker market really needs. The breakout boards from Adafruit are good, no doubt. However, if you keep buying individual modules for each different project, you end up spending more than what this board has to offer. If we keep the cost aside for a while, there is the issue of getting authentic and well-manufactured breakout boards. Chinese clones have become so common that it is really difficult to tell an original from a fake one. The use of a fake chip may result in a design house rejecting it in the evaluation phase, though it may have been ideal for the design.

Another use case I find for this is quick prototyping for experienced makers who have no idea what their next project might be. If you are like me and participate aggressively in online contests, I know you have faced the dilemma of having to order sensors for a project that demands submission in a period of just two weeks. In such a situation, having the Omron evaluation kit is handy. I was a little disappointed with the fact that I could not implement the project for which I had applied for the roadtest in the first place. However, that disappointment as been brushed aside by the great performance of the other sensors on the board which would feature in many future projects and blogs on the community. I thank Omron and element14 once again for the chance to test this wonderful product. I recommend it to every maker- beginner or advanced.

 

Cheers,

Moinak

Anonymous
Parents
  • What happened? Reading your review was like watching a televised hockey game where the score is tied and the station cut overs to the news. This is a technique that the national broadcasting company in Canada uses to encourage viewers to tune in.

     

    I loved your intro. The explanation of the kit was great. The testing results sufficient to want me to purchase a sensor board and then.......

     

    Did your Internet ink run out?

Comment
  • What happened? Reading your review was like watching a televised hockey game where the score is tied and the station cut overs to the news. This is a technique that the national broadcasting company in Canada uses to encourage viewers to tune in.

     

    I loved your intro. The explanation of the kit was great. The testing results sufficient to want me to purchase a sensor board and then.......

     

    Did your Internet ink run out?

Children