Skip to content

Commit

Permalink
Implement extra buttons (#1)
Browse files Browse the repository at this point in the history
Update code to support extra buttons
  • Loading branch information
ecoreng authored Jul 4, 2023
1 parent 0ceba09 commit aa3a2e2
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 4 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ FlexRingGauge is a device that aims to be a meter with a display and a configura

While in theory the device is not tied to SimHub, this firmware emulates an Arduino board that has been flashed with the SimHub firmware.

## Required components
## Components
- 24 RGB LED Ring - aka "NeoPixel"
- 1.3" OLED Screen with the SH1106 driver that can be wired for I2C (usually these have 4 pins only)
- D1 Mini (ESP8266)
- (optional) 2 push buttons (Normally open)

The ESP866 was selected over any Arduino board due to cost, form factor available memory AND performance. In the past I tested a similar meter stand alone framework and the affordable Arduino boards struggled to keep up, while the ESP8266 was passed with flying colors.
The SSH1106 screen was picked due to its availability, size and cost.
The SH1106 screen was picked due to its availability, size and cost.
Given that SimHub doesn't support neither the ESP board nor the SH1106 driver for the screen, this repository exists.

## Circuit
Expand Down
Binary file modified diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions lib/Button/SHButton.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#ifndef __SHBUTTON_H__
#define __SHBUTTON_H__

#include <Arduino.h>
#include "SHFastIO.h"

typedef void(*SHButtonChanged) (int, byte);

class SHButton {
private:

FastDigitalPin button;
uint8_t buttonState;
int buttonLastState = -1;
bool vccToPinWiring;
unsigned long buttonLastStateChanged;
byte id;
SHButtonChanged shButtonChangedCallback;
int logicMode;

int getState(int value) {
int res = 0;
if (!vccToPinWiring) {
res = value == HIGH ? 0 : 1;
}
else {
res = value == HIGH ? 1 : 0;
}
if (logicMode == 1) {
res = res ? 0 : 1;
}
return res;
}

public:

void begin(byte buttonid, uint8_t buttonPin, SHButtonChanged changedcallback, bool vccToPinWiring, int logicMode) {
button.begin(buttonPin);
this->vccToPinWiring = vccToPinWiring;
this->logicMode = logicMode;

if (buttonPin > 0) {
if (!vccToPinWiring) {
pinMode(buttonPin, INPUT_PULLUP);
}
else {
pinMode(buttonPin, INPUT);
}
}
id = buttonid;
buttonLastState = getState(button.digitalReadPin());
shButtonChangedCallback = changedcallback;

if (buttonLastState)
shButtonChangedCallback(id, buttonLastState);
}

uint8_t getPressed() {
return !buttonLastState;
}

void read() {
buttonState = getState(button.digitalReadPin());
if (buttonState != buttonLastState && buttonLastStateChanged - millis() > 50) {
shButtonChangedCallback(id, buttonState);
buttonLastState = buttonState;
buttonLastStateChanged = millis();
}
}
};

#endif
31 changes: 31 additions & 0 deletions lib/Button/SHDebouncer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef __SHDEBOUNCER_H__
#define __SHDEBOUNCER_H__

#include <Arduino.h>

class SHDebouncer {
private:
unsigned long lastPoll = 0;
unsigned long debounceDelay;
public:
SHDebouncer(byte delay) {
debounceDelay = (unsigned long)delay;
}

SHDebouncer() {
}

void begin(byte delay) {
debounceDelay = delay;
}

bool Debounce() {
if (millis() - lastPoll > debounceDelay) {
lastPoll = millis();
return true;
}
return false;
}
};

#endif
31 changes: 31 additions & 0 deletions lib/Button/SHFastIO.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef __SHFASTIO_H__
#define __SHFASTIO_H__
#include <Arduino.h>

class FastDigitalPin {
private:
uint8_t pin = -1;
bool pinIsValid = false;
public:

bool isValid() {
return true;
}

void begin(int pin) {
if (pin < 0) {
pinIsValid = false;
}
else {
pinIsValid = true;
this->pin = pin;
}
}

int digitalReadPin()
{
return digitalRead(this->pin);
}
};

#endif
2 changes: 1 addition & 1 deletion src/SHCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void Command_SetBaudrate()

void Command_ButtonsCount()
{
FlowSerialWrite((byte)(0));
FlowSerialWrite((byte)(ENABLED_BUTTONS_COUNT));
FlowSerialFlush();
}

Expand Down
42 changes: 41 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define LEDRING_SHIFT_BY_180_DEGREES 1 // If set to 1, uses an alternative mapping for leds that's shifted 180 degrees
#define FASTLED_ESP8266_RAW_PIN_ORDER // fastLED will try to guess your pin number if not set up and won't work
#define LED_BUILTIN D4 // Pin number to activate to control the built in LED
#define INCLUDE_BUTTONS // SimHub visible only buttons (no gamepad)

/** *****************************************
*
Expand All @@ -28,6 +29,8 @@
#include <Wire.h>
#include <FlowSerialRead.h>
#include <SHRGBLedsNeoPixelFastLed.h>
#include <SHButton.h>
#include <SHDebouncer.h>

#if PLUS > 0
# include <plus.h>
Expand All @@ -41,20 +44,57 @@ unsigned long lastSerialActivity = 0;
SHGLCD_I2COLED screen;
SHRGBLedsNeoPixelFastLeds shRGBLedsWS2812B;


#ifdef INCLUDE_BUTTONS
#define ENABLED_BUTTONS_COUNT 2 // How many SimHub visible only buttons to add (no gamepad)
#define BUTTON_PIN_1 D3 // Button 1 Pin (to GND, not inverted)
#define BUTTON_PIN_2 D7 // Button 2 Pin (to GND, not inverted)
int BUTTON_PINS[] = { BUTTON_PIN_1, BUTTON_PIN_2 };
SHButton button1, button2;
SHButton* BUTTONS[] = { &button1, &button2 };
#endif

SHDebouncer ButtonsDebouncer(10);

#if PLUS > 0
PlusController plus(&screen, &shRGBLedsWS2812B);
#endif

#include <SHCommands.h>

void idle(bool critical) {}

void buttonStatusChanged(int buttonId, byte Status) {
arqserial.CustomPacketStart(0x03, 2);
arqserial.CustomPacketSendByte(buttonId);
arqserial.CustomPacketSendByte(Status);
arqserial.CustomPacketEnd();
}

void idle(bool critical) {
if (ButtonsDebouncer.Debounce()) {
bool changed = false;
#ifdef INCLUDE_BUTTONS
for (int btnIdx = 0; btnIdx < ENABLED_BUTTONS_COUNT; btnIdx++) {
BUTTONS[btnIdx]->read();
}
#endif
}
}

void setup()
{
FlowSerialBegin(19200);
shRGBLedsWS2812B.begin(WS2812B_RGBLEDCOUNT, WS2812B_RIGHTTOLEFT, PLUS == 0);
screen.init(PLUS == 0);
arqserial.setIdleFunction(idle);

#ifdef INCLUDE_BUTTONS
// EXTERNAL BUTTONS INIT
for (int btnIdx = 0; btnIdx < ENABLED_BUTTONS_COUNT; btnIdx++) {
BUTTONS[btnIdx]->begin(btnIdx + 1, BUTTON_PINS[btnIdx], buttonStatusChanged, 0, 0); // notice the hardcoded zeroes
}
#endif

#if PLUS > 0
plus.init();
#endif
Expand Down

0 comments on commit aa3a2e2

Please sign in to comment.