
AI Thinker BU03 pinout
<aside> <img src="/icons/light-bulb_gray.svg" alt="/icons/light-bulb_gray.svg" width="40px" />
AI thinker BU03-kit specification
https://www.makerfabs.cc/article/esp32-uwb-indoor-positioning-test.html
https://fcniufr8ibx1.feishu.cn/wiki/TpEiwDZlRiPHyTkZzrecZohFnVf
https://fcniufr8ibx1.feishu.cn/wiki/VjNfwjJugii6mjklQqxc59jDnhg
https://github.com/au5ton/KeilForDocker
</aside>
https://docs.google.com/presentation/d/1z5fIBbtb-PHsTmm5DOIb_CDVoF5X1-BGCwXxVB5uRxU/edit?usp=share_link
<aside> <img src="/icons/cellular_gray.svg" alt="/icons/cellular_gray.svg" width="40px" />
reading from board:
-----------------------------Raw data: b'\\xAA%\\x01\\xEE\\x07\\x00\\x00\\xD8\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
Base Station Distances:
BS0: 2.030m
BS1: 1.240m
BS2: Not visible
BS3: Not visible
BS4: Not visible
BS5: Not visible
BS6: Not visible
BS7: Not visible
-----------------------------Raw data: b'\\xAA%\\x01\\xB2\\x07\\x00\\x00\\xD8\\x04\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'
Base Station Distances:
BS0: 1.970m
BS1: 1.240m
BS2: Not visible
BS3: Not visible
BS4: Not visible
BS5: Not visible
</aside>
#include <math.h>
#if defined(ARDUINO_UNOWIFIR4)
#include <WiFiS3.h>
#else
#include <WiFiNINA.h>
#endif
#include <ArduinoHttpClient.h>
const char* ssid = "sandbox370";
const char* pass = "+s0a+s03!2gether?";
const char* endpoint = "<http://10.23.10.67:3000/update>";
const char* serverAddress = "10.23.10.67";
int port = 3000;
const char* TAG_ID = "TAG_A";
float baseDistance = 2.3;
WiFiClient wifi;
HttpClient client(wifi, serverAddress, port);
unsigned long lastSend = 0;
float wifiRequestCoolDown = 1000;
void setup() {
Serial.begin(115200);
Serial1.begin(115200);
Serial.print("base distance:");
Serial.println(baseDistance);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\\nWiFi connected!");
}
void sendLocationRequest(float x, float y) {
String payload = String("{\\"id\\":\\"") + TAG_ID + "\\",\\"x\\":" + x + ",\\"y\\":" + y + "}";
Serial.println("POSTing: " + payload);
client.beginRequest();
client.post("/update");
client.sendHeader("Content-Type", "application/json");
client.sendHeader("Content-Length", payload.length());
client.beginBody();
client.print(payload);
client.endRequest();
int statusCode = client.responseStatusCode();
String response = client.responseBody();
Serial.print("Status: "); Serial.println(statusCode);
Serial.print("Response: "); Serial.println(response);
Serial.println();
}
bool decodeUwbDistances(uint8_t* data, int dataLen, float* distances) {
// Initialize all distances to -1 (equivalent to None)
for (int i = 0; i < 8; i++) {
distances[i] = -1.0;
}
if (dataLen < 35) {
return false;
}
// Check for header pattern
if (data[0] != 0xaa || data[1] != 0x25 || data[2] != 0x01) {
return false;
}
// Extract distance data (skip header, process 4-byte chunks)
for (int i = 0; i < 8; i++) { // 8 base stations (0-7)
int byteOffset = 3 + (i * 4); // Each distance is 4 bytes
if (byteOffset + 3 < dataLen) {
// Read as little-endian 32-bit integer
uint32_t distanceRaw = data[byteOffset] |
(data[byteOffset + 1] << 8) |
(data[byteOffset + 2] << 16) |
(data[byteOffset + 3] << 24);
// Convert to meters
if (distanceRaw > 0) {
distances[i] = distanceRaw / 1000.0;
} else {
distances[i] = -1.0; // No signal/not visible
}
}
}
return true;
}
// <https://www.makerfabs.cc/article/esp32-uwb-indoor-positioning-test.html>
void calculateXY(float left_distance, float right_distance, float &x, float &y) {
float cos_a = (left_distance * left_distance + baseDistance * baseDistance - right_distance * right_distance) / (2 * left_distance * baseDistance);
x = left_distance * cos_a;
y = left_distance * sqrt(1 - cos_a * cos_a);
}
void printDistances(float* distances, bool validData) {
if (!validData) {
Serial.println("Invalid data received");
return;
}
Serial.println("Base Station Distances:");
for (int i = 0; i < 8; i++) {
if (distances[i] > 0) {
Serial.print(" BS");
Serial.print(i);
Serial.print(": ");
Serial.print(distances[i], 3);
Serial.println("m");
} else {
Serial.print(" BS");
Serial.print(i);
Serial.println(": Not visible");
}
}
Serial.println("------------------------------");
}
void loop() {
static uint8_t buffer[256];
static int bufferIndex = 0;
static bool messageStarted = false;
unsigned long now = millis();
// Check if anything is available in buffer
while (Serial1.available()) {
uint8_t incomingByte = Serial1.read();
// Look for start of message
if (!messageStarted && incomingByte == 0xAA) {
messageStarted = true;
bufferIndex = 0;
buffer[bufferIndex++] = incomingByte;
} else if (messageStarted) {
buffer[bufferIndex++] = incomingByte;
// Check if we have a complete message (35+ bytes expected)
if (bufferIndex >= 35) {
// Print raw data
Serial.print("Raw data: b'");
for (int i = 0; i < bufferIndex; i++) {
if (buffer[i] >= 32 && buffer[i] <= 126) {
Serial.print((char)buffer[i]);
} else {
Serial.print("\\\\x");
if (buffer[i] < 16) Serial.print("0");
Serial.print(buffer[i], HEX);
}
}
Serial.println("'");
// Decode distances
float distances[8];
bool validData = decodeUwbDistances(buffer, bufferIndex, distances);
printDistances(distances, validData);
if (distances[0] != -1 && distances[1] != -1 && now - lastSend >= wifiRequestCoolDown) {
lastSend = now;
float x, y;
calculateXY(distances[0], distances[1], x, y);
sendLocationRequest(x, y);
}
// Reset for next message
messageStarted = false;
bufferIndex = 0;
}
// Prevent buffer overflow
if (bufferIndex >= 256) {
messageStarted = false;
bufferIndex = 0;
}
}
}
}
