Arduino® Nano ESP32-based ultrasonic distance sensor, comprising electrical schematic and firmware required to build the device, part of the "Museum Alert" project.
Concurrent WiFi + BLE operation: an interesting technical aspect is the stable, simultaneous operation of WiFi and Bluetooth Low Energy on the Arduino Nano ESP32, powered by the u-blox® NORA-W106 (ESP32-S3) module. Since the ESP32-S3 uses a single 2.4GHz radio for both protocols, careful RF resource management is essential to avoid conflicts that typically lead to instability in dual-mode implementations.
Important: please review the disclaimer before using this project.
Q.ty | Component |
---|---|
1 | Breadboard (5 columns on each side, dual power rails) |
1 | Arduino® Nano ESP32 (with headers) |
1 | HC-SR04 ultrasonic sensor |
3 | Green led |
1 | Red led |
1 | 4-pin push button |
4 | 220Ω resistor |
4 | Pin Jumper Wire (Male-Female) |
6 | Pin Jumper Wire (Male-Male) |
Board | Name | Arduino® Nano ESP32 |
SKU | ABX00092 | |
Microcontroller | – | u-blox® NORA-W106 (ESP32-S3) |
USB connector | – | USB-C® |
Pins | Built-in LED Pin | 13 |
Built-in RGB LED pins | 14–16 | |
Digital I/O Pins | 14 | |
Analog input pins | 8 | |
PWM pins | 5 | |
External interrupts | All digital pins | |
Connectivity | Wi-Fi® | u-blox® NORA-W106 (ESP32-S3) |
Bluetooth® | u-blox® NORA-W106 (ESP32-S3) | |
Communication | UART | 2x |
I2C | 1x, A4 (SDA), A5 (SCL) | |
SPI | D11 (COPI), D12 (CIPO), D13 (SCK); any GPIO for Chip Select (CS) | |
Power | I/O Voltage | 3.3 V |
Input voltage (nominal) | 6–21 V | |
Source Current per I/O Pin | 40 mA | |
Sink Current per I/O Pin | 28 mA | |
Clock speed | Processor | up to 240 MHz |
Memory | ROM | 384 kB |
SRAM | 512 kB | |
External Flash | 128 Mbit (16 MB) | |
RAM | 8 MB (NORA-W106-10B) | |
Dimensions | Width | 18 mm |
Length | 45 mm |
Full technical specifications are available on manufactorer's website.
General | Model | HC-SR04 |
Sensor Type | Ultrasonic Distance Sensor | |
Operating Principle | Echo ranging | |
Electrical | Operating Voltage | 5 V |
Operating Current | 15 mA | |
Working Frequency | 40 kHz | |
Performance | Measuring Range | 2 cm – 400 cm |
Accuracy | ±3 mm | |
Effectual Angle | < 15° | |
Resolution | ~0.3 cm | |
Interface | Trigger Input Pulse | ≥10 µs |
Echo Output Pulse | TTL pulse proportional to distance | |
Interface Type | Digital (TTL) | |
Connector Type | 4-pin header | |
Pinout | VCC, Trig, Echo, GND | |
Timing | Measuring Cycle | ~60 ms |
Response Time | ~750 µs | |
Physical | Dimensions | 45 mm × 20 mm × 15 mm |
Weight | ~10 g |
- Arduino IDE: version 2.3.6 or higher;
- operating system: Windows, macOS, or GNU/Linux.
The sketch depends on two external libraries and several ESP32 platform libraries:
Library | Version |
---|---|
ArduinoJson | 7.4.2 |
PubSubClient | 2.8 |
The following libraries are bundled with the ESP32 Arduino Core and automatically available:
Library | Version | Function |
---|---|---|
WiFi | 2.0.0 | WiFi connectivity management |
WiFiClientSecure | 2.0.0 | TLS/SSL secure connections |
Preferences | 2.0.0 | Non-volatile storage (NVS) access |
ESP32 BLE Arduino | 2.0.0 | Bluetooth Low Energy functionality |
External Libraries - Install via Arduino Library Manager:
- open the Arduino IDE;
- go to Tools → Manage Libraries...;
- search for ArduinoJson and install version 7.4.2 or above;
- search for PubSubClient and install version 2.8 or above.
Platform Libraries - These are automatically included when you install the ESP32 Arduino Core via the Board Manager.
Alternatively, you can add libraries manually to your lib
folder if you're using PlatformIO or another build system.
This device requires the following components to be deployed and configured before use:
-
Museum Alert API - AWS CDK infrastructure project that provides:
- AWS API Gateway endpoints;
- Amazon Cognito authentication and authorization services;
- AWS IoT Core configuration;
- required environment configuration files.
After deploying the Museum Alert API Infrastructure:
- find the configuration: look for the
ArduinoSketchConfiguration
output in your CDK deployment console; - copy the complete configuration: the deployment output provides a ready-to-copy
IOT_CORE_ENDPOINT
C++ constant expression; - update configuration file: replace the placeholder configuration in
config.h
.
-
Museum Alert Desktop - Cross-platform Angular Electron desktop application suitable for provisioning, configuring, and testing the Museum Alert Sensor (MAS). The software most notably provides:
- user registration and authentication flow;
- Museum Alert Sensor (MAS) onboarding in the authenticated user's Company device pool;
- Museum Alert Sensor (MAS) configuration (distance, BLE Eddystone-URL beacon) and testing.
- Plug your Museum Alert Sensor (MAS) into your computer using a USB-C cable;
- in the Arduino IDE, go to Tools → Board and select Arduino Nano ESP32;
- select the correct port under Tools → Port.
The sketch can be compiled in two modes:
Debug Mode (default):
- enables logging: comprehensive debug output via serial console for troubleshooting;
- forces initialization delay: 20-second startup delay to allow time for debugging during development;
- activation: comment out
#define RELEASE_BUILD
inconfig.h
(line 19).
Release Mode:
- disables logging: no debug output, optimized for production use;
- fast startup: no initialization delays, immediate operation;
- activation: uncomment
#define RELEASE_BUILD
inconfig.h
(line 19).
Using Arduino IDE:
- open the sketch file (
.ino
) from this repository; - modify the
#define RELEASE_BUILD
line inconfig.h
as needed (see build modes above); - click the Upload button (right arrow icon);
- wait for the sketch to compile and upload;
- once complete, the board will automatically reset and begin running the sketch.
Using Arduino CLI:
# Debug build (with logging and delays) - compile only
arduino-cli compile --fqbn arduino:esp32:nano_nora --build-property build.extra_flags="-DESP32" --verbose
# Debug build (with logging and delays) - compile and upload
arduino-cli compile --fqbn arduino:esp32:nano_nora --build-property build.extra_flags="-DESP32" --upload --port /dev/cu.usbmodem* --verbose
# Release build (optimized, no logging) - compile only
arduino-cli compile --fqbn arduino:esp32:nano_nora --build-property build.extra_flags="-DRELEASE_BUILD -DESP32" --verbose
# Release build (optimized, no logging) - compile and upload
arduino-cli compile --fqbn arduino:esp32:nano_nora --build-property build.extra_flags="-DRELEASE_BUILD -DESP32" --upload --port /dev/cu.usbmodem* --verbose
Note: Replace /dev/cu.usbmodem*
with your actual port (use arduino-cli board list
to find the correct port).
After flashing:
- open Serial Monitor in the Arduino IDE (Tools → Serial Monitor);
- you should see debug output or sensor status messages confirming the sketch is running.
Important: before using the "Museum Alert Desktop" application with this device, make sure to close the Arduino IDE (including the Serial Monitor). The USB port can only be used by one application at a time, and leaving the Arduino IDE open will prevent the desktop application from connecting to the device.
- If upload fails, press the RESET button on the sensor's board and try again;
- ensure no other application is using the serial port;
- double-check that the correct board and port are selected.
The device operates through a finite state machine with the following states:
State | Description | Available Commands |
---|---|---|
STARTED | Initial state when device boots up | None |
CONFIGURE_WIFI | Device is waiting for WiFi credentials | REFRESH_WIFI_CREDENTIALS , SET_WIFI_CREDENTIALS |
CONNECT_TO_WIFI | Device is attempting to connect to WiFi | None (automatic transition) |
CONFIGURE_CERTIFICATES | Device is waiting for provisioning certificates | SET_PROVISIONING_CERTIFICATES |
PROVISION_DEVICE | Device is registering with AWS IoT Core | None (automatic transition) |
CONNECT_TO_MQTT_BROKER | Device is connecting to MQTT broker | None (automatic transition) |
DEVICE_INITIALIZED | Device is operational and monitoring distance | RESET , GET_CONFIGURATION , SET_CONFIGURATION |
FATAL_ERROR | Device encountered an unrecoverable error | RESET |
The device accepts commands via USB serial port using JSON format wrapped with <|
and |>
markers.
<|{
"cid": "<correlation_id>",
"commandType": <command_type_number>,
"payload": { /* command-specific data */ }
}|>
Command | Type ID | Available States | Payload | Description |
---|---|---|---|---|
SET_WIFI_CREDENTIALS | 2 | CONFIGURE_WIFI |
{"ssid": "network_name", "password": "network_password"} |
Sets WiFi network credentials |
REFRESH_WIFI_CREDENTIALS | 1 | CONFIGURE_WIFI |
None | Requests fresh WiFi networks scan |
SET_PROVISIONING_CERTIFICATES | 0 | CONFIGURE_CERTIFICATES |
{"tempCertPem": "...", "tempPrivateKey": "...", "idToken": "..."} |
Provides AWS IoT temporary provisioning certificates and Cognito user session token |
RESET | 3 | FATAL_ERROR |
None | Forces device reset and storage wipe |
The device sends messages and responses in JSON format wrapped with <|
and |>
markers:
Message Type | Type ID | Description |
---|---|---|
APP_STATE | 0 | Current device state notification |
WIFI_NETWORKS_LIST | 1 | Available WiFi networks |
ERROR | 2 | Error notification with error code |
ACKNOWLEDGMENT | 3 | Command acknowledgment |
Note: the correlation id property cid
is only returned when the message is a response to a client-initiated request.
APP_STATE - Device state change notification:
<|{
"type": 0,
"sn": "MAS-EC357A188534",
"data": {
"appState": 2
}
}|>
WIFI_NETWORKS_LIST - Available WiFi networks scan result:
<|{
"type": 1,
"sn": "MAS-EC357A188534",
"cid": "MAS-EC357A188534-a6a2c35b-9f74-4af8-8b0c-5f35c7e6f992-1756458740990",
"data": [
{
"ssid": "Museum_WiFi_5G",
"rssi": -42,
"encryptionType": 3
},
{
"ssid": "Office_Network",
"rssi": -67,
"encryptionType": 3
}
]
}|>
ERROR - Error notification with error codes:
<|{
"type": 2,
"sn": "MAS-EC357A188534",
"cid": "MAS-EC357A188534-a6a2c35b-9f74-4af8-8b0c-5f35c7e6f992-1756458740990",
"data": {
"error": 0
}
}|>
ACKNOWLEDGMENT - Command acknowledgment with no payload (e.g.: SET_WIFI_CREDENTIALS
):
<|{
"type": 3,
"sn": "MAS-EC357A188534",
"cid": "MAS-EC357A188534-a6a2c35b-9f74-4af8-8b0c-5f35c7e6f992-1756458740990"
}|>
Error Code | Error Type | Description |
---|---|---|
0 | INVALID_WIFI_CREDENTIALS | WiFi credentials are invalid or missing |
1 | FAILED_WIFI_CONNECTION_ATTEMPT | Unable to connect to WiFi network |
2 | INVALID_DEVICE_PROVISIONING_SETTINGS | Provisioning certificates are invalid |
3 | INVALID_DEVICE_COMMAND | Unknown or invalid command received |
4 | FAILED_PROVISIONING_SETTINGS_STORAGE | Cannot save provisioning data to storage |
5 | FAILED_DEVICE_PROVISIONING_ATTEMPT | Device registration with AWS IoT failed |
6 | FAILED_MQTT_BROKER_CONNECTION | Cannot connect to MQTT broker |
7 | FAILED_DEVICE_CONFIGURATION_RETRIEVAL | Cannot load device configuration |
8 | FAILED_SENSOR_DETECTION_REPORT | Cannot send sensor data via MQTT |
When in DEVICE_INITIALIZED
state, the device accepts commands via MQTT on topic:
companies/{company_id}/devices/{device_id}/commands
{
"type": <command_type_number>,
"cid": "correlation_id",
... command-specific data ...
}
Command | Type ID | Payload | Description |
---|---|---|---|
RESET | 0 | None | Remotely resets the device |
GET_CONFIGURATION | 1 | None | Retrieves current device configuration |
SET_CONFIGURATION | 2 | {"distance": 25.0, "beaconUrl": "https://example.com"} |
Updates device configuration |
Parameter | Type | Range | Description |
---|---|---|---|
distance | Float | 2.0 - 400.0 cm | Minimum distance threshold for alert |
beaconUrl | String | Max 18 chars (encoded) | BLE beacon Eddystone-URL |
The device publishes messages on topic:
companies/{company_id}/devices/{device_id}/events
Message Type | Type ID | Description |
---|---|---|
ALARM | 0 | Distance threshold breach detected |
CONNECTION_STATUS | 1 | Device connectivity status |
CONFIGURATION | 2 | Current device configuration (response to GET_CONFIGURATION) |
ACK | 3 | Command acknowledgment |
ALARM - Distance threshold breach notification:
{
"sn": "MAS-EC357A188534",
"timestamp": 1756816515275,
"type": 0,
"data": {
"distance": 18
}
}
CONNECTION_STATUS - Device connectivity status (sent automatically):
{
"sn": "MAS-EC357A188534",
"timestamp": 1756816515275,
"type": 1,
"data": {
"connected": true
}
}
CONFIGURATION - Current device configuration (response to GET_CONFIGURATION
):
{
"cid": "MAS-EC357A188534-a6a2c35b-9f74-4af8-8b0c-5f35c7e6f992-1756458740990",
"sn": "MAS-EC357A188534",
"timestamp": 1756816515275,
"type": 2,
"data": {
"distance": 30.0,
"beaconUrl": "https://example.com",
"firmware": "1.0.0"
}
}
ACK - Command acknowledgment (response to RESET
):
{
"type": 3,
"cid": "MAS-EC357A188534-a6a2c35b-9f74-4af8-8b0c-5f35c7e6f992-1756458740990"
}
The device includes a custom reset button (separate from the Arduino's built-in reset) connected to pin 7.
Behavior:
- button press: pressing the reset button for longer than three seconds triggers a device reset;
- function: erases all stored settings (WiFi credentials, certificates, configuration) and restarts the device;
- use case: factory reset when device needs to be reconfigured or is in an unrecoverable state.
The device has 4 LEDs that provide visual feedback about the current device state and status:
LED | Pin | Color | Function |
---|---|---|---|
WiFi | 4 | Green | WiFi connection status |
Status | 3 | Green | Device state indicator |
Alarm | 2 | Red | Distance alarm status |
Sensor Power | - | Green | Ultrasonic sensor power (always on) |
Device State | WiFi LED | Status LED | Alarm LED | Description |
---|---|---|---|---|
STARTED | OFF | Slow blink (520ms) | OFF | Device booting up |
CONFIGURE_WIFI | OFF | Medium blink (260ms) | OFF | Waiting for WiFi credentials |
CONNECT_TO_WIFI | OFF | Slow blink (520ms) | OFF | Attempting WiFi connection |
CONFIGURE_CERTIFICATES | Variable | Medium blink (260ms) | OFF | Waiting for provisioning certificates |
PROVISION_DEVICE | Variable | Fast blink (130ms) | OFF | Registering with AWS IoT Core |
CONNECT_TO_MQTT_BROKER | Variable | Slow blink (520ms) | OFF | Connecting to MQTT broker |
DEVICE_INITIALIZED | Variable | Solid ON | Variable | Device operational and monitoring |
FATAL_ERROR | Variable | Very fast blink (75ms) | Variable | Unrecoverable error state |
WiFi LED (Green, Pin 4):
- ON: WiFi connected;
- OFF: WiFi disconnected.
Status LED (Green, Pin 3):
- Solid ON: device fully operational (DEVICE_INITIALIZED state);
- Slow Blink (520ms): starting up or transitioning;
- Medium Blink (260ms): waiting for configuration input;
- Fast Blink (130ms): active provisioning process;
- Very Fast Blink (75ms): fatal error - device reset required.
Alarm LED (Red, Pin 2):
- ON: distance threshold breached (object detected within alert distance);
- OFF: no alert condition (distance above threshold);
- Note: only active when device is in DEVICE_INITIALIZED state.
This open source project, including all its submodules, documentation, and associated code (collectively, the "Project"), is provided for educational and experimental purposes only.
THE PROJECT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. THE AUTHOR MAKES NO WARRANTIES ABOUT THE ACCURACY, RELIABILITY, COMPLETENESS, OR TIMELINESS OF THE PROJECT OR ITS COMPONENTS.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE PROJECT OR THE USE OR OTHER DEALINGS IN THE PROJECT. THIS INCLUDES, BUT IS NOT LIMITED TO:
- AWS Costs: any charges incurred from AWS services deployed using the provided CDK templates;
- Hardware Damage: damage to Arduino boards, sensors, or other electronic components;
- Data Loss: loss of data or configuration settings;
- Service Interruptions: downtime or interruptions to connected services;
- Security Issues: any security vulnerabilities or breaches;
- Indirect Damages: lost profits, business interruption, or consequential damages of any kind.
By using this Project, you acknowledge and agree that:
- you use the Project entirely at your own risk;
- you are responsible for understanding AWS pricing and monitoring your usage to avoid unexpected charges;
- you should implement appropriate security measures for any production deployments;
- you are responsible for compliance with all applicable laws and regulations in your jurisdiction;
- you should test thoroughly in development environments before any production use;
- you are responsible for backing up any important data or configurations.
This project may create AWS resources that incur charges; users are solely responsible for:
- understanding AWS pricing models;
- monitoring their AWS usage and costs;
- properly terminating or deleting resources when no longer needed;
- reviewing and understanding all CloudFormation templates before deployment.
This Project may include or reference third-party libraries, services, or components. The author is not responsible for the functionality, security, or licensing of these third-party components. Users should review and comply with all applicable third-party licenses and terms of service.
Users may modify and distribute this Project under the terms of the applicable open source license. However, any modifications or distributions must include this disclaimer, and the author bears no responsibility for modified versions of the Project.
This Project is not intended to replace professional consultation. For production systems or critical applications, please consult with qualified professionals in the relevant fields.
By downloading, cloning, forking, or otherwise using this Project, you acknowledge that you have read, understood, and agree to be bound by this disclaimer.