Mihai Popa
Published © Apache-2.0

Portable gas analyzer - enhanced

Here is presented the finalisation of this project, with connection to local WiFi and implementation of the Settings section

AdvancedFull instructions provided6 hours261
Portable gas analyzer - enhanced

Things used in this project

Hardware components

ESP8266 ESP-01
Espressif ESP8266 ESP-01
×1
Raspberry Pi Zero
Raspberry Pi Zero
×1
esp8266 UART programmer adapter
×1
esp8266 power adapter
×1
usb to serial adapter working at 3.3V
×1

Software apps and online services

Raspberry Pi OS (Legacy) Lite
Node-RED
Node-RED
Microsoft Visual Studio 2022 - C#

Story

Read more

Schematics

Main board reworked v2.0

The latest PCB and schematic version, with all the corrections done.

Code

esp8266_flasher.exe

C/C++
ESP8266-01 flashing tool
No preview (download only).

v0.9.2.2 AT Firmware.bin

BatchFile
Firnware to be flashed on ESP8266-01 for AT communications
No preview (download only).

flows.json

JSON
Config file used in Node-Red to display the data from sensor
[
    {
        "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.

Gas sensor settings.exe

C#
Settings application, written in C#
No preview (download only).

Gas sensor settings.zip

C#
Source code of Settings application
No preview (download only).

Gas_sensor_enhanced.ino

C/C++
Gas_sensor_enhanced ino applicxation
/**
 * @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.

Credits

Mihai Popa
8 projects • 7 followers
I am working as test engineer for navigation systems in auto industry. I am passionate about science, IoT, ML, MCU programming, SF movies.
Contact

Comments

Please log in or sign up to comment.