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:
2026-05-07 16:00:21 +10:00
commit 295abb37ee
122 changed files with 38142 additions and 0 deletions
+84
View File
@@ -0,0 +1,84 @@
// 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 wii5_bindata.ino
* @brief Test sketch: BinData layout/split exercise.
*/
/*
* BinData Test
*/
#include <WII5Sh3dConsole.h>
#include <WII5_board.h>
#include <WII5Setup.h>
#include <WII5.h>
#include <WII5Data.h>
void setup() {
wii5Setup.setupConsole();
wii5Setup.setupIO();
console.printf(F("waiting 1 seconds\r\n"));
delay(1000);
console.printf(F("WII5: Test BinData\r\n"));
}
elapsedMillis wdtWait = 0;
void loop() {
if (wdtWait > 5100) {
digitalWrite(WDT_RESET, LOW);
wdtWait = 0;
}
else if (wdtWait > 5000) {
digitalWrite(WDT_RESET, HIGH);
}
sh3dNodeIO.loop();
console.loop();
if (console.available()) {
switch(console.getCommand()) {
case '0':
break;
}
}
console.printf(F("BinData: showSizes\n"));
wii5BinData.showSizes();
console.printf(F("BinData: Map Bits test\n"));
for (uint8_t x = 0; x < 16; x++) {
uint32_t t = 0;
SetBit(t, x);
console.printf(F("X = %d, T = %lu\r\n"), x, t);
for (uint8_t y = 0; y < 16; y++) {
if (BitVal(t, y)) {
console.printf(F("BitVal matched on %d\r\n"), y);
}
}
}
console.printf(F("BinData: Trying first 32 types\r\n"));
uint32_t binDataType = 0;
while(1) {
binDataType = (binDataType << 1) + 1;
console.printf(F("BinData: type=%lu, size=%d\r\n"), binDataType, wii5BinData.getSize(binDataType));
console.flush();
delay(500);
console.printf(F("BinData: Creating\r\n"));
wii5BinData.setData(binDataType, wii5BinaryIridium, 340); // TODO Hard coded
console.flush();
delay(500);
console.printf(F("BinData: type=%lu, size=%d\r\n"), binDataType, wii5BinData.getSize(binDataType));
wii5BinData.dumpData(binDataType, wii5BinaryIridium, 340); // TODO Hard coded
console.flush();
delay(500);
}
delay(5000);
}
+22
View File
@@ -0,0 +1,22 @@
// 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 wii5_commands.ino
* @brief Test sketch: command-protocol exercise.
*/
#include "WII5.h"
#include "WII5Commands.h"
void setup () {
Serial.begin(115200);
wii5Setup.begin();
}
void loop() {
wii5Commands.loop();
}
+102
View File
@@ -0,0 +1,102 @@
// 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 wii5_gps.ino
* @brief Test sketch: GPS NMEA passthrough.
*/
#include <WII5Sh3dConsole.h>
#include <WII5.h>
#include <WII5GPS.h>
#define SerialGPS Serial1
void setup() {
// Serial
Serial.begin(57600);
console.begin();
console.add(&Serial);
console.printf(F("WII5: Test GPS"));
// GPS
wii5Gps.begin();
// wii5Gps.begin(&SerialGPS);
// wii5Gps.setMode(WII5GPS_PASSTHROUGH);
wii5Gps.start();
// wii5Gps.setMode(WII5GPS_PASSTHROUGH);
}
void loop() {
if (SerialGPS.available()) {
char c = SerialGPS.read();
Serial.write(c);
}
return;
console.loop();
wii5Gps.loop();
if (console.available()) {
console.printf(F("CONSOLE: Got command %c=%d \r\n"),
console.getCommand(),
console.getVal()
);
switch(console.getCommand()) {
case '0':
console.printf(F("setMode WII5GPS_OFF\n"));
wii5Gps.setMode(WII5GPS_OFF);
break;
case '1':
console.printf(F("setMode WII5GPS_QUIET\n"));
wii5Gps.setMode(WII5GPS_QUIET);
break;
case '2':
console.printf(F("setMode WII5GPS_PASSTHROUGH\n"));
wii5Gps.setMode(WII5GPS_PASSTHROUGH);
break;
case '3':
console.printf(F("setMode WII5GPS_TIMEONCE\n"));
wii5Gps.setMode(WII5GPS_TIMEONCE);
break;
case '4':
console.printf(F("setMode WII5GPS_POSONCE\n"));
wii5Gps.setMode(WII5GPS_POSONCE);
break;
case '5':
console.printf(F("setMode WII5GPS_POSACCURATE\n"));
wii5Gps.setMode(WII5GPS_POSACCURATE);
break;
case '6':
console.printf(F("setMode WII5GPS_POSREPEAT\n"));
wii5Gps.setMode(WII5GPS_POSREPEAT);
break;
case 'D':
// TODO dump;
wii5Gps.dump();
break;
case 'S':
// Status
// - Number of bytes received
// - Lock status etc
break;
default:
console.printf(F("GPS Help:\r\n"));
console.printf(F(" 0 - mode off\r\n"));
console.printf(F(" 1 - mode Quiet\r\n"));
console.printf(F(" 2 - mode Passthrough\r\n"));
console.printf(F(" 3 - mode Time Once\r\n"));
console.printf(F(" 4 - mode Position Once\r\n"));
console.printf(F(" 5 - mode Position Accurate\r\n"));
console.printf(F(" 6 - mode Position Repeat\r\n"));
console.printf(F(" D - Dump all data\r\n"));
console.printf(F(" S - Show status\r\n"));
};
}
}
+55
View File
@@ -0,0 +1,55 @@
// 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 wii5_iridium.ino
* @brief Test sketch: Iridium AT-command exercise.
*/
#include <WII5Sh3dConsole.h>
#include <WII5.h>
#include <WII5Iridium.h>
void setup() {
wii5Setup.begin();
}
void loop() {
wii5Iridium.loop();
console.loop();
if (console.available()) {
switch(console.getCommand()) {
case 'p':
console.log(LOG_DEBUG, F("IRIDIUM: 'p' passthrough toggle"));
wii5Iridium.setPassthrough(!wii5Iridium.getPassthrough());
console.log(LOG_DEBUG, F("IRIDIUM: passthrough=%d"), int(wii5Iridium.getPassthrough()));
break;
case 'd':
console.log(LOG_DEBUG, F("IRIDIUM: 'd' debug toggle"));
wii5Iridium.setDebug(!wii5Iridium.getDebug());
console.log(LOG_DEBUG, F("IRIDIUM: debug=%d"), int(wii5Iridium.getDebug()));
break;
case 's':
console.log(LOG_DEBUG, F("IRIDIUM: 's' start"));
wii5Iridium.start();
break;
case 'S':
console.log(LOG_DEBUG, F("IRIDIUM: 'S' stop"));
wii5Iridium.stop();
break;
default:
break;
};
console.log(LOG_DEBUG, F("IRIDIUM: Status = %d"), wii5Iridium.getPassthrough());
wii5Iridium.displayStatus();
wii5Iridium.displayError();
}
}
+179
View File
@@ -0,0 +1,179 @@
// 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 wii5_pins.ino
* @brief Test sketch: multi-port serial passthrough for pin diagnostics.
*/
/*
WII5 Pins
*/
#include <elapsedMillis.h>
#include <WII5Sh3dConsole.h>
#include <Sh3dNodeUtil.h>
// TODO This needs serious normalisation for each serial port ! (A class perhaps)
// Large Line buffers for passthrough
#define BUFFER_SIZE 250
#define DEVICE_MAX 6
Stream *streams[DEVICE_MAX];
char bufferSerial[DEVICE_MAX][BUFFER_SIZE + 1];
uint8_t bufferPos[DEVICE_MAX];
uint32_t count = 0;
elapsedMillis passthroughWait = 0;
bool passthroughSerial[DEVICE_MAX];
void setup() {
// Short delay for debug / errors
Serial.begin(57600);
console.begin();
console.add(&Serial);
sh3dNodeUtil.begin();
// Start each port at the right speed
Serial1.begin(9600);
streams[1] = &Serial1;
Serial2.begin(9600);
streams[2] = &Serial2;
Serial3.begin(9600);
streams[3] = &Serial3;
// Serial4.begin(9600);
// streams[4] = &Serial4;
// Serial5.begin(9600);
// streams[5] = &Serial5;
}
void processSerial(uint8_t id) {
if (!streams[id]) {
return;
}
while (streams[id]->available()) {
char c = streams[id]->read();
// Ignore start/stop character of Wind Sonic
if (c == 2)
c = 'W';
if (c == 3)
c = '*';
if ( (c != '\n') && (c != '\r') ) {
if (bufferPos[id] < BUFFER_SIZE) {
bufferSerial[id][bufferPos[id]] = c;
bufferPos[id]++;
bufferSerial[id][bufferPos[id]] = '\0';
}
}
else {
if (passthroughSerial[id]) {
console.printf(F("S%d: (%lu) %s\r\n"), id, millis(), bufferSerial[id]);
}
}
}
}
elapsedMillis wait = 0;
void loop() {
// TODO Should not be hard coded 60 seconds
/*
if (passthroughWait > 60000) {
// Skips 0
for (uint8_t id = 1; id < DEVICE_MAX; id++) {
passthroughSerial[id] = false;
}
console.log(LOG_INFO, F("Passthrough time expired"));
}
*/
console.loop();
if (console.available()) {
console.log(LOG_INFO, F("WII5 Console command received - %c=%d"),
console.getCommand(),
console.getVal()
);
switch(console.getCommand()) {
case '1':
case '2':
case '3':
case '4':
case '5':
// Toggle
passthroughSerial[(uint8_t)console.getCommand() - '0']
= !passthroughSerial[(uint8_t)console.getCommand() - '0'];
// Reset timer - all off when hits time
passthroughWait = 0;
console.log(LOG_INFO,
F("Passthrough enabled for $d for %d seconds"),
(uint16_t)console.getCommand() - '0',
60
);
break;
case 'A':
for (uint8_t id = 1; id < DEVICE_MAX; id++) {
passthroughSerial[id] = true;
}
passthroughWait = 0;
break;
// Output
case 'o':
console.printf(F("OUTPU pin=%d\r\n"), int(console.getVal()));
pinMode(int(console.getVal()), OUTPUT);
break;
// Output
case 'i':
console.printf(F("INPUT pin=%d\r\n"), int(console.getVal()));
pinMode(int(console.getVal()), INPUT);
break;
// Low
case 'x':
console.printf(F("LOW pin=%d\r\n"), int(console.getVal()));
digitalWrite(int(console.getVal()), LOW);
console.printf(F("pin=%d\r\n"), int(digitalRead(console.getVal())));
break;
// HIGH
case 'X':
console.printf(F("HIGH pin=%d\r\n"), int(console.getVal()));
digitalWrite(int(console.getVal()), HIGH);
console.printf(F("pin=%d\r\n"), int(digitalRead(console.getVal())));
break;
// Low
case 's':
console.printf(F("pin=%d, state=%d\r\n"), int(console.getVal()), int(digitalRead(console.getVal())));
break;
case 'd':
// dumpPins();
break;
case 'h':
case 'H':
console.log(LOG_INFO, F(" WII5: Pins"));
console.log(LOG_INFO, F(" 'L' - Stop logging"));
break;
}
}
if (wait > 500) {
console.printf(F("pin=%d, state=%d\r\n"), int(console.getVal()), int(digitalRead(console.getVal())));
wait = 0;
}
// Process all incoming Serial
for (uint8_t id = 1; id < DEVICE_MAX; id++) {
processSerial(id);
}
}
+64
View File
@@ -0,0 +1,64 @@
// 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 wii5_sparton.ino
* @brief Test sketch: Sparton AHRS exercise.
*/
#include <WII5Sh3dConsole.h>
#include <WII5.h>
#include <WII5Sparton.h>
void setup() {
Serial.begin(115200);
console.begin(WII5_BUFFER_CONSOLE_PRINT, WII5_BUFFER_CONSOLE_CMD);
console.add(&Serial);
console.printf(F("WII5: Test Sparton"));
wii5Sparton.begin();
wii5Sparton.setDebug(true);
wii5Sparton.setRecords(5000);
wii5Sparton.start();
}
void loop() {
wii5Sparton.loop();
console.loop();
if (console.available()) {
uint32_t val = console.getVal();
console.printf(F("CONSOLE: Got command %c=%d \r\n"),
console.getCommand(),
val
);
switch(console.getCommand()) {
// Toggle Sparton passthrough
case 'p':
console.log(LOG_DEBUG, F("SPARTON: 'p' passthrough toggle"));
wii5Sparton.setPassthrough(!wii5Sparton.getPassthrough());
break;
case 's':
if (val < 100) val = 100;
console.log(LOG_DEBUG, F("SPARTON: 's' start with %lu records"), val);
wii5Sparton.setRecords(val);
wii5Sparton.start();
break;
case 'S':
console.log(LOG_DEBUG, F("SPARTON: 'S' stop"));
wii5Sparton.stop();
break;
default:
break;
};
console.log(LOG_DEBUG, F("SPARTON: Status = %d"), wii5Sparton.getPassthrough());
wii5Sparton.displayStatus();
wii5Sparton.displayError();
}
}
@@ -0,0 +1,25 @@
// 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 wii5_weather_18b20.ino
* @brief Test sketch: DS18B20 temperature exercise.
*/
#include "WII5.h"
#include "WII5Weather_18B20.h"
void setup () {
Serial.begin(115200);
console.begin();
console.add(&Serial);
wii5Weather_18B20.begin();
}
void loop() {
wii5Weather_18B20.temperatureRead();
delay(5000);
}