dashboard: expand RS485 sensor diagnostics
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
- Expanded telemetry with BNO086 motion/gravity/stability reports and GPS HDOP/data ages.
|
- Expanded telemetry with BNO086 motion/gravity/stability reports and GPS HDOP/data ages.
|
||||||
- Hardened RS485 transmission with local-echo draining, frame-size guards, a larger RX buffer, and boot/frame sequence identifiers.
|
- Hardened RS485 transmission with local-echo draining, frame-size guards, a larger RX buffer, and boot/frame sequence identifiers.
|
||||||
- Added dashboard reception of XIAO sensor-node RS485 telemetry and a basic diagnostics-overlay view without changing Cargo `/api/v1` behavior.
|
- Added dashboard reception of XIAO sensor-node RS485 telemetry and a basic diagnostics-overlay view without changing Cargo `/api/v1` behavior.
|
||||||
|
- Expanded dashboard sensor-node diagnostics with yaw, GPS altitude, and UTC.
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ The dashboard enclosure will include an M9N GPS module connected to the ESP32-S3
|
|||||||
|
|
||||||
The dashboard has a hidden diagnostics overlay. Tap the top status card five times within roughly three seconds to open it. Double-tap the diagnostics overlay to return to the main dashboard.
|
The dashboard has a hidden diagnostics overlay. Tap the top status card five times within roughly three seconds to open it. Double-tap the diagnostics overlay to return to the main dashboard.
|
||||||
|
|
||||||
The overlay also provides the initial XIAO sensor-node bring-up view. The dashboard receives newline-delimited JSON through its onboard automatic-direction RS485 transceiver on GPIO43 RX / GPIO44 TX at 115200 baud. It shows link age, received/dropped/error counts, IMU pitch/roll/accuracy/stability, and GPS fix/satellites/HDOP/location/speed. Cargo communication remains WiFi/HTTP through `/api/v1`.
|
The overlay also provides the initial XIAO sensor-node bring-up view. The dashboard receives newline-delimited JSON through its onboard automatic-direction RS485 transceiver on GPIO43 RX / GPIO44 TX at 115200 baud. It shows link age, received/dropped/error counts, IMU pitch/roll/yaw/accuracy/stability, and GPS fix/satellites/HDOP/location/altitude/speed/UTC. Cargo communication remains WiFi/HTTP through `/api/v1`.
|
||||||
|
|
||||||
The diagnostics overlay shows dashboard version, uptime, heap, WiFi RSSI/IP, Cargo API status, Cargo firmware version, Cargo uptime, Cargo heap, Cargo AP client count, BMS status, relay count, and basic control-loop state.
|
The diagnostics overlay shows dashboard version, uptime, heap, WiFi RSSI/IP, Cargo API status, Cargo firmware version, Cargo uptime, Cargo heap, Cargo AP client count, BMS status, relay count, and basic control-loop state.
|
||||||
|
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ This keeps the endpoint model simple while reducing payload size, JSON parsing,
|
|||||||
|
|
||||||
The dashboard listens to the onboard RS485 receiver at 115200 baud using GPIO43 RX and GPIO44 TX. The Waveshare transceiver switches direction automatically, so no DE GPIO is required. The receiver accepts newline-delimited schema-v1 `sensor_status` JSON from the XIAO node and does not change Cargo HTTP/control behavior.
|
The dashboard listens to the onboard RS485 receiver at 115200 baud using GPIO43 RX and GPIO44 TX. The Waveshare transceiver switches direction automatically, so no DE GPIO is required. The receiver accepts newline-delimited schema-v1 `sensor_status` JSON from the XIAO node and does not change Cargo HTTP/control behavior.
|
||||||
|
|
||||||
Open the hidden diagnostics overlay to view link state, frame sequence/gaps/errors, IMU accuracy/stability/pitch/roll, and GPS fix/satellites/HDOP/location/speed. This is a temporary bring-up view ahead of a dedicated off-road page.
|
Open the hidden diagnostics overlay to view link state, frame sequence/gaps/errors, IMU accuracy/stability/pitch/roll/yaw, and GPS fix/satellites/HDOP/location/altitude/speed/UTC. This is a temporary bring-up view ahead of a dedicated off-road page.
|
||||||
|
|
||||||
Arduino **USB CDC On Boot** must remain enabled so dashboard debug `Serial` output uses native USB rather than competing with the GPIO43/GPIO44 RS485 UART.
|
Arduino **USB CDC On Boot** must remain enabled so dashboard debug `Serial` output uses native USB rather than competing with the GPIO43/GPIO44 RS485 UART.
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
static const char *DASHBOARD_VERSION = "0.1.130-rs485-diagnostics";
|
static const char *DASHBOARD_VERSION = "0.1.131-rs485-sensor-stats";
|
||||||
|
|
||||||
// First-boot defaults. Credentials are stored in Preferences/NVS after setup.
|
// First-boot defaults. Credentials are stored in Preferences/NVS after setup.
|
||||||
static const char *DASHBOARD_DEFAULT_CARGO_WIFI_SSID = "OverlandController";
|
static const char *DASHBOARD_DEFAULT_CARGO_WIFI_SSID = "OverlandController";
|
||||||
|
|||||||
@@ -702,6 +702,9 @@ static void update_diagnostics_overlay()
|
|||||||
text += " / ";
|
text += " / ";
|
||||||
text += String(sensor_node.rollDegrees, 1);
|
text += String(sensor_node.rollDegrees, 1);
|
||||||
text += sensor_node.levelCalibrationValid ? " deg calibrated" : " deg raw";
|
text += sensor_node.levelCalibrationValid ? " deg calibrated" : " deg raw";
|
||||||
|
text += "\n Yaw: ";
|
||||||
|
text += String(sensor_node.yawDegrees, 1);
|
||||||
|
text += " deg";
|
||||||
text += "\n GPS: ";
|
text += "\n GPS: ";
|
||||||
text += sensor_node.gpsOnline ? (sensor_node.gpsFix ? "fix" : "no fix") : "offline";
|
text += sensor_node.gpsOnline ? (sensor_node.gpsFix ? "fix" : "no fix") : "offline";
|
||||||
text += " sats ";
|
text += " sats ";
|
||||||
@@ -718,6 +721,10 @@ static void update_diagnostics_overlay()
|
|||||||
}
|
}
|
||||||
text += "\n Speed: ";
|
text += "\n Speed: ";
|
||||||
text += sensor_node.gpsFix ? String(sensor_node.speedKph, 1) + " km/h" : String("--");
|
text += sensor_node.gpsFix ? String(sensor_node.speedKph, 1) + " km/h" : String("--");
|
||||||
|
text += "\n Altitude: ";
|
||||||
|
text += sensor_node.gpsFix ? String(sensor_node.altitudeMeters, 1) + " m" : String("--");
|
||||||
|
text += "\n UTC: ";
|
||||||
|
text += sensor_node.utcTime.length() ? sensor_node.utcTime : String("--");
|
||||||
|
|
||||||
text += "\n\nCargo ESP\n";
|
text += "\n\nCargo ESP\n";
|
||||||
text += " Version: ";
|
text += " Version: ";
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ void parseSensorNodeFrame(const String &line) {
|
|||||||
sensorNodeStatus.imuValid = imu["valid"] | false;
|
sensorNodeStatus.imuValid = imu["valid"] | false;
|
||||||
sensorNodeStatus.imuAccuracy = imu["accuracy"] | 0;
|
sensorNodeStatus.imuAccuracy = imu["accuracy"] | 0;
|
||||||
sensorNodeStatus.stability = String((const char *)(imu["stability"] | "unknown"));
|
sensorNodeStatus.stability = String((const char *)(imu["stability"] | "unknown"));
|
||||||
|
sensorNodeStatus.yawDegrees = imu["yaw_degrees"] | 0.0f;
|
||||||
|
|
||||||
JsonObject level = imu["level_calibration"];
|
JsonObject level = imu["level_calibration"];
|
||||||
sensorNodeStatus.levelCalibrationValid = level["valid"] | false;
|
sensorNodeStatus.levelCalibrationValid = level["valid"] | false;
|
||||||
@@ -74,7 +75,26 @@ void parseSensorNodeFrame(const String &line) {
|
|||||||
sensorNodeStatus.hdop = gps["hdop"].isNull() ? 0 : (float)gps["hdop"];
|
sensorNodeStatus.hdop = gps["hdop"].isNull() ? 0 : (float)gps["hdop"];
|
||||||
sensorNodeStatus.latitude = gps["latitude"] | 0.0;
|
sensorNodeStatus.latitude = gps["latitude"] | 0.0;
|
||||||
sensorNodeStatus.longitude = gps["longitude"] | 0.0;
|
sensorNodeStatus.longitude = gps["longitude"] | 0.0;
|
||||||
|
sensorNodeStatus.altitudeMeters = gps["altitude_meters"] | 0.0f;
|
||||||
sensorNodeStatus.speedKph = gps["speed_kph"] | 0.0f;
|
sensorNodeStatus.speedKph = gps["speed_kph"] | 0.0f;
|
||||||
|
JsonObject utc = gps["utc"];
|
||||||
|
if (!utc.isNull() && (int)(utc["year"] | 0) > 0) {
|
||||||
|
char utcBuffer[24];
|
||||||
|
snprintf(
|
||||||
|
utcBuffer,
|
||||||
|
sizeof(utcBuffer),
|
||||||
|
"%04d-%02d-%02d %02d:%02d:%02dZ",
|
||||||
|
(int)(utc["year"] | 0),
|
||||||
|
(int)(utc["month"] | 0),
|
||||||
|
(int)(utc["day"] | 0),
|
||||||
|
(int)(utc["hour"] | 0),
|
||||||
|
(int)(utc["minute"] | 0),
|
||||||
|
(int)(utc["second"] | 0)
|
||||||
|
);
|
||||||
|
sensorNodeStatus.utcTime = utcBuffer;
|
||||||
|
} else {
|
||||||
|
sensorNodeStatus.utcTime = "";
|
||||||
|
}
|
||||||
sensorNodeStatus.firmwareVersion = String((const char *)(doc["firmware_version"] | ""));
|
sensorNodeStatus.firmwareVersion = String((const char *)(doc["firmware_version"] | ""));
|
||||||
sensorNodeStatus.frameReceived = true;
|
sensorNodeStatus.frameReceived = true;
|
||||||
sensorNodeStatus.framesReceived++;
|
sensorNodeStatus.framesReceived++;
|
||||||
@@ -132,4 +152,3 @@ bool sensorNodeRs485Connected() {
|
|||||||
const SensorNodeDashboardStatus &getSensorNodeStatus() {
|
const SensorNodeDashboardStatus &getSensorNodeStatus() {
|
||||||
return sensorNodeStatus;
|
return sensorNodeStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,12 +20,15 @@ struct SensorNodeDashboardStatus {
|
|||||||
uint32_t satellites = 0;
|
uint32_t satellites = 0;
|
||||||
float pitchDegrees = 0;
|
float pitchDegrees = 0;
|
||||||
float rollDegrees = 0;
|
float rollDegrees = 0;
|
||||||
|
float yawDegrees = 0;
|
||||||
float speedKph = 0;
|
float speedKph = 0;
|
||||||
float hdop = 0;
|
float hdop = 0;
|
||||||
|
float altitudeMeters = 0;
|
||||||
double latitude = 0;
|
double latitude = 0;
|
||||||
double longitude = 0;
|
double longitude = 0;
|
||||||
String stability = "unknown";
|
String stability = "unknown";
|
||||||
String firmwareVersion;
|
String firmwareVersion;
|
||||||
|
String utcTime;
|
||||||
unsigned long lastFrameMs = 0;
|
unsigned long lastFrameMs = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,4 +36,3 @@ void initSensorNodeRs485();
|
|||||||
void processSensorNodeRs485();
|
void processSensorNodeRs485();
|
||||||
bool sensorNodeRs485Connected();
|
bool sensorNodeRs485Connected();
|
||||||
const SensorNodeDashboardStatus &getSensorNodeStatus();
|
const SensorNodeDashboardStatus &getSensorNodeStatus();
|
||||||
|
|
||||||
|
|||||||
@@ -44,3 +44,9 @@ def test_dashboard_receives_sensor_node_rs485_for_diagnostics():
|
|||||||
assert 'strcmp(type, "sensor_status")' in receiver
|
assert 'strcmp(type, "sensor_status")' in receiver
|
||||||
assert 'doc["frame_sequence"]' in receiver
|
assert 'doc["frame_sequence"]' in receiver
|
||||||
assert "framesDropped" in receiver
|
assert "framesDropped" in receiver
|
||||||
|
assert 'imu["yaw_degrees"]' in receiver
|
||||||
|
assert 'gps["altitude_meters"]' in receiver
|
||||||
|
assert 'JsonObject utc = gps["utc"]' in receiver
|
||||||
|
assert r'text += "\n Yaw: "' in dashboard
|
||||||
|
assert r'text += "\n Altitude: "' in dashboard
|
||||||
|
assert r'text += "\n UTC: "' in dashboard
|
||||||
|
|||||||
Reference in New Issue
Block a user