Initial commit: ESP32-S3 UWB positioning system
- Added anchor and tag implementations for MaUWB modules - Configured for 6.8Mbps communication with range filtering - Support for multiple tags (tag/tag2 environments) - OLED display integration for real-time measurements - Simplified code without sleep mode and OTA functionality - Complete UWBHelper library for AT command interface
This commit is contained in:
commit
a89215b7ff
10 changed files with 806 additions and 0 deletions
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
# PlatformIO
|
||||||
|
.pio/
|
||||||
|
.vscode/
|
||||||
|
.pioenvs/
|
||||||
|
.piolibdeps/
|
||||||
|
.clang_complete
|
||||||
|
.gcc-flags.json
|
||||||
|
|
||||||
|
# Build artifacts
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.map
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.vscode/
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# OS files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temporary files
|
||||||
|
*.tmp
|
||||||
|
*.temp
|
||||||
|
|
||||||
|
#Claude code
|
||||||
|
.Claude
|
||||||
BIN
Makerfabs UWB AT Module AT Command Manual(v1.1.1).pdf
Normal file
BIN
Makerfabs UWB AT Module AT Command Manual(v1.1.1).pdf
Normal file
Binary file not shown.
40
README.md
Normal file
40
README.md
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# MaUWB ESP32-S3 Positioning System
|
||||||
|
|
||||||
|
Ultra-wideband (UWB) positioning system using ESP32-S3 and Makerfabs UWB modules.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
- ESP32-S3 based anchor and tag devices
|
||||||
|
- Real-time distance measurement with <10cm accuracy
|
||||||
|
- OLED display for status and measurements
|
||||||
|
- Multiple tag support (up to 64 tags)
|
||||||
|
- 6.8Mbps communication rate
|
||||||
|
- Serial debugging output
|
||||||
|
|
||||||
|
## Hardware
|
||||||
|
- ESP32-S3 DevKit
|
||||||
|
- Makerfabs UWB AT Module
|
||||||
|
- SSD1306 OLED Display (128x64)
|
||||||
|
|
||||||
|
## Environments
|
||||||
|
- `anchor`: Base station for positioning
|
||||||
|
- `tag`: Mobile device for tracking (ID 1)
|
||||||
|
- `tag2`: Mobile device for tracking (ID 2)
|
||||||
|
|
||||||
|
## Build & Upload
|
||||||
|
```bash
|
||||||
|
# Build specific environment
|
||||||
|
pio run -e anchor
|
||||||
|
pio run -e tag
|
||||||
|
|
||||||
|
# Upload to device
|
||||||
|
pio run -e tag -t upload
|
||||||
|
|
||||||
|
# Monitor serial output
|
||||||
|
pio device monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
- Network ID: 1234
|
||||||
|
- Baud Rate: 115200
|
||||||
|
- Communication: 6.8Mbps
|
||||||
|
- Range filtering: Enabled
|
||||||
198
lib/UWBHelper/UWBHelper.cpp
Normal file
198
lib/UWBHelper/UWBHelper.cpp
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
#include "UWBHelper.h"
|
||||||
|
|
||||||
|
UWBHelper::UWBHelper(HardwareSerial* serial, int reset) {
|
||||||
|
uwbSerial = serial;
|
||||||
|
resetPin = reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::begin() {
|
||||||
|
pinMode(resetPin, OUTPUT);
|
||||||
|
digitalWrite(resetPin, HIGH);
|
||||||
|
|
||||||
|
uwbSerial->begin(115200);
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
// Test communication
|
||||||
|
String response = sendCommand("AT?", 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UWBHelper::reset() {
|
||||||
|
digitalWrite(resetPin, LOW);
|
||||||
|
delay(100);
|
||||||
|
digitalWrite(resetPin, HIGH);
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::configureDevice(int deviceId, bool isAnchor, int dataRate, int rangeFilter) {
|
||||||
|
String cmd = "AT+SETCFG=" + String(deviceId) + "," +
|
||||||
|
String(isAnchor ? 1 : 0) + "," +
|
||||||
|
String(dataRate) + "," +
|
||||||
|
String(rangeFilter);
|
||||||
|
|
||||||
|
String response = sendCommand(cmd, 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::setCapacity(int tagCount, int timeSlot, int extMode) {
|
||||||
|
String cmd = "AT+SETCAP=" + String(tagCount) + "," +
|
||||||
|
String(timeSlot) + "," +
|
||||||
|
String(extMode);
|
||||||
|
|
||||||
|
String response = sendCommand(cmd, 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::setNetwork(int networkId) {
|
||||||
|
String cmd = "AT+SETPAN=" + String(networkId);
|
||||||
|
String response = sendCommand(cmd, 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::enableReporting(bool enable) {
|
||||||
|
String cmd = "AT+SETRPT=" + String(enable ? 1 : 0);
|
||||||
|
String response = sendCommand(cmd, 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::saveConfiguration() {
|
||||||
|
String response = sendCommand("AT+SAVE", 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::restartDevice() {
|
||||||
|
String response = sendCommand("AT+RESTART", 2000);
|
||||||
|
return isResponseOK(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
String UWBHelper::sendCommand(String command, int timeout) {
|
||||||
|
String response = "";
|
||||||
|
|
||||||
|
Serial.println("Sending: " + command);
|
||||||
|
uwbSerial->println(command);
|
||||||
|
|
||||||
|
unsigned long startTime = millis();
|
||||||
|
while ((millis() - startTime) < timeout) {
|
||||||
|
while (uwbSerial->available()) {
|
||||||
|
char c = uwbSerial->read();
|
||||||
|
response += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.length() > 0) {
|
||||||
|
Serial.println("Response: " + response);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::parseRangeData(String data, DeviceData devices[], int maxDevices) {
|
||||||
|
if (!data.startsWith("AT+RANGE=")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse tid (Tag ID)
|
||||||
|
int tidIndex = data.indexOf("tid:");
|
||||||
|
if (tidIndex == -1) return false;
|
||||||
|
|
||||||
|
int commaPos = data.indexOf(',', tidIndex);
|
||||||
|
if (commaPos == -1) return false;
|
||||||
|
|
||||||
|
int deviceId = data.substring(tidIndex + 4, commaPos).toInt();
|
||||||
|
|
||||||
|
// Parse range data
|
||||||
|
int rangeIndex = data.indexOf("range:(");
|
||||||
|
if (rangeIndex == -1) return false;
|
||||||
|
|
||||||
|
int rangeStart = rangeIndex + 7;
|
||||||
|
int rangeEnd = data.indexOf(')', rangeStart);
|
||||||
|
if (rangeEnd == -1) return false;
|
||||||
|
|
||||||
|
String rangeData = data.substring(rangeStart, rangeEnd);
|
||||||
|
float distance = rangeData.toFloat() / 100.0; // Convert cm to meters
|
||||||
|
|
||||||
|
// Parse RSSI data
|
||||||
|
int rssiIndex = data.indexOf("rssi:(");
|
||||||
|
float rssi = 0.0;
|
||||||
|
if (rssiIndex != -1) {
|
||||||
|
int rssiStart = rssiIndex + 6;
|
||||||
|
int rssiComma = data.indexOf(',', rssiStart);
|
||||||
|
if (rssiComma == -1) rssiComma = data.indexOf(')', rssiStart);
|
||||||
|
if (rssiComma != -1) {
|
||||||
|
rssi = data.substring(rssiStart, rssiComma).toFloat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update device data
|
||||||
|
for (int i = 0; i < maxDevices; i++) {
|
||||||
|
if (devices[i].deviceId == deviceId || !devices[i].active) {
|
||||||
|
devices[i].deviceId = deviceId;
|
||||||
|
devices[i].distance = distance;
|
||||||
|
devices[i].rssi = rssi;
|
||||||
|
devices[i].lastUpdate = millis();
|
||||||
|
devices[i].active = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String UWBHelper::getVersion() {
|
||||||
|
return sendCommand("AT+GETVER?", 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UWBHelper::isResponseOK(String response) {
|
||||||
|
return response.indexOf("OK") >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UWBHelper::printDiagnostics() {
|
||||||
|
Serial.println("=== UWB Diagnostics ===");
|
||||||
|
Serial.println("Version: " + getVersion());
|
||||||
|
Serial.println("Config: " + sendCommand("AT+GETCFG?", 2000));
|
||||||
|
Serial.println("Capacity: " + sendCommand("AT+GETCAP?", 2000));
|
||||||
|
Serial.println("Power: " + sendCommand("AT+GETPOW?", 2000));
|
||||||
|
}
|
||||||
|
|
||||||
|
// DistanceFilter Implementation
|
||||||
|
DistanceFilter::DistanceFilter() : index(0), filled(false) {
|
||||||
|
for (int i = 0; i < FILTER_SIZE; i++) readings[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float DistanceFilter::addReading(float distance) {
|
||||||
|
readings[index] = distance;
|
||||||
|
index = (index + 1) % FILTER_SIZE;
|
||||||
|
if (index == 0) filled = true;
|
||||||
|
|
||||||
|
return getFilteredValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
float DistanceFilter::getFilteredValue() {
|
||||||
|
if (!filled && index == 0) return readings[0];
|
||||||
|
|
||||||
|
// Median filter
|
||||||
|
float sorted[FILTER_SIZE];
|
||||||
|
int count = filled ? FILTER_SIZE : index;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
sorted[i] = readings[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simple bubble sort
|
||||||
|
for (int i = 0; i < count - 1; i++) {
|
||||||
|
for (int j = 0; j < count - i - 1; j++) {
|
||||||
|
if (sorted[j] > sorted[j + 1]) {
|
||||||
|
float temp = sorted[j];
|
||||||
|
sorted[j] = sorted[j + 1];
|
||||||
|
sorted[j + 1] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sorted[count / 2]; // Return median
|
||||||
|
}
|
||||||
|
|
||||||
|
void DistanceFilter::reset() {
|
||||||
|
index = 0;
|
||||||
|
filled = false;
|
||||||
|
for (int i = 0; i < FILTER_SIZE; i++) readings[i] = 0;
|
||||||
|
}
|
||||||
60
lib/UWBHelper/UWBHelper.h
Normal file
60
lib/UWBHelper/UWBHelper.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#ifndef UWBHELPER_H
|
||||||
|
#define UWBHELPER_H
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <HardwareSerial.h>
|
||||||
|
|
||||||
|
struct DeviceData {
|
||||||
|
int deviceId;
|
||||||
|
float distance;
|
||||||
|
float rssi;
|
||||||
|
unsigned long lastUpdate;
|
||||||
|
bool active;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UWBHelper {
|
||||||
|
private:
|
||||||
|
HardwareSerial* uwbSerial;
|
||||||
|
int resetPin;
|
||||||
|
|
||||||
|
public:
|
||||||
|
UWBHelper(HardwareSerial* serial, int reset);
|
||||||
|
|
||||||
|
// Initialization
|
||||||
|
bool begin();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
bool configureDevice(int deviceId, bool isAnchor, int dataRate = 1, int rangeFilter = 1);
|
||||||
|
bool setCapacity(int tagCount, int timeSlot = 10, int extMode = 1);
|
||||||
|
bool setNetwork(int networkId);
|
||||||
|
bool enableReporting(bool enable);
|
||||||
|
bool saveConfiguration();
|
||||||
|
bool restartDevice();
|
||||||
|
|
||||||
|
// Communication
|
||||||
|
String sendCommand(String command, int timeout = 2000);
|
||||||
|
bool parseRangeData(String data, DeviceData devices[], int maxDevices);
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
String getVersion();
|
||||||
|
bool isResponseOK(String response);
|
||||||
|
void printDiagnostics();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Data filtering class
|
||||||
|
class DistanceFilter {
|
||||||
|
private:
|
||||||
|
static const int FILTER_SIZE = 5;
|
||||||
|
float readings[FILTER_SIZE];
|
||||||
|
int index;
|
||||||
|
bool filled;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DistanceFilter();
|
||||||
|
float addReading(float distance);
|
||||||
|
float getFilteredValue();
|
||||||
|
void reset();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UWBHELPER_H
|
||||||
6
node_modules/.package-lock.json
generated
vendored
Normal file
6
node_modules/.package-lock.json
generated
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"name": "platformiotest",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {}
|
||||||
|
}
|
||||||
65
platformio.ini
Normal file
65
platformio.ini
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
; PlatformIO Project Configuration File
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed, etc.
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
|
||||||
|
[platformio]
|
||||||
|
default_envs = anchor
|
||||||
|
|
||||||
|
; Common configuration for both anchor and tag
|
||||||
|
[env]
|
||||||
|
platform = espressif32
|
||||||
|
board = esp32-s3-devkitc-1
|
||||||
|
framework = arduino
|
||||||
|
monitor_speed = 115200
|
||||||
|
monitor_filters = esp32_exception_decoder
|
||||||
|
build_flags =
|
||||||
|
-DCORE_DEBUG_LEVEL=3
|
||||||
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
|
|
||||||
|
; Library dependencies
|
||||||
|
lib_deps =
|
||||||
|
adafruit/Adafruit GFX Library@^1.11.7
|
||||||
|
adafruit/Adafruit SSD1306@^2.5.7
|
||||||
|
adafruit/Adafruit BusIO@^1.14.4
|
||||||
|
bblanchon/ArduinoJson@^6.21.3
|
||||||
|
|
||||||
|
; Anchor device configuration
|
||||||
|
[env:anchor]
|
||||||
|
build_src_filter = +<*> -<main_tag.cpp>
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DDEVICE_TYPE_ANCHOR=1
|
||||||
|
-DUWB_INDEX=0
|
||||||
|
-DNETWORK_ID=1234
|
||||||
|
|
||||||
|
; Tag device configuration
|
||||||
|
[env:tag]
|
||||||
|
build_src_filter = +<*> -<main_anchor.cpp>
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DDEVICE_TYPE_TAG=1
|
||||||
|
-DUWB_INDEX=1
|
||||||
|
-DNETWORK_ID=1234
|
||||||
|
|
||||||
|
; Tag device 2 (copy and modify UWB_INDEX for multiple tags)
|
||||||
|
[env:tag2]
|
||||||
|
build_src_filter = +<*> -<main_anchor.cpp>
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DDEVICE_TYPE_TAG=1
|
||||||
|
-DUWB_INDEX=2
|
||||||
|
-DNETWORK_ID=1234
|
||||||
|
|
||||||
|
; Development environment with debugging
|
||||||
|
[env:debug]
|
||||||
|
build_src_filter = +<*> -<main_tag.cpp>
|
||||||
|
build_flags =
|
||||||
|
${env.build_flags}
|
||||||
|
-DDEVICE_TYPE_ANCHOR=1
|
||||||
|
-DUWB_INDEX=0
|
||||||
|
-DNETWORK_ID=1234
|
||||||
|
-DDEBUG_ENABLED=1
|
||||||
|
debug_tool = esp-prog
|
||||||
|
debug_init_break = tbreak setup
|
||||||
56
src/config.h
Normal file
56
src/config.h
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
// Hardware Pin Definitions for ESP32-S3
|
||||||
|
#define RESET_PIN 16
|
||||||
|
#define IO_RXD2 18
|
||||||
|
#define IO_TXD2 17
|
||||||
|
#define I2C_SDA 39
|
||||||
|
#define I2C_SCL 38
|
||||||
|
|
||||||
|
// UWB Configuration
|
||||||
|
#ifndef UWB_INDEX
|
||||||
|
#define UWB_INDEX 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef NETWORK_ID
|
||||||
|
#define NETWORK_ID 1234
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// System Configuration
|
||||||
|
#define MAX_DEVICES 10
|
||||||
|
#define MAX_ANCHORS 8
|
||||||
|
#define UWB_TAG_COUNT 64
|
||||||
|
|
||||||
|
// Timing Configuration
|
||||||
|
#define DISPLAY_UPDATE_INTERVAL 500
|
||||||
|
#define DEVICE_TIMEOUT 5000
|
||||||
|
#define SLEEP_DURATION 5000
|
||||||
|
|
||||||
|
// Display Configuration
|
||||||
|
#define SCREEN_WIDTH 128
|
||||||
|
#define SCREEN_HEIGHT 64
|
||||||
|
#define OLED_RESET -1
|
||||||
|
#define SCREEN_ADDRESS 0x3C
|
||||||
|
|
||||||
|
// Serial Configuration
|
||||||
|
#define SERIAL_BAUD 115200
|
||||||
|
|
||||||
|
// Debug Configuration
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
#define DEBUG_PRINT(x) Serial.print(x)
|
||||||
|
#define DEBUG_PRINTLN(x) Serial.println(x)
|
||||||
|
#define DEBUG_PRINTF(x, ...) Serial.printf(x, __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define DEBUG_PRINT(x)
|
||||||
|
#define DEBUG_PRINTLN(x)
|
||||||
|
#define DEBUG_PRINTF(x, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// WiFi Configuration (optional)
|
||||||
|
#ifdef OTA_ENABLED
|
||||||
|
#define WIFI_SSID "YOUR_WIFI_SSID"
|
||||||
|
#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
||||||
183
src/main_anchor.cpp
Normal file
183
src/main_anchor.cpp
Normal file
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "UWBHelper.h"
|
||||||
|
|
||||||
|
// Function declarations
|
||||||
|
void showStartupScreen();
|
||||||
|
void processUWBData();
|
||||||
|
void updateDisplay();
|
||||||
|
void checkTimeouts();
|
||||||
|
|
||||||
|
// Hardware setup
|
||||||
|
HardwareSerial uwbSerial(2);
|
||||||
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
UWBHelper uwb(&uwbSerial, RESET_PIN);
|
||||||
|
|
||||||
|
// Data storage
|
||||||
|
DeviceData tags[UWB_TAG_COUNT];
|
||||||
|
DistanceFilter tagFilters[UWB_TAG_COUNT];
|
||||||
|
String response = "";
|
||||||
|
unsigned long lastDisplayUpdate = 0;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(SERIAL_BAUD);
|
||||||
|
Serial.println("Starting UWB Anchor...");
|
||||||
|
|
||||||
|
// Initialize I2C and display
|
||||||
|
Wire.begin(I2C_SDA, I2C_SCL);
|
||||||
|
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
||||||
|
Serial.println(F("SSD1306 allocation failed"));
|
||||||
|
for (;;);
|
||||||
|
}
|
||||||
|
|
||||||
|
showStartupScreen();
|
||||||
|
|
||||||
|
// Initialize UWB
|
||||||
|
uwbSerial.begin(115200, SERIAL_8N1, IO_RXD2, IO_TXD2);
|
||||||
|
delay(1000);
|
||||||
|
|
||||||
|
if (!uwb.begin()) {
|
||||||
|
Serial.println("UWB initialization failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure as anchor
|
||||||
|
Serial.println("Configuring as Anchor...");
|
||||||
|
uwb.configureDevice(UWB_INDEX, true); // true = anchor
|
||||||
|
uwb.setCapacity(UWB_TAG_COUNT, 10, 1);
|
||||||
|
uwb.setNetwork(NETWORK_ID);
|
||||||
|
uwb.enableReporting(true);
|
||||||
|
uwb.saveConfiguration();
|
||||||
|
delay(1000);
|
||||||
|
uwb.restartDevice();
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
// Initialize tag data
|
||||||
|
for (int i = 0; i < UWB_TAG_COUNT; i++) {
|
||||||
|
tags[i].deviceId = -1;
|
||||||
|
tags[i].active = false;
|
||||||
|
tags[i].distance = 0.0;
|
||||||
|
tags[i].rssi = 0.0;
|
||||||
|
tags[i].lastUpdate = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Anchor ready! Waiting for tags...");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
processUWBData();
|
||||||
|
updateDisplay();
|
||||||
|
checkTimeouts();
|
||||||
|
|
||||||
|
// Handle serial passthrough for debugging
|
||||||
|
while (Serial.available()) {
|
||||||
|
uwbSerial.write(Serial.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processUWBData() {
|
||||||
|
while (uwbSerial.available()) {
|
||||||
|
char c = uwbSerial.read();
|
||||||
|
if (c == '\r') continue;
|
||||||
|
if (c == '\n') {
|
||||||
|
if (response.length() > 0) {
|
||||||
|
Serial.println("Received: " + response);
|
||||||
|
if (uwb.parseRangeData(response, tags, UWB_TAG_COUNT)) {
|
||||||
|
// Successfully parsed data
|
||||||
|
for (int i = 0; i < UWB_TAG_COUNT; i++) {
|
||||||
|
if (tags[i].active && (millis() - tags[i].lastUpdate < 1000)) {
|
||||||
|
Serial.printf("Tag %d: %.2fm, RSSI: %.1fdBm\n",
|
||||||
|
tags[i].deviceId, tags[i].distance, tags[i].rssi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDisplay() {
|
||||||
|
if (millis() - lastDisplayUpdate < DISPLAY_UPDATE_INTERVAL) return;
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.printf("Anchor %d (Net:%d)", UWB_INDEX, NETWORK_ID);
|
||||||
|
|
||||||
|
display.setCursor(0, 10);
|
||||||
|
display.println("Active Tags:");
|
||||||
|
|
||||||
|
// Show active tags
|
||||||
|
int activeCount = 0;
|
||||||
|
int yPos = 20;
|
||||||
|
|
||||||
|
for (int i = 0; i < UWB_TAG_COUNT && yPos < 55; i++) {
|
||||||
|
if (tags[i].active) {
|
||||||
|
display.setCursor(0, yPos);
|
||||||
|
display.printf("T%d: %.2fm", tags[i].deviceId, tags[i].distance);
|
||||||
|
display.setCursor(70, yPos);
|
||||||
|
display.printf("%.0fdBm", tags[i].rssi);
|
||||||
|
yPos += 8;
|
||||||
|
activeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeCount == 0) {
|
||||||
|
display.setCursor(0, 30);
|
||||||
|
display.println("No active tags");
|
||||||
|
display.setCursor(0, 40);
|
||||||
|
display.println("Waiting...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status line
|
||||||
|
display.setCursor(0, 56);
|
||||||
|
display.printf("Active: %d/%d", activeCount, UWB_TAG_COUNT);
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
lastDisplayUpdate = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkTimeouts() {
|
||||||
|
unsigned long currentTime = millis();
|
||||||
|
for (int i = 0; i < UWB_TAG_COUNT; i++) {
|
||||||
|
if (tags[i].active && (currentTime - tags[i].lastUpdate > DEVICE_TIMEOUT)) {
|
||||||
|
tags[i].active = false;
|
||||||
|
Serial.printf("Tag %d timeout\n", tags[i].deviceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showStartupScreen() {
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println("MaUWB Anchor");
|
||||||
|
|
||||||
|
display.setCursor(0, 15);
|
||||||
|
display.printf("ID: %d", UWB_INDEX);
|
||||||
|
|
||||||
|
display.setCursor(0, 25);
|
||||||
|
display.printf("Network: %d", NETWORK_ID);
|
||||||
|
|
||||||
|
display.setCursor(0, 35);
|
||||||
|
display.println("6.8Mbps Mode");
|
||||||
|
|
||||||
|
display.setCursor(0, 45);
|
||||||
|
display.println("Initializing...");
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
|
|
||||||
168
src/main_tag.cpp
Normal file
168
src/main_tag.cpp
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
#include "config.h"
|
||||||
|
#include "UWBHelper.h"
|
||||||
|
|
||||||
|
// Function declarations
|
||||||
|
void showStartupScreen();
|
||||||
|
void processUWBData();
|
||||||
|
void updateDisplay();
|
||||||
|
void checkTimeouts();
|
||||||
|
|
||||||
|
// Hardware setup
|
||||||
|
HardwareSerial uwbSerial(2);
|
||||||
|
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
|
||||||
|
UWBHelper uwb(&uwbSerial, RESET_PIN);
|
||||||
|
|
||||||
|
// Data storage
|
||||||
|
DeviceData anchors[MAX_ANCHORS];
|
||||||
|
String response = "";
|
||||||
|
unsigned long lastDisplayUpdate = 0;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(SERIAL_BAUD);
|
||||||
|
|
||||||
|
// Initialize display
|
||||||
|
Wire.begin(I2C_SDA, I2C_SCL);
|
||||||
|
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
|
||||||
|
Serial.println(F("SSD1306 allocation failed"));
|
||||||
|
for (;;);
|
||||||
|
}
|
||||||
|
|
||||||
|
showStartupScreen();
|
||||||
|
|
||||||
|
// Initialize UWB
|
||||||
|
uwbSerial.begin(115200, SERIAL_8N1, IO_RXD2, IO_TXD2);
|
||||||
|
|
||||||
|
if (!uwb.begin()) {
|
||||||
|
Serial.println("UWB initialization failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure as tag
|
||||||
|
Serial.println("Configuring as Tag...");
|
||||||
|
uwb.configureDevice(UWB_INDEX, false); // false = tag
|
||||||
|
uwb.setCapacity(10, 10, 1);
|
||||||
|
uwb.setNetwork(NETWORK_ID);
|
||||||
|
uwb.enableReporting(true);
|
||||||
|
uwb.saveConfiguration();
|
||||||
|
delay(1000);
|
||||||
|
uwb.restartDevice();
|
||||||
|
delay(2000);
|
||||||
|
|
||||||
|
// Initialize anchor data
|
||||||
|
for (int i = 0; i < MAX_ANCHORS; i++) {
|
||||||
|
anchors[i].deviceId = -1;
|
||||||
|
anchors[i].active = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("Tag ready!");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
processUWBData();
|
||||||
|
updateDisplay();
|
||||||
|
checkTimeouts();
|
||||||
|
|
||||||
|
// Handle serial passthrough for debugging
|
||||||
|
while (Serial.available()) {
|
||||||
|
uwbSerial.write(Serial.read());
|
||||||
|
}
|
||||||
|
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
void processUWBData() {
|
||||||
|
while (uwbSerial.available()) {
|
||||||
|
char c = uwbSerial.read();
|
||||||
|
if (c == '\r') continue;
|
||||||
|
if (c == '\n') {
|
||||||
|
if (response.length() > 0) {
|
||||||
|
Serial.println("Received: " + response);
|
||||||
|
if (uwb.parseRangeData(response, anchors, MAX_ANCHORS)) {
|
||||||
|
// Successfully parsed data
|
||||||
|
for (int i = 0; i < MAX_ANCHORS; i++) {
|
||||||
|
if (anchors[i].active && (millis() - anchors[i].lastUpdate < 1000)) {
|
||||||
|
Serial.printf("Anchor %d: %.2fm, RSSI: %.1fdBm\n",
|
||||||
|
anchors[i].deviceId, anchors[i].distance, anchors[i].rssi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response = "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
response += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDisplay() {
|
||||||
|
if (millis() - lastDisplayUpdate < DISPLAY_UPDATE_INTERVAL) return;
|
||||||
|
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
|
||||||
|
// Header
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.printf("Tag %d (Net:%d)", UWB_INDEX, NETWORK_ID);
|
||||||
|
|
||||||
|
display.setCursor(0, 10);
|
||||||
|
display.println("Anchors:");
|
||||||
|
|
||||||
|
int activeCount = 0;
|
||||||
|
int yPos = 20;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ANCHORS && yPos < 55; i++) {
|
||||||
|
if (anchors[i].active) {
|
||||||
|
display.setCursor(0, yPos);
|
||||||
|
display.printf("A%d: %.2fm", anchors[i].deviceId, anchors[i].distance);
|
||||||
|
display.setCursor(70, yPos);
|
||||||
|
display.printf("%.0fdBm", anchors[i].rssi);
|
||||||
|
yPos += 8;
|
||||||
|
activeCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeCount == 0) {
|
||||||
|
display.setCursor(0, 30);
|
||||||
|
display.println("Searching...");
|
||||||
|
display.setCursor(0, 40);
|
||||||
|
display.println("for anchors...");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status line
|
||||||
|
display.setCursor(0, 56);
|
||||||
|
display.printf("Found: %d/%d", activeCount, MAX_ANCHORS);
|
||||||
|
|
||||||
|
display.display();
|
||||||
|
lastDisplayUpdate = millis();
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkTimeouts() {
|
||||||
|
unsigned long currentTime = millis();
|
||||||
|
for (int i = 0; i < MAX_ANCHORS; i++) {
|
||||||
|
if (anchors[i].active && (currentTime - anchors[i].lastUpdate > DEVICE_TIMEOUT)) {
|
||||||
|
anchors[i].active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void showStartupScreen() {
|
||||||
|
display.clearDisplay();
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setTextColor(SSD1306_WHITE);
|
||||||
|
display.setCursor(0, 0);
|
||||||
|
display.println("MaUWB Tag");
|
||||||
|
display.setCursor(0, 15);
|
||||||
|
display.printf("ID: %d", UWB_INDEX);
|
||||||
|
display.setCursor(0, 25);
|
||||||
|
display.printf("Network: %d", NETWORK_ID);
|
||||||
|
display.setCursor(0, 35);
|
||||||
|
display.println("Initializing...");
|
||||||
|
display.display();
|
||||||
|
delay(2000);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue