|
/* |
|
Copyright (c) 2022 Marc Khouri |
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
of this software and associated documentation files (the "Software"), to deal |
|
in the Software without restriction, including without limitation the rights |
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
copies of the Software, and to permit persons to whom the Software is |
|
furnished to do so, subject to the following conditions: |
|
|
|
The above copyright notice and this permission notice shall be included in all |
|
copies or substantial portions of the Software. |
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
SOFTWARE. |
|
*/ |
|
#include <Wire.h> |
|
#include <avr/sleep.h> |
|
#include <avr/power.h> |
|
#include <avr/wdt.h> |
|
|
|
// Toggles |
|
#define SERIAL_EN false // Enable serial output |
|
#define VERBOSE false // Verbose serial output |
|
#define ENABLE_SLEEP true // Go to sleep eventually |
|
#define SLEEP_TIMEOUT_MS 600000UL // Idle timeout before sleep. 600,000 == 10 minutes (ardwiino uses 10min). |
|
|
|
// Pin definitions |
|
#define PIN_GREEN 9 |
|
#define PIN_RED 8 |
|
#define PIN_YELLOW 7 |
|
#define PIN_BLUE 6 |
|
#define PIN_ORANGE 5 |
|
#define PIN_I2C_POWER 4 // guitar neck only uses 3mA, we can source 40mA |
|
#define PIN_WAKEUP 2 |
|
|
|
// Constants / structs |
|
#define I2C_ADDRESS 0x0D |
|
const char * hex = "0123456789ABCDEF"; |
|
typedef struct { |
|
bool green; |
|
bool red; |
|
bool yellow; |
|
bool blue; |
|
bool orange; |
|
} Buttons; |
|
|
|
void setup() { |
|
Wire.begin(); // join i2c bus (address optional for master) |
|
pinMode(PIN_GREEN, OUTPUT); |
|
pinMode(PIN_RED, OUTPUT); |
|
pinMode(PIN_YELLOW, OUTPUT); |
|
pinMode(PIN_BLUE, OUTPUT); |
|
pinMode(PIN_ORANGE, OUTPUT); |
|
pinMode(PIN_I2C_POWER, OUTPUT); |
|
digitalWrite(PIN_I2C_POWER, HIGH); |
|
pinMode(PIN_WAKEUP, INPUT_PULLUP); |
|
if (SERIAL_EN) { |
|
Serial.begin(115200); |
|
Serial.println("Setup complete"); |
|
} |
|
} |
|
|
|
void wakeIsr() { |
|
detachInterrupt(digitalPinToInterrupt(PIN_WAKEUP)); |
|
digitalWrite(PIN_I2C_POWER, HIGH); |
|
if (SERIAL_EN) { Serial.println("Waking up"); } |
|
} |
|
|
|
void goToSleep() { |
|
// for (i = 2; i < 20; i++) { // all pins to one rail or the other - power saving |
|
// pinMode(i,OUTPUT); |
|
// digitalWrite(i,LOW); |
|
// } |
|
digitalWrite(PIN_I2C_POWER, LOW); |
|
ADCSRA = 0; // disable ADC for power saving |
|
wdt_disable(); // disable WDT for power saving |
|
set_sleep_mode (SLEEP_MODE_PWR_DOWN); // Deep sleep |
|
sleep_enable(); |
|
sleep_bod_disable(); // disable brownout detector during sleep |
|
attachInterrupt(digitalPinToInterrupt(PIN_WAKEUP), wakeIsr, LOW); // wakeup on signal |
|
sleep_cpu(); // now go to sleep |
|
} |
|
|
|
void diagnoseTransmissionError(byte code) { |
|
// Docs for wire.endtransmission: https://docs.particle.io/cards/firmware/wire-i2c/endtransmission/ |
|
switch(code) { |
|
case 0: |
|
Serial.println("success"); |
|
break; |
|
case 1: |
|
Serial.println("busy timeout upon entering endTransmission()"); |
|
break; |
|
case 2: |
|
Serial.println("START bit generation timeout"); |
|
break; |
|
case 3: |
|
Serial.println("end of address transmission timeout"); |
|
break; |
|
case 4: |
|
Serial.println("data byte transfer timeout"); |
|
break; |
|
case 5: |
|
Serial.println("data byte transfer succeeded, busy timeout immediately after"); |
|
break; |
|
case 6: |
|
Serial.println("timeout waiting for peripheral to clear stop bit"); |
|
break; |
|
default: |
|
Serial.print("Unknown return from EndTransmission: "); |
|
Serial.println(code); |
|
} |
|
} |
|
|
|
unsigned int readFromSerial(uint8_t* arr, unsigned int expectedByteCount) { |
|
unsigned int readCount = 0; |
|
Wire.requestFrom(I2C_ADDRESS, expectedByteCount); // request N bytes from peripheral device |
|
while (Wire.available() && (readCount < expectedByteCount)) { // peripheral may send less than requested |
|
arr[readCount] = Wire.read(); // receive a byte as character |
|
readCount++; |
|
} |
|
return readCount; |
|
} |
|
|
|
void printByteArray(uint8_t* arr, unsigned int len) { |
|
Serial.print("Read "); |
|
Serial.print(len); |
|
Serial.print(" bytes:"); |
|
for(unsigned int i = 0; i < len; i++) { |
|
Serial.print(" 0x"); |
|
Serial.print(hex[(arr[i]>>4) & 0xF]); |
|
Serial.print(hex[arr[i] & 0xF]); |
|
} |
|
Serial.println(""); |
|
} |
|
|
|
Buttons twoBytesToButton(uint8_t* arr) { |
|
Buttons buttons = {.green = false, .red = false, .yellow = false, .blue = false, .orange = false}; |
|
char topButtons = arr[0]; |
|
if (topButtons & 0x10) { |
|
buttons.green = true; |
|
} |
|
if (topButtons & 0x20) { |
|
buttons.red = true; |
|
} |
|
if (topButtons & 0x80) { |
|
buttons.yellow = true; |
|
} |
|
if (topButtons & 0x40) { |
|
buttons.blue = true; |
|
} |
|
if (topButtons & 0x01) { |
|
buttons.orange = true; |
|
} |
|
|
|
// The remaining array on the real guitar 5 bytes long. It varies in a stable |
|
// way when pressing on the touch pad. However, we can get away with just |
|
// looking at the first byte of the five, since it has a unique value for |
|
// each combination of touchpad presses. |
|
// There's gotta be a pattern here, but I'm too tired to spot it... |
|
switch (arr[1]) { |
|
case 0x00: |
|
// no buttons |
|
break; |
|
case 0x95: |
|
buttons.green = true; break; |
|
case 0xCD: |
|
buttons.red = true; break; |
|
case 0x1A: |
|
buttons.yellow = true; break; |
|
case 0x49: |
|
buttons.blue = true; break; |
|
case 0x7F: |
|
buttons.orange = true; break; |
|
case 0xB0: |
|
buttons.green = true; buttons.red = true; break; |
|
case 0x19: |
|
buttons.green = true; buttons.yellow = true; break; |
|
case 0x47: |
|
buttons.green = true; buttons.blue = true; break; |
|
case 0x7B: |
|
buttons.green = true; buttons.orange = true; break; |
|
case 0xE6: |
|
buttons.red = true; buttons.yellow = true; break; |
|
case 0x48: |
|
buttons.red = true; buttons.blue = true; break; |
|
case 0x7D: |
|
buttons.red = true; buttons.orange = true; break; |
|
case 0x2F: |
|
buttons.yellow = true; buttons.blue = true; break; |
|
case 0x7E: |
|
buttons.yellow = true; buttons.orange = true; break; |
|
case 0x66: |
|
buttons.blue = true; buttons.orange = true; break; |
|
case 0x65: |
|
buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x64: |
|
buttons.red = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x7C: |
|
buttons.red = true; buttons.yellow = true; buttons.orange = true; break; |
|
case 0x2E: |
|
buttons.red = true; buttons.yellow = true; buttons.blue = true; break; |
|
case 0x62: |
|
buttons.green = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x7A: |
|
buttons.green = true; buttons.yellow = true; buttons.orange = true; break; |
|
case 0x2D: |
|
buttons.green = true; buttons.yellow = true; buttons.blue = true; break; |
|
case 0x79: |
|
buttons.green = true; buttons.red = true; buttons.orange = true; break; |
|
case 0x46: |
|
buttons.green = true; buttons.red = true; buttons.blue = true; break; |
|
case 0xE5: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; break; |
|
case 0x63: |
|
buttons.red = true; buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x61: |
|
buttons.green = true; buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x60: |
|
buttons.green = true; buttons.red = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x78: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; buttons.orange = true; break; |
|
case 0x2C: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; buttons.blue = true; break; |
|
case 0x5F: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
default: |
|
if (SERIAL_EN) { |
|
Serial.print("Unrecognized pattern! "); |
|
printByteArray(&arr[1], 1); |
|
} |
|
} |
|
|
|
return buttons; |
|
} |
|
|
|
void printButtons(Buttons buttons) { |
|
if (VERBOSE) { Serial.print("Green: "); } |
|
Serial.print(buttons.green); |
|
if (VERBOSE) { Serial.print(" Red: "); } |
|
Serial.print(buttons.red); |
|
if (VERBOSE) { Serial.print(" Yellow: "); } |
|
Serial.print(buttons.yellow); |
|
if (VERBOSE) { Serial.print(" Blue: "); } |
|
Serial.print(buttons.blue); |
|
if (VERBOSE) { Serial.print(" Orange: "); } |
|
Serial.println(buttons.orange); |
|
} |
|
|
|
void buttonsToDigitalOut (Buttons buttons) { |
|
if (buttons.green) { |
|
digitalWrite(PIN_GREEN, HIGH); |
|
} else { |
|
digitalWrite(PIN_GREEN, LOW); |
|
} |
|
if (buttons.red) { |
|
digitalWrite(PIN_RED, HIGH); |
|
} else { |
|
digitalWrite(PIN_RED, LOW); |
|
} |
|
if (buttons.yellow) { |
|
digitalWrite(PIN_YELLOW, HIGH); |
|
} else { |
|
digitalWrite(PIN_YELLOW, LOW); |
|
} |
|
if (buttons.blue) { |
|
digitalWrite(PIN_BLUE, HIGH); |
|
} else { |
|
digitalWrite(PIN_BLUE, LOW); |
|
} |
|
if (buttons.orange) { |
|
digitalWrite(PIN_ORANGE, HIGH); |
|
} else { |
|
digitalWrite(PIN_ORANGE, LOW); |
|
} |
|
} |
|
|
|
bool isInitialized = false; |
|
unsigned long lastChange = 0; |
|
unsigned int loopCounter = 0; // Only used to control how often debug output is printed |
|
void loop() { |
|
if (ENABLE_SLEEP && (millis() - lastChange > SLEEP_TIMEOUT_MS)) { |
|
if (SERIAL_EN) { |
|
Serial.println("Going to sleep"); |
|
} |
|
isInitialized = false; |
|
delay(1000); // give time for any serial printing to finish |
|
goToSleep(); |
|
if (SERIAL_EN) { Serial.println("Resetting sleep timer"); } |
|
lastChange = millis(); // Reset sleep timer after wakeup |
|
} |
|
|
|
if (!isInitialized) { |
|
delay(1000); |
|
if (SERIAL_EN) { |
|
Serial.println("Not yet initialized"); |
|
} |
|
Wire.beginTransmission(I2C_ADDRESS); // Transmit to device |
|
Wire.write(0x00); // Sends value byte |
|
byte error = Wire.endTransmission(); // Stop transmitting |
|
if (error != 0) { |
|
if (SERIAL_EN) { diagnoseTransmissionError(error); } |
|
return; |
|
} |
|
|
|
unsigned int expectedInitBytes = 7; |
|
uint8_t values[expectedInitBytes]; |
|
unsigned int readCount = readFromSerial(values, expectedInitBytes); |
|
if (SERIAL_EN && VERBOSE) { printByteArray(values, readCount); } |
|
if (readCount == expectedInitBytes) { |
|
if (SERIAL_EN) { Serial.println("Initialized"); } |
|
isInitialized = true; |
|
} else { |
|
if (SERIAL_EN) { Serial.println("Wrong byte count read"); } |
|
} |
|
return; |
|
} |
|
|
|
if (SERIAL_EN && VERBOSE) { |
|
delay(500); // read every N ms |
|
} else { |
|
delay(0.5); // The guitar leaves a 9ms gap between reads, but it seems like we can go lower |
|
} |
|
|
|
Wire.beginTransmission(0x0D); // Transmit to device |
|
Wire.write(0x12); // Sends value byte to set the memory address we want to read from |
|
byte error = Wire.endTransmission(); // Stop transmitting |
|
if (error != 0) { |
|
if (SERIAL_EN) { diagnoseTransmissionError(error); } |
|
return; |
|
} |
|
|
|
/* Note: the real guitar body sets the memory address for the read to be `0x10` instead of |
|
* `0x12`. Then, the real guitar body reads two bytes from the neck. These two bytes seem |
|
* to always be identical and useless. sanjay900 suggested that we can instead set the |
|
* memory address to be `0x12`, and skip the initial two byte read! |
|
|
|
// This read of two bytes seems to always contain the same information. Not clear why it needs to happen. |
|
unsigned int expectedInitialByteCount = 2; |
|
uint8_t valuesInitial[expectedInitialByteCount]; |
|
unsigned int readCountInitial = readFromSerial(valuesInitial, expectedByteCountInitial); |
|
if (SERIAL_EN && VERBOSE) { printByteArray(valuesInitial, readCountInitial); } |
|
if (readCountInitial != expectedByteCountInitial) { |
|
if (SERIAL_EN) { Serial.println("Wrong byte count read"); } |
|
return; |
|
} |
|
*/ |
|
|
|
|
|
// The guitar will send up to 6 bytes in this response, but we only need the first |
|
// one for the buttons and the second byte to decode the touchpad. The remaining |
|
// bytes seems useless. |
|
unsigned int expectedByteCount = 2; |
|
uint8_t values[expectedByteCount]; |
|
unsigned int readCount = readFromSerial(values, expectedByteCount); |
|
if (SERIAL_EN && VERBOSE) { printByteArray(values, readCount); } |
|
if (readCount != expectedByteCount) { |
|
if (SERIAL_EN) { Serial.println("Wrong byte count read"); } |
|
return; |
|
} |
|
Buttons buttons = twoBytesToButton(values); |
|
buttonsToDigitalOut(buttons); |
|
if (buttons.green || buttons.red || buttons.yellow || buttons.blue || buttons.orange) { |
|
lastChange = millis(); |
|
} |
|
|
|
if (SERIAL_EN) { |
|
loopCounter++; |
|
// Only print out the button state periodically, so polling stays fast |
|
if (loopCounter % 25 == 0) { |
|
printButtons(buttons); |
|
} |
|
} |
|
} |
|
#include <Wire.h> |
|
#include <avr/sleep.h> |
|
#include <avr/power.h> |
|
#include <avr/wdt.h> |
|
|
|
// Toggles |
|
#define SERIAL_EN false // Enable serial output |
|
#define VERBOSE false // Verbose serial output |
|
#define ENABLE_SLEEP true // Go to sleep eventually |
|
#define SLEEP_TIMEOUT_MS 10000UL // Idle timeout before sleep |
|
|
|
// Pin definitions |
|
#define PIN_GREEN 9 |
|
#define PIN_RED 8 |
|
#define PIN_YELLOW 7 |
|
#define PIN_BLUE 6 |
|
#define PIN_ORANGE 5 |
|
#define PIN_I2C_POWER 4 // guitar neck only uses 3mA, we can source 40mA |
|
#define PIN_WAKEUP 2 |
|
|
|
// Constants / structs |
|
#define I2C_ADDRESS 0x0D |
|
const char * hex = "0123456789ABCDEF"; |
|
typedef struct { |
|
bool green; |
|
bool red; |
|
bool yellow; |
|
bool blue; |
|
bool orange; |
|
} Buttons; |
|
|
|
void setup() { |
|
Wire.begin(); // join i2c bus (address optional for master) |
|
pinMode(PIN_GREEN, OUTPUT); |
|
pinMode(PIN_RED, OUTPUT); |
|
pinMode(PIN_YELLOW, OUTPUT); |
|
pinMode(PIN_BLUE, OUTPUT); |
|
pinMode(PIN_ORANGE, OUTPUT); |
|
pinMode(PIN_I2C_POWER, OUTPUT); |
|
digitalWrite(PIN_I2C_POWER, HIGH); |
|
pinMode(PIN_WAKEUP, INPUT_PULLUP); |
|
if (SERIAL_EN) { |
|
Serial.begin(115200); |
|
Serial.println("Setup complete"); |
|
} |
|
} |
|
|
|
void wakeIsr() { |
|
detachInterrupt(digitalPinToInterrupt(PIN_WAKEUP)); |
|
digitalWrite(PIN_I2C_POWER, HIGH); |
|
if (SERIAL_EN) { Serial.println("Waking up"); } |
|
} |
|
|
|
void goToSleep() { |
|
// for (i = 2; i < 20; i++) { // all pins to one rail or the other - power saving |
|
// pinMode(i,OUTPUT); |
|
// digitalWrite(i,LOW); |
|
// } |
|
digitalWrite(PIN_I2C_POWER, LOW); |
|
ADCSRA = 0; // disable ADC for power saving |
|
wdt_disable(); // disable WDT for power saving |
|
set_sleep_mode (SLEEP_MODE_PWR_DOWN); // Deep sleep |
|
sleep_enable(); |
|
sleep_bod_disable(); // disable brownout detector during sleep |
|
attachInterrupt(digitalPinToInterrupt(PIN_WAKEUP), wakeIsr, LOW); // wakeup on signal |
|
sleep_cpu(); // now go to sleep |
|
} |
|
|
|
void diagnoseTransmissionError(byte code) { |
|
// Docs for wire.endtransmission: https://docs.particle.io/cards/firmware/wire-i2c/endtransmission/ |
|
switch(code) { |
|
case 0: |
|
Serial.println("success"); |
|
break; |
|
case 1: |
|
Serial.println("busy timeout upon entering endTransmission()"); |
|
break; |
|
case 2: |
|
Serial.println("START bit generation timeout"); |
|
break; |
|
case 3: |
|
Serial.println("end of address transmission timeout"); |
|
break; |
|
case 4: |
|
Serial.println("data byte transfer timeout"); |
|
break; |
|
case 5: |
|
Serial.println("data byte transfer succeeded, busy timeout immediately after"); |
|
break; |
|
case 6: |
|
Serial.println("timeout waiting for peripheral to clear stop bit"); |
|
break; |
|
default: |
|
Serial.print("Unknown return from EndTransmission: "); |
|
Serial.println(code); |
|
} |
|
} |
|
|
|
unsigned int readFromSerial(uint8_t* arr, unsigned int expectedByteCount) { |
|
unsigned int readCount = 0; |
|
Wire.requestFrom(I2C_ADDRESS, expectedByteCount); // request N bytes from peripheral device |
|
while (Wire.available() && (readCount < expectedByteCount)) { // peripheral may send less than requested |
|
arr[readCount] = Wire.read(); // receive a byte as character |
|
readCount++; |
|
} |
|
return readCount; |
|
} |
|
|
|
void printByteArray(uint8_t* arr, unsigned int len) { |
|
Serial.print("Read "); |
|
Serial.print(len); |
|
Serial.print(" bytes:"); |
|
for(unsigned int i = 0; i < len; i++) { |
|
Serial.print(" 0x"); |
|
Serial.print(hex[(arr[i]>>4) & 0xF]); |
|
Serial.print(hex[arr[i] & 0xF]); |
|
} |
|
Serial.println(""); |
|
} |
|
|
|
Buttons sixBytesToButton(uint8_t* arr) { |
|
Buttons buttons = {.green = false, .red = false, .yellow = false, .blue = false, .orange = false}; |
|
char topButtons = arr[0]; |
|
if (topButtons & 0x10) { |
|
buttons.green = true; |
|
} |
|
if (topButtons & 0x20) { |
|
buttons.red = true; |
|
} |
|
if (topButtons & 0x80) { |
|
buttons.yellow = true; |
|
} |
|
if (topButtons & 0x40) { |
|
buttons.blue = true; |
|
} |
|
if (topButtons & 0x01) { |
|
buttons.orange = true; |
|
} |
|
|
|
// The remaining array is 5 bytes long. It varies in a stable way |
|
// when pressing on the touch pad. |
|
// There's gotta be a pattern here, but I'm too tired to spot it... |
|
// We can get away with just looking at the first byte because they're unique. |
|
switch (arr[1]) { |
|
case 0x00: |
|
// no buttons |
|
break; |
|
case 0x95: |
|
buttons.green = true; break; |
|
case 0xCD: |
|
buttons.red = true; break; |
|
case 0x1A: |
|
buttons.yellow = true; break; |
|
case 0x49: |
|
buttons.blue = true; break; |
|
case 0x7F: |
|
buttons.orange = true; break; |
|
case 0xB0: |
|
buttons.green = true; buttons.red = true; break; |
|
case 0x19: |
|
buttons.green = true; buttons.yellow = true; break; |
|
case 0x47: |
|
buttons.green = true; buttons.blue = true; break; |
|
case 0x7B: |
|
buttons.green = true; buttons.orange = true; break; |
|
case 0xE6: |
|
buttons.red = true; buttons.yellow = true; break; |
|
case 0x48: |
|
buttons.red = true; buttons.blue = true; break; |
|
case 0x7D: |
|
buttons.red = true; buttons.orange = true; break; |
|
case 0x2F: |
|
buttons.yellow = true; buttons.blue = true; break; |
|
case 0x7E: |
|
buttons.yellow = true; buttons.orange = true; break; |
|
case 0x66: |
|
buttons.blue = true; buttons.orange = true; break; |
|
case 0x65: |
|
buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x64: |
|
buttons.red = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x7C: |
|
buttons.red = true; buttons.yellow = true; buttons.orange = true; break; |
|
case 0x2E: |
|
buttons.red = true; buttons.yellow = true; buttons.blue = true; break; |
|
case 0x62: |
|
buttons.green = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x7A: |
|
buttons.green = true; buttons.yellow = true; buttons.orange = true; break; |
|
case 0x2D: |
|
buttons.green = true; buttons.yellow = true; buttons.blue = true; break; |
|
case 0x79: |
|
buttons.green = true; buttons.red = true; buttons.orange = true; break; |
|
case 0x46: |
|
buttons.green = true; buttons.red = true; buttons.blue = true; break; |
|
case 0xE5: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; break; |
|
case 0x63: |
|
buttons.red = true; buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x61: |
|
buttons.green = true; buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x60: |
|
buttons.green = true; buttons.red = true; buttons.blue = true; buttons.orange = true; break; |
|
case 0x78: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; buttons.orange = true; break; |
|
case 0x2C: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; buttons.blue = true; break; |
|
case 0x5F: |
|
buttons.green = true; buttons.red = true; buttons.yellow = true; buttons.blue = true; buttons.orange = true; break; |
|
default: |
|
if (SERIAL_EN) { |
|
Serial.print("Unrecognized pattern! "); |
|
printByteArray(&arr[1], 1); |
|
} |
|
} |
|
|
|
return buttons; |
|
} |
|
|
|
void printButtons(Buttons buttons) { |
|
if (VERBOSE) { Serial.print("Green: "); } |
|
Serial.print(buttons.green); |
|
if (VERBOSE) { Serial.print(" Red: "); } |
|
Serial.print(buttons.red); |
|
if (VERBOSE) { Serial.print(" Yellow: "); } |
|
Serial.print(buttons.yellow); |
|
if (VERBOSE) { Serial.print(" Blue: "); } |
|
Serial.print(buttons.blue); |
|
if (VERBOSE) { Serial.print(" Orange: "); } |
|
Serial.println(buttons.orange); |
|
} |
|
|
|
void buttonsToDigitalOut (Buttons buttons) { |
|
if (buttons.green) { |
|
digitalWrite(PIN_GREEN, HIGH); |
|
} else { |
|
digitalWrite(PIN_GREEN, LOW); |
|
} |
|
if (buttons.red) { |
|
digitalWrite(PIN_RED, HIGH); |
|
} else { |
|
digitalWrite(PIN_RED, LOW); |
|
} |
|
if (buttons.yellow) { |
|
digitalWrite(PIN_YELLOW, HIGH); |
|
} else { |
|
digitalWrite(PIN_YELLOW, LOW); |
|
} |
|
if (buttons.blue) { |
|
digitalWrite(PIN_BLUE, HIGH); |
|
} else { |
|
digitalWrite(PIN_BLUE, LOW); |
|
} |
|
if (buttons.orange) { |
|
digitalWrite(PIN_ORANGE, HIGH); |
|
} else { |
|
digitalWrite(PIN_ORANGE, LOW); |
|
} |
|
} |
|
|
|
bool isInitialized = false; |
|
unsigned long lastChange = 0; |
|
void loop() { |
|
if (ENABLE_SLEEP && (millis() - lastChange > SLEEP_TIMEOUT_MS)) { |
|
if (SERIAL_EN) { |
|
Serial.println("Going to sleep"); |
|
} |
|
isInitialized = false; |
|
delay(1000); // give time for any serial printing to finish |
|
goToSleep(); |
|
if (SERIAL_EN) { Serial.println("Resetting sleep timer"); } |
|
lastChange = millis(); // Reset sleep timer after wakeup |
|
} |
|
|
|
if (!isInitialized) { |
|
delay(1000); |
|
if (SERIAL_EN) { |
|
Serial.println("Not yet initialized"); |
|
} |
|
Wire.beginTransmission(I2C_ADDRESS); // Transmit to device |
|
Wire.write(0x00); // Sends value byte |
|
byte error = Wire.endTransmission(); // Stop transmitting |
|
if (error != 0) { |
|
if (SERIAL_EN) { diagnoseTransmissionError(error); } |
|
return; |
|
} |
|
|
|
unsigned int expectedInitBytes = 7; |
|
uint8_t values[expectedInitBytes]; |
|
unsigned int readCount = readFromSerial(values, expectedInitBytes); |
|
if (SERIAL_EN && VERBOSE) { printByteArray(values, readCount); } |
|
if (readCount == expectedInitBytes) { |
|
if (SERIAL_EN) { Serial.println("Initialized"); } |
|
isInitialized = true; |
|
} else { |
|
if (SERIAL_EN) { Serial.println("Wrong byte count read"); } |
|
} |
|
return; |
|
} |
|
|
|
if (SERIAL_EN && VERBOSE) { |
|
delay(500); // read every N ms |
|
} else { |
|
delay(2); // The guitar leaves a 9ms gap between reads, but it seems like we can go lower |
|
} |
|
|
|
Wire.beginTransmission(0x0D); // Transmit to device |
|
Wire.write(0x10); // Sends value byte |
|
byte error = Wire.endTransmission(); // Stop transmitting |
|
if (error != 0) { |
|
if (SERIAL_EN) { diagnoseTransmissionError(error); } |
|
return; |
|
} |
|
|
|
unsigned int expectedByteCount = 2; |
|
uint8_t values[expectedByteCount]; |
|
unsigned int readCount = readFromSerial(values, expectedByteCount); |
|
if (SERIAL_EN && VERBOSE) { printByteArray(values, readCount); } |
|
if (readCount == expectedByteCount) { |
|
// Serial.println("First read done"); |
|
} else { |
|
if (SERIAL_EN) { Serial.println("Wrong byte count read"); } |
|
return; |
|
} |
|
|
|
unsigned int expectedByteCount2 = 6; |
|
uint8_t values2[expectedByteCount2]; |
|
unsigned int readCount2 = readFromSerial(values2, expectedByteCount2); |
|
if (SERIAL_EN && VERBOSE) { printByteArray(values2, readCount2); } |
|
if (readCount2 == expectedByteCount2) { |
|
// Serial.println("Second read done"); |
|
} else { |
|
if (SERIAL_EN) { Serial.println("Wrong byte count read"); } |
|
return; |
|
} |
|
Buttons buttons = sixBytesToButton(values2); |
|
buttonsToDigitalOut(buttons); |
|
if (buttons.green || buttons.red || buttons.yellow || buttons.blue || buttons.orange) { |
|
lastChange = millis(); |
|
} |
|
if (SERIAL_EN) { printButtons(buttons); } |
|
} |