main
alastaira 2022-12-14 14:32:24 +00:00
rodzic cd86d94617
commit 622e00990f
4 zmienionych plików z 5601 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,126 @@
/**
* ESP32 Morse decoder using trained machine learning model
*/
// DEFINES
// IS_TRAINING should be defined when gathering training data
// Otherwise, comment out to use the model to classify inputs
// #define IS_TRAINING
// INCLUDES
// See https://github.com/LennartHennigs/Button2
#include "src/Button2.h"
// Uncomment when doing classification
#ifndef IS_TRAINING
#include "model.h"
#endif
// CONSTANTS
// This pin will be used to read dots and dashes - could be connected to any digital input that can be sustained - e.g. a mic, light sensor, or button
const byte inputPin = 16;
// This pin will be driven HIGH when input is being received
const byte buzzerPin = 5;
// This LED will light up when receiving character input
const byte ledPin = 2;
// Every letter can be represented by a maximum of four Morse dots/dashes (if including numbers, this needs to increase to five)
const byte patternLength = 4;
// The maximum allowed space between "dots" and "dashes" within the same character
// If the time elapsed since last input was received exceeds this value, we'll move onto the next character
const int intraCharacterPause = 800;
// GLOBALS
// A button object
Button2 button;
// The duration of inputs received in the current pattern
float pattern[patternLength];
// The time at which the last input was received
unsigned long lastReleaseTime;
// Counter of how many elements have been received in the current pattern
uint8_t counter;
// If we're not training...
#ifndef IS_TRAINING
// Grab a reference to the model's classifier function exported from SciKit-Learn
Eloquent::ML::Port::RandomForest classifier;
#endif
void setup() {
// Initialise serial monitor connection
Serial.begin(115200);
Serial.println(__FILE__ __DATE__);
// Initialise LED and buzzer pins used for user feedback
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);
// Configure the button input
// Anything less than 30ms we won't register as an input at all
button.setDebounceTime(30);
button.setPressedHandler(onButtonPress);
button.setReleasedHandler(onButtonRelease);
button.begin(inputPin);
}
// Called when a complete character pattern has been received
void onCharacterReceive() {
// If we're gathering training data
#ifdef IS_TRAINING
// Send the pattern input to the serial monitor
for(int i=0; i<patternLength; i++) {
Serial.print(pattern[i]);
Serial.print(i==patternLength-1 ? '\n' : ',');
}
// If we're applying the classifier function
#else
// Send the model's prediction to the serial monitor
Serial.println(classifier.predictLabel(pattern));
#endif
// Reset the counter and pattern array
counter = 0;
memset(pattern, 0, sizeof(pattern));
// Turn LED off to show we have finished reading input
digitalWrite(ledPin, LOW);
}
// When any sort of input is received
void onButtonPress(Button2& btn) {
// Turn the LED on when we're recording input
digitalWrite(ledPin, HIGH);
// Make the buzzer sound
digitalWrite(buzzerPin, HIGH);
}
// When any sort of input ends
void onButtonRelease(Button2& btn) {
// Record how long the most recent input lasted for
unsigned int duration = btn.wasPressedFor();
// Store it in the pattern array
pattern[counter] = duration;
// Store the current time
lastReleaseTime = millis();
// Increment the index in the pattern array
counter++;
// If we've recorded the maximum number of dots/dashes, we know the input
// must have ended
if(counter == patternLength) {
onCharacterReceive();
}
// Turn the buzzer off
digitalWrite(buzzerPin, LOW);
}
void loop() {
// Read button input
button.loop();
// If we've received at least one dot/dash, but user is not currently inputting anything, and the time elapsed
// since last input was released exceeds threshold
if(!button.isPressed() && counter > 0 && millis() - lastReleaseTime > intraCharacterPause) {
// Assume the character has ended
onCharacterReceive();
}
}

4834
model.h 100644

Plik diff jest za duży Load Diff

451
src/Button2.cpp 100644
Wyświetl plik

@ -0,0 +1,451 @@
/////////////////////////////////////////////////////////////////
/*
Button2.cpp - Arduino Library to simplify working with buttons.
Created by Lennart Hennigs.
*/
/////////////////////////////////////////////////////////////////
#include "Button2.h"
/////////////////////////////////////////////////////////////////
// initalize static counter
int Button2::_nextID = 0;
/////////////////////////////////////////////////////////////////
// default contructor
Button2::Button2() {
pin = UNDEFINED_PIN;
id = _nextID++;
}
/////////////////////////////////////////////////////////////////
// contructor
Button2::Button2(byte attachTo, byte buttonMode /* = INPUT_PULLUP */, boolean activeLow /* = true */) {
begin(attachTo, buttonMode, activeLow);
}
/////////////////////////////////////////////////////////////////
void Button2::begin(byte attachTo, byte buttonMode /* = INPUT_PULLUP */, boolean activeLow /* = true */) {
pin = attachTo;
id = _nextID++;
#ifndef _IGNORE_LONG
longclick_detected_counter = 0;
longclick_detected_retriggerable = false;
#endif
_pressedState = activeLow ? LOW : HIGH;
if (attachTo != VIRTUAL_PIN) {
pinMode(attachTo, buttonMode);
}
// state = activeLow ? HIGH : LOW;
state = _getState();
prev_state = state ;
}
/////////////////////////////////////////////////////////////////
void Button2::setDebounceTime(unsigned int ms) {
debounce_time_ms = ms;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickTime(unsigned int ms) {
longclick_time_ms = ms;
}
/////////////////////////////////////////////////////////////////
void Button2::setDoubleClickTime(unsigned int ms) {
doubleclick_time_ms = ms;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::getDebounceTime() const {
return debounce_time_ms;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::getLongClickTime() const {
return longclick_time_ms;
}
/////////////////////////////////////////////////////////////////
unsigned int Button2::getDoubleClickTime() const {
return doubleclick_time_ms;
}
/////////////////////////////////////////////////////////////////
byte Button2::getPin() const {
return pin;
}
/////////////////////////////////////////////////////////////////
void Button2::setButtonStateFunction(StateCallbackFunction f) {
get_state_cb = f;
}
/////////////////////////////////////////////////////////////////
bool Button2::operator == (Button2 &rhs) {
return (this == &rhs);
}
/////////////////////////////////////////////////////////////////
void Button2::setChangedHandler(CallbackFunction f) {
change_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setPressedHandler(CallbackFunction f) {
pressed_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setReleasedHandler(CallbackFunction f) {
released_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setClickHandler(CallbackFunction f) {
click_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setTapHandler(CallbackFunction f) {
tap_cb = f;
}
/////////////////////////////////////////////////////////////////
#ifndef _IGNORE_LONG
void Button2::setLongClickHandler(CallbackFunction f) {
long_cb = f;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickDetectedRetriggerable(bool retriggerable) {
longclick_detected_retriggerable = retriggerable;
}
/////////////////////////////////////////////////////////////////
void Button2::setLongClickDetectedHandler(CallbackFunction f) {
longclick_detected_cb = f;
}
#endif
/////////////////////////////////////////////////////////////////
#ifndef _IGNORE_DOUBLE
void Button2::setDoubleClickHandler(CallbackFunction f) {
double_cb = f;
}
#endif
/////////////////////////////////////////////////////////////////
#ifndef _IGNORE_TRIPLE
void Button2::setTripleClickHandler(CallbackFunction f) {
triple_cb = f;
}
#endif
/////////////////////////////////////////////////////////////////
unsigned int Button2::wasPressedFor() const {
return down_time_ms;
}
/////////////////////////////////////////////////////////////////
boolean Button2::isPressed() const {
return (state == _pressedState);
}
/////////////////////////////////////////////////////////////////
boolean Button2::isPressedRaw() {
return (_getState() == _pressedState);
}
/////////////////////////////////////////////////////////////////
byte Button2::getNumberOfClicks() const {
return click_count;
}
/////////////////////////////////////////////////////////////////
clickType Button2::getType() const {
return last_click_type;
}
/////////////////////////////////////////////////////////////////
int Button2::getID() const {
return id;
}
/////////////////////////////////////////////////////////////////
void Button2::setID(int newID) {
id = newID;
}
/////////////////////////////////////////////////////////////////
String Button2::clickToString(clickType type) const {
if (type == single_click) return "click";
#ifndef _IGNORE_LONG
if (type == long_click) return "long click";
#endif
#ifndef _IGNORE_DOUBLE
if (type == double_click) return "double click";
#endif
#ifndef _IGNORE_TRIPLE
if (type == triple_click) return "triple click";
#endif
return "empty";
}
/////////////////////////////////////////////////////////////////
bool Button2::wasPressed() const {
return was_pressed;
}
/////////////////////////////////////////////////////////////////
clickType Button2::read(bool keepState /* = false */) {
if (!keepState) {
clickType res = last_click_type;
last_click_type = empty;
was_pressed = false;
return res;
}
return last_click_type;
}
/////////////////////////////////////////////////////////////////
clickType Button2::wait(bool keepState /* = false */) {
while(!wasPressed()) {
loop();
}
return read(keepState);
}
/////////////////////////////////////////////////////////////////
void Button2::waitForClick(bool keepState /* = false */) {
do {
while(!wasPressed()) {
loop();
}
} while(read() != single_click);
}
/////////////////////////////////////////////////////////////////
#ifndef _IGNORE_DOUBLE
void Button2::waitForDouble(bool keepState /* = false */) {
do {
while(!wasPressed()) {
loop();
}
} while(read() != double_click);
}
#endif
/////////////////////////////////////////////////////////////////
#ifndef _IGNORE_TRIPLE
void Button2::waitForTriple(bool keepState /* = false */) {
do {
while(!wasPressed()) {
loop();
}
} while(read() != triple_click);
}
#endif
/////////////////////////////////////////////////////////////////
#ifndef _IGNORE_LONG
void Button2::waitForLong(bool keepState /* = false */) {
do {
while(!wasPressed()) {
loop();
}
} while(read() != long_click);
}
#endif
/////////////////////////////////////////////////////////////////
byte Button2::_getState() const {
if (get_state_cb != NULL) {
return get_state_cb();
} else {
return digitalRead(pin);
}
}
/////////////////////////////////////////////////////////////////
void Button2::loop() {
if (pin != UNDEFINED_PIN) {
unsigned long now = millis();
prev_state = state;
state = _getState();
// is button pressed?
if (state == _pressedState) {
// is it pressed now?
if (prev_state != _pressedState) {
down_ms = now;
pressed_triggered = false;
click_ms = down_ms;
// trigger pressed event (after debounce has passed)
} else if (!pressed_triggered && (now - down_ms >= debounce_time_ms)) {
pressed_triggered = true;
click_count++;
if (change_cb != NULL) change_cb (*this);
if (pressed_cb != NULL) pressed_cb (*this);
}
#ifndef _IGNORE_LONG
// is a longpress detected callback defined?
if (longclick_detected_cb != NULL) {
// check to see that the longclick_ms period has been exceeded and call the appropriate callback
bool longclick_period_detected = now - down_ms >= (longclick_time_ms * (longclick_detected_counter + 1));
if (longclick_period_detected && !longclick_detected_reported) {
longclick_detected_reported = true;
longclick_detected = true;
if (longclick_detected_retriggerable) {
// increate the counter and reset the "reported" flag (as the counter will stop the false trigger)
longclick_detected_counter++;
longclick_detected_reported = false;
}
if (longclick_detected_cb != NULL) longclick_detected_cb(*this);
}
}
#endif
// is the button released?
} else if (state != _pressedState) {
// is it released right now?
if (prev_state == _pressedState) {
down_time_ms = now - down_ms;
// is it beyond debounce time?
if (down_time_ms >= debounce_time_ms) {
last_click_type = single_click;
// trigger release
if (change_cb != NULL) change_cb (*this);
if (released_cb != NULL) released_cb (*this);
// trigger tap
if (tap_cb != NULL) tap_cb (*this);
#ifndef _IGNORE_LONG
// was it a longclick? (preceeds single / double / triple clicks)
if (down_time_ms >= longclick_time_ms) {
longclick_detected = true;
}
#endif
}
// is the button released and the time has passed for multiple clicks?
} else if (now - click_ms > doubleclick_time_ms) {
#ifndef _IGNORE_LONG
// was there a longclick?
if (longclick_detected) {
// was it part of a combination?
if (click_count == 1) {
last_click_type = long_click;
if (long_cb != NULL) long_cb (*this);
was_pressed = true;
}
longclick_detected = false;
longclick_detected_reported = false;
longclick_detected_counter = 0;
// determine the number of single clicks
} else if (click_count > 0) {
#else
// determine the number of single clicks
if (click_count > 0) {
#endif
switch (click_count) {
case 1:
last_click_type = single_click;
if (click_cb != NULL) click_cb (*this);
break;
#ifndef _IGNORE_DOUBLE
#ifdef _IGNORE_TRIPLE // all multi-clicks are now identified as double
default:
#else
case 2:
#endif
last_click_type = double_click;
if (double_cb != NULL) double_cb (*this);
break;
#endif
#ifndef _IGNORE_TRIPLE
case 3:
last_click_type = triple_click;
if (triple_cb != NULL) triple_cb (*this);
break;
#endif
}
was_pressed = true;
}
// clean up
click_count = 0;
click_ms = 0;
}
}
}
}
/////////////////////////////////////////////////////////////////
void Button2::reset() {
pin = UNDEFINED_PIN;
click_count = 0;
last_click_type = empty;
down_time_ms = 0;
pressed_triggered = false;
#ifndef _IGNORE_LONG
longclick_detected = false;
longclick_detected_reported = false;
longclick_detected_counter = 0;
#endif
pressed_cb = NULL;
released_cb = NULL;
change_cb = NULL;
tap_cb = NULL;
click_cb = NULL;
#ifndef _IGNORE_LONG
long_cb = NULL;
longclick_detected_cb = NULL;
#endif
#ifndef _IGNORE_DOUBLE
double_cb = NULL;
#endif
#ifndef _IGNORE_TRIPLE
triple_cb = NULL;
#endif
}
/////////////////////////////////////////////////////////////////

190
src/Button2.h 100644
Wyświetl plik

@ -0,0 +1,190 @@
/////////////////////////////////////////////////////////////////
/*
Button2.h - Arduino Library to simplify working with buttons.
Created by Lennart Hennigs.
*/
/////////////////////////////////////////////////////////////////
#pragma once
#ifndef Button2_h
#define Button2_h
/////////////////////////////////////////////////////////////////
// uncommenting one of these compiler switches will remove the
// code for one of these click types
// #define _IGNORE_DOUBLE
// #define _IGNORE_TRIPLE
// #define _IGNORE_LONG
/////////////////////////////////////////////////////////////////
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
#include <functional>
#endif
#include <Arduino.h>
/////////////////////////////////////////////////////////////////
#ifdef _IGNORE_DOUBLE
#pragma message "Not compiling double click code"
#endif
#ifdef _IGNORE_TRIPLE
#pragma message "Not compiling triple click code"
#endif
#ifdef _IGNORE_LONG
#pragma message "Not compiling long click code"
#endif
/////////////////////////////////////////////////////////////////
#define DEBOUNCE_MS 50
#define LONGCLICK_MS 200
#define DOUBLECLICK_MS 300
#define UNDEFINED_PIN 255
#define VIRTUAL_PIN 254
/////////////////////////////////////////////////////////////////
enum clickType {
single_click,
#ifndef _IGNORE_DOUBLE
double_click,
#endif
#ifndef _IGNORE_TRIPLE
triple_click,
#endif
#ifndef _IGNORE_LONG
long_click,
#endif
empty
};
class Button2 {
protected:
int id;
byte pin;
byte state;
byte prev_state;
byte click_count = 0;
clickType last_click_type = empty;
bool was_pressed = false;
unsigned long click_ms;
unsigned long down_ms;
#ifndef _IGNORE_LONG
bool longclick_detected_retriggerable;
uint16_t longclick_detected_counter = 0;
bool longclick_detected = false;
bool longclick_detected_reported = false;
#endif
unsigned int debounce_time_ms = DEBOUNCE_MS;
unsigned int longclick_time_ms = LONGCLICK_MS;
unsigned int doubleclick_time_ms = DOUBLECLICK_MS;
unsigned int down_time_ms = 0;
bool pressed_triggered = false;
#if defined(ARDUINO_ARCH_ESP32) || defined(ESP8266)
typedef std::function<void(Button2 &btn)> CallbackFunction;
typedef std::function<byte()> StateCallbackFunction;
#else
typedef void (*CallbackFunction)(Button2 &);
typedef byte (*StateCallbackFunction)();
#endif
StateCallbackFunction get_state_cb = NULL;
CallbackFunction pressed_cb = NULL;
CallbackFunction released_cb = NULL;
CallbackFunction change_cb = NULL;
CallbackFunction tap_cb = NULL;
CallbackFunction click_cb = NULL;
#ifndef _IGNORE_LONG
CallbackFunction long_cb = NULL;
CallbackFunction longclick_detected_cb = NULL;
#endif
#ifndef _IGNORE_DOUBLE
CallbackFunction double_cb = NULL;
#endif
#ifndef _IGNORE_TRIPLE
CallbackFunction triple_cb = NULL;
#endif
public:
Button2();
Button2(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true);
void begin(byte attachTo, byte buttonMode = INPUT_PULLUP, boolean activeLow = true);
void setDebounceTime(unsigned int ms);
void setLongClickTime(unsigned int ms);
void setDoubleClickTime(unsigned int ms);
unsigned int getDebounceTime() const;
unsigned int getLongClickTime() const;
unsigned int getDoubleClickTime() const;
byte getPin() const;
void reset();
void setButtonStateFunction(StateCallbackFunction f);
void setChangedHandler(CallbackFunction f);
void setPressedHandler(CallbackFunction f);
void setReleasedHandler(CallbackFunction f);
void setTapHandler(CallbackFunction f);
void setClickHandler(CallbackFunction f);
#ifndef _IGNORE_DOUBLE
void setDoubleClickHandler(CallbackFunction f);
#endif
#ifndef _IGNORE_TRIPLE
void setTripleClickHandler(CallbackFunction f);
#endif
#ifndef _IGNORE_LONG
void setLongClickHandler(CallbackFunction f);
void setLongClickDetectedHandler(CallbackFunction f);
void setLongClickDetectedRetriggerable(bool retriggerable);
#endif
unsigned int wasPressedFor() const;
boolean isPressed() const;
boolean isPressedRaw();
bool wasPressed() const;
clickType read(bool keepState = false);
clickType wait(bool keepState = false);
void waitForClick(bool keepState = false);
#ifndef _IGNORE_DOUBLE
void waitForDouble(bool keepState = false);
#endif
#ifndef _IGNORE_TRIPLE
void waitForTriple(bool keepState = false);
#endif
#ifndef _IGNORE_LONG
void waitForLong(bool keepState = false);
#endif
byte getNumberOfClicks() const;
clickType getType() const;
String clickToString(clickType type) const;
int getID() const;
void setID(int newID);
bool operator == (Button2 &rhs);
void loop();
private:
static int _nextID;
byte _pressedState;
byte _getState() const;
};
/////////////////////////////////////////////////////////////////
#endif
/////////////////////////////////////////////////////////////////