Files
scottp 295abb37ee Initial public release of WII5 Buoy firmware
Firmware for an autonomous wave-measurement buoy (ATmega2560-based
WII5 v2 board). Reads wave motion from a Sparton AHRS-M1/M2 IMU,
samples GPS and battery state, and reports back over Iridium SBD
satellite telemetry. Originally developed 2012-2024.

This is the first public release. Code, documentation, and field-tested
operating modes (Capture, Sleep, Position, ManualTest, SelfTest,
LowBattery) are licensed under Apache 2.0 — see LICENSE and NOTICE.

See README.md for an overview and build instructions, CONTRIBUTING.md
for how to contribute, and DEPLOYMENTS.md for the field-deployment log.
2026-05-07 16:27:18 +10:00

340 lines
11 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2012-2024 Scott Penrose <scottp@dd.com.au> and WII5 Buoy contributors
//
// This file is part of WII5 Buoy firmware.
// See LICENSE for full terms.
/**
* @file WII5Display.cpp
* @brief Status display helpers: SD-block view, formatted metadata dumps.
*/
/*
WII5Display
*/
#include <Arduino.h>
#include <WII5Display.h>
// TODO Move to utils (could also be pinMode by different inputs)
uint8_t bit;
uint8_t port;
volatile uint8_t *reg;
volatile uint8_t *out;
int wii5_getPinMode(uint8_t pin) {
if (pin >= NUM_PINS) return (-1);
// TODO analog?
bit = digitalPinToBitMask(pin);
port = digitalPinToPort(pin);
reg = portModeRegister(port);
if (*reg & bit) return (OUTPUT);
out = portOutputRegister(port);
return ((*out & bit) ? INPUT_PULLUP : INPUT);
}
uint32_t minutes;
uint32_t used;
uint32_t WII5Display::minutesUntilNext(uint32_t period) {
minutes = (hour() * 60) + minute();
used = minutes % period;
return period - used;
}
// SD / Storage Display Helpers
void WII5Display::sdBlockView(uint32_t block_in, bool show_results, bool show_raw) {
// Read block
#ifdef WII5_DEBUG_DISPLAY
Serial.print(F("# BLOCK="));
Serial.println(block_in);
#endif
if (!sdBlock.read(block_in)) {
console.log(LOG_ERROR, F("Unable to view block - %lu"), block_in);
return;
}
uint32_t dataBlockStart = sdBlock.metadata->dataBlockStart;
uint32_t dataBlocks = sdBlock.metadata->dataBlocks;
uint32_t resultsBlockStart = sdBlock.metadata->resultsBlockStart;
uint32_t resultsBlocks = sdBlock.metadata->resultsBlocks;
console.printf(F("@Block,metadata,address=%lu,deviceId=%lu,recordId=%lu\r\n"),
block_in, sdBlock.metadata->deviceId, sdBlock.metadata->recordId
);
console.printf(F("@Block,metadata,dataBlockStart=%lu,dataBlocks=%lu"),
dataBlockStart, dataBlocks
);
console.printf(F("@Block,metadata,resultsBlockStart=%lu,resultsBlocks=%lu"),
resultsBlockStart, resultsBlocks
);
printMetadataBlock((WII5MetaDataObject*)&sdBlock.metadata->data);
if (show_results) {
console.printf(F("# Loading results block\r\n"));
if (!sdBlock.read(resultsBlockStart)) {
console.log(LOG_ERROR, F("Unable to view resultsBlock - %lu"), resultsBlockStart);
return;
}
WII5Processed* processed = (WII5Processed*)sdBlock.block->data;
#ifdef WII5_DEBUG_DISPLAY
void* a = &processed->processed1;
void* b = &processed->processed1.part1float;
void* c = &processed->processed2.part2float;
void* d = &processed->processed2.part2int;
Serial.println(int(processed));
Serial.println(int(a));
Serial.println(int(b));
Serial.println(int(c));
Serial.println(int(d));
Serial.println(int(sizeof(WII5Processed)));
Serial.println(int(sizeof(WII5Processed1)));
Serial.println(int(sizeof(WII5Processed2)));
for (uint8_t i = 0; i < 73; i++) {
Serial.println(processed->processed1.part1float[i]);
}
for (uint8_t i = 0; i < 20; i++) {
Serial.println(processed->processed2.part2float[i]);
}
for (uint8_t i = 0; i < 62; i++) {
Serial.println(processed->processed2.part2int[i]);
}
#else
(void)processed;
#endif
console.print("# END out/processed.out");
console.printNewLine();
}
if (show_raw) {
console.log(LOG_ERROR, F("Raw view not yet implemented"));
}
}
void WII5Display::printMetadataBlock(WII5MetaDataObject* metadata) {
// TODO MAke this use metadtat abvoe but look the same
console.printf(F("@Metadata,start,print block\r\n"));
console.printf(F("@Metadata,deviceId=%ld,recordId=%ld\r\n"),
metadata->deviceId, metadata->recordCount
);
console.printf(F("@Metadata,time=%ld,uptime=%ld\r\n"),
metadata->last, metadata->uptime
);
console.printf(F("@Metadata,temperature=%ld,battery=%ld\r\n"),
metadata->temperatureValue, metadata->batteryValue
);
console.printf(F("@Metadata,lat=%ld,lon=%ld,alt=%ld,sats=%ld,hdop=%ld\r\n"),
long(wii5Gps.gps->location.lat() * GPS_POS_MULT), long(wii5Gps.gps->location.lng() * GPS_POS_MULT), long(wii5Gps.gps->altitude.meters() * GPS_POS_MULT),
metadata->gpsLat, metadata->gpsLon, metadata->gpsHdop
);
/*
console.printf(F("@Metadata,capture,timeError=%ld,sizeError=-%ld,writeMin=%ld,writeMax=%ld,writeOver=%ld\r\n"),
wii5Sparton.captureWriteMax, wii5Sparton.captureWriteMin, wii5Sparton.captureWriteOver,
wii5Sparton.statsTimeError, wii5Sparton.serialSizeError
);
*/
console.printf(F("@Metadata,end,print block\r\n"));
}
uint8_t a_start;
uint8_t a_end;
uint8_t p;
void WII5Display::dumpPins() {
a_start = analogInputToDigitalPin(0);
a_end = a_start + NUM_ANALOG_INPUTS - 1;
console.printf(F("# Pins:\r\n"));
for (p = 0; p < NUM_PINS; p++) {
console.printf(F("# D%02d "), int(p));
console.printf(F("%s "), wii5Strings.strPinMode(wii5_getPinMode(p)));
console.printf(F(" "));
// Long term - if analog - analogRead too or instead
if ((p >= a_start) && (p <= a_end)) {
console.printf(F("%04d"), analogRead(p));
}
else {
console.printf(F("%s"), wii5Strings.strState(digitalRead(p)));
}
console.printf(F(" "));
console.printf(F("%s"), wii5Strings.strPinArduinoName(p));
console.printf(F(" "));
console.printf(F("%s"), wii5Strings.strPinWII5Name(p));
console.printf(F(" | "));
// TODO - Arduino name, D3, A4, MOSI, etc
// TODO - WII5 name - e.g. GPS Power Switch
// 4 per line?
if ( ((p+1) % 3) == 0) {
console.printNewLine();
}
}
console.printNewLine();
}
void WII5Display::atMathsSend(const __FlashStringHelper *area, const __FlashStringHelper *out, ... ){
console.printf(F("@Maths,"));
console.printf(area);
console.printf(F(","));
va_list argptr;
va_start(argptr, out);
VSNPRINTF(wii5BufferConsolePrint, WII5_BUFFER_CONSOLE_PRINT, (const char *)out, argptr);
va_end(argptr);
console.print(wii5BufferConsolePrint);
// console.printf(F(",%lu/%lu,"), wii5Config.getDeviceId(), wii5Config.getRecordCount());
// console.printDateTime(),
console.printNewLine();
}
void WII5Display::atStatsSend(const __FlashStringHelper *area, const __FlashStringHelper *out, ... ){
console.printf(F("@Stats,"));
console.printf(area);
console.printf(F(",%lu/%lu,"), wii5Config.getDeviceId(), wii5Config.getRecordCount());
console.printDateTime(),
console.printf(F(","));
va_list argptr;
va_start(argptr, out);
VSNPRINTF(wii5BufferConsolePrint, WII5_BUFFER_CONSOLE_PRINT, (const char *)out, argptr);
va_end(argptr);
console.print(wii5BufferConsolePrint);
console.printNewLine();
}
void WII5Display::atDataSend(const __FlashStringHelper *area, const __FlashStringHelper *out, ... ){
console.printf(F("@Data,"));
console.printf(area);
console.printf(F(",%lu/%lu,"), wii5Config.getDeviceId(), wii5Config.getRecordCount());
console.printDateTime(),
console.printf(F(","));
va_list argptr;
va_start(argptr, out);
VSNPRINTF(wii5BufferConsolePrint, WII5_BUFFER_CONSOLE_PRINT, (const char *)out, argptr);
va_end(argptr);
console.print(wii5BufferConsolePrint);
console.printNewLine();
}
// NOTE ! This is the StatusSerial - not the console
void WII5Display::statusSend(const __FlashStringHelper *area, const __FlashStringHelper *out, ... ){
#ifdef WII5_STATUS_SERIAL
SerialStatus.print(area);
SerialStatus.print(",");
// Low level - assume we know device id - see if we can send it
// SerialStatus.print(wii5Config.getDeviceId());
// SerialStatus.print("/");
SerialStatus.print(wii5Config.getRecordCount());
// SerialStatus.printDateTime(),
SerialStatus.print(F(","));
va_list argptr;
va_start(argptr, out);
VSNPRINTF(wii5BufferConsolePrint, WII5_BUFFER_CONSOLE_PRINT, (const char *)out, argptr);
va_end(argptr);
SerialStatus.print(wii5BufferConsolePrint);
SerialStatus.println();
#endif
}
void WII5Display::atCommandSend(const __FlashStringHelper *area, const __FlashStringHelper *out, ... ){
console.printf(F("@WII5,"));
console.printf(area);
// TODO @WII5 commands do not normally have device and date details
console.printf(F(",%lu/%lu,"), wii5Config.getDeviceId(), wii5Config.getRecordCount());
console.printDateTime(),
console.printf(F(","));
va_list argptr;
va_start(argptr, out);
VSNPRINTF(wii5BufferConsolePrint, WII5_BUFFER_CONSOLE_PRINT, (const char *)out, argptr);
va_end(argptr);
console.print(wii5BufferConsolePrint);
console.printNewLine();
}
void WII5Display::atCommsSend(const __FlashStringHelper *area, const __FlashStringHelper *out, ... ){
console.printf(F("@Comms,"));
console.printf(area);
console.printf(F(",%lu/%lu,"), wii5Config.getDeviceId(), wii5Config.getRecordCount());
console.printDateTime(),
console.printf(F(","));
va_list argptr;
va_start(argptr, out);
VSNPRINTF(wii5BufferConsolePrint, WII5_BUFFER_CONSOLE_PRINT, (const char *)out, argptr);
va_end(argptr);
console.print(wii5BufferConsolePrint);
console.printNewLine();
}
void WII5Display::printMetadata() {
console.printf(F("@Metadata,deviceId=%ld,recordId=%ld\r\n"),
wii5Config.getDeviceId(), wii5Config.getRecordCount()
);
console.printf(F("@Metadata,time=%ld,uptime=%ld\r\n"),
now(), wii5Controller.uptime
);
console.printf(F("@Metadata,temperature=%ld,battery=%ld\r\n"),
wii5Weather_18B20.value, wii5Battery.value
);
#ifdef WII5_GPS
console.printf(F("@Metadata,lat=%ld,lon=%ld,alt=%ld,sats=%ld,hdop=%ld\r\n"),
long(wii5Gps.gps->location.lat() * GPS_POS_MULT), long(wii5Gps.gps->location.lng() * GPS_POS_MULT), long(wii5Gps.gps->altitude.meters() * GPS_POS_MULT),
wii5Gps.gps->satellites.value(), wii5Gps.gps->hdop.value()
);
#endif
console.printf(F("@Metadata,capture,timeError=%ld,sizeError=-%ld,writeMin=%ld,writeMax=%ld,writeOver=%ld\r\n"),
wii5Sparton.captureWriteMax, wii5Sparton.captureWriteMin, wii5Sparton.captureWriteOver,
wii5Sparton.statsTimeError, wii5Sparton.serialSizeError
);
}
void WII5Display::updateMetadata(void* ref = NULL) {
if (ref) {
metadata = (WII5MetaDataObject*)ref;
}
if (metadata) {
memset(metadata, 0, sizeof(WII5MetaDataObject));
metadata->deviceId = wii5Config.getDeviceId();
metadata->recordCount = wii5Config.getRecordCount();
// TODO last is now? What about start of capture time?
metadata->last = now();
metadata->age = 0;
metadata->uptime = wii5Controller.uptime;
metadata->temperatureValue = wii5Weather_18B20.value;
metadata->temperatureAge = wii5Weather_18B20.age;
metadata->batteryValue = wii5Battery.value;
metadata->batteryAge = wii5Battery.age;
#ifdef WII5_GPS
// TODO move to float
metadata->gpsLat = wii5Gps.gps->location.lat();
metadata->gpsLon = wii5Gps.gps->location.lng();
metadata->gpsAlt = wii5Gps.gps->altitude.meters();
metadata->gpsSat = wii5Gps.gps->satellites.value();
metadata->gpsHdop = wii5Gps.gps->hdop.value();
// fixtime
// age
metadata->gpsAge = wii5Gps.gps->time.age();
#endif
metadata->captureWriteMax = wii5Sparton.captureWriteMax;
metadata->captureWriteMin = wii5Sparton.captureWriteMin;
metadata->captureWriteOver = wii5Sparton.captureWriteOver;
metadata->captureTimeError = wii5Sparton.statsTimeError;
metadata->captureSizeError = wii5Sparton.serialSizeError;
metadata->captureStartTime = wii5ModeCapture.startTime;
metadata->mode = (uint8_t)wii5Controller.getMode();
}
}
WII5Display wii5Display;