Hardware components | ||||||
| × | 3 | ||||
| × | 1 | ||||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
![]() |
| × | 1 | |||
![]() |
| × | 1 | |||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
![]() |
| × | 1 | |||
Software apps and online services | ||||||
| ||||||
![]() |
| |||||
| ||||||
![]() |
| |||||
![]() |
| |||||
| ||||||
Hand tools and fabrication machines | ||||||
![]() |
| |||||
|
Thanks to the DFRobot BLE Sensor Beacon Free trial, I was able to discover and test the BLE modules with various analog and digital sensors, creating an sensors network for my home.
The sensors value are sent to a "base station" (FreeBeetle ESP32-E) using BLE in beacon mode.
Fermion: BLE Sensor Beacon is a wireless beacon that broadcasts sensor data via Bluetooth 5.3 and can be powered by CR2032 coin cell battery. Full description of these modules can be found here: SKU:TEL0168
For this project I used analog and digital sensors connected to BLE Sensor Beacon. The analog sensors are used to monitor the plants condissions and the digital sensors are used to monitor the weather and air.
Analog sensors- MCP9700 temperature sensor - datasheet attached;
- Analog Ambient Light sensor V2.1 (provided by DFRobot BLE Sensor Beacon Free trial);
- Moisture sensor V2 (provided by DFRobot BLE Sensor Beacon Free trial).
All these analog sensors are connected to one BLE Beacon module as follow:
- MCP9700 is connected to ADC Channel 2, MPGIO2;
- Ambient light is connected to ADC Channel 1, MPGIO5;
- Moisture sensor is connected to ADC Channel 0, MPGIO4.
This module is used to monitor the temperature, moisture and light on my house balcony, where I have some plants; the data are sent then to Node-Red and from these are displayed into UI. In this way I can see when is the right time to water the plants.
The BLE Beacon module use an configuration file to store all the necessary settings.
For this module I create a special case 3D printed.
All necessary files used for BLE beacon configuration and 3D print case are attached.
OBS: the data are sent via beacon at every 5 seconds.
BLE Beacon analog schematic:
- SHT40 - Humidity and Temperature sensor (provided by DFRobot BLE Sensor Beacon Free trial);
- ICP-10111 - Pressure sensor used to measure atmospheric pressure and barometer;
For SHT40 I used the configuration file provided by DFRobot here; this file is attached.
For ICP-10111 I used the example provided also DFRobot here; the file is attached.
Both sensors, SHT40 and ICP-10111 have the same footprint and pinout and are connected to each BLE Beacon sensor as in the above picture (this picture is from the DFRobot site, the config page):
I created also an 3D printed case for the digital sensors; files are attached
OBS: the data are sent via beacon at every 1 second for ICP10111 and 5 seconds for SHT40.
Setup the BLE beaconThe BLE Beacon module cen be set using the application "NanoBeaconConfigToolMpTool_V3.2.29". Link with this application here.
Because the BLE Beacon module can be flashed only one time, this application give the oportunity to test the configuration in RAM, using the option "Run in RAM".
For this project I created an configuration for analog sensors and I used the already creaded ones for the digital sensors SHT40 and ICP10111. all the configuration files are attached.
To see details about the application used for settings, please see this link from DFRobot.
To load the configuration, press on the "Load" button (from Configuration tab) and select the specific configuration file. Then, run in RAM, to test the configuration of Burn/Program if the configuration is set and working.
OBS: the configuration that is used for I2C sensors can not be tested in RAM due to small amount of RAM. That why I used the recomended configuration for that two digital sensors.
Usefull links for tested configurations:
- configuration tested by DFRobot;
- configuration created by the InPlay Inc, the manufacturer of BLE Beacon IN100.
In order to monitor the data from BLE Beacon, I used the recomended mobile phone application: NanoBeacon BLE Scanner. The data from these beacons can de monitored also using the other BLE application, such "LightBlue", "BLE Tool", etc.
The one of the usefull function of this application is "Filters". Here we can set a filter based on the BLE device name of address and the data will be displayed only for this device. This is very usefull in the development/testing process (called "Run in RAM"), before flashing the final configuration into IN100 chip.
In the above picture, the device called "Analog" is set in filter and only the data from this are displayed:
All the data from these BLE Beacons are collected by the FireBeetle ESP32-E and then are sent to Node-Red via WiFi. I choose to sent the data to Node-Red using MQTT protocol via WiFi because I have already the Node-Red set and I decided to us it for this project.
The code in written in Arduino IDE 1.8.19 and is based on the examples provided by DFRobot.
In the example provided by DFrobot I have done some modifications (because some errors at compilations):
- in the Main loop, I chanded the following line:
BLEScanResults foundDevices = pBLEScan->start(scanTime, false);
with this one:
BLEScanResults *foundDevices = pBLEScan->start(scanTime, false);
- in the sensor reading loop, I changed this line:
std::string strManufacturerData = advertisedDevice.getManufacturerData();
with this one:
std::string strManufacturerData = advertisedDevice.getManufacturerData().c_str();
Then, I adapted the code to read all the analog and digital sensors.
After that I tryed to use the same ESP32-E board to send data in MQTT format to Node-red. But because the internal mempry of ESP32 is ocupied almost 85% with BLE libraries and code to read sensors, and packing the MQTT need more than 120% of internal memory, I choose to use an ESP8266-01 to "pack" the MQTT messages and then to send them to Node-Red.
OBS: the library that can be used to send/receive (publish/subscribe) messaged using MQTT protocol over WiFi with ESP32 is this: "pubsubclient". But with this library and all the BLE stuff, the amount of code "consume"about 120% of the flash memory of ESP32-E. That why I choose to send all the data to ESP8266-01.
I flashed the ESP8266-01 with a special firmware (attached), and using "AT commands", the data from ESP32 will be sent to Node-Red, in the local area.
The communication between ESP32 and ESP8266-01 is done via RS232 interface. On ESp32 side, I activated Serial2 on pins 16 and 17 (RXD2 on pin 16 and TXD2 on pin 17); see the above schematic. On ESP8266-01 side I used the special serial and power adapter.
ESP8266-01 is flashed with software "v0.9.2.2 AT Firmware.bin: using application "esp8266_flasher.exe", both files attached.
The communication between ESP32 and ESP8266 is done using UART at 9600 baud rate.
OBS: information about how to update the ESP8266-01 software can be found here.
Now, all the data are sent from ESP32 to ESP8266-01 using AT commands via UART; then, ESP8266-01 will "pack" the data into MQTT format and will send them to Node-Red in local network via WiFi connection.
Display dataTo display data in Node-Red I used an Raspberry Pi Zero. 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/
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:
Thank to DFRobot newleter and BLE Sensor Beacon Free trial I discovered these very easy to set modules that can be used to monitor a large variety of digital and analog sensors.
The module is presented in a small form factor, with the pinouts adapted for a large variaty of sensors (provided by DFRobot) and powered by a coin cell CR3023 battery.
The setup is easy, thank to the setup application and a large variety of examples, provided by DFRobit and InnPlay Inc.
Next steps- increase the project with more sensors (wsing more beacons);
- find a better way to display data in Node-red dashboard;
Analog sensors.cfg
JSON{
"version": "3.13",
"advSet": [
{
"id": 0,
"bdAddr": "060504030201",
"addrType": "static",
"addrKey": 0,
"staticAddrGen": 1,
"addrGenInterval": 90,
"interval": 5000,
"authEn": 0,
"authKey": 0,
"authSaltType": 2,
"authSaltValue": 0,
"authEaxCountType": 0,
"authEaxCountValue": 0,
"ui_format": "custom",
"uid2tlm_ratio": 0.0,
"eddystoneTxPower": 0,
"chCtrl": 0,
"advModeTrigEn": 0,
"is1MPhy": 1,
"phyRate": "1M",
"isStandardBle": 1,
"cte": 0,
"cteLen": 0,
"randomDlyType": 0,
"payloadVer": 3,
"payload": [
{
"len": 7,
"type": 9,
"data": "416e616c6f67"
},
{
"len": 12,
"type": 255,
"data": "0505<ADC CH2 2byte 1 0><VCC 1byte 0 0><TEMP 2byte 1 0><ADC CH0 2byte 1 0><ADC CH1 2byte 1 0>"
}
]
}
],
"txSetting": {
"txPower": -1,
"txPaGain": -1,
"sleepAftTx": 1,
"uartSingleWire": 0,
"uartPinSel": 0,
"xoCap": 12,
"xoStableTime": 36,
"xoGm": 16,
"ch0": 37,
"ch1": 38,
"ch2": 39,
"key0": "000102030405060708090a0b0c0d0e0f"
},
"gpio": [
{
"id": 0,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 1,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 2,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 3,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 4,
"digital": "disable",
"pu-pd": 0,
"wakeup": "disable",
"advTrig": "disable",
"latch": 1,
"maskb": 0
},
{
"id": 5,
"digital": "disable",
"pu-pd": 0,
"wakeup": "disable",
"advTrig": "disable",
"latch": 1,
"maskb": 0
},
{
"id": 6,
"digital": "disable",
"pu-pd": 0,
"wakeup": "disable",
"advTrig": "disable",
"latch": 1,
"maskb": 0
},
{
"id": 7,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
}
],
"vccUnit": 0.03125,
"tempUnit": 0.01,
"adc": [
{
"ch": 0,
"enable": 1,
"vddCtrlEn": 0,
"gndCtrlEn": 0,
"skipSample": 2,
"avgSample": 16,
"sampleInterval": 4,
"delayStart": 4,
"delaySample": 13,
"unitLsb": 0.00078125,
"valueV14": 1.4,
"valueV04": 0.4
},
{
"ch": 1,
"enable": 1,
"vddCtrlEn": 0,
"gndCtrlEn": 0,
"skipSample": 2,
"avgSample": 16,
"sampleInterval": 4,
"delayStart": 4,
"delaySample": 13,
"unitLsb": 0.00078125,
"valueV14": 1.4,
"valueV04": 0.4
},
{
"ch": 2,
"enable": 1,
"vddCtrlEn": 0,
"gndCtrlEn": 0,
"skipSample": 2,
"avgSample": 16,
"sampleInterval": 4,
"delayStart": 4,
"delaySample": 13,
"unitLsb": 0.001,
"valueV14": 2.898,
"valueV04": 0.828
},
{
"ch": 3,
"enable": 0
}
],
"calibration": {},
"i2c": {
"coldBootEn": 0,
"warmBootEn": 0
},
"i2c2": {
"coldBootEn": 0,
"warmBootEn": 0
},
"i2c3": {
"coldBootEn": 0,
"warmBootEn": 0
},
"pulse": {
"enable": 0
},
"qdec": {
"enable": 0,
"runGpioEdgeDetect": 0,
"clearCount": 0,
"endCode": 15,
"twoStep": 0,
"interval": 999,
"timeout": 1999,
"gpio": [],
"code": []
},
"wdt": {
"timerWraparound": 0,
"enable": 0,
"wakeupChip": 0,
"initValue": 30
},
"edgeCount": {
"enable": 0
},
"sqWave": {
"en": 0
},
"rtc": {
"enable": 0,
"clock": 0,
"type": 0
},
"regSetting": [
"write: 0 1 3 3480 2010000",
"write: 0 1 3 3484 3030002",
"write: 0 1 1 34c8 102",
"write: 0 1 3 3494 10020018",
"write: 3 1 0 1084 0",
"write: 3 1 0 11C8 0",
"write: 3 1 0 1c44 c6",
"write: 3 1 0 1ad0 58",
"write: 3 1 1 1d04 731",
"write: 0 1 0 3240 42",
"write: 0 1 0 3500 03"
],
"regSettingCust": [],
"settingPolice": 0,
"regValTrigEn": 0
}
[
{
"id": "e304fa4e58cbecd1",
"type": "tab",
"label": "Flow 2",
"disabled": false,
"info": "",
"env": []
},
{
"id": "efdbe5c6c654a12d",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/altitude",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 110,
"y": 140,
"wires": [
[
"42a78e3759f2b063"
]
]
},
{
"id": "f79063db513de7da",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/AnalogExternalTempInteger",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 180,
"y": 340,
"wires": [
[
"ba37a2b88a0ed90c"
]
]
},
{
"id": "6ce73293a358dbfc",
"type": "ui_chart",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 2,
"width": 5,
"height": 4,
"label": "MCP9700 temp",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 920,
"y": 340,
"wires": [
[]
]
},
{
"id": "42a78e3759f2b063",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "35c4ebb396aeece3",
"order": 3,
"width": 6,
"height": 4,
"gtype": "gage",
"title": "Altitude",
"label": "meters",
"format": "{{value}}",
"min": 0,
"max": "500",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 480,
"y": 140,
"wires": []
},
{
"id": "e779a2ce1a81c8be",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/analog_batteryVoltage",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 160,
"y": 600,
"wires": [
[
"e03f1b02372b5906"
]
]
},
{
"id": "14fbf550ef98e783",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/analog_MoistureData",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 160,
"y": 660,
"wires": [
[
"e89e5e3272c488f4"
]
]
},
{
"id": "132efbee9efb80d8",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/analog_LightData",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 150,
"y": 720,
"wires": [
[
"e53a0c823775a55e"
]
]
},
{
"id": "e03f1b02372b5906",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 7,
"width": 4,
"height": 4,
"gtype": "gage",
"title": "Battery voltage",
"label": "volts",
"format": "{{value}}",
"min": 0,
"max": "3.5",
"colors": [
"#ec0e6e",
"#b4ee5d",
"#3acb6a"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 500,
"y": 600,
"wires": []
},
{
"id": "e89e5e3272c488f4",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 6,
"width": 4,
"height": 4,
"gtype": "gage",
"title": "Moisture sensor",
"label": "units",
"format": "{{value}}",
"min": 0,
"max": "2000",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 500,
"y": 660,
"wires": []
},
{
"id": "e53a0c823775a55e",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 5,
"width": 4,
"height": 4,
"gtype": "gage",
"title": "Light sensor",
"label": "units",
"format": "{{value}}",
"min": "0",
"max": "2000",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 490,
"y": 720,
"wires": []
},
{
"id": "ad1c8bf84ba5773b",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/temperature",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 130,
"y": 80,
"wires": [
[
"9212e6e74813e158",
"a062287a56ea6b17"
]
]
},
{
"id": "9212e6e74813e158",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "35c4ebb396aeece3",
"order": 1,
"width": 6,
"height": 4,
"gtype": "gage",
"title": "Temperature",
"label": "C",
"format": "{{value}}",
"min": 0,
"max": "60",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 490,
"y": 80,
"wires": []
},
{
"id": "a062287a56ea6b17",
"type": "ui_chart",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "35c4ebb396aeece3",
"order": 2,
"width": 6,
"height": 4,
"label": "Temperature",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 490,
"y": 40,
"wires": [
[]
]
},
{
"id": "843e4a24401f9145",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 1,
"width": 7,
"height": 4,
"gtype": "gage",
"title": "MCP9700 temp",
"label": "C",
"format": "{{value}}",
"min": 0,
"max": "50",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 920,
"y": 400,
"wires": []
},
{
"id": "af2bd4c95af86eee",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/atmospheric_pressure",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 160,
"y": 200,
"wires": [
[
"4719b65865ab77db"
]
]
},
{
"id": "4719b65865ab77db",
"type": "function",
"z": "e304fa4e58cbecd1",
"name": "Convert Pa in mmHg",
"func": "// Preia valoarea presiunii din mesaj\nvar pressurePa = msg.payload;\n\n// Transform valoarea din Pa n mmHg\nvar pressureMmHg = Math.round(pressurePa / 133.3223684);\n\n// Creeaz un nou mesaj cu valoarea transformat\nmsg.payload = pressureMmHg;\n\n// Returneaz mesajul\nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 340,
"y": 260,
"wires": [
[
"76d89e8d970f05e3"
]
]
},
{
"id": "76d89e8d970f05e3",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "35c4ebb396aeece3",
"order": 4,
"width": 6,
"height": 4,
"gtype": "gage",
"title": "Atmospheric pressure",
"label": "mmHg",
"format": "{{value}}",
"min": "700",
"max": "800",
"colors": [
"#b30012",
"#e6e600",
"#04fb21"
],
"seg1": "740",
"seg2": "760",
"diff": false,
"className": "",
"x": 520,
"y": 200,
"wires": []
},
{
"id": "ba37a2b88a0ed90c",
"type": "function",
"z": "e304fa4e58cbecd1",
"name": "Combine integer and decimal for external temp sensor",
"func": "// Verific dac flow context are deja datele pentru partea ntreag i partea zecimal\nlet partIntreaga = flow.get('partIntreaga') || null;\nlet partZecimala = flow.get('partZecimala') || null;\n\nif (msg.topic === \"esp32/AnalogExternalTempInteger\") {\n partIntreaga = msg.payload;\n flow.set('partIntreaga', partIntreaga);\n} else if (msg.topic === \"esp32/AnalogExternalTempDecimal\") {\n partZecimala = msg.payload;\n flow.set('partZecimala', partZecimala);\n}\n\n// Dac ambele pri sunt disponibile, combin-le\nif (partIntreaga !== null && partZecimala !== null) {\n let numarComplet = parseFloat(partIntreaga + '.' + partZecimala);\n\n // Reseteaz prile n flow context\n flow.set('partIntreaga', null);\n flow.set('partZecimala', null);\n\n // Trimite numrul complet pentru afiare\n msg.payload = numarComplet;\n return msg;\n} else {\n // Dac nu avem nc ambele pri, nu trimitem nimic\n return null;\n}\n",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 580,
"y": 360,
"wires": [
[
"6ce73293a358dbfc",
"843e4a24401f9145"
]
]
},
{
"id": "cb456cd7124c3af4",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/AnalogExternalTempDecimal",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 180,
"y": 400,
"wires": [
[
"ba37a2b88a0ed90c"
]
]
},
{
"id": "fa96a33041c9425f",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/AnalogInternalTempInteger",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 170,
"y": 460,
"wires": [
[
"7ca2d5244f392948"
]
]
},
{
"id": "fa243e4ba081f644",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/AnalogInternalTempDecimal",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 180,
"y": 520,
"wires": [
[
"7ca2d5244f392948"
]
]
},
{
"id": "7ca2d5244f392948",
"type": "function",
"z": "e304fa4e58cbecd1",
"name": "Combine integer and decimal for interna temp sensor",
"func": "// Verific dac flow context are deja datele pentru partea ntreag i partea zecimal\nlet partIntreaga = flow.get('partIntreaga') || null;\nlet partZecimala = flow.get('partZecimala') || null;\n\nif (msg.topic === \"esp32/AnalogInternalTempInteger\") {\n partIntreaga = msg.payload;\n flow.set('partIntreaga', partIntreaga);\n} else if (msg.topic === \"esp32/AnalogInternalTempDecimal\") {\n partZecimala = msg.payload;\n flow.set('partZecimala', partZecimala);\n}\n\n// Dac ambele pri sunt disponibile, combin-le\nif (partIntreaga !== null && partZecimala !== null) {\n let numarComplet = parseFloat(partIntreaga + '.' + partZecimala);\n\n // Reseteaz prile n flow context\n flow.set('partIntreaga', null);\n flow.set('partZecimala', null);\n\n // Trimite numrul complet pentru afiare\n msg.payload = numarComplet;\n return msg;\n} else {\n // Dac nu avem nc ambele pri, nu trimitem nimic\n return null;\n}",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 580,
"y": 480,
"wires": [
[
"4ff3829940597873",
"e781571db5355b78"
]
]
},
{
"id": "e781571db5355b78",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 3,
"width": 7,
"height": 4,
"gtype": "gage",
"title": "Internal beacon temp",
"label": "C",
"format": "{{value}}",
"min": 0,
"max": "50",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 940,
"y": 520,
"wires": []
},
{
"id": "4ff3829940597873",
"type": "ui_chart",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "797ca08f74dee60b",
"order": 4,
"width": 5,
"height": 4,
"label": "Internal beacon temp",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 940,
"y": 460,
"wires": [
[]
]
},
{
"id": "5843d70aaa5ff3b0",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/sht40_temperature",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 150,
"y": 820,
"wires": [
[
"8dbf113aac34ead1",
"27b45588fe35f85a",
"0cb4895a9905657a"
]
]
},
{
"id": "14bbc828829b30e9",
"type": "mqtt in",
"z": "e304fa4e58cbecd1",
"name": "",
"topic": "esp32/sht40_humidity",
"qos": "2",
"datatype": "auto-detect",
"broker": "590e3ceaadf4028f",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 140,
"y": 960,
"wires": [
[
"d11c433c9e47e93a",
"0cb4895a9905657a"
]
]
},
{
"id": "0cb4895a9905657a",
"type": "function",
"z": "e304fa4e58cbecd1",
"name": "Calculate ITU and DEW",
"func": "// Stocarea valorilor primite n contextul de flux\nif (msg.topic === \"esp32/sht40_temperature\") {\n flow.set(\"temperature\", parseFloat(msg.payload));\n} else if (msg.topic === \"esp32/sht40_humidity\") {\n flow.set(\"humidity\", parseFloat(msg.payload));\n}\n\n// Obinerea valorilor din context\nvar temperature = flow.get(\"temperature\");\nvar humidity = flow.get(\"humidity\");\n\n// Calcularea punctului de rou i a factorului de confort\nif (temperature !== undefined && humidity !== undefined) {\n // Calcularea punctului de rou\n var dewPoint = temperature - ((100 - humidity) / 5);\n\n // Calcularea factorului de confort (indicele de cldur)\n var heatIndex = temperature - ((0.55 - 0.0055 * humidity) * (temperature - 14.5));\n\n // Trimiterea rezultatelor n dou mesaje separate\n var msg1 = { topic: \"dewPoint\", payload: dewPoint.toFixed(2) };\n var msg2 = { topic: \"heatIndex\", payload: heatIndex.toFixed(2) };\n\n return [msg1, msg2];\n} else {\n return null; // Dac nu avem ambele valori, nu trimitem nimic\n}\n",
"outputs": 2,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 430,
"y": 900,
"wires": [
[
"20273cb17d411f08"
],
[
"7afb545880a6dae1"
]
]
},
{
"id": "27b45588fe35f85a",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "8d899e0618059e63",
"order": 1,
"width": 7,
"height": 4,
"gtype": "gage",
"title": "Temperature",
"label": "C",
"format": "{{value}}",
"min": 0,
"max": "50",
"colors": [
"#00b500",
"#e6e600",
"#ca3838"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 490,
"y": 840,
"wires": []
},
{
"id": "8dbf113aac34ead1",
"type": "ui_chart",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "8d899e0618059e63",
"order": 2,
"width": 5,
"height": 4,
"label": "Temperature",
"chartType": "line",
"legend": "false",
"xformat": "HH:mm:ss",
"interpolate": "linear",
"nodata": "",
"dot": false,
"ymin": "",
"ymax": "",
"removeOlder": 1,
"removeOlderPoints": "",
"removeOlderUnit": "3600",
"cutout": 0,
"useOneColor": false,
"useUTC": false,
"colors": [
"#1f77b4",
"#aec7e8",
"#ff7f0e",
"#2ca02c",
"#98df8a",
"#d62728",
"#ff9896",
"#9467bd",
"#c5b0d5"
],
"outputs": 1,
"useDifferentColor": false,
"className": "",
"x": 490,
"y": 780,
"wires": [
[]
]
},
{
"id": "d11c433c9e47e93a",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "8d899e0618059e63",
"order": 3,
"width": 12,
"height": 4,
"gtype": "gage",
"title": "Humidity",
"label": "%RH",
"format": "{{value}}",
"min": 0,
"max": "100",
"colors": [
"#dbf070",
"#0dfd69",
"#fd0808"
],
"seg1": "50",
"seg2": "80",
"diff": false,
"className": "",
"x": 480,
"y": 960,
"wires": []
},
{
"id": "20273cb17d411f08",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "8d899e0618059e63",
"order": 4,
"width": 6,
"height": 4,
"gtype": "gage",
"title": "DEW point",
"label": "units",
"format": "{{value}}",
"min": "-10",
"max": "40",
"colors": [
"#d0f80d",
"#0af539",
"#fb8e8e"
],
"seg1": "",
"seg2": "",
"diff": false,
"className": "",
"x": 690,
"y": 860,
"wires": []
},
{
"id": "7afb545880a6dae1",
"type": "ui_gauge",
"z": "e304fa4e58cbecd1",
"name": "",
"group": "8d899e0618059e63",
"order": 5,
"width": 6,
"height": 4,
"gtype": "gage",
"title": "Confort factor ITU",
"label": "",
"format": "{{value}} C",
"min": 0,
"max": "100",
"colors": [
"#b4ea1f",
"#00e663",
"#f92424"
],
"seg1": "40",
"seg2": "80",
"diff": false,
"className": "",
"x": 710,
"y": 920,
"wires": []
},
{
"id": "590e3ceaadf4028f",
"type": "mqtt-broker",
"name": "",
"broker": "localhost",
"port": "1883",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closeRetain": "false",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "797ca08f74dee60b",
"type": "ui_group",
"name": "Analog sensors: MCP9700, Light and Moisture",
"tab": "996774836996b55a",
"order": 1,
"disp": true,
"width": "12",
"collapse": false,
"className": ""
},
{
"id": "35c4ebb396aeece3",
"type": "ui_group",
"name": "Digital sensor: ICP10111",
"tab": "996774836996b55a",
"order": 1,
"disp": true,
"width": "12",
"collapse": false,
"className": ""
},
{
"id": "8d899e0618059e63",
"type": "ui_group",
"name": "Digital sensor: SHT40",
"tab": "996774836996b55a",
"order": 3,
"disp": true,
"width": 12,
"collapse": false,
"className": ""
},
{
"id": "996774836996b55a",
"type": "ui_tab",
"name": "DFRobot",
"icon": "dashboard",
"disabled": false,
"hidden": false
}
]
ICP10111.cfg
JSON{
"version": "3.12",
"advSet": [
{
"id": 0,
"bdAddr": "060504030201",
"addrType": "public",
"addrKey": 0,
"staticAddrGen": 1,
"addrGenInterval": 90,
"interval": 1000,
"authEn": 0,
"authKey": 0,
"authSaltType": 2,
"authSaltValue": 0,
"authEaxCountType": 0,
"authEaxCountValue": 0,
"ui_format": "custom",
"uid2tlm_ratio": 0.0,
"eddystoneTxPower": 0,
"chCtrl": 0,
"advModeTrigEn": 0,
"is1MPhy": 1,
"phyRate": "1M",
"isStandardBle": 1,
"cte": 0,
"cteLen": 0,
"randomDlyType": 0,
"payloadVer": 3,
"payload": [
{
"len": 9,
"type": 9,
"data": "4943503130313131"
},
{
"len": 17,
"type": 255,
"data": "0505<I2C1R0 14byte 0 0>"
}
]
}
],
"txSetting": {
"txPower": 0,
"txPaGain": -1,
"sleepAftTx": 1,
"uartSingleWire": 0,
"uartPinSel": 0,
"xoCap": 12,
"xoStableTime": 36,
"xoGm": 16,
"ch0": 37,
"ch1": 38,
"ch2": 39,
"key0": "000102030405060708090a0b0c0d0e0f"
},
"gpio": [
{
"id": 0,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 1,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 2,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 3,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 4,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 5,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 6,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 7,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
}
],
"vccUnit": 0.03125,
"tempUnit": 0.01,
"adc": [
{
"ch": 0,
"enable": 0
},
{
"ch": 1,
"enable": 0
},
{
"ch": 2,
"enable": 0
},
{
"ch": 3,
"enable": 0
}
],
"calibration": {},
"i2c": {
"sw0PwrCtl": 0,
"coldBootEn": 1,
"warmBootEn": 1,
"sclPin": 7,
"sdaPin": 3,
"addrBit": 7,
"speed": 100,
"slvAddr": 99,
"memStoreLen": 14,
"memStoreOffset": 0,
"cmd": [
"i2c tx: 3 c5",
"i2c tx: 3 95",
"i2c tx: 3 0",
"i2c tx: 3 66",
"i2c tx: 3 9c",
"i2c null:",
"i2c tx: 3 c7",
"i2c tx: 3 f7",
"i2c null:",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:",
"i2c tx: 3 c7",
"i2c tx: 3 f7",
"i2c null:",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:",
"i2c tx: 3 c7",
"i2c tx: 3 f7",
"i2c null:",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:",
"i2c tx: 3 c7",
"i2c tx: 3 f7",
"i2c null:",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:",
"i2c wait: 3 1 8",
"i2c tx: 3 68",
"i2c tx: 3 25",
"i2c null:",
"i2c wait: 3 1 37",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:",
"i2c tx: 3 48",
"i2c tx: 3 a3",
"i2c null:",
"i2c wait: 3 1 37",
"i2c rx: 3",
"i2c rx: 3",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:"
]
},
"i2c2": {
"coldBootEn": 0,
"warmBootEn": 0
},
"i2c3": {
"coldBootEn": 0,
"warmBootEn": 0
},
"pulse": {
"enable": 0
},
"qdec": {
"enable": 0,
"runGpioEdgeDetect": 0,
"clearCount": 0,
"endCode": 15,
"twoStep": 0,
"interval": 999,
"timeout": 1999,
"gpio": [],
"code": []
},
"wdt": {
"timerWraparound": 0,
"enable": 0,
"wakeupChip": 0,
"initValue": 30
},
"edgeCount": {
"enable": 0
},
"sqWave": {
"en": 0
},
"rtc": {
"enable": 0,
"clock": 0,
"type": 0
},
"regSetting": [
"write: 0 1 3 3480 2010000",
"write: 0 1 3 3484 3030002",
"write: 0 1 1 34c8 102",
"write: 0 1 3 3494 10020018",
"write: 3 1 0 1084 0",
"write: 3 1 0 11C8 0",
"write: 3 1 0 1c44 c6",
"write: 3 1 0 1ad0 58",
"write: 3 1 1 1d04 731",
"write: 0 1 0 3240 42",
"write: 0 1 0 3500 03"
],
"regSettingCust": [],
"settingPolice": 0
}
{
"version": "3.12",
"advSet": [
{
"id": 0,
"bdAddr": "060504030201",
"addrType": "public",
"addrKey": 0,
"staticAddrGen": 1,
"addrGenInterval": 90,
"interval": 5000,
"authEn": 0,
"authKey": 0,
"authSaltType": 2,
"authSaltValue": 0,
"authEaxCountType": 0,
"authEaxCountValue": 0,
"ui_format": "custom",
"uid2tlm_ratio": 0.0,
"eddystoneTxPower": 0,
"chCtrl": 0,
"advModeTrigEn": 0,
"is1MPhy": 1,
"phyRate": "1M",
"isStandardBle": 1,
"cte": 0,
"cteLen": 0,
"randomDlyType": 0,
"payloadVer": 3,
"payload": [
{
"len": 6,
"type": 9,
"data": "5348543430"
},
{
"len": 8,
"type": 255,
"data": "0505<I2C1R0 5byte 0 0>"
}
]
}
],
"txSetting": {
"txPower": 0,
"txPaGain": -1,
"sleepAftTx": 1,
"uartSingleWire": 0,
"uartPinSel": 0,
"xoCap": 12,
"xoStableTime": 36,
"xoGm": 16,
"ch0": 37,
"ch1": 38,
"ch2": 39,
"key0": "000102030405060708090a0b0c0d0e0f"
},
"gpio": [
{
"id": 0,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 1,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 2,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 3,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 4,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 5,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
},
{
"id": 6,
"digital": "oeHigh",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 1,
"maskb": 0
},
{
"id": 7,
"digital": "default",
"pu-pd": 1,
"wakeup": "disable",
"advTrig": "disable",
"latch": 0,
"maskb": 0
}
],
"vccUnit": 0.03125,
"tempUnit": 0.01,
"adc": [
{
"ch": 0,
"enable": 0
},
{
"ch": 1,
"enable": 0
},
{
"ch": 2,
"enable": 0
},
{
"ch": 3,
"enable": 0
}
],
"calibration": {},
"i2c": {
"sw0PwrCtl": 1,
"coldBootEn": 1,
"warmBootEn": 1,
"sclPin": 7,
"sdaPin": 3,
"addrBit": 7,
"speed": 100,
"slvAddr": 68,
"memStoreLen": 5,
"memStoreOffset": 0,
"cmd": [
"i2c tx: 3 fd",
"i2c null:",
"i2c wait: 3 1 4e",
"i2c rx: 3",
"i2c rx: 3",
"i2c rx: 3",
"i2c rx: 3",
"i2c rx: 3",
"i2c null:"
]
},
"i2c2": {
"coldBootEn": 0,
"warmBootEn": 0
},
"i2c3": {
"coldBootEn": 0,
"warmBootEn": 0
},
"pulse": {
"enable": 0
},
"qdec": {
"enable": 0,
"runGpioEdgeDetect": 0,
"clearCount": 0,
"endCode": 15,
"twoStep": 0,
"interval": 999,
"timeout": 1999,
"gpio": [],
"code": []
},
"wdt": {
"timerWraparound": 0,
"enable": 0,
"wakeupChip": 0,
"initValue": 30
},
"edgeCount": {
"enable": 0
},
"sqWave": {
"en": 0
},
"rtc": {
"enable": 0,
"clock": 0,
"type": 0
},
"regSetting": [
"write: 0 1 3 3480 2010000",
"write: 0 1 3 3484 3030002",
"write: 0 1 1 34c8 102",
"write: 0 1 3 3494 10020018",
"write: 3 1 0 1084 0",
"write: 3 1 0 11C8 0",
"write: 3 1 0 1c44 c6",
"write: 3 1 0 1ad0 58",
"write: 3 1 1 1d04 731",
"write: 0 1 0 3240 42",
"write: 0 1 0 3500 03"
],
"regSettingCust": [],
"settingPolice": 0
}
#include <Arduino.h>
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <BLEAdvertisedDevice.h>
#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8))
#define RXD2 16 //communication using Serial2
#define TXD2 17 //communication using Serial
//connection to local wifi to send data to Node-Red:
const char* ssid = "xxxx"; //WiFi SSID to connect to send data to Node-Red
const char* pass = "xxxx"; //WiFi pass to connect to send data to Node-Red
const char* mqtt_client_id = "xxxx"; //MQTT client ID used by Node-red
const char* mqtt_user = "xxxx"; //MQTT user for Node-Red; see Node-Red documentation
const char* mqtt_pass = "xxxx"; //MQTT pass for Node-Red; see Node-Red documentation
const char* mqtt_host = "xxxx"; //MQTT host for Node-Red; see Node-Red documentation
char finalTopic_wifi[200]; //used to send the complete string to Node-Red
//variables used for several timers:
unsigned long interval_1 = 30000; //used to send the data to Node-Red via WiFi; (30000 = 30000ms)
unsigned long timer_1 = 0; //used to send data at every 30 seconds (30000 = 30000ms)
uint32_t timer = millis();
//variable used for SHT40 sensor:
float TemperatureData, HumidityData;
float Temperature, Humidity;
float SHT40_temperature, SHT40_humidity;
//variable used for Analog sensors:
int ExternalTempData;
int InternalTempData;
int BatteryData;
int MoistureData;
int LightData;
int AnalogExternalTempInteger;
int AnalogExternalTempDecimal;
int AnalogInternalTempInteger;
int AnalogInternalTempDecimal;
float batteryVoltage;
//variable used to transfer data to MQTT for ICP10111
float ICP10111_temperature;
float ICP10111_elevation;
float ICP10111_airPressure;
//variables, structures and functions used for ICP10111 sensor:
/**
* @struct sInvInvpres_t
* @brief Store the calculated data
*/
typedef struct {
float sensorConstants[4]; // OTP values
float pPaCalib[3];
float LUTLower;
float LUTUpper;
float quadrFactor;
float offstFactor;
}sInvInvpres_t;
/**
* @struct sGetTempAndAirPressure_t
* @brief Save the temperature and air pressure data
*/
typedef struct {
float temp;
float airPressure;
float elevation;
}sGetTempAndAirPressure_t;
/**
* @struct sInitialData_t
* @brief Store initial check data
*/
typedef struct {
float s1;
float s2;
float s3;
}sInitialData_t;
/**
* @struct sUltimatelyData_t
* @brief Store ultimate check data
*/
typedef struct {
float A;
float B;
float C;
}sUltimatelyData_t;
/**
* @enum eWorkPattern_t
* @brief Work mode select
*/
typedef enum {
eLowPower_P = 0x401A, /**<Low Power Mode Conversion Time: 1.8ms Pressure RMS Noise:3.2Pa*/
eNormal_P = 0x48A3, /**<Normal Mode Conversion Time:6.3ms Pressure RMS Noise:1.6Pa*/
eLowNoise_P = 0x5059, /**<Low Noise Mode Conversion Time:23.8ms Pressure RMS Noise:0.8Pa*/
eUltraLowNoise_P = 0x58E0, /**<Ultra Low Noise Mode Conversion Time:94.5 Pressure RMS Noise:0.4Pa*/
eLowPower_T = 0x609C,
eNormal_T = 0x6825,
eLowNoise_T = 0x70DF,
eUltraLowNoise_T = 0x7866,
}eWorkPattern_t;
sInvInvpres_t _dataStorage;
sInvInvpres_t* _d = &_dataStorage;
sGetTempAndAirPressure_t _tempAndAirPressure;
sGetTempAndAirPressure_t* _t = &_tempAndAirPressure;
sInitialData_t _inputData;
sInitialData_t* _i = &_inputData;
sUltimatelyData_t _outData;
sUltimatelyData_t* _o = &_outData;
bool optSetFlag = false;
//fucntions used for ICP1011 sensor:
void initBase(sInvInvpres_t* s, short* otp)
{
for (uint8_t i = 0; i < 4; i++) {
s->sensorConstants[i] = otp[i];
}
s->pPaCalib[0] = 45000.0;
s->pPaCalib[1] = 80000.0;
s->pPaCalib[2] = 105000.0;
s->LUTLower = 3.5 * ((uint32_t)1 << 20);
s->LUTUpper = 11.5 * ((uint32_t)1 << 20);
s->quadrFactor = 1 / 16777216.0;
s->offstFactor = 2048.0;
}
void calculateConversionConstants(float* pPa, sInitialData_t* i)
{
_o->C = (i->s1 * i->s2 * (pPa[0] - pPa[1]) + \
i->s2 * i->s3 * (pPa[1] - pPa[2]) + \
i->s3 * i->s1 * (pPa[2] - pPa[0])) / \
(i->s3 * (pPa[0] - pPa[1]) + i->s1 * (pPa[1] - pPa[2]) + \
i->s2 * (pPa[2] - pPa[0]));
_o->A = (pPa[0] * i->s1 - pPa[1] * i->s2 - (pPa[1] - pPa[0]) * _o->C) / (i->s1 - i->s2);
_o->B = (pPa[0] - _o->A) * (i->s1 + _o->C);
}
void getTempAndAirPressure(uint16_t _temp, uint32_t _airPressure)
{
float t;
// temperature
t = (float)(_temp - 32768);
_i->s1 = _d->LUTLower + (float)(_d->sensorConstants[0] * t * t) * _d->quadrFactor;
_i->s2 = _d->offstFactor * _d->sensorConstants[3] + (float)(_d->sensorConstants[1] * t * t) * _d->quadrFactor;
_i->s3 = _d->LUTUpper + (float)(_d->sensorConstants[2] * t * t) * _d->quadrFactor;
calculateConversionConstants(_d->pPaCalib, _i);
// pressure
_t->airPressure = _o->A + (_o->B / (_o->C + _airPressure));
_t->temp = -45.f + 175.f / 65536.f * _temp;
// altitude
_t->elevation = 44330 * (1.0 - pow((_t->airPressure / 100.0) / 1015.0, 0.1903));
}
int scanTime = 5; // In seconds
BLEScan *pBLEScan;
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
if (advertisedDevice.haveName()) {
std:: string deviceName = advertisedDevice.getName().c_str(); // Convert to std::string
// Serial.print("Device name: ");
// Serial.println(deviceName.c_str());
if (deviceName == "Analog")
{
Serial.print("Device name: ");
Serial.println(deviceName.c_str());
std::string strManufacturerData = advertisedDevice.getManufacturerData().c_str(); // Convert to std::string
uint8_t cManufacturerData[100];
strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0);
Serial.printf("Manufacturer Data Length: %d ", strManufacturerData.length());
for (size_t i = 0; i < strManufacturerData.length(); i++)
{
Serial.printf("[%X]", cManufacturerData[i]);
}
Serial.println();
//read RAW data and extract the last 10 bytes:
// Advertisement Data (Raw Data)
uint8_t* advData = advertisedDevice.getPayload();
size_t advLength = advertisedDevice.getPayloadLength();
// Convertim advData ntr-un std::string
std::string advDataStr((char*)advData, advLength);
// Extrage doar ultimii 11 octei (sau mai puin, dac lungimea este mai mic)
size_t startIdx = (advLength > 11) ? (advLength - 11) : 0;
size_t numBytes = (advLength > 11) ? 11 : advLength;
std::string usefulData = advDataStr.substr(startIdx, numBytes);
Serial.printf("Useful Data Length: %d", usefulData.length());
// Serial.printf("Useful Data Length: %d\n", usefulData.length());
// Serial.println();
// Serial.print("Useful Data: ");
for (size_t i = 0; i < usefulData.length(); i++)
{
Serial.printf("[%02X]", (unsigned char)usefulData[i]);
}
Serial.println();
// Getting raw data from
int ExternalTempData = float((usefulData[2] << 8 | usefulData[3]));
BatteryData = int(usefulData[4]);
int InternalTempData = float(usefulData[5] << 8 | usefulData[6]);
MoistureData = int(usefulData[7] << 8 | usefulData[8]);
LightData = int(usefulData[9] << 8 | usefulData[10]);
ExternalTempData = ExternalTempData - 500;
float scaling_factor = 32.9; //is calculated from: 2.9V/88, where 88 is the value sent by the BLE Beacon ADC
batteryVoltage = BatteryData * scaling_factor / 1000.0;
int ExternalTempInteger = ExternalTempData / 10;
int ExternalTempDecimal = ExternalTempData % 10;
int BatteryInteger = BatteryData / 100;
int BatteryDecimal = BatteryData % 100;
int InternalTempInteger = InternalTempData / 100;
int InternalTempDecimal = InternalTempData % 100;
AnalogExternalTempInteger = ExternalTempInteger;
AnalogExternalTempDecimal = ExternalTempDecimal;
AnalogInternalTempInteger = InternalTempInteger;
AnalogInternalTempDecimal = InternalTempDecimal;
//Display data:
Serial.println();
//display external temp sensor value:
Serial.print("ExternalTemp: ");
Serial.print(ExternalTempInteger);
Serial.print(",");
Serial.print(ExternalTempDecimal); Serial.println(" ");
//Display battery voltage
Serial.print("Battery voltage: ");
Serial.print(batteryVoltage);
Serial.println(" V");
//display internal temp sensor value:
Serial.print("InternalTemp: ");
Serial.print(InternalTempInteger);
Serial.print(",");
if (InternalTempDecimal < 10)
{
Serial.print("0");
}
Serial.print(InternalTempDecimal); Serial.println(" ");
//Display moisture sensor value:
Serial.print("Moisture sensor value: ");
Serial.println(MoistureData);
//Display Light sensor value:
Serial.print("Light sensor value: ");
Serial.println(LightData);
Serial.println("------------------");
}
else if (deviceName == "SHT40")
{
Serial.print("Device name: ");
Serial.println(deviceName.c_str());
std::string strManufacturerData = advertisedDevice.getManufacturerData().c_str(); // Convert to std::string
uint8_t cManufacturerData[100];
strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0);
Serial.printf("Manufacturer Data Length: %d ", strManufacturerData.length());
for (size_t i = 0; i < strManufacturerData.length(); i++)
{
Serial.printf("[%X]", cManufacturerData[i]);
}
// Getting raw data from SHT40
TemperatureData = int(cManufacturerData[2] << 8 | cManufacturerData[3]);
HumidityData = int(cManufacturerData[5] << 8 | cManufacturerData[6]);
// Convert raw data into temperature and humidity data
Temperature = (175 * TemperatureData / 65535) - 45;
Humidity = (125 * HumidityData / 65535) - 6;
SHT40_temperature = Temperature;
SHT40_humidity = Humidity;
Serial.println();
Serial.print("TemperatureData: ");
Serial.print(Temperature);
Serial.println("");
Serial.print("HumidityData: ");
Serial.print(Humidity);
Serial.println("%");
Serial.println("------------------");
}
else if (deviceName == "ICP10111")
{
Serial.print("Device name: ");
Serial.println(advertisedDevice.getName().c_str());
std::string strManufacturerData = advertisedDevice.getManufacturerData().c_str(); // Convert to std::string
uint8_t cManufacturerData[100];
strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0);
Serial.printf("strManufacturerData: %d ", strManufacturerData.length());
for (int i = 0; i < strManufacturerData.length(); i++) {
Serial.printf("[%X]", cManufacturerData[i]);
}
Serial.println();
//read RAW data and extract the last 14 bytes:
// Advertisement Data (Raw Data)
uint8_t* advData = advertisedDevice.getPayload();
size_t advLength = advertisedDevice.getPayloadLength();
// Convertim advData ntr-un std::string
std::string advDataStr((char*)advData, advLength);
// Extrage doar ultimii 16 octei (sau mai puin, dac lungimea este mai mic)
size_t startIdx = (advLength > 16) ? (advLength - 16) : 0;
size_t numBytes = (advLength > 16) ? 16 : advLength;
std::string usefulData = advDataStr.substr(startIdx, numBytes);
Serial.printf("Useful Data Length: %d\n", usefulData.length());
Serial.print("Useful Data: ");
for (size_t i = 0; i < usefulData.length(); i++)
{
Serial.printf("[%02X]", (unsigned char)usefulData[i]);
}
Serial.println();
//end of read raw data
if (!optSetFlag) { //Initialize calibration data
short out[4];
for (uint8_t i = 0; i < 4; i++) {
out[i] = usefulData[i * 2 + 2] << 8 | usefulData[i * 2 + 3];
}
initBase(_d, out);
optSetFlag = true;
}
//Get raw data from ICP10111
uint16_t temp = (uint16_t)usefulData[10] << 8 | usefulData[11];
uint32_t airPressure = (uint32_t)usefulData[12] << 16 | (uint32_t)usefulData[13] << 8 | (uint32_t)usefulData[15];
//Convert raw data:
getTempAndAirPressure(temp, airPressure);
ICP10111_temperature = _t->temp;
ICP10111_elevation = _t->elevation;
ICP10111_airPressure = _t->airPressure;
Serial.println();
Serial.print("Read air pressure:");
Serial.print(_t->airPressure);
Serial.println("Pa");
Serial.print("Read temperature:");// Serial.print(temp);
Serial.print(_t->temp);
Serial.println("C");
Serial.print("Read altitude:");
Serial.print(_t->elevation);
Serial.println("m");
Serial.println("------------------");
}
}
}
};
void setup() {
Serial.begin(115200);
Serial.println("Scanning...");
Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); //comunication via Serial2
Serial.println("Comunication with ESP8266-01 is open...");
connect_to_local_wifi();
BLEDevice::init("");
pBLEScan = BLEDevice::getScan(); // create new scan
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setActiveScan(true); // active scan uses more power, but get results faster
pBLEScan->setInterval(100);
pBLEScan->setWindow(99); // less or equal setInterval value
}
void read_data_from_serial2()
{
while (Serial2.available() > 0)
{
String citire = Serial2.readString();
Serial.print("Response: ");
Serial.println(citire);
}
}
void connect_to_local_wifi()
{
//Verify disponibility of ESP8266-01:
Serial2.println("AT");
Serial.println("AT");
read_data_from_serial2();
delay(1000);
//Reset ESP8266-01:
Serial2.println("AT+RST");
Serial.println("AT+RST");
read_data_from_serial2();
delay(2000);
//Set ESP8266 mode 3 = access point and station mode
Serial2.println("AT+CWMODE=3");
Serial.println("AT+CWMODE=3");
read_data_from_serial2();
delay(5000);
String command = "AT+CWJAP=\"";
command += ssid;
command += "\",\"";
command += pass;
command += "\"";
Serial2.println(command);
Serial.println(command);
read_data_from_serial2();
delay(5000);
Serial2.println("AT+CIFSR"); //connect to AP
Serial.println("AT+CIFSR"); //connect to AP
read_data_from_serial2();
delay(100);
}
void send_mqtt_data_to_node_red()
{
sprintf(finalTopic_wifi, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"", mqtt_client_id, mqtt_user, mqtt_pass);
Serial2.println(finalTopic_wifi);
Serial.println(finalTopic_wifi);
read_data_from_serial2();
delay(500);
memset(finalTopic_wifi, '\0', sizeof(finalTopic_wifi)); //reset the string
sprintf(finalTopic_wifi, "AT+MQTTCONN=0,\"%s\",1883,1", mqtt_host);
Serial2.println(finalTopic_wifi);
Serial.println(finalTopic_wifi);
read_data_from_serial2();
delay(500);
Serial.println("Send data from analog sensors: ");
//send Mmqtt messages with data from MCP9700 and internal sensors:
Serial2.println("AT+MQTTPUB=0,\"esp32/analog_batteryVoltage\",\"" + String(batteryVoltage) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/analog_batteryVoltage\",\"" + String(batteryVoltage) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/analog_MoistureData\",\"" + String(round(MoistureData)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/analog_MoistureData\",\"" + String(MoistureData) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/analog_LightData\",\"" + String(round(LightData)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/analog_LightData\",\"" + String(LightData) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/AnalogExternalTempInteger\",\"" + String(round(AnalogExternalTempInteger)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/AnalogExternalTempInteger\",\"" + String(AnalogExternalTempInteger) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/AnalogExternalTempDecimal\",\"" + String(round(AnalogExternalTempDecimal)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/AnalogExternalTempDecimal\",\"" + String(AnalogExternalTempDecimal) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/AnalogInternalTempInteger\",\"" + String(round(AnalogInternalTempInteger)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/AnalogInternalTempInteger\",\"" + String(AnalogInternalTempInteger) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/AnalogInternalTempDecimal\",\"" + String(round(AnalogInternalTempDecimal)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/AnalogInternalTempDecimal\",\"" + String(AnalogInternalTempDecimal) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/sh40_temperature\",\"" + String(round(SHT40_temperature)) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/sht40_temperature\",\"" + String(SHT40_temperature) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial.println("Send data from digital SHT40 sensor: ");
//send Mmqtt messages with data from SHT40:
Serial2.println("AT+MQTTPUB=0,\"esp32/sh40_temperature\",\"" + String(SHT40_temperature) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/sht40_temperature\",\"" + String(SHT40_temperature) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/sht40_humidity\",\"" + String(SHT40_humidity) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/sht40_humidity\",\"" + String(SHT40_humidity) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial.println("Send data from digital ICP10111 sensor: ");
//send Mmqtt messages with data from ICP10111:
Serial2.println("AT+MQTTPUB=0,\"esp32/temperature\",\"" + String(ICP10111_temperature) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/temperature\",\"" + String(ICP10111_temperature) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/altitude\",\"" + String(ICP10111_elevation) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/altitude\",\"" + String(ICP10111_elevation) + "\",1,0");
read_data_from_serial2();
delay(500);
Serial2.println("AT+MQTTPUB=0,\"esp32/atmospheric_pressure\",\"" + String(ICP10111_airPressure) + "\",1,0");
Serial.println("AT+MQTTPUB=0,\"esp32/atmospheric_pressure\",\"" + String(ICP10111_airPressure) + "\",1,0");
read_data_from_serial2();
delay(500);
}
void loop() {
BLEScanResults *foundDevices = pBLEScan->start(scanTime, false);
Serial.print("Devices found: ");
Serial.println(foundDevices->getCount());
pBLEScan->clearResults(); // delete results from BLEScan buffer to release memory
delay(2000); //2 sec pause
//send data to Node-Red with lower rate than are displayed serial console:
if (millis() - timer_1 > interval_1)
{
timer_1 = millis();
send_mqtt_data_to_node_red(); //send data to Node-Red
}
}
No preview (download only).
Comments