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.
This commit is contained in:
+339
@@ -0,0 +1,339 @@
|
||||
// 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;
|
||||
Reference in New Issue
Block a user