Hardware components | ||||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
![]() |
| |||||
|
This project is continuation of the initial project "Portable gas analyzer" - https://www.hackster.io/popa-mihai/portable-gas-analyzer-55291e
Only the remaining parts of the initial project will be discussed here, respectively:
- WiFi connection to Node-Red;
- Settings from PC application.
A. Setup WiFi
I add an esp8266-01 module connected to Serial2 of Microchip AVR IoT Mini Cellular Board. This serial port is selected with U1 CD4052B.
Communication between Microchip AVR IoT Mini Cellular Board and ESP0266-01 is done via AT commands. For that I used the following firmware in ESP8266: v0.9.2.2 AT Firmware.bin. The tool used for flash is esp8266_flasher.exe. Both files are attached to this project.
B. Setup Raspberry PI Zero
I used RPI Zero to hold Node-Red. The RPI Zero OS is flashed on SD card without UI. In order to get ready, I followed instruction from the official site and I installed RPI PI OS Lite: https://www.raspberrypi.com/software/operating-systems/
In order to find the RPI PI in the local network, I used several methods:
- I "look" into my router for "Raspberry Pi";
- I used "Nmap - Zenmap GUI" to scan my local network;
- I used "Fing" Android application to scan the local network;
- I connect an display and keyboard to Raspberry Pi Zero.
After that, I used PuTTy to connect to the Raspberry PI Zero IP and access the RPI console.
Note: during the RPI OS setup, user, pass, wifi ssid and pass are set; use the user and pass to access the RPI console.
Once the Raspberry PI Zero is set, up and running, I installed the Mosquito MQTT broker, following this command, in RPI console: sudo apt install -y mosquitto mosquitto-clients
More info about Mosquitto here: https://randomnerdtutorials.com/how-to-install-mosquitto-broker-on-raspberry-pi/
To verify if the mosquitto broker is up and running after RPI restart, type this in RPI console: mosquitto -v
After the mosquitto broker is running, start configure an user and password will be created; see this instruction for this: https://randomnerdtutorials.com/how-to-install-mosquitto-broker-on-raspberry-pi/
Proceed to install Node-red to Raspberry Pi Zero using the following command in RPI console: bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered)
In order to Node-Red start automatically after RPI boot, use this command in RPI console: sudo systemctl enable nodered.service
More info about how to install Node-Red to Raspberry PI here: https://randomnerdtutorials.com/getting-started-node-red-raspberry-pi/
Now, we can access Node-Red from any browser from the local network, typing the following sentence: http://Your_RPi_IP_address:1880/
Note: You can connect an display and keyboard to RPI Zero, in case that the IP can not be found; then, from console, type hostname -I and you will find the IP allocated to PRI.
Now, we will install Dashboard to Node-Red, using the information from here: https://randomnerdtutorials.com/getting-started-node-red-dashboard/
At the end, import the configuration file attached, named "flows.json".
Now, We can access the Node-Red UI typing in browser: http://Your_RPi_IP_address:1880/ui
The UI should look like this:
The settings information are stored into onboard eeprom memory 25CSM04 Serial EEPROM - 4 Mb, connected on SPI0.
To send and retrieve settings data to/from Portable gas analyzer, I created an PC application, using VisualC# (application and code are attached).
This is the UI of the application:
The functionality of the most of the options from PC Application are self explanatory. Most important settings are: timers, from "Timers settings" tab:
- interval_1 = set the interval for reading GPS data from GPS receiver;
- interval_2 = set the interval for reading on-board sensors;
- interval_3 = set interval to send data to Datacake over GSM;
- interval_4 = set interval to Node-Red over WiFi local network.
In case that the GPS data are not available and/or GSM connection available, the RTC can be set using option "Set RTC"; date and time are read from PC and are sent to on board RTC module.
"Datacake Settings" settings will be completed in application dedicated tab (and then will be sent to the "Portable gas analyzer" eeprom memory) after the account and the rest of the settings will be done on Datacake IoT portal; see first part of the project, Datacake dedicated part.
"WiFi Settings" contain the data used to connect to local WiFi network and mqtt broker. The data used to connect to mqtt Node-Red are set when the Mosquitto server is set on Raspberry Pi Zero 2; see above information.
From the "Memory management" tab, three actions can be done:
- read all settings data from gas sensor eeprom memory;
- clear the gas sensor Settings memory (erase the settings memory);
- reset the gas sensor.
Once all data from Gas sensor are read, these data can be saved on local PC into a.txt file; then, all these data can be uploaded in the application, to be sent thet to the Gas sensor eeprom memory. These operations can be done from "File management" option.
Important: all the Settings operation can be done only with SW1 DIP switch position 3 Settings = ON
Exception: if all the data are deleted (memory is cleared) but no new data are set into the Gas sensor eeprom memory, then the sensor will stay in Setting mode even SW1 DIP switch Settings = OFF.
Communication between Gas sensor and PC with Settings application is done via serial port, using an serial adapter working at 3.3V!
The connection will be set as following: Rx from Gas sensor will be connected to Tx from USB-UART adapter and Tx from gas sensor will be connected to Rx from the USB-UART adapter; and of course, GND will be common.
OBS: the USB-UART adapter will be set for 3.3V; otherwise, the Microchip AVR IoT Mini Cellular Board will be damaged.
On PC side, the corresponding COM port will be selected in Settings application.
Improvements:No preview (download only).
[
{
"id": "59c21aa936bd3e9d",
"type": "tab",
"label": "Flow 1",
"disabled": false,
"info": "",
"env": []
},
{
"id": "9fe1eb22b554a1ef",
"type": "ui_switch",
"z": "59c21aa936bd3e9d",
"name": "2. Lamp",
"label": "switch",
"tooltip": "",
"group": "959ffcf60a145f11",
"order": 4,
"width": 0,
"height": 0,
"passthru": true,
"decouple": "false",
"topic": "room/lamp",
"topicType": "msg",
"style": "",
"onvalue": "true",
"onvalueType": "bool",
"onicon": "",
"oncolor": "",
"offvalue": "false",
"offvalueType": "bool",
"officon": "",
"offcolor": "",
"animate": false,
"className": "",
"x": 100,
"y": 780,
"wires": [
[
"d94dad74f383ecc6"
]
]
},
{
"id": "ba978490e33285ec",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "Temperature",
"topic": "bme280_1/temperature",
"qos": "2",
"datatype": "utf8",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 110,
"y": 60,
"wires": [
[
"b03bbfb7868bbcc0",
"41bacc07026d9f29"
]
]
},
{
"id": "8bd185d1586f5407",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "Humidity",
"topic": "bme280_1/humidity",
"qos": "2",
"datatype": "utf8",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 120,
"y": 140,
"wires": [
[
"b340e0ac5e4747c2",
"23544fb8e9a77088"
]
]
},
{
"id": "d94dad74f383ecc6",
"type": "mqtt out",
"z": "59c21aa936bd3e9d",
"name": "Lamp",
"topic": "room/lamp",
"qos": "2",
"retain": "true",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "590e3ceaadf4028f",
"x": 430,
"y": 780,
"wires": []
},
{
"id": "b340e0ac5e4747c2",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "2. Humidity",
"group": "40a0ad60a6914ad6",
"order": 2,
"width": "0",
"height": "0",
"gtype": "gage",
"title": "Humidity",
"label": "%RH",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#a3d51a",
"#48a6ad",
"#f91a73"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 390,
"y": 140,
"wires": []
},
{
"id": "76215abc33a5e861",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "Pressure",
"topic": "bme280_1/pressure",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 120,
"y": 220,
"wires": [
[
"4fccdd4bb0a6304c"
]
]
},
{
"id": "4fccdd4bb0a6304c",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "3. Pressure atm",
"group": "40a0ad60a6914ad6",
"order": 3,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Pressure atm",
"label": "mmHg",
"format": "{{value}}",
"min": "700",
"max": "800",
"colors": [
"#00b500",
"#2dd7b5",
"#8aea2a"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 400,
"y": 220,
"wires": []
},
{
"id": "b03bbfb7868bbcc0",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "1. Temperature",
"group": "40a0ad60a6914ad6",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Temperature",
"label": "C",
"format": "{{value}}",
"min": 0,
"max": "50",
"colors": [
"#f7f25e",
"#1ae07a",
"#ca3838"
],
"seg1": "15",
"seg2": "36",
"diff": false,
"className": "",
"x": 400,
"y": 60,
"wires": []
},
{
"id": "90e02aefb4018103",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "VOC",
"topic": "gas_1/voc",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 340,
"wires": [
[
"b86464a32334e59b"
]
]
},
{
"id": "3fcf11f61b3e3dd8",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "CO",
"topic": "gas_1/co",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 400,
"wires": [
[
"9cdefd429f2aa5e3"
]
]
},
{
"id": "32f88e8c0d11a480",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "Smoke",
"topic": "gas_1/smoke",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 460,
"wires": [
[
"f808ee187ba76107"
]
]
},
{
"id": "a054691e607f690d",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "CH4",
"topic": "gas_2/ch4",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 520,
"wires": [
[
"1893865bf9ce798a"
]
]
},
{
"id": "80872bcfd5977682",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "ODOR",
"topic": "gas_2/odor",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 580,
"wires": [
[
"a3eaef1a19fcc4f2"
]
]
},
{
"id": "b79159443e9d18aa",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "NH3",
"topic": "gas_2/nh3",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 640,
"wires": [
[
"278e45a1bfecb9fc"
]
]
},
{
"id": "b86464a32334e59b",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "1. VOC",
"group": "e18e7bfc71e1b6af",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "VOC",
"label": "ppm",
"format": "{{value}}",
"min": 0,
"max": "550",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "125",
"seg2": "250",
"diff": false,
"className": "",
"x": 380,
"y": 340,
"wires": []
},
{
"id": "9cdefd429f2aa5e3",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "2. CO",
"group": "e18e7bfc71e1b6af",
"order": 3,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "CO",
"label": "ppm",
"format": "{{value}}",
"min": 0,
"max": "5500",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 370,
"y": 400,
"wires": []
},
{
"id": "f808ee187ba76107",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "3. Smoke",
"group": "e18e7bfc71e1b6af",
"order": 5,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Smoke",
"label": "ppm",
"format": "{{value}}",
"min": 0,
"max": "1200",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 380,
"y": 460,
"wires": []
},
{
"id": "1893865bf9ce798a",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "1. CH4",
"group": "89e107cc8a1d59b9",
"order": 2,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "CH4",
"label": "ppm",
"format": "{{value}}",
"min": 0,
"max": "12000",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 380,
"y": 520,
"wires": []
},
{
"id": "a3eaef1a19fcc4f2",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "2. ODOR",
"group": "89e107cc8a1d59b9",
"order": 4,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "ODOR",
"label": "ppm",
"format": "{{value}}",
"min": 0,
"max": "200",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 380,
"y": 580,
"wires": []
},
{
"id": "278e45a1bfecb9fc",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "3. HN3",
"group": "89e107cc8a1d59b9",
"order": 5,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "NH3",
"label": "ppm",
"format": "{{value}}",
"min": 0,
"max": "350",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 380,
"y": 640,
"wires": []
},
{
"id": "a26e069e6f2ef316",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "",
"topic": "Red_colour",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 590,
"y": 400,
"wires": [
[
"3c0399f38c158195"
]
]
},
{
"id": "5c0e8a330f6bda42",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "",
"topic": "Green_colour",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 590,
"y": 460,
"wires": [
[
"2ffe79980f2d91c8"
]
]
},
{
"id": "9a1b24e2d1a3cc5f",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "",
"topic": "Blue_colour",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 590,
"y": 520,
"wires": [
[
"37cd7151230bf074"
]
]
},
{
"id": "ca0f6df3eb6892f4",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "",
"topic": "IR_colour",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 600,
"y": 580,
"wires": [
[
"738b16aeff50a44f"
]
]
},
{
"id": "8bcbe512f694aa4f",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "",
"topic": "Clear_colour",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 590,
"y": 640,
"wires": [
[
"571c6a2baea1091c",
"93970a8dbd12db22"
]
]
},
{
"id": "3c0399f38c158195",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "f384dca67cae377d",
"order": 1,
"width": 0,
"height": 0,
"name": "1. Red colour",
"label": "Red",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "",
"fontSize": 16,
"color": "#fc2803",
"x": 810,
"y": 400,
"wires": []
},
{
"id": "2ffe79980f2d91c8",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "f384dca67cae377d",
"order": 2,
"width": 0,
"height": 0,
"name": "2. Green colour",
"label": "Green",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "",
"fontSize": 16,
"color": "#04f608",
"x": 820,
"y": 460,
"wires": []
},
{
"id": "37cd7151230bf074",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "f384dca67cae377d",
"order": 3,
"width": 0,
"height": 0,
"name": "3. Blue colour",
"label": "Blue",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "",
"fontSize": 16,
"color": "#0414fb",
"x": 820,
"y": 520,
"wires": []
},
{
"id": "738b16aeff50a44f",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "f384dca67cae377d",
"order": 4,
"width": 0,
"height": 0,
"name": "4. IR colour",
"label": "IR",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "",
"fontSize": 16,
"color": "#b6afaf",
"x": 810,
"y": 580,
"wires": []
},
{
"id": "571c6a2baea1091c",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "f384dca67cae377d",
"order": 5,
"width": 0,
"height": 0,
"name": "5. Clear colour",
"label": "Clear",
"format": "{{msg.payload}}",
"layout": "row-left",
"className": "",
"style": true,
"font": "",
"fontSize": 16,
"color": "#f9f6f6",
"x": 820,
"y": 640,
"wires": []
},
{
"id": "2c5327db39ed707b",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "DEW point",
"topic": "bme280_2/dew_point",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 600,
"y": 60,
"wires": [
[
"72f352753f8e90e7"
]
]
},
{
"id": "aad4d40ee6fdd194",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "Altitude relative",
"topic": "bme280_2/altitude_rel",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 580,
"y": 180,
"wires": [
[
"8ba21a3db155876b"
]
]
},
{
"id": "8ba21a3db155876b",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "3. Altitude relative",
"group": "1c641bc26d9970dd",
"order": 2,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Altitude relative",
"label": "meters",
"format": "{{value}}",
"min": "0",
"max": "250",
"colors": [
"#00b500",
"#04fb56",
"#3acbae"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 830,
"y": 180,
"wires": []
},
{
"id": "72f352753f8e90e7",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "1. DEW point",
"group": "1c641bc26d9970dd",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "DEW point",
"label": "C",
"format": "{{value}}",
"min": "-20",
"max": "50",
"colors": [
"#d31212",
"#00e68e",
"#2c32dd"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 810,
"y": 60,
"wires": []
},
{
"id": "53055fa5c0466ff7",
"type": "mqtt in",
"z": "59c21aa936bd3e9d",
"name": "Confort factor",
"topic": "bme280_2/confort_factor",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 590,
"y": 120,
"wires": [
[
"5a2e58131d7e02b2"
]
]
},
{
"id": "5a2e58131d7e02b2",
"type": "ui_gauge",
"z": "59c21aa936bd3e9d",
"name": "2. Confort factor",
"group": "1c641bc26d9970dd",
"order": 1,
"width": 0,
"height": 0,
"gtype": "gage",
"title": "Confort factor",
"label": "%",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 820,
"y": 120,
"wires": []
},
{
"id": "a49fd71ee19f61a5",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "959ffcf60a145f11",
"order": 3,
"width": 0,
"height": 0,
"name": "2. Ultimul mesaj",
"label": "Last message:",
"format": "{{msg.payload}}",
"layout": "row-spread",
"className": "",
"style": false,
"font": "",
"fontSize": 16,
"color": "#000000",
"x": 1060,
"y": 740,
"wires": []
},
{
"id": "1f48b7189eea5be8",
"type": "function",
"z": "59c21aa936bd3e9d",
"name": "Show last message",
"func": "// Acceseaz variabila global cu timpul ultimului mesaj primit\nvar lastMessageReceived = context.global.lastMessageReceived;\n\n// Verific dac variabila a fost definit\nif (lastMessageReceived) {\n // Creeaz un obiect Date pentru timpul curent\n var currentDate = new Date();\n // Actualizeaz variabila cu timpul curent\n context.global.lastMessageReceived = currentDate;\n // Formateaz timpul actualizat ntr-un format mai uor de citit\n var formattedTime = currentDate.toLocaleString();\n // Actualizeaz mesajul cu timpul formatat\n// msg.payload = \"Ultimul mesaj: \" + formattedTime;\n msg.payload = formattedTime;\n} else {\n msg.payload = \"Nu a fost recepionat niciun mesaj MQTT nc.\";\n}\n\n// Returneaz mesajul pentru a fi trimis mai departe\nreturn msg;\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 830,
"y": 740,
"wires": [
[]
]
},
{
"id": "960fce6f0e7a296b",
"type": "function",
"z": "59c21aa936bd3e9d",
"name": "function 5",
"func": "// Acceseaz variabila global cu timpul ultimului mesaj primit\nvar lastMessageReceived = context.global.lastMessageReceived;\n\n// Verific dac variabila a fost definit i adaug timpul ultimului mesaj primit la mesajul de ieire\nif (lastMessageReceived) {\n msg.payload = \"Ultimul mesaj MQTT a fost recepionat la: \" + lastMessageReceived;\n} else {\n msg.payload = \"Nu a fost recepionat niciun mesaj MQTT nc.\";\n}\n\n// Returneaz mesajul pentru a fi trimis mai departe\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 800,
"y": 800,
"wires": [
[
"544857b32eee4d32"
]
]
},
{
"id": "544857b32eee4d32",
"type": "debug",
"z": "59c21aa936bd3e9d",
"name": "debug 1",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 1040,
"y": 800,
"wires": []
},
{
"id": "c5bed6eeed8b4738",
"type": "inject",
"z": "59c21aa936bd3e9d",
"name": "",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "1",
"crontab": "",
"once": true,
"onceDelay": "1",
"topic": "",
"payload": "",
"payloadType": "date",
"x": 130,
"y": 880,
"wires": [
[
"a9e6d18f53bbd0de",
"c1947e87376fc7ce"
]
]
},
{
"id": "a9e6d18f53bbd0de",
"type": "debug",
"z": "59c21aa936bd3e9d",
"name": "debug 2",
"active": true,
"tosidebar": true,
"console": false,
"tostatus": false,
"complete": "false",
"statusVal": "",
"statusType": "auto",
"x": 360,
"y": 940,
"wires": []
},
{
"id": "c3daccf5760de8b8",
"type": "ui_text",
"z": "59c21aa936bd3e9d",
"group": "959ffcf60a145f11",
"order": 1,
"width": 0,
"height": 0,
"name": "1. Date and time",
"label": "Date and time",
"format": "{{msg.payload}}",
"layout": "row-spread",
"className": "",
"style": false,
"font": "",
"fontSize": 16,
"color": "#000000",
...
This file has been truncated, please download it to see its full contents.
/**
* @brief This example uses username and password authentication to connect to a
* MQTT server.
*/
#include <Arduino.h>
#include <http_client.h>
#include <TimeLib.h>
#include <ecc608.h>
#include <led_ctrl.h>
#include <log.h>
#include <lte.h>
#include <mqtt_client.h>
#include <mcp9808.h>
#include <veml3328.h>
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ILI9341.h"
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME280.h"
#include <Adafruit_GPS.h>
#include <DS3231.h>
DS3231 clock1;
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C
//used fo read the settings from eeprom:
byte topicLength; //used to set the string length in read eepron function at initializing the system
char settings_string[55]; //used to read settings from eeprom
//MQTT parameters used for datacake; see Datacake documentation:
char MQTT_SUB_TOPIC[55]; //see datacake documentation
char MQTT_THING_NAME[55]; //see datacake documentation
char MQTT_BROKER[55]; //see datacake documentation
char MOTT_USERNAME[55]; //see datacake documentation
char MOTT_PASSWORD[55]; //see datacake documentation
#define MQTT_PORT 1883
#define MQTT_USE_TLS false
#define MQTT_KEEPALIVE 1200 //30 //180 //90 //60
#define MQTT_USE_ECC false
//#define MQTT_TIME_OUT (30000)
#define MQTT_PRINT_MESSAGE true
//Define strings and sub-strings used to create the final string to be sent do datacake:
char camp1[55];
char camp2[50];
char camp3[55];
char camp4[15];
char finalTopic[200]; //used to send the complete string to Datacake
String message_to_publish; //used to format the string for Datacake transmission or wifi transmission to Node Red
//how to create mqtt message for Datacake:
//"camp1/MQTT_THING_NAME/camp3/variable_name",variable_value
//variables used to set WiFi and MQTT comunication to Node-Red:
char wifi_ssid[55]; //WiFi SSID to connect to send data to Node-Red
char wifi_pass[50]; //WiFi pass to connect to send data to Node-Red
char mqtt_client_id[50]; //MQTT client ID used by Node-red
char mqtt_user[50]; //MQTT user for Node-Red; see Node-Red documentation
char mqtt_pass[50]; //MQTT pass for Node-Red; see Node-Red documentation
char mqtt_host[50]; //MQTT host for Node-Red; see Node-Red documentation
char finalTopic_wifi[200]; //used to send the complete string to Node-Red
//how to create MQTTUSERCFG and MQTTCONN for Node-Red:
//Serial2.println("AT+MQTTUSERCFG=0,1,\"mqtt_client_id\",\"mqtt_user\",\"mqtt_pass\",0,0,\"\"");
//Serial2.println("AT+MQTTCONN=0,\"mqtt_host\",1883,1");
//variables used for GPS:
#define SerialDebug Serial3 //add this to print via Serial
#define GPSSerial Serial2 //define hardware serial port the GPS is connected to, can be Serial1, Serial2, Serial3 or Serial4
Adafruit_GPS GPS(&GPSSerial); //@brief Interface for the GPS
static char latitude[16] = "0";
static char longitude[16] = "0";
static char time1[24] = "0";
static bool has_parsed = false; //@brief Whether or not we've parsed one GPS entry. Prevents sending zeros whilst having fix after boot.
//setup gps data new:
// Set GPSECHO to 'false' to turn off echoing the GPS data to the Serial console
// Set GPSECHO to 'true' if you want to debug and listen to the raw GPS sentences
#define GPSECHO false
//variables used for RTC, to set and read date and time from RTC Ds3231:
byte year_set;
byte month_set;
byte date_set;
byte dOW; //dOW = day of week
byte hour_set;
byte minute_set;
byte second_set;
bool century = false;
bool h12Flag = false; //Set 12/24h mode. True is 12-h, false is 24-hour.
bool pmFlag = false;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
int time_zone = 2; //time zone value for Romania is GMT +2
int dts = 0; //variable used to set DTS automatically
//Set external PINs used by the Adafruit TFT library, by default:
#define TFT_DC PIN_PE2
#define TFT_CS PIN_PD7
#define TFT_RST PIN_PD5
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
//For comunication with external eeprom 25CSM04:
#define eeprom_CS PIN_PE3
//variables used for several timers:
unsigned long interval_1; //used to read data from GPS module
unsigned long interval_2; //used in main loop to read sensors and display data on TFT
unsigned long interval_3; //used to send data to Datacake
unsigned long interval_4; //used to send the data to Node-Red via WiFi
unsigned long timer_1 = 0;
unsigned long timer_2 = 0;
unsigned long timer_3 = 0;
unsigned long timer_4 = 0;
uint32_t timer = millis();
float celsius; //used to display temp from board temp sensor
//used to comunicate with eeprom memory:
byte data_from_eeprom = 0xFF; //default value
byte data_to_write = 0xFF; //default value
unsigned long eeprom_address = 0x00; //default value
int string_length; //used to calculate the length of received string for Settings
byte cnt; //used to read the characters from received string to save them into eeprom
char char_from_string; //used to read one character from received string and write it into eeprom
String inputString = ""; //string used to store the message came from serial port
static char MyBuffer[60]; //used to display several messages on TFT in Settings section
String setting_name; //used to display the parameter name on TFT, in Settings section
byte first_eeprom_address_value; //verify if the Settings from eeprom was deleted to enter automatinaly in Settings
//used with project sensors:
int sensorVOC = 0; //default value
int sensorCO = 0; //default value
int sensorSmoke = 0; //default value
int sensorCH4 = 0; //default value
int sensorODOR = 0; //default value
int sensorNH3 = 0; //default value
int settings_switch = 0; //default value
//variable used to set the levels read with ADC for switch settings:
int gsm_value = 420; //default value
int wifi_value = 470; //default value
int gsm_wifi_value = 520; //default value
int settings_value = 615; //default value
int settings_gsm_value = 700; //default value
int settings_wifi_value = 844; //default value
int settings_gsm_wifi_value = 1019; //default value
int dip_switch_offset = 8; //default value
//used to signaling if the GSM connetion is established or not:
bool gsm_connection_established = false; //default value
long value_to_display = 0; //used in display data on TFT function
int y_value = 40; //used in display data on TFT function
//used in function to read/set/verify the switch function:
bool oldvalue_gsm = false;
bool newvalue_gsm = false; //used to set GSM on and OFF deom DIP switch
bool send_data_over_gsm = false; //used to send or not data over GSM
bool oldvalue_wifi = false;
bool newvalue_wifi = false; //used to set WiFi on and OFF from DIP switch
bool send_data_over_wifi = false; //used to send or not data over WiFi
bool oldvalue_settings = false;
bool newvalue_settings = false; //used to set WiFi on and OFF from DIP switch
bool settings_status = false; //used to set Settings ON or Off
//used to connect/disconnect to gsm network:
static volatile bool disconnected_event = false;
void disconnectedFromNetwork(void) { disconnected_event = true; }
static volatile bool connected_to_broker = false;
void disconnectedFromBroker(void) { connected_to_broker = false; }
int connection_retry = 0; //used to count number of connection to GSM and MQTT if fails
//used to retrive time and date to be set on RTC:
unsigned long t_unix_date1;
#define TIMEZONE_URL "worldtimeapi.org"
#define TIMEZONE_URI "/api/timezone/Europe/Oslo.txt"
long getTimeFromResponse(String* resp)
{
int unix_time_index = resp->indexOf(String("unixtime: "));
int utx_datetime_index = resp->indexOf(String("utc_datetime"));
return resp->substring(unix_time_index + 10, utx_datetime_index - 1).toInt();
}
//used to calculate day of the week:
int calculateDayOfWeek(int day_ss, int month_ss, int year_ss)
{
if (month_ss < 3)
{
month_ss += 12;
year_ss -= 1;
}
int K = year_ss % 100;
int J = year_ss / 100;
int f = day_ss + (13 * (month_ss + 1)) / 5 + K + (K / 4) + (J / 4) - (2 * J);
return (f % 7 + 7) % 7;
}
//******************************************************************************
//************************************ SETUP ***********************************
//******************************************************************************
void setup()
{
//************************
Wire1.begin(); //setup I2C1 comunication with sensors
//************************
Log.begin(115200);
SerialDebug.begin(115200);
Serial2.begin(9600); //setup Serial2 comunicatrion
//************************ LEDs settings
LedCtrl.begin();
LedCtrl.startupCycle();
//************************ GPS settings
initializeGPS();
//************************ TFT settings and title message
tft.begin();
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(4);
tft.setCursor(0, 0);
tft.println("Gas sensor");
//************************ Set eeprom 25CSM04 CS pin:
pinConfigure(PIN_PE3, PIN_DIR_OUTPUT); //Set pin as output for eeprom CS
digitalWrite(PIN_PE3, HIGH); //initial value for eeprom CS
//************************ Set digital pins used for selection of analog sensors:
pinConfigure(PIN_PA7, PIN_DIR_OUTPUT); //Enable pin from sensor board
pinConfigure(PIN_PB5, PIN_DIR_OUTPUT); //S0 pin from sensor board
pinConfigure(PIN_PD0, PIN_DIR_OUTPUT); //S1 pin from sensor board
pinConfigure(PIN_PE1, PIN_DIR_OUTPUT); //S2 pin from sensor board
digitalWrite(PIN_PA7, HIGH); //initial value for Enable pin
digitalWrite(PIN_PB5, LOW); //initial value for S0 pin
digitalWrite(PIN_PD0, LOW); //initial value for S1 pin
digitalWrite(PIN_PE1, LOW); //initial value for S2 pin
//Set analog pin unsed to read the analog sensors:
analogRead(PIN_PD6);
//************************ Set digital pins used for selection of serial communication (default = GPS):
pinConfigure(PIN_PD4, PIN_DIR_OUTPUT); //Enable pin from sensor board
pinConfigure(PIN_PD1, PIN_DIR_OUTPUT); //S0 pin from sensor board
pinConfigure(PIN_PD3, PIN_DIR_OUTPUT); //S1 pin from sensor board
digitalWrite(PIN_PD1, HIGH); //initial value for S0 pin
digitalWrite(PIN_PD3, LOW); //initial value for S1 pin
digitalWrite(PIN_PD4, LOW); //initial value for Enable pin
//************************ Initialize internal sensors: MCP9808 and Veml3328
// Initialize MCP9808 library for temperature sensor
// const int8_t error = Mcp9808.begin(0x18); // Rev1
const int8_t error = Mcp9808.begin(); // Rev2
if (error)
{
SerialDebug.println("Error: could not start MCP9808 library");
}
Veml3328.begin(); //start colour sensor readings
//************************ BME280 settings
unsigned status;
// default settings
//status = bme.begin();
// You can also pass in a Wire library object like &Wire2
status = bme.begin(0x76, &Wire1); //set MBE280 on I2C1
if (!status)
{
SerialDebug.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
SerialDebug.print("SensorID was: 0x"); SerialDebug.println(bme.sensorID(),16);
SerialDebug.print(" ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
SerialDebug.print(" ID of 0x56-0x58 represents a BMP 280,\n");
SerialDebug.print(" ID of 0x60 represents a BME 280.\n");
SerialDebug.print(" ID of 0x61 represents a BME 680.\n");
while (1) delay(10);
}
//***************** Function used during development and testing:
// read_ADC_for_switch(); //used to read ADC values for switch state, for testing purpose only
// i2c_scanner(); //used to discovery the I2C devices connected to the bus
// set_rtc_date_and_time(); //used to set RTC
// read_date_and_time_from_GSM(); //used to read date and time from GSM
// set_rtc_date_and_time_from_gsm(); //used to set date and time read from GSM in RTC
// compensation_ADC_with_temperature(); //used to calibrate ARD reading with ambient temperature
//************************** Read data from eeprom memory:
read_settings_data_from_eeprom(); //used to read settings data from eeprom memory
//************************** Check DIP switch for comunication options:
check_DIP_switch_at_startup(); //check DIP switch possition 1,2 and 3
//************************** Display initial data omn TFT:
display_initial_data_on_tft(); //display the initial data on TFT
}
//******************************************************************************
//*************** Functions used in development of only for testing: ***********
//******************************************************************************
//used to scan the devices connected on I2C bus to discover the device ID:
void i2c_scanner()
{
//info here: https://playground.arduino.cc/Main/I2cScanner/
byte error, address;
int nDevices;
SerialDebug.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire1.beginTransmission(address);
error = Wire1.endTransmission();
if (error == 0)
{
SerialDebug.print("I2C device found at address 0x");
if (address<16)
SerialDebug.print("0");
SerialDebug.print(address,HEX);
SerialDebug.println(" !");
nDevices++;
}
else if (error==4)
{
SerialDebug.print("Unknown error at address 0x");
if (address<16)
SerialDebug.print("0");
SerialDebug.println(address,HEX);
}
}
if (nDevices == 0)
SerialDebug.println("No I2C devices found\n");
else
SerialDebug.println("done\n");
delay(5000); // wait 5 seconds for next scan
}
void read_ADC_for_switch() //used to read ADC values for switch state
{
bucla_1:
//read settings dip switch:
read_DIP_switch_status();
delay(1000);
goto bucla_1;
}
void compensation_ADC_with_temperature()
{
bucla_2:
read_DIP_switch_status();
read_internal_temp_sensor();
delay(1000);
goto bucla_2;
}
//******************************************************************************
//*************** Functions ****************************************************
//******************************************************************************
//*************************************************
//********** Functions related to RESET MCU********
//*************************************************
void resetViaSWR() //function used to reset the MCU properly:
{
//see the DxCore implementation: https://github.com/SpenceKonde/DxCore
tft.fillRect(0,180,240,200,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setCursor(0,180);
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.print("The whole system will be reseted to apply the new settings!");
delay(2500); //only for visual efect
Serial.println("Reset at request of user code.");
delay(100);
_PROTECTED_WRITE(RSTCTRL.SWRR,1);
// if (RSTCTRL_SWRF_bm) {
// Serial.println("Reset at request of user code.");
// }
}
//************************************************************
//********** Functions related to display data to TFT:********
//************************************************************
void display_initial_data_on_tft()
{
//*****Clear the TFT display messages from initialization GSM and MQTT stage:
tft.setCursor(0, 40); tft.fillRect(0,40,240,280, 0x0000); //0x0000 = Black
//*****Fill the name of the sensors on TFT:
tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 40); tft.println("Col RED:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204, 40); tft.println("val");
tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 56); tft.println("Col GREEN:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204, 56); tft.println("val");
tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 72); tft.println("Col BLUE:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204, 72); tft.println("val");
tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 88); tft.println("Col IR:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204, 88); tft.println("val");
tft.setTextColor(ILI9341_MAGENTA); tft.setTextSize(2); tft.setCursor(0, 104); tft.println("Col CLEAR:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204, 104); tft.println("val");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(0, 120); tft.println("Gas VOC:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204 ,120); tft.println("ppm");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(0, 136); tft.println("Gas CO:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204 ,136); tft.println("ppm");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(0, 152); tft.println("Gas Smoke:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204 ,152); tft.println("ppm");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(0, 168); tft.println("Gas CH4:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204 ,168); tft.println("ppm");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(0, 184); tft.println("Gas Odor:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204 ,184); tft.println("ppm");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(0, 200); tft.println("Gas NH3:");
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.setCursor(204 ,200); tft.println("ppm");
tft.setTextColor(ILI9341_CYAN); tft.setTextSize(2); tft.setCursor(0, 216); tft.println("Temperature:");
tft.setCursor(190, 216);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.println((char)247);
tft.setCursor(202, 216);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.println("C");
tft.setTextColor(ILI9341_CYAN); tft.setTextSize(2); tft.setCursor(0, 232); tft.println("Humidity:");
tft.setCursor(190, 232);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.println("%RH");
tft.setTextColor(ILI9341_CYAN); tft.setTextSize(2); tft.setCursor(0, 248); tft.println("Pressure:");
tft.setCursor(190, 248);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.println("mmHg");
tft.setTextColor(ILI9341_CYAN); tft.setTextSize(2); tft.setCursor(0, 264); tft.println("Altitude r:");
tft.setCursor(190, 264);
tft.setTextColor(ILI9341_WHITE); tft.setTextSize(2); tft.println("m");
tft.setCursor(108, 304); //display GSM tag on TFT
tft.fillRect(108,304,48,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.println("GPS");
tft.setCursor(168, 304); //display GSM tag on TFT
tft.fillRect(168,304,72,16,0x0000); // x,y,width,height (latime,inaltime) ,0x0000 = Black
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println("MQTT");
}
void display_data_on_TFT() //display data on TFT:
{
//colors for display txt on TFT:
//BLACK = 0x0000
//BLUE = 0x001F
//RED = 0xF800
//GREEN = 0x07E0
//CYAN = 0x07FF
//MAGENTA = 0xF81F
//YELLOW = 0xFFE0
//WHITE = 0xFFFF
display_values_on_TFT (Veml3328.getRed(),40,0x07E0); //0x07E0 = GREEN
display_values_on_TFT (Veml3328.getGreen(),56, 0x07E0); //0x07E0 = GREEN
display_values_on_TFT (Veml3328.getBlue(),72, 0x07E0); //0x07E0 = GREEN
display_values_on_TFT (Veml3328.getIR(),88, 0x07E0); //0x07E0 = GREEN
display_values_on_TFT (Veml3328.getClear(),104, 0x07E0); //0x07E0 = GREEN
if (sensorVOC < 10) //https://www.figarosensor.com/knowledge/voc.html
{
display_values_on_TFT (sensorVOC,120, 0x07E0); //0...500 ppm
}
else
{
display_values_on_TFT (sensorVOC,120, 0xFFE0); //0...500 ppm
}
if (sensorCO < 10) //https://www.co2meter.com/blogs/news/carbon-monoxide-levels-chart
{
display_values_on_TFT (sensorCO,136, 0x07E0); //5...5000 ppm
}
else
{
display_values_on_TFT (sensorCO,136, 0xFFE0); //5...5000 ppm
}
if (sensorSmoke < 20) //https://nj.gov/health/eoh/rtkweb/documents/fs/1594.pdf
{
display_values_on_TFT (sensorSmoke,152, 0x07E0); //10...1000 ppm (C3H8 - Propane)
}
else
{
display_values_on_TFT (sensorSmoke,152, 0xFFE0); //10...1000 ppm (C3H8 - Propane)
}
if (sensorCH4 < 10) //https://healthmatters.io/understand-blood-test-results/methane-ch4#:~:text=Optimal%20Result%3A%200%20%2D%2010%20ppm,to%20address%20other%20clinical%20questions.
{
display_values_on_TFT (sensorCH4,168, 0x07E0); //1...10000 ppm (CH4 - Methane, C3H8 - Propane)
}
else
{
display_values_on_TFT (sensorCH4,168, 0xFFE0); //1...10000 ppm (CH4 - Methane, C3H8 - Propane)
}
if (sensorODOR < 5) //https://www.ncbi.nlm.nih.gov/pmc/articles/PMC10504204/
{
display_values_on_TFT (sensorODOR,184, 0x07E0); //0.5...50 ppm (H2S, Alcohol, and acetone gas)
}
else
{
display_values_on_TFT (sensorODOR,184, 0xFFE0); //0.5...50 ppm (H2S, Alcohol, and acetone gas)
}
if (sensorNH3 < 50) //https://www.co2meter.com/blogs/news/4-gas-monitors
{
display_values_on_TFT (sensorNH3,200, 0x07E0); //1...300 ppm (NH3 - Ammonia)
}
else
{
display_values_on_TFT (sensorNH3,200, 0xFFE0); //1...300 ppm (NH3 - Ammonia)
}
tft.setCursor(160, 216);
tft.fillRect(160,216,24,16,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.println(int(round(bme.readTemperature())));
tft.setCursor(160, 232);
tft.fillRect(160,232,24,16,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.println(int(round(bme.readHumidity())));
tft.setCursor(148, 248);
tft.fillRect(148,248,36,16,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.println(int((round(bme.readPressure() / 100.0F))*0.750)); //pressure in mmHg
tft.setCursor(148, 264);
tft.fillRect(148,264,36,16,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.println(int(round(bme.readAltitude(SEALEVELPRESSURE_HPA))));
}
void display_values_on_TFT(long value_to_display, int y_value, word color)
{
if (value_to_display>99999) //afiseaza zeci de mii
{
tft.setCursor(124, y_value);
tft.fillRect(124,y_value,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(color);
tft.setCursor(124, y_value);
tft.setTextSize(2);
tft.println(value_to_display);
}
else
{
if (value_to_display<100000)
{
if (value_to_display>9999) //afiseaza mii
{
tft.setCursor(124, y_value);
tft.fillRect(124,y_value,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(color);
tft.setCursor(136, y_value);
tft.setTextSize(2);
tft.println(value_to_display);
}
else
{
if (value_to_display<10000) //afiseaza sute
{
if (value_to_display>999)
{
tft.setCursor(124, y_value);
tft.fillRect(124,y_value,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(color);
tft.setCursor(148, y_value);
tft.setTextSize(2);
tft.println(value_to_display);
}
else
{
if (value_to_display<1000) //afiseaza zeci
{
if (value_to_display>99)
{
tft.setCursor(124,y_value);
tft.fillRect(124,y_value,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(color);
tft.setCursor(160, y_value);
tft.setTextSize(2);
tft.println(value_to_display);
}
else
{
if (value_to_display<100) //afiseaza unitati
{
if (value_to_display>9)
{
tft.setCursor(124, y_value);
tft.fillRect(124,y_value,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(color);
tft.setCursor(172, y_value);
tft.setTextSize(2);
tft.println(value_to_display);
}
else
{
if (value_to_display<10)
{
tft.setCursor(124, y_value);
tft.fillRect(124,y_value,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(color);
tft.setCursor(184, y_value);
tft.setTextSize(2);
tft.println(value_to_display);
}
}
}
}
}
}
}
}
}
}
}
void display_connection_to_GSM()
{
//************************** Connecting to GSM:
tft.setCursor(0, 40);
tft.fillRect(0,40,240,16,0x0000); //0x0000 = Black 0x001F = Blue
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.setCursor(0, 40);
tft.println("Connecting to GMS...");
connect_to_GSM(); //Establish LTE connection
tft.setCursor(0, 40);
tft.fillRect(0,40,240,16,0x0000); //0x0000 = Black 0x001F = Blue
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.setCursor(0, 40);
tft.println("Connected to GSM!");
delay(1000); //delay only for showing data on TFT
}
void display_connection_to_Datacake()
{
//************************** Connecting to MQTT Datacake broker:
tft.setCursor(0, 72);
tft.fillRect(0,72,240,32,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.setCursor(0, 72);
tft.println("Start connection to Datacake IoT...");
connect_to_mqtt_broker(); //Connecting to MQTT broker
tft.setCursor(0, 72);
tft.fillRect(0,72,240,32,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.setCursor(0, 72);
tft.println("Connection created to Datacake IoT");
delay(2000); //delay only for showing data on TFT
}
void display_gsm_wifi_status_on_tft()
{
//display GSM tag:
if(newvalue_gsm)
{
tft.setCursor(0, 304);
tft.fillRect(0,304,36,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.println("GSM");
}
else
{
tft.setCursor(0, 304);
tft.fillRect(0,304,36,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.println("GSM");
}
//display WiFi tag:
if(newvalue_wifi)
{
tft.setCursor(48, 304);
tft.fillRect(48,304,48,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.println("WiFi");
}
else
{
tft.setCursor(48, 304);
tft.fillRect(48,304,48,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.println("WiFi");
}
//display MQTT tag:
if((newvalue_gsm && newvalue_wifi) || newvalue_gsm || newvalue_wifi)
{
tft.setCursor(168, 304); //display GSM tag on TFT
tft.fillRect(168,304,48,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println("MQTT");
}
else
{
tft.setCursor(168, 304); //display GSM tag on TFT
tft.fillRect(168,304,72,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.println("MQTT");
}
}
void display_RTC_on_TFT()
{
//display automatically DTS for Summer/Winter change time for RTC and GSM:
//information for date is read from RTC, because is already set
//and is used also for display date and time for GPS:
automatic_daylight_saving_time_set();
//display time and date...for font 2, one digit has 12 pixels x 16 pixels
if ( GPS.day<=0 )
{
SerialDebug.println("GPS data not available; display Date and time from RTC.");
tft.setCursor(0, 285); //304 //display time and date: hh:mm dd/mm/yyyy
tft.fillRect(0,285,220,16,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
if ( (clock1.getHour(h12Flag, pmFlag) + time_zone + dts) <10 )
{
tft.setCursor(0, 285); tft.print("0");
tft.setCursor(12, 285); tft.print((clock1.getHour(h12Flag, pmFlag) + time_zone + dts), DEC);
}
else
{
if ((clock1.getHour(h12Flag, pmFlag) + time_zone + dts) >=24)
{
tft.setCursor(0, 285); tft.print("0");
tft.setCursor(12, 285); tft.print((clock1.getHour(h12Flag, pmFlag) + time_zone + dts) - 24, DEC);
}
else
{
tft.setCursor(0, 285); tft.print(clock1.getHour(h12Flag, pmFlag) + time_zone + dts, DEC);
}
}
tft.setCursor(24, 285); tft.print(":");
if ( clock1.getMinute() < 10 )
{
tft.setCursor(36, 285); tft.print("0");
tft.setCursor(48, 285); tft.print(clock1.getMinute(), DEC);
}
else
{
tft.setCursor(36, 285); tft.print(clock1.getMinute(), DEC);
}
if ( clock1.getDate() < 10 )
{
if ((time_zone + dts) >0 ) //verify if date and time is displayed for GMT = 0 or not
{
if ((clock1.getHour(h12Flag, pmFlag) >20 && (clock1.getHour(h12Flag, pmFlag) >= 23)))
{
tft.setCursor(72, 285); tft.print("0");
tft.setCursor(84, 285); tft.print((clock1.getDate() + 1), DEC); //add a day because GMT > 0
}
else
{
tft.setCursor(72, 285); tft.print("0");
tft.setCursor(84, 285); tft.print((clock1.getDate()), DEC); //add a day because GMT > 0
}
}
else
{
tft.setCursor(72, 285); tft.print("0");
tft.setCursor(84, 285); tft.print(clock1.getDate(), DEC);
}
}
else
{
if ((time_zone + dts) > 0)//verify if date and time is displayed for gmt = 0 or not
{
if ( (clock1.getHour(h12Flag, pmFlag) >20 && (clock1.getHour(h12Flag, pmFlag) >= 23)) )
{
tft.setCursor(72, 285); tft.print((clock1.getDate() + 1), DEC); //add a day because GMT > 0
}
else
{
tft.setCursor(72, 285); tft.print((clock1.getDate()), DEC); //add a day because GMT > 0
}
}
else
{
tft.setCursor(72, 285); tft.print(clock1.getDate(), DEC);
}
}
tft.setCursor(96, 285); tft.print("/");
if (clock1.getMonth(century)<10)
{
tft.setCursor(108, 285); tft.print("0");
tft.setCursor(120, 285); tft.print(clock1.getMonth(century), DEC);
}
else
{
tft.setCursor(108, 285); tft.print(clock1.getMonth(century), DEC);
}
tft.setCursor(132, 285); tft.print("/");
tft.println(2000 + clock1.getYear(), DEC);
}
else
{
SerialDebug.println("GPS data available; display Date and time from GPS.");
tft.setCursor(0, 285); //304 //display time and date: hh:mm dd/mm/yyyy
tft.fillRect(0,285,220,16,0x0000); //0x0000 = Black
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
// if (GPS.hour<10)
if ( (GPS.hour + time_zone + dts, DEC) <10)
{
tft.setCursor(0, 285); tft.print("0");
tft.setCursor(12, 285); tft.print(GPS.hour + time_zone + dts, DEC);
}
else
{
tft.setCursor(0, 285); tft.print(GPS.hour + time_zone + dts, DEC);
}
tft.setCursor(24, 285); tft.print(":");
if (GPS.minute<10)
{
tft.setCursor(36, 285); tft.print("0");
tft.setCursor(48, 285); tft.print(GPS.minute, DEC);
}
else
{
tft.setCursor(36, 285); tft.print(GPS.minute, DEC);
}
if (GPS.day<10)
{
tft.setCursor(72, 285); tft.print("0");
tft.setCursor(84, 285); tft.print(GPS.day, DEC);
}
else
{
tft.setCursor(72, 285); tft.print(GPS.day, DEC);
}
tft.setCursor(96, 285); tft.print("/");
if (GPS.month<10)
{
tft.setCursor(108, 285); tft.print("0");
tft.setCursor(120, 285); tft.print(GPS.month, DEC);
}
else
{
tft.setCursor(108, 285); tft.print(GPS.month, DEC);
}
tft.setCursor(132, 285); tft.print("/");
tft.println(2000 + GPS.year, DEC);
}
}
void display_gps_tag_and_sattelites()
{
if (GPS.month == 0) //I choose "month", because shoule have value from 1 to 12 if GPS data are available
{
tft.setCursor(108, 304); //change label colour ig GPS data is aqquired
tft.fillRect(108,304,36,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_RED);
tft.setTextSize(2);
tft.println("GPS");
}
// if ((GPS.month, DEC) >0) //set GPS tag in yellow is gps fix is aquired
else
{
if (GPS.month > 0 && (int)GPS.satellites <= 0 )
{
tft.setCursor(108, 304); //change label colour ig GPS data is aqquired
tft.fillRect(108,304,36,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_YELLOW);
tft.setTextSize(2);
tft.println("GPS");
tft.setCursor(144, 304); //display GSM tag on TFT
tft.fillRect(144,304,12,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println((int)GPS.satellites);
SerialDebug.print("GPS galben ");
SerialDebug.println(GPS.month, DEC);
}
else
{
tft.setCursor(108, 304); //change label colour ig GPS data is aqquired
tft.fillRect(108,304,36,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_GREEN);
tft.setTextSize(2);
tft.println("GPS");
tft.setCursor(144, 304); //display GSM tag on TFT
tft.fillRect(144,304,12,16,0x0000); // x,y,width,height (latime,inaltime),0x0000 = Black
tft.setTextColor(ILI9341_WHITE);
tft.setTextSize(2);
tft.println((int)GPS.satellites);
}
}
}
//************************************************************
//********** Functions related to DIP switch: ****************
//************************************************************
void read_DIP_switch_status()
{
digitalWrite(PIN_PB5, HIGH); //set value for S0 pin
digitalWrite(PIN_PD0, HIGH); //set value for S1 pin
digitalWrite(PIN_PE1, HIGH); //set value for S2 pin
digitalWrite(PIN_PA7, LOW); //activate sensors
int numReadings = 10;
float sum = 0;
//read ADC values and calculate the sum:
for (int ii = 0; ii < numReadings; ii++)
{
int reading = analogRead(PIN_PD6);
sum += reading;
delay(10); //a small delay between readings to stabilize the values
}
//calculating the average:
settings_switch = sum / numReadings;
SerialDebug.println("");
SerialDebug.printf("Settings switch value: %u\r\n\r\n", settings_switch);
digitalWrite(PIN_PA7, HIGH); //deactivate sensors
}
void check_DIP_switch_at_startup()
{
read_DIP_switch_status(); //read settings dip switch:
SerialDebug.println("");
SerialDebug.printf("Settings at startup: %u\r\n\r\n", settings_switch);
...
This file has been truncated, please download it to see its full contents.
Comments
Please log in or sign up to comment.