Skip to content

Instantly share code, notes, and snippets.

@nmwilk
Created April 8, 2022 11:32
Show Gist options
  • Save nmwilk/4e5822870045452df20277294257e210 to your computer and use it in GitHub Desktop.
Save nmwilk/4e5822870045452df20277294257e210 to your computer and use it in GitHub Desktop.
#include <BleGamepad.h> // https://github.com/lemmingDev/ESP32-BLE-Gamepad
#include <Encoder.h> // Encoder - by Paul Stoffregen
BleGamepad bleGamepad("SimWheel");
#define TIME unsigned int
#define ENCODER_PULSES_PER_STEP 2
#define numOfRotaryButtons 10 // 5 rotaries x 2 (rotate left and right)
#define numOfhc165 3
#define dataWidth numOfhc165 * 8
#define pulseWidthUSec 5
#define BytesValT unsigned int
byte previousButtonStates[dataWidth];
byte currentButtonStates[dataWidth];
byte previousRotaryStates[numOfRotaryButtons];
byte currentRotaryStates[numOfRotaryButtons];
BytesValT pinValues;
Encoder r1(13, 12);
Encoder r2(26, 4);
Encoder r3(16, 17);
Encoder r4(14, 27);
Encoder r5(33, 25);
int ploadPin165 = 15;
int cePin165 = 18;
int dataPin165 = 5;
int clockPin165 = 2;
bool firstConnected = true;
const float BattMin = 3.2;
const float BattMax = 4.28;
const float BattRange = BattMax - BattMin;
int readBatteryCounter = 0;
TIME lastUpdate = 0;
void setup()
{
Serial.begin(9600);
bleGamepad.begin(numOfRotaryButtons + dataWidth);
bleGamepad.setAutoReport(false);
pinMode(ploadPin165, OUTPUT);
pinMode(cePin165, OUTPUT);
pinMode(clockPin165, OUTPUT);
pinMode(dataPin165, INPUT);
digitalWrite(clockPin165, LOW);
digitalWrite(ploadPin165, HIGH);
}
void loop()
{
TIME ms = millis();
bool bleGamepadConnected = bleGamepad.isConnected();
if (bleGamepadConnected && firstConnected) {
Serial.println("set up setting battery level");
firstConnected = false;
bleGamepad.setBatteryLevel(readBattery());
Serial.println("set up complete");
}
bool inputsChanged = processInput(bleGamepadConnected, ms);
if (bleGamepadConnected && inputsChanged) {
bleGamepad.sendReport();
delay(20);
}
if (bleGamepadConnected && readBatteryCounter++ > 500) {
bleGamepad.setBatteryLevel(readBattery());
readBatteryCounter = 0;
}
}
byte bitIsSet(BytesValT bytes, int bitNumber) {
return (bytes >> bitNumber) & 1;
}
bool processInput(bool gamePadConnected, TIME ms) {
pinValues = read165s(ms);
processRotary(&r1, 0, ENCODER_PULSES_PER_STEP);
processRotary(&r2, 2, ENCODER_PULSES_PER_STEP);
processRotary(&r3, 4, ENCODER_PULSES_PER_STEP);
processRotary(&r4, 6, ENCODER_PULSES_PER_STEP);
processRotary(&r5, 8, 4);
for (byte currentIndex = 0 ; currentIndex < dataWidth ; currentIndex++)
{
currentButtonStates[currentIndex] = bitIsSet(pinValues, currentIndex);
if (!gamePadConnected) continue;
if (currentButtonStates[currentIndex] != previousButtonStates[currentIndex])
{
if (currentButtonStates[currentIndex] == HIGH)
{
bleGamepad.press(numOfRotaryButtons + currentIndex + 1);
}
else
{
bleGamepad.release(numOfRotaryButtons + currentIndex + 1);
}
}
}
if (gamePadConnected) {
bool buttonsChanged = currentButtonStates != previousButtonStates;
if (buttonsChanged)
{
for (byte currentIndex = 0; currentIndex < dataWidth ; currentIndex++)
{
previousButtonStates[currentIndex] = currentButtonStates[currentIndex];
}
}
bool rotariesChanged = currentRotaryStates != previousRotaryStates;
for (byte currentIndex = 0 ; currentIndex < numOfRotaryButtons ; currentIndex++)
{
if (currentRotaryStates[currentIndex] != previousRotaryStates[currentIndex])
{
if (currentRotaryStates[currentIndex] == HIGH)
{
bleGamepad.press(currentIndex + 1);
}
else
{
bleGamepad.release(currentIndex + 1);
}
}
}
for (byte currentIndex = 0 ; currentIndex < numOfRotaryButtons ; currentIndex++) {
previousRotaryStates[currentIndex] = currentRotaryStates[currentIndex];
}
return rotariesChanged || buttonsChanged;
} else {
return false;
}
}
int readBattery() {
float voltage = readBatteryPin() / 4096.0 * 7.445;
int percentage = min(max(0, (int)round(((voltage - BattMin) / BattRange) * 100)), 100);
return percentage;
}
float readBatteryPin() {
return analogRead(35);
}
void processRotary(Encoder* encoder, int offset, int pulsesPerStep) {
long rVal = encoder->read();
if (abs(rVal) >= pulsesPerStep) {
if (rVal > 0) {
currentRotaryStates[offset + 1] = HIGH;
currentRotaryStates[offset] = LOW;
} else {
currentRotaryStates[offset + 1] = LOW;
currentRotaryStates[offset] = HIGH;
}
encoder->write(0);
} else {
currentRotaryStates[offset] = LOW;
currentRotaryStates[offset + 1] = LOW;
}
}
BytesValT read165s(TIME ms)
{
long bitVal;
BytesValT bytesVal = 0;
/* Trigger a parallel Load to latch the state of the data lines,
*/
digitalWrite(cePin165, HIGH);
digitalWrite(ploadPin165, LOW);
delayMicroseconds(pulseWidthUSec);
digitalWrite(ploadPin165, HIGH);
digitalWrite(cePin165, LOW);
/* Loop to read each bit value from the serial out line
of the SN74HC165N.
*/
for (int i = 0; i < dataWidth; i++)
{
bitVal = digitalRead(dataPin165);
/* Set the corresponding bit in bytesVal.
*/
bytesVal |= (bitVal << ((dataWidth - 1) - i));
/* Pulse the Clock (rising edge shifts the next bit).
*/
digitalWrite(clockPin165, HIGH);
delayMicroseconds(pulseWidthUSec);
digitalWrite(clockPin165, LOW);
}
if (ms - lastUpdate > 1000) {
lastUpdate = ms;
Serial.println(bytesVal, BIN);
}
return (bytesVal);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment