===== SmartMe Network Laboratory - Arancino =====
The SmartMe lab is located at the Engineering building of the University of Messina, in Messina, Italy.
==== Introduction ====
Its main goal is to provide a single-node testbed consisting of a next-generation “combo” board, a.k.a. Single-Board Computer (SBC) mainly focusing on smart environment monitoring scenarios. To this purpose, 1 node has been equipped and made available to students for practicing in these contexts. This node is a combo SBC which combines onto the same node a so-called “carrier board”, in this case, an Arancino(TM) by smartme.IO, with an Arduino-like MCU (based on an ARM Cortex M0+ @48MHz) built-in, hosting a Raspberry Pi 3 Compute Module (CM3). In this section, we will focus on this Arancino node and its sensors/actuators.
==== Prerequisites ====
To approach this laboratory, the student should grasp:
* basic Node-RED concepts like nodes, flows, deploy and dashboard;
* quantities and metrics related to the physical quantities that can be detected using BME-class sensors: temperature (°C and F), relative humidity (%), pressure (hPa).
==== Technical details ====
The Arancino node is Internet-connected and reachable over its (hosted) IoT gateway (the CM3). The Arancino board is depicted in the following photo:
FIGURE arancino
The Lab configuration is shown in the figure below:
FIGURE internet-router-arancino-cam
==== Node schematic ====
The Arancino is interconnected/equipped as reported in the following schematic:
FIGURE riquadro sx in basso (+sensore)
==== Sensors ====
The node is equipped with the MIKROE-2467 “Environment click” [1] sensor shield, a mikroBUS™-socket compatible module, which hosts the BME680 environmental sensor from Bosch (datasheet available online [2]). This click module is designed to run on a 3.3V power supply.
The BME680 is a (combined) digital gas, humidity, pressure, and temperature sensor based on proven sensing principles.
The humidity sensor provides an extremely fast response time for fast context awareness applications and high overall accuracy over a wide temperature range. The pressure sensor is an absolute barometric pressure sensor with extremely high accuracy and resolution.
The integrated temperature sensor has been optimized for the lowest noise and highest resolution. Its output is used for temperature compensation of the pressure and humidity sensors and can also be used for estimation of the ambient temperature.
The gas sensor within the BME680 can detect a broad range of gases to measure indoor air quality for personal well being. Gases that can be detected by the BME680 include Volatile Organic Compounds (VOC) from paints (such as formaldehyde), lacquers, paint strippers, cleaning supplies, furnishings, office equipment, glues, adhesives, and alcohol.
^ Parameter ^ BME680
|Operating Supply Voltage |1.71V to 3.6V |
|Current Consumption | 2.1μA @1Hz (humidity + temperature) |
| " "|3.1μA @1Hz (pressure + temperature)|
| " "| 3.7μA @1Hz (humidity + pressure + temperature) |
| " "|0.09‒12mA (p/h/T/gas, depending on operation mode) 0.15μA (sleep mode) |
| Humidity Range |0‒100% r.H. |
|Temperature Range| -40‒+85°C |
| Pressure Range |300‒1100 hPa |
| Package | 3.0mm x 3.0mm x 0.93mm (metal lid LGA) |
| Resolution |20-bit (IIR)|
It can be used to test indoor air quality, to control HVAC (heating, ventilation, and air conditioning) systems, in a weather station, sports applications and more. It communicates with the target (micro)controller over SPI or I2C interface.
As shown in the previous figure, the 1Wire mode was chosen while the operating voltage is 5V. The sensor is connected to pin 8 of Arduino Uno.
[1] https://www.mikroe.com/environment-click
[2] https://download.mikroe.com/documents/datasheets/BME680.pdf
==== Actuators ====
There are no mechanical actuators or displays in this laboratory. The node is nevertheless equipped with three LEDs, including an MCU-controlled one. By default, i.e., after MCU reset (which may be triggered by quick double-click on the corresponding button) the aforementioned LED starts “breathing”, slowly. The “breathing” effect may be felt when an LED turns on and off by fading in and fading out, respectively.
==== Power supply ====
The node is powered by an external DC (5V) power supply over USB.
Software, libraries, and externals
The following libraries are required to run the example proposed below for the hands-on labs:
* SPI
* Adafruit Sensor
* Adafruit BME680
* Arancino
* BME680 sensor library by Adafruit depends on the “Adafruit Unified Sensor”, so it is mandatory to include this library.
The BME680 sensor library can be imported (including required deps) to the source code via:
#include
#include
#include
Then configure your BME680 sensor:
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME680 bme680_sensor; // I2C
// Set up oversampling and filter initialization
bme680_sensor.setTemperatureOversampling(BME680_OS_8X);
bme680_sensor.setHumidityOversampling(BME680_OS_2X);
bme680_sensor.setPressureOversampling(BME680_OS_4X);
bme680_sensor.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme680_sensor.setGasHeater(320, 150); // 320*C for 150 ms
MQTT connectivity protocol requires a dedicated library. The “MQTT” library uses the Arduino Ethernet Client API for interacting with the underlying network hardware. This library provides a client for doing simple publish/subscribe messaging with a server that supports MQTT. The Arancino library can be imported to the source code via:
#include
==== “platformio.ini” (Project Configuration File) ====
The Project configuration file is named platformio.ini.
[env:uno]
platform = atmelsam
board = arancino
framework = arduino
lib_ldf_mode=deep+
lib_compat_mode=strict
lib_deps =
Arancino
Adafruit Unified Sensor
Adafruit BME680 Library
build_flags =
-Wno-reorder
-Wno-return-type
==== Communication ====
The user can connect and program the node (micro)controller by using the Distancelab environment by booking it for the time he needs. The user can use the Redis protocol. Redis stands for ...
Redis is a machine-to-machine (M2M) connectivity protocol usable for “Internet of Things” solutions. MQTT is a simple messaging protocol, designed for constrained devices with low-bandwidth. So, it’s the perfect solution for Internet of Things applications.
This publish/subscribe messaging pattern requires a message broker. The broker is responsible for distributing messages to interested clients based on the topic of a message. So a client must connect to a broker in order to:
publish messages specifying a topic so that other clients that have subscribed to that topic will be able to receive those messages;
receive messages subscribing to a specific topic.
FIGURE
==== Limits ====
A single user only can program the (micro)controller at any time. But each user, connected to the Distancelab (board)-hosted Node-RED instance, can concurrently read/visualize the metrics, if Node-RED is enabled to retrieve values from Redis, i.e., as long as samples read from sensors get periodically written onto predefined Redis variables.
M1: Redis-based MCU/MPU comm, including MPU-side NodeRED-based viz
==== Target group ====
This hands-on lab guide is intended for master students. Other target groups may benefit from it only if they follow it after having dealt with all the exercises proposed which belong to lower levels of difficulty.
==== Prerequisites ====
Liquid Crystal library, you may refer to the B1 exercise.
RGB LED. All the nodes of SmartME Network Laboratory are equipped with an RGB LED of which, however, at the moment it is only possible to use the green light component. This single colour component can be considered and effectively managed as a separate LED.
To turn on the green LED, you have to follow three steps:
first, you have to define a variable int that will hold the number of the pin that the LED is connected to (int greenPin = 3;);
second, you need to configure this variable in output mode (OUTPUT). You do this with a call to the pinMode(pin, mode) function, inside the setup() function.
finally, you need to send to this variable a HIGH signal by using the digitalWrite(pin, value) function, inside the loop() function. To turn off the LED, you need to send a LOW signal value to this pin. You can make the LED flash by changing the length of the HIGH and LOW states.
The digital pins either give you 5V (when turned HIGH) or 0V (when turned LOW) and the output is a square wave signal.
PWM stands for Pulse Width Modulation and it is a technique used in controlling the brightness of the LED. The PWM pins are labelled with ~ sign.
The function analogWrite() can be used to generate a PWM signal in those digital pins that are labelled with ~ sign.
The frequency of this generated signal for most pins will be about 490Hz and you can give the value from 0-255 using this function.
* analogWrite(0) means a signal of 0% duty cycle.
* analogWrite(127) means a signal of 50% duty cycle.
* analogWrite(255) means a signal of 100% duty cycle.
The function random() generates pseudo-random numbers. The syntax is random(max) or random(min, max), where min represents the lower bound of the random value, inclusive (optional); and max represents the upper bound of the random value, exclusive. The function returns a long random number between min and max-1.
The circuit:
RGB GREEN pin = Arduino pin 3 (~)
==== Scenario ====
Initialize the LCD screen with the label “rand value”, then calculates a random value. If randomValue is lower than lowThreshold, turn on the LED. If randomValue is upper than highThreshold, turn off the LED. After 1 second, print the random value to the LCD. Then, turn on the LED for 3 seconds, assigning it a value of brightness which corresponds to the random value previously calculated. Note, you can set the two values of the threshold as you prefer. You can modify the brightness of the LED because, although it is connected to a digital pin, the Arduino pin number 3 is labelled with ~ sign (PWM).
==== Result ====
You should see the LED on or off for 1 second, a second if the value is greater or less than the predefined threshold values. Next, you should see the randomly calculated value between 0 and 255 on the LCD, at that point, the LED should assign a brightness associated with that value, and keep it for 3 seconds.
==== Start ====
There are no special steps to be performed.
==== Steps ====
=== Step 1 ===
Include required libraries:
#include
#include
#include
#include
#include
and define a number of directives:
#define DEBUG true // enable debug
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
Declare the sensor(s) object, a number of useful vars and constants, and assign them (e.g., initial) values when required:
Adafruit_BME680 bme680_sensor; // I2C
// Used here to set a pin number:
const int ledPin = LED_BUILTIN; // the number of the LED pin
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
unsigned long currentMill=0;
const long interval = 150; // interval at which to blink (milliseconds)
float humidity_read;
float temperature_read;
float pressure_read;
unsigned long previousMillisTemp = 0; // environment reading interval
const long intervalTemp = 400; // interval at which to blink(milliseconds)
Functions declarations cannot be omitted (when using .cpp extension instead of .ino one):
void bme680_begin();
void bme680_read();
void blink_status();
=== Step 2 ===
Functions definitions for: BME680 sensor initialization and settings configuration (''bme680_begin()''); sample(s) acquisition, and writing into MPU-visible Redis variables (''bme680_read()''); (diagnostic) LED blinking (''blink_status()'').
void bme680_begin(){
if(!bme680_sensor.begin()) {
Arancino.println("Could not find BME680 sensor, check wiring!");
while (1);
}
// Set up oversampling and filter initialization
bme680_sensor.setTemperatureOversampling(BME680_OS_8X);
bme680_sensor.setHumidityOversampling(BME680_OS_2X);
bme680_sensor.setPressureOversampling(BME680_OS_4X);
bme680_sensor.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme680_sensor.setGasHeater(320, 150); // 320*C for 150 ms
}
void bme680_read(){
unsigned long currentMillis = millis();
if(currentMillis - previousMillisTemp >= intervalTemp) {
previousMillisTemp = currentMillis;
if(!bme680_sensor.performReading()) {
Arancino.println("Failed to perform reading :(");
return;
}
temperature_read = bme680_sensor.temperature;
humidity_read = bme680_sensor.humidity;
pressure_read = bme680_sensor.pressure / 100.0;
Arancino.set("temperature", temperature_read);
Arancino.set("humidity", humidity_read);
Arancino.set("pressure", pressure_read);
}
}
void blink_status(){
currentMill= millis();
if(currentMill - previousMillis >= interval) {
previousMillis = currentMill;
if(ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
}
=== Step 3 ===
Initialize the Redis structures (Arancino.begin()), initialize/setup the (e.g., I2C) communication logic (''Wire.begin()'') and sensor(s) (''bme680_begin()''), declare the LED pin as OUTPUT - we suggest to perform those steps within the setup() function:
void setup(void)
{
Arancino.begin();
Arancino.publish(0,"empty"); // need to populate default key
// redis-side
Wire.begin();
bme680_begin();
pinMode(ledPin, OUTPUT);
}
=== Step 4 ===
Implement ''loop()'' to do in the order:
- perform the readings (see Step 2 for more details);
- blink the led (see Step 2 for more details).
void loop()
{
bme680_read();
blink_status();
}
==== Result validation ====
Observe the state of the LED for 1 second, this status will depend on the random number calculated and the threshold values that have been set. Observe the random value calculated on the display monitor. Observe how the random value affects the LED brightness for 3 seconds.
==== Platformio.ini ====
[env:uno]
platform = atmelsam
board = arancino
framework = arduino
lib_ldf_mode=deep+
lib_compat_mode=strict
lib_deps =
Arancino
Adafruit Unified Sensor
Adafruit BME680 Library
build_flags =
-Wno-reorder
-Wno-return-type
==== aran.cpp ====
#include
#include
#include
#include
#include
#define DEBUG true // enable debug
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME680 bme680_sensor; // I2C
// Used here to set a pin number:
const int ledPin = LED_BUILTIN; // the number of the LED pin
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was
//updated
unsigned long currentMill=0;
const long interval = 150; // interval at which to blink
// (milliseconds)
float humidity_read;
float temperature_read;
float pressure_read;
unsigned long previousMillisTemp = 0; // environment reading
// interval
const long intervalTemp = 400; // interval at which to blink
// (milliseconds)
void bme680_begin();
void bme680_read();
void blink_status();
void bme680_begin(){
if(!bme680_sensor.begin()) {
Arancino.println("Could not find BME680 sensor, check wiring!");
while (1);
}
// Set up oversampling and filter initialization
bme680_sensor.setTemperatureOversampling(BME680_OS_8X);
bme680_sensor.setHumidityOversampling(BME680_OS_2X);
bme680_sensor.setPressureOversampling(BME680_OS_4X);
bme680_sensor.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme680_sensor.setGasHeater(320, 150); // 320*C for 150 ms
}
void bme680_read(){
unsigned long currentMillis = millis();
if(currentMillis - previousMillisTemp >= intervalTemp) {
previousMillisTemp = currentMillis;
if(!bme680_sensor.performReading()) {
Arancino.println("Failed to perform reading :(");
return;
}
temperature_read = bme680_sensor.temperature;
humidity_read = bme680_sensor.humidity;
pressure_read = bme680_sensor.pressure / 100.0;
Arancino.set("temperature", temperature_read);
Arancino.set("humidity", humidity_read);
Arancino.set("pressure", pressure_read);
}
}
void blink_status(){
currentMill= millis();
if(currentMill - previousMillis >= interval) {
previousMillis = currentMill;
if(ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(ledPin, ledState);
}
}
void setup(void)
{
Arancino.begin();
Arancino.publish(0,"empty"); // need to populate default key
// redis-side
Wire.begin();
bme680_begin();
pinMode(ledPin, OUTPUT);
}
void loop()
{
bme680_read();
blink_status();
}