295abb37ee
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.
328 lines
8.4 KiB
C++
328 lines
8.4 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 WII5Sh3dIO.cpp
|
|
* @brief I/O abstraction: buttons, LEDs, and the IO loop tick.
|
|
*/
|
|
|
|
/*
|
|
|
|
TODO 2024 - Check this is only valid IO for Buttons and LEDs
|
|
|
|
*/
|
|
|
|
#include <Arduino.h> //assumes Arduino IDE v1.0 or greater
|
|
#include "WII5Sh3dIO.h"
|
|
#include "WII5Sh3dConsole.h"
|
|
#include <PushButton.h> // Include the PushButton library
|
|
#include <elapsedMillis.h>
|
|
|
|
/* ----------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
// TODO Can we use NUM_PINS
|
|
#define LED_MAX 100
|
|
#define BUTTON_MAX 100
|
|
|
|
void WII5Sh3dIO::begin(uint8_t led1, uint8_t led2, uint8_t btn1, uint8_t btn2) {
|
|
begin(led1, led2, 0, 0, btn1, btn2, 0, 0);
|
|
}
|
|
|
|
// Quick method to save buttons before begin
|
|
void WII5Sh3dIO::preButton(PushButton* btn1, PushButton* btn2, PushButton* btn3, PushButton* btn4) {
|
|
button1 = btn1;
|
|
button2 = btn2;
|
|
button3 = btn3;
|
|
button4 = btn4;
|
|
}
|
|
|
|
void WII5Sh3dIO::begin(uint8_t led1, uint8_t led2, uint8_t led3, uint8_t led4, uint8_t btn1, uint8_t btn2, uint8_t btn3, uint8_t btn4) {
|
|
console.log(LOG_DEBUG, F("LEDS: %d,%d,%d,%d BUTTONS: %d,%d,%d,%d"), int(led1), int(led2), int(led3), int(led4), int(btn1), int(btn2), int(btn3), int(btn4));
|
|
if (_begin)
|
|
return;
|
|
_begin = true;
|
|
|
|
if (led1 > 0 && led1 < LED_MAX) {
|
|
led1_pin = led1;
|
|
pinMode(led1_pin, OUTPUT);
|
|
}
|
|
if (led2 > 0 && led2 < LED_MAX) {
|
|
led2_pin = led2;
|
|
pinMode(led2_pin, OUTPUT);
|
|
}
|
|
if (led3 > 0 && led3 < LED_MAX) {
|
|
led3_pin = led2;
|
|
pinMode(led3_pin, OUTPUT);
|
|
}
|
|
if (led4 > 0 && led4 < LED_MAX) {
|
|
led4_pin = led2;
|
|
pinMode(led4_pin, OUTPUT);
|
|
}
|
|
|
|
if (btn1 > 0 && btn1 < BUTTON_MAX) {
|
|
pinMode(btn1, INPUT_PULLUP);
|
|
if (!button1)
|
|
button1 = new PushButton(btn1);
|
|
button1_pin = btn1;
|
|
// TODO
|
|
button1_activelow = true;
|
|
button1->setActiveLogic(LOW);
|
|
}
|
|
|
|
if (btn2 > 0 && btn2 < BUTTON_MAX) {
|
|
pinMode(btn2, INPUT_PULLUP);
|
|
if (!button2)
|
|
button2 = new PushButton(btn2);
|
|
button2->setActiveLogic(LOW);
|
|
button2_pin = btn2;
|
|
button2_activelow = true;
|
|
}
|
|
|
|
if (btn3 > 0 && btn3 < BUTTON_MAX) {
|
|
pinMode(btn3, INPUT_PULLUP);
|
|
if (!button3)
|
|
button3 = new PushButton(btn3);
|
|
button3_pin = btn3;
|
|
button3_activelow = true;
|
|
button3->setActiveLogic(LOW);
|
|
}
|
|
|
|
if (btn4 > 0 && btn4 < BUTTON_MAX) {
|
|
pinMode(btn4, INPUT_PULLUP);
|
|
if (!button4)
|
|
button4 = new PushButton(btn4);
|
|
button4_pin = btn4;
|
|
button4_activelow = true;
|
|
button4->setActiveLogic(LOW);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void WII5Sh3dIO::loop() {
|
|
if (lastLedLoop > 100) {
|
|
if (led1_pin > 0) {
|
|
// Max time and not default mode
|
|
if ((led1_max > 0) && (led1_mode != led1_default) && (led1_timeon > led1_max))
|
|
led1Set(led1_default);
|
|
if (!updateLED(led1_pin, led1_mode, led1_state)) {
|
|
led1Set(led1_default);
|
|
}
|
|
led1_state++;
|
|
}
|
|
if (led2_pin > 0) {
|
|
if ((led2_max > 0) && (led2_mode != led2_default) && (led2_timeon > led2_max))
|
|
led2Set(led2_default);
|
|
if (!updateLED(led2_pin, led2_mode, led2_state)) {
|
|
led2Set(led2_default);
|
|
}
|
|
led2_state++;
|
|
}
|
|
if (led3_pin > 0) {
|
|
if ((led3_max > 0) && (led3_mode != led3_default) && (led3_timeon > led3_max))
|
|
led3Set(led3_default);
|
|
if (!updateLED(led3_pin, led3_mode, led3_state)) {
|
|
led3Set(led3_default);
|
|
}
|
|
led3_state++;
|
|
}
|
|
if (led4_pin > 0) {
|
|
if ((led4_max > 0) && (led4_mode != led4_default) && (led4_timeon > led4_max))
|
|
led4Set(led4_default);
|
|
if (!updateLED(led4_pin, led4_mode, led4_state)) {
|
|
led4Set(led4_default);
|
|
}
|
|
led4_state++;
|
|
}
|
|
lastLedLoop = 0;
|
|
}
|
|
|
|
if (button1)
|
|
button1->update();
|
|
if (button2)
|
|
button2->update();
|
|
if (button3)
|
|
button3->update();
|
|
if (button4)
|
|
button4->update();
|
|
}
|
|
|
|
// Return 0 - nothing, or button number, 1,2,3
|
|
// TODO - Improve the performance of this to be tiny, so it does not have to be run every singel 8 seconds
|
|
uint8_t WII5Sh3dIO::buttonSleepCheck() {
|
|
if (button1) {
|
|
// pinMode(button1_pin, button1_activelow ? INPUT_PULLUP : INPUT);
|
|
if (digitalRead(button1_pin) == (button1_activelow ? LOW : HIGH)) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (button2) {
|
|
// pinMode(button2_pin, button2_activelow ? INPUT_PULLUP : INPUT);
|
|
if (digitalRead(button2_pin) == (button2_activelow ? LOW : HIGH)) {
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// TODO - Consider adding automatic getting Temperature at timed internval (except for blocking issue)
|
|
// TODO - Consider adding ability to read ALL sensors, not just one
|
|
|
|
void WII5Sh3dIO::sleep() {
|
|
// TODO - Toun off 18B20
|
|
// TODO - Turn off GPS
|
|
// TODO - Turn off LED (which may mean make them float/pull high)
|
|
|
|
// TODO - Make sure the LEDs are actually off
|
|
// digitalWrite(LED1, HIGH);
|
|
// digitalWrite(LED2, HIGH);
|
|
}
|
|
|
|
void WII5Sh3dIO::wake() {
|
|
}
|
|
|
|
#define inverted false
|
|
|
|
// Called once per 100ms
|
|
bool WII5Sh3dIO::updateLED(uint8_t pin, uint8_t mode, uint8_t state) {
|
|
// console.log(LOG_INFO, F("Updating LED %d, %d, %d"), pin, mode, state);
|
|
switch (mode) {
|
|
case LED_OFF: // Off
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_ON: // On
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
break;
|
|
case LED_SLOW: // Slow
|
|
if ((state % 4) == 0)
|
|
digitalWrite(pin, !digitalRead(pin));
|
|
break;
|
|
case LED_FAST: // Fast
|
|
if ((state % 2) == 0)
|
|
digitalWrite(pin, !digitalRead(pin));
|
|
break;
|
|
case LED_SHORT: // Short - on for a short period
|
|
if ((state % 6) == 0)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_DOUBLE: // Double - 2 short, long off
|
|
if ( ((state % 8) == 0) || ((state % 8) == 2) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_TRIPPLE: // Trpple - 3 short, long off
|
|
if ( ((state % 12) == 0) || ((state % 12) == 2) || ((state % 12) == 4) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_SHORT_LONG: // Long - very slow long on and off
|
|
if ((state % 12) == 0)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_DOUBLE_LONG:
|
|
if ( ((state % 16) == 0) || ((state % 16) == 2) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_TRIPPLE_LONG:
|
|
if ( ((state % 20) == 0) || ((state % 20) == 2) || ((state % 20) == 4) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
|
|
case LED_SHORT_GAP: // Long - very slow long on and off
|
|
if ((state % 12) == 0)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_DOUBLE_GAP:
|
|
if ( ((state % 48) == 0) || ((state % 48) == 2) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_TRIPPLE_GAP:
|
|
if ( ((state % 50) == 0) || ((state % 50) == 2) || ((state % 50) == 4) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
|
|
case LED_ONE:
|
|
if ((state % 12) == 0)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_TWO:
|
|
if ( ((state % 16) == 0) || ((state % 16) == 2) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_THREE:
|
|
if ( ((state % 20) == 0) || ((state % 20) == 2) || ((state % 20) == 4) )
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
break;
|
|
case LED_FOUR:
|
|
// TODO Simplify
|
|
break;
|
|
case LED_FIVE:
|
|
break;
|
|
case LED_SIX:
|
|
break;
|
|
case LED_SEVEN:
|
|
break;
|
|
case LED_EIGHT:
|
|
break;
|
|
case LED_NINE:
|
|
break;
|
|
case LED_TEN:
|
|
break;
|
|
case LED_ONCE:
|
|
if (state < 10)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else if (state < 20)
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
else {
|
|
return false;
|
|
}
|
|
break;
|
|
// TODO seemed to be just one long beep
|
|
case LED_TWICE:
|
|
if (state < 10)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else if (state < 20)
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
else if (state < 30)
|
|
digitalWrite(pin, inverted ? LOW : HIGH);
|
|
else if (state < 40)
|
|
digitalWrite(pin, inverted ? HIGH : LOW);
|
|
else {
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
WII5Sh3dIO sh3dNodeIO;
|