Indoor Tracking System with MaUWB_ESP32S3 for High Precision Positioning
Table of Contents
Indoor Tracking System:
Indoor Tracking System with MaUWB_ESP32S3 for High Precision Positioning- With the MakerFabs MaUWB DW3000 chipset, you can perform precise indoor and outdoor position tracking of humans and objects within a 500-meter range. And the coolest thing is, you can use this chipset with any controller that has a simple UART port.
I have multiple of these chipsets, which I plan to use to create multiple tags and anchors for precise position tracking. I have already started experimenting and created one tag, which worked exceptionally well. However, I still need to create more tags to implement the position tracking algorithm and design a computer application for live tracking of, let’s say, kids, employees, objects, or even pets. Today, we are going to make a simple yet highly precise indoor tracking system.
So, without any further delay, let’s get started!!!
Overview MaUWB_ESP32S3:
In Sept. 2024, MakerFabs released an updated version of the MaUWB_DW3000 based on the ESP32.
The latest version, however, is based on the ESP32S3 Microcontroller Unit (MCU) by Espressif. This is equipped with WiFi and features Bluetooth 5 (LE). It is commonly used for IoT and embedded applications. This new update is just mind-blowing, and they call it the MaUWB_ESP32S3 UWB module.
- The board also has a small I2C supported SSD1306 Oled display module.
- Additionally, there are two USB-C type ports; one is labeled as USB-Native and the other one is labeled as USB-TTL.
- This small connector is for the battery and it’s also labeled as BAT and the pins are labeled with + and – signs.
- It has multiple pin Headers along the sides for connecting to other components and sensors.
On the backside is the MaUWB Chipset and a lot of tiny components. But thanks to MakerFab’s for making it completely open source, so, you can go ahead and check it’s schematic and other documents.
MaUWB-DW3000 Working:
The MaUWB chipset is composed of the STM32 and DW3000/PA. The STM32 is pre-programmed by Makerfabs, drives the DW3000 module and handles all tasks in multiple anchor/tag applications, such as time slot setting, and reports the final result via UART to the ESP32S3 through simple AT commands. And as I mentioned at the start of the video, you can use this chipset with any controller that has a simple UART port.
What is UWB?
Ultra-wideband (UWB) is a short-range wireless communication protocol that operates through radio waves, enabling secure, reliable ranging and precision sensing. In field applications, the primary challenge for UWB is signal conflicts and interference when multiple anchors and tags are present.
This UWB module solves mutual conflicts among multiple anchors and tags, supporting up to 8 anchors and 64 tags in an application to create a multi-anchor, multi-tag positioning system. It also supports antenna delay settings for both tags and anchors.
MaUWB_ESP32S3 Features:
- Board USB supply voltage range is from 4.8V to 5.5V. 5.0V Typical.
- Board Battery supply voltage range is from 3.4V to 4.2V. 3.7V Typical.
- Supports AT commands.
- DW3000 with PA, max range 500M
- It has two modes of data transmission rate 850kbps and 6.8Mbps.
- The maximum packet length is 1023 bytes, which meets the application requirements of high data volume exchange.
- The module serial port communication baud rate is 115200.
- Comply with IEEE802.15.4-2011 ultra-wideband standard.
- Easy to integrate without additional RF design.
- Support CH5 (6489.6MHZ) RF band.
- Strong resistance to multi-path fading.
- Precision 0.5M(in range 100m)
- The system supports 8 Anchors 64 tags.
- The module supports free configuration of refresh rate, up to 100Hz.
- Module (Tag) deep hibernation working current as low as 35uA, working current 34mA.
For now, I only have these two boards, so I am going to use one as a Tag and the other as an Anchor.
When you first power up these modules, if the boards are pre-programmed, you will be able to see the Tag and Anchor IDs, frequencies, and information about the maximum number of supported tags, which is 64.
If, however, you do not see this information, or if both boards are programmed as tags or as anchors, there is no need to worry. We have programs that allow us to set which board will act as a Tag and which as an Anchor. We can change the Tag and Anchor IDs. Anyway, let’s open the programs.
I downloaded these programs from the MakerFab’s official product page. These are the original programs; I did not change a single line of code.
If you want to use a board as an Anchor, upload the Anchor program. If you want to use the board as a Tag, simply upload the Tag program.
Currently, the Anchor ID is 0, and the Tag ID is also 0.
MaUWB_ESP32S3 Anchor Program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
/* For ESP32S3 UWB AT Demo Use 2.0.0 Wire Use 1.11.7 Adafruit_GFX_Library Use 1.14.4 Adafruit_BusIO Use 2.0.0 SPI Use 2.5.7 Adafruit_SSD1306 */ // User config ------------------------------------------ #define UWB_INDEX 0 #define ANCHOR #define FREQ_6800K #define UWB_TAG_COUNT 64 // User config end ------------------------------------------ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Arduino.h> #define SERIAL_LOG Serial #define SERIAL_AT mySerial2 HardwareSerial SERIAL_AT(2); // ESP32S3 #define RESET 16 #define IO_RXD2 18 #define IO_TXD2 17 #define I2C_SDA 39 #define I2C_SCL 38 Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { pinMode(RESET, OUTPUT); digitalWrite(RESET, HIGH); SERIAL_LOG.begin(115200); SERIAL_LOG.print(F("Hello! ESP32-S3 AT command V1.0 Test")); SERIAL_AT.begin(115200, SERIAL_8N1, IO_RXD2, IO_TXD2); SERIAL_AT.println("AT"); Wire.begin(I2C_SDA, I2C_SCL); delay(1000); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 SERIAL_LOG.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.clearDisplay(); logoshow(); sendData("AT?", 2000, 1); sendData("AT+RESTORE", 5000, 1); sendData(config_cmd(), 2000, 1); sendData(cap_cmd(), 2000, 1); sendData("AT+SETRPT=1", 2000, 1); sendData("AT+SAVE", 2000, 1); sendData("AT+RESTART", 2000, 1); } long int runtime = 0; String response = ""; String rec_head = "AT+RANGE"; void loop() { // put your main code here, to run repeatedly: while (SERIAL_LOG.available() > 0) { SERIAL_AT.write(SERIAL_LOG.read()); yield(); } while (SERIAL_AT.available() > 0) { char c = SERIAL_AT.read(); if (c == '\r') continue; else if (c == '\n' || c == '\r') { SERIAL_LOG.println(response); response = ""; } else response += c; } } // SSD1306 void logoshow(void) { display.clearDisplay(); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.println(F("MaUWB DW3000")); display.setCursor(0, 20); // Start at top-left corner // display.println(F("with STM32 AT Command")); display.setTextSize(2); String temp = ""; temp = temp + "A" + UWB_INDEX; temp = temp + " 6.8M"; display.println(temp); display.setCursor(0, 40); temp = "Total: "; temp = temp + UWB_TAG_COUNT; display.println(temp); display.display(); delay(2000); } String sendData(String command, const int timeout, boolean debug) { String response = ""; // command = command + "\r\n"; SERIAL_LOG.println(command); SERIAL_AT.println(command); // send the read character to the SERIAL_LOG long int time = millis(); while ((time + timeout) > millis()) { while (SERIAL_AT.available()) { // The esp has data so display its output to the serial window char c = SERIAL_AT.read(); // read the next character. response += c; } } if (debug) { SERIAL_LOG.println(response); } return response; } String config_cmd() { String temp = "AT+SETCFG="; // Set device id temp = temp + UWB_INDEX; // Set device role temp = temp + ",1"; // Set frequence 850k or 6.8M temp = temp + ",1"; // Set range filter temp = temp + ",1"; return temp; } String cap_cmd() { String temp = "AT+SETCAP="; // Set Tag capacity temp = temp + UWB_TAG_COUNT; // Time of a single time slot temp = temp + ",10"; return temp; } |
MaUWB_ESP32S3 Tag Program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
/* For ESP32S3 UWB AT Demo Use 2.0.0 Wire Use 1.11.7 Adafruit_GFX_Library Use 1.14.4 Adafruit_BusIO Use 2.0.0 SPI Use 2.5.7 Adafruit_SSD1306 */ // User config ------------------------------------------ #define UWB_INDEX 0 // tag id #define TAG #define FREQ_850K #define UWB_TAG_COUNT 64 // User config end ------------------------------------------ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Arduino.h> #define SERIAL_LOG Serial #define SERIAL_AT mySerial2 HardwareSerial SERIAL_AT(2); #define RESET 16 #define IO_RXD2 18 #define IO_TXD2 17 #define I2C_SDA 39 #define I2C_SCL 38 Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { pinMode(RESET, OUTPUT); digitalWrite(RESET, HIGH); SERIAL_LOG.begin(115200); SERIAL_LOG.print(F("Hello! ESP32-S3 AT command V1.0 Test")); SERIAL_AT.begin(115200, SERIAL_8N1, IO_RXD2, IO_TXD2); SERIAL_AT.println("AT"); Wire.begin(I2C_SDA, I2C_SCL); delay(1000); // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3C for 128x32 SERIAL_LOG.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.clearDisplay(); logoshow(); sendData("AT?", 2000, 1); sendData("AT+RESTORE", 5000, 1); sendData(config_cmd(), 2000, 1); sendData(cap_cmd(), 2000, 1); sendData("AT+SETRPT=1", 2000, 1); sendData("AT+SAVE", 2000, 1); sendData("AT+RESTART", 2000, 1); } long int runtime = 0; String response = ""; String rec_head = "AT+RANGE"; void loop() { // put your main code here, to run repeatedly: while (SERIAL_LOG.available() > 0) { SERIAL_AT.write(SERIAL_LOG.read()); yield(); } while (SERIAL_AT.available() > 0) { char c = SERIAL_AT.read(); if (c == '\r') continue; else if (c == '\n' || c == '\r') { SERIAL_LOG.println(response); response = ""; } else response += c; } } // SSD1306 void logoshow(void) { display.clearDisplay(); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.println(F("MaUWB DW3000")); display.setCursor(0, 20); // Start at top-left corner // display.println(F("with STM32 AT Command")); display.setTextSize(2); String temp = ""; temp = temp + "T" + UWB_INDEX; temp = temp + " 850k"; display.println(temp); display.setCursor(0, 40); temp = "Total: "; temp = temp + UWB_TAG_COUNT; display.println(temp); display.display(); delay(2000); } String sendData(String command, const int timeout, boolean debug) { String response = ""; // command = command + "\r\n"; SERIAL_LOG.println(command); SERIAL_AT.println(command); // send the read character to the SERIAL_LOG long int time = millis(); while ((time + timeout) > millis()) { while (SERIAL_AT.available()) { // The esp has data so display its output to the serial window char c = SERIAL_AT.read(); // read the next character. response += c; } } if (debug) { SERIAL_LOG.println(response); } return response; } String config_cmd() { String temp = "AT+SETCFG="; // Set device id temp = temp + UWB_INDEX; // Set device role temp = temp + ",0"; // Set frequence 850k or 6.8M temp = temp + ",0"; // Set range filter temp = temp + ",1"; return temp; } String cap_cmd() { String temp = "AT+SETCAP="; // Set Tag capacity temp = temp + UWB_TAG_COUNT; // Time of a single time slot temp = temp + ",15"; return temp; } |
Since these boards are based on the ESP32S3 microcontroller units, make sure the ESP32 boards are installed in the Arduino IDE. For guidance, you can read my getting started article on the ESP32, where I explain how to add the ESP32 Board Manager URL link in the Arduino IDE and install the entire ESP32 boards package.
Once the ESP32 boards are installed, ensure that the ESP32S3 Dev Module is available in the list.
You will also need to install these two libraries
Adafruit_GFX.h
Adafruit_SSD1306.h
For the Oled display module. Let me show you how to install these libraries in the Arduino IDE.
Copy the Library name Adafruit_GFX.
Go to the Sketch Menu then to Include Library, and click on the Manage Libraries.
Paste the library name in the search box.
Now, follow the same steps for the Adafruit_SSD1306.
You can see I have installed both the libraries.
Finally, you can go ahead and upload the programs. For this, simply go to the Tools menu > Board > ESP32 Arduino and select the ESP32S3 Dev Module.
Again go to the Tools Menu > Port: and select the correct communication port. Repeat the same steps for both the boards “Anchor and Tag”. On my laptop the Anchor board is connected to the communication port 11 and the Tag board is connected to the Com port 12. On your system these ports may be different.
Once the programs are successfully uploaded.
You can see that nothing has changed on the displays because I uploaded the same programs. With the company’s code, you can only monitor the values on the Serial Monitor, where you can view the range and RSSI values in real-time.
You can see the Range in cm, right now its around 24cm. Let me change the position of the Tag to see if the distance and RSSI values change.
After moving it to the other side of the table, the distance changed to 157cm. You will see one to 2cm difference when it comes to accuracy. But don’t expect such level of accuracy when the distance is 100 meters or more.
Anyway, its working great as expected, but it seems impractical to sit in front of a laptop to use the Serial Monitor. The Serial Monitor is useful for debugging, but for practical use, it doesn’t feel right. Since the boards already have OLED displays, why not print this information directly on them?
To achieve this, I modified the programs.
Modified Anchor Program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
/* For ESP32S3 UWB AT Demo Use 2.0.0 Wire Use 1.11.7 Adafruit_GFX_Library Use 1.14.4 Adafruit_BusIO Use 2.0.0 SPI Use 2.5.7 Adafruit_SSD1306 */ // User config ------------------------------------------ #define UWB_INDEX 0 #define ANCHOR #define FREQ_6800K #define UWB_TAG_COUNT 64 // User config end ------------------------------------------ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Arduino.h> #define SERIAL_LOG Serial #define SERIAL_AT mySerial2 HardwareSerial SERIAL_AT(2); // ESP32S3 #define RESET 16 #define IO_RXD2 18 #define IO_TXD2 17 #define I2C_SDA 39 #define I2C_SCL 38 Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { pinMode(RESET, OUTPUT); digitalWrite(RESET, HIGH); SERIAL_LOG.begin(115200); SERIAL_LOG.print(F("Hello! ESP32-S3 AT command V1.0 Test")); SERIAL_AT.begin(115200, SERIAL_8N1, IO_RXD2, IO_TXD2); SERIAL_AT.println("AT"); Wire.begin(I2C_SDA, I2C_SCL); delay(1000); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) // Address 0x3C for 128x32 { SERIAL_LOG.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.clearDisplay(); logoshow(); sendData("AT?", 2000, 1); sendData("AT+RESTORE", 5000, 1); sendData(config_cmd(), 2000, 1); sendData(cap_cmd(), 2000, 1); sendData("AT+SETRPT=1", 2000, 1); sendData("AT+SAVE", 2000, 1); sendData("AT+RESTART", 2000, 1); } long int runtime = 0; String response = ""; String rec_head = "AT+RANGE"; void loop() { while (SERIAL_LOG.available() > 0) { SERIAL_AT.write(SERIAL_LOG.read()); yield(); } while (SERIAL_AT.available() > 0) { char c = SERIAL_AT.read(); if (c == '\r') continue; else if (c == '\n' || c == '\r') { SERIAL_LOG.println(response); // Call function to extract and display tag ID, range, and RSSI displayTagRangeAndRSSI(response); response = ""; } else response += c; } } // SSD1306 void logoshow(void) { display.clearDisplay(); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.println(F("MaUWB DW3000")); display.setCursor(0, 20); // Start at top-left corner display.setTextSize(2); String temp = ""; temp = temp + "A" + UWB_INDEX; temp = temp + " 6.8M"; display.println(temp); display.setCursor(0, 40); temp = "Total: "; temp = temp + UWB_TAG_COUNT; display.println(temp); display.display(); delay(2000); } String sendData(String command, const int timeout, boolean debug) { String response = ""; SERIAL_LOG.println(command); SERIAL_AT.println(command); // send the read character to the SERIAL_LOG long int time = millis(); while ((time + timeout) > millis()) { while (SERIAL_AT.available()) { char c = SERIAL_AT.read(); // read the next character. response += c; } } if (debug) { SERIAL_LOG.println(response); } return response; } String config_cmd() { String temp = "AT+SETCFG="; // Set device id temp = temp + UWB_INDEX; // Set device role temp = temp + ",1"; // Anchor // Set frequency 850k or 6.8M temp = temp + ",1"; // Set range filter temp = temp + ",1"; return temp; } String cap_cmd() { String temp = "AT+SETCAP="; // Set Tag capacity temp = temp + UWB_TAG_COUNT; // Time of a single time slot temp = temp + ",10"; return temp; } void displayTagRangeAndRSSI(String data) { // Find the tag ID, range, and RSSI in the string int tidIndex = data.indexOf("tid:"); int rangeIndex = data.indexOf("range:("); int rssiIndex = data.indexOf("rssi:("); if (tidIndex != -1 && rangeIndex != -1 && rssiIndex != -1) { // Extract Tag ID int tidStart = tidIndex + 4; // Move past "tid:" int tidEnd = data.indexOf(',', tidStart); String tagID = data.substring(tidStart, tidEnd); // Get the Tag ID // Extract range values int rangeStart = rangeIndex + 7; // Move past "range:(" int rangeEnd = data.indexOf(')', rangeStart); String range = data.substring(rangeStart, rangeEnd); // Get the range values as a string // Extract RSSI values int rssiStart = rssiIndex + 6; // Move past "rssi:(" int rssiEnd = data.indexOf(')', rssiStart); String rssi = data.substring(rssiStart, rssiEnd); // Get the RSSI values as a string // Clear the display and print the extracted values display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("Tag ID: " + tagID); display.println("Range: " + range); display.println("RSSI: " + rssi); display.display(); } } |
Modified Tag Program:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
/* For ESP32S3 UWB AT Demo Use 2.0.0 Wire Use 1.11.7 Adafruit_GFX_Library Use 1.14.4 Adafruit_BusIO Use 2.0.0 SPI Use 2.5.7 Adafruit_SSD1306 */ // User config ------------------------------------------ #define UWB_INDEX 0 // Tag ID #define TAG #define FREQ_850K #define UWB_TAG_COUNT 64 // User config end ------------------------------------------ #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Arduino.h> #define SERIAL_LOG Serial #define SERIAL_AT mySerial2 HardwareSerial SERIAL_AT(2); #define RESET 16 #define IO_RXD2 18 #define IO_TXD2 17 #define I2C_SDA 39 #define I2C_SCL 38 Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { pinMode(RESET, OUTPUT); digitalWrite(RESET, HIGH); SERIAL_LOG.begin(115200); SERIAL_LOG.print(F("Hello! ESP32-S3 AT command V1.0 Test")); SERIAL_AT.begin(115200, SERIAL_8N1, IO_RXD2, IO_TXD2); SERIAL_AT.println("AT"); Wire.begin(I2C_SDA, I2C_SCL); delay(1000); if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) // Address 0x3C for 128x32 { SERIAL_LOG.println(F("SSD1306 allocation failed")); for (;;) ; // Don't proceed, loop forever } display.clearDisplay(); logoshow(); sendData("AT?", 2000, 1); sendData("AT+RESTORE", 5000, 1); sendData(config_cmd(), 2000, 1); sendData(cap_cmd(), 2000, 1); sendData("AT+SETRPT=1", 2000, 1); sendData("AT+SAVE", 2000, 1); sendData("AT+RESTART", 2000, 1); } long int runtime = 0; String response = ""; String rec_head = "AT+RANGE"; void loop() { while (SERIAL_LOG.available() > 0) { SERIAL_AT.write(SERIAL_LOG.read()); yield(); } while (SERIAL_AT.available() > 0) { char c = SERIAL_AT.read(); if (c == '\r') continue; else if (c == '\n' || c == '\r') { SERIAL_LOG.println(response); // Call function to extract and display tag ID, range, and RSSI displayTagRangeAndRSSI(response); response = ""; } else response += c; } } // SSD1306 void logoshow(void) { display.clearDisplay(); display.setTextSize(1); // Normal 1:1 pixel scale display.setTextColor(SSD1306_WHITE); // Draw white text display.setCursor(0, 0); // Start at top-left corner display.println(F("MaUWB DW3000")); display.setCursor(0, 20); // Start at top-left corner display.setTextSize(2); String temp = ""; temp = temp + "T" + UWB_INDEX; // Show Tag ID temp = temp + " 850k"; // Frequency display.println(temp); display.setCursor(0, 40); temp = "Total: "; temp = temp + UWB_TAG_COUNT; display.println(temp); display.display(); delay(2000); } String sendData(String command, const int timeout, boolean debug) { String response = ""; SERIAL_LOG.println(command); SERIAL_AT.println(command); // send the read character to the SERIAL_LOG long int time = millis(); while ((time + timeout) > millis()) { while (SERIAL_AT.available()) { char c = SERIAL_AT.read(); // read the next character. response += c; } } if (debug) { SERIAL_LOG.println(response); } return response; } String config_cmd() { String temp = "AT+SETCFG="; // Set device id temp = temp + UWB_INDEX; // Set device role (0 for Tag) temp = temp + ",0"; // Set frequency (0 for 850k) temp = temp + ",0"; // Set range filter temp = temp + ",1"; return temp; } String cap_cmd() { String temp = "AT+SETCAP="; // Set Tag capacity temp = temp + UWB_TAG_COUNT; // Time of a single time slot temp = temp + ",15"; return temp; } void displayTagRangeAndRSSI(String data) { // Find the tag ID, range, and RSSI in the string int tidIndex = data.indexOf("tid:"); int rangeIndex = data.indexOf("range:("); int rssiIndex = data.indexOf("rssi:("); if (tidIndex != -1 && rangeIndex != -1 && rssiIndex != -1) { // Extract Tag ID int tidStart = tidIndex + 4; // Move past "tid:" int tidEnd = data.indexOf(',', tidStart); String tagID = data.substring(tidStart, tidEnd); // Get the Tag ID // Extract range values int rangeStart = rangeIndex + 7; // Move past "range:(" int rangeEnd = data.indexOf(')', rangeStart); String range = data.substring(rangeStart, rangeEnd); // Get the range values as a string // Extract RSSI values int rssiStart = rssiIndex + 6; // Move past "rssi:(" int rssiEnd = data.indexOf(')', rssiStart); String rssi = data.substring(rssiStart, rssiEnd); // Get the RSSI values as a string // Clear the display and print the extracted values display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("Tag ID: " + tagID); display.println("Range: " + range); display.println("RSSI: " + rssi); display.display(); } } For extracting the Tag ID, range, and RSSI values, I defined this function. void displayTagRangeAndRSSI(String data) { // Find the tag ID, range, and RSSI in the string int tidIndex = data.indexOf("tid:"); int rangeIndex = data.indexOf("range:("); int rssiIndex = data.indexOf("rssi:("); if (tidIndex != -1 && rangeIndex != -1 && rssiIndex != -1) { // Extract Tag ID int tidStart = tidIndex + 4; // Move past "tid:" int tidEnd = data.indexOf(',', tidStart); String tagID = data.substring(tidStart, tidEnd); // Get the Tag ID // Extract range values int rangeStart = rangeIndex + 7; // Move past "range:(" int rangeEnd = data.indexOf(')', rangeStart); String range = data.substring(rangeStart, rangeEnd); // Get the range values as a string // Extract RSSI values int rssiStart = rssiIndex + 6; // Move past "rssi:(" int rssiEnd = data.indexOf(')', rssiStart); String rssi = data.substring(rssiStart, rssiEnd); // Get the RSSI values as a string // Clear the display and print the extracted values display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("Tag ID: " + tagID); display.println("Range: " + range); display.println("RSSI: " + rssi); display.display(); } } |
This function has no return type but takes one argument of type String. We provide it with the response message, and using these instructions, we extract the Tag ID, range, and RSSI values, which we then print on the OLED display module. The same exact function, I have also used on the Tag side.
I have already uploaded the modified programs and now let’s watch this Indoor Tracking System in action.
Now, I can directly read the distance and RSSI values on the displays. The Anchor is still connected to the laptop because I am using it to power the anchor board.
However, I have powered the Tag board using my regulated 5V power supply and the 4S lithium-ion battery. Instead of using such a large battery and power supply, I could also use a small LiPo battery, but unfortunately, I don’t have one right now.
Since I need to make it portable, this is the only setup I could think of for now. I will take the Tag outside so you can see the distance. I cannot go very far, but I will cover enough distance for you to get the idea.
I walked out of my room to the balcony, which is 1252cm away from the Anchor board. Now, for the exact location, you can read my article on “Indoor Positioning System“. In this project, i have used multiple Anchors.
So, that’s all for now.
Watch Video Tutorial: