Skip to content

Instantly share code, notes, and snippets.

@eighthree
Last active November 19, 2017 22:58
Show Gist options
  • Save eighthree/cc8c640cb6b0898cfc6d7130f8ae3f4d to your computer and use it in GitHub Desktop.
Save eighthree/cc8c640cb6b0898cfc6d7130f8ae3f4d to your computer and use it in GitHub Desktop.
Code that runs a bunch of things for diy retropie build
/* Hyperbox 1.0
* USB Serial interface using a Cortex M0 dev board + sensors for use with
* a Raspberry Pi.
*
* Current Consumption/Battery Level monitor + Keyboard HID
*
* Made specifically for use with a Hyperpixel display from Pimoroni
* https://github.com/pimoroni/hyperpixel
* as it uses up all the GPIO's on a Pi.
*
* Controller based on:
* https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/Joy_game_controller/Joy_game_controller.ino
*
* Uses an Adafruit Joy Featherwing (for now)
* but I recommend you omit this.
*
* BOM:
* Sparkfun Lipo Fuel Gauge
* Adafruit INA219 Current Breakout Board
* Adafruit Feather M0 Express
* Adafruit Joy Featherwing (w/Seesaw)
* Adafruit Powerboost 1000c
* Raspberry Pi Zero W
* LM75A Temperature Sensor
*/
#include <Wire.h>
#include <Adafruit_INA219.h>
#include "Adafruit_seesaw.h"
#include <Keyboard.h>
#include <M2M_LM75A.h>
#include "MAX17043.h"
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel strip = Adafruit_NeoPixel(1, 8, NEO_GRB + NEO_KHZ800);
Adafruit_INA219 ina219;
Adafruit_seesaw ss;
M2M_LM75A lm75a;
MAX17043 batteryMonitor;
#define BUTTON_RIGHT 6
#define BUTTON_DOWN 7
#define BUTTON_LEFT 9
#define BUTTON_UP 10
#define BUTTON_SEL 14
uint32_t button_mask = (1 << BUTTON_RIGHT) | (1 << BUTTON_DOWN) |
(1 << BUTTON_LEFT) | (1 << BUTTON_UP) | (1 << BUTTON_SEL);
#define IRQ_PIN 5
struct {
uint32_t lastChangeTime;
} jbutton[] = {
0,
0,
0,
0,
0,
0,
};
struct { // Button structure:
int8_t pin; // Button is wired between this pin and GND
uint8_t key; // Corresponding key code to send
bool prevState;
uint32_t lastChangeTime;
} button[] = {
{ A0, 'r' }, // Button 0 Blue
{ A1, 'l'}, // Button 1 Pink
{ A2, 'x' }, // Button 2 Yellow
{ A3, 'y' }, // Button 3 Green
{ A4, 'b' }, // Joystick select click
{ A5, 'a' }, // Joystick select click
};
#define N_BUTTONS (sizeof(button) / sizeof(button[0]))
#define DEBOUNCE_US 600 // Button debounce time, microseconds
struct { // Joystick axis structure (2 axes per stick):
int8_t pin; // Analog pin where stick axis is connected
int lower; // Typical value in left/upper position
int upper; // Typical value in right/lower positionax
uint8_t key1; // Key code to send when left/up
uint8_t key2; // Key code to send when down/right
int value; // Last-read-and-mapped value (0-1023)
int8_t state;
} axis[] = {
{ 3, 65, 1023, KEY_LEFT_ARROW, KEY_RIGHT_ARROW }, // X axis
{ 2, 1023, 65, KEY_DOWN_ARROW , KEY_UP_ARROW }, // Y axis
};
#define N_AXES (sizeof(axis) / sizeof(axis[0]))
int last_x = 0, last_y = 0;
uint8_t state = 0;
float previousVoltage;
volatile byte jstate = LOW;
void setup(void)
{
Serial.begin(115200);
while (!Serial) {
// will pause Zero, Leonardo, etc until serial console opens
delay(1);
}
uint8_t i;
strip.begin();
strip.setBrightness(40);
strip.show(); // Initialize all pixels to 'off'
/* INA219 */
uint32_t currentFrequency;
ina219.begin();
ina219.setCalibration_32V_1A();
lm75a.begin();
// Serial.println("HypeM0 - Services Starting");
/* SEESAW */
if(!ss.begin(0x49)){
// Serial.println("SYS: Adafruit Seesaw connection ERROR!");
while(1);
}
else{
//Serial.println("SYS: Adafruit Seesaw started");
//Serial.println(ss.getVersion(), HEX);
}
/* KEYBOARD */
Keyboard.begin();
// Initialize button states...
for(i=0; i<N_BUTTONS; i++) {
pinMode(button[i].pin, INPUT_PULLUP);
button[i].prevState = digitalRead(button[i].pin);
button[i].lastChangeTime = micros();
}
for(i=0; i<N_AXES; i++) {
int value = map(ss.analogRead(axis[i].pin), axis[i].lower, axis[i].upper, 0, 1023);
if(value > (1023 * 4 / 5)) {
Keyboard.press(axis[i].key2);
axis[i].state = 1;
} else if(value < (1023 / 5)) {
Keyboard.press(axis[i].key1);
axis[i].state = -1;
} else {
axis[i].state = 0;
}
}
batteryMonitor.reset();
batteryMonitor.quickStart();
previousVoltage = batteryMonitor.getVCell();
ss.pinModeBulk(button_mask, INPUT_PULLUP);
ss.setGPIOInterrupts(button_mask, 1);
pinMode(IRQ_PIN, INPUT);
attachInterrupt(IRQ_PIN, joyint, CHANGE);
}
int interval = 10000;
unsigned long previousMillis = 0;
void joyint() {
jstate = !jstate;
}
void loop(void)
{
unsigned long currentMillis = millis();
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
if ((unsigned long)(currentMillis - previousMillis) >= interval && state > 2) {
shuntvoltage = ina219.getShuntVoltage_mV();
busvoltage = ina219.getBusVoltage_V();
current_mA = ina219.getCurrent_mA();
loadvoltage = busvoltage + (shuntvoltage / 1000);
float cellVoltage = batteryMonitor.getVCell();
Serial.print(String(cellVoltage, 4) + ",");
float stateOfCharge = int(batteryMonitor.getSoC());
Serial.print(String(stateOfCharge) + ",");
Serial.print(String(current_mA) + ",");
Serial.print(String(lm75a.getTemperature()) + ",");
int batSlope = (currentMillis - previousMillis)/(cellVoltage - previousVoltage);
if( batSlope >= 0.1 ) {
Serial.print("1\n");
} else {
Serial.print("0\n");
}
if (current_mA > 100) {
if(stateOfCharge >= 90) {
colorWipe(strip.Color(0, 0, 40), 50);
} else if (stateOfCharge >= 70) {
colorWipe(strip.Color(0, 40, 0), 50);
} else if (stateOfCharge >= 60) {
colorWipe(strip.Color(0, 100, 90), 50);
} else if (stateOfCharge >= 50) {
colorWipe(strip.Color(30, 180, 0), 50);
} else if (stateOfCharge >= 30) {
colorWipe(strip.Color(100, 150, 0), 50);
} else
colorWipe(strip.Color(100,0,0), 50);
}
else if (batSlope >= 0.1) {
colorWipe(strip.Color(200,120,0), 50);
} else {
colorWipe(strip.Color(128,0,128), 50);
}
previousVoltage = cellVoltage;
previousMillis = currentMillis;
}
/* Joy Featherwing Read */
if(!jstate){
delay(10);
uint32_t buttons = ss.digitalReadBulk(button_mask);
//Serial.println(buttons, BIN);
if (! (buttons & (1 << BUTTON_RIGHT))) {
if( currentMillis - jbutton[0].lastChangeTime >= 50)
{
Serial.println("CMD_RIGHT\n");
}
jbutton[0].lastChangeTime = currentMillis;
}
if (! (buttons & (1 << BUTTON_DOWN))) {
if( currentMillis - jbutton[1].lastChangeTime >= 75)
{
Serial.println("CMD_DOWN\n");
}
jbutton[1].lastChangeTime = currentMillis;
}
if (! (buttons & (1 << BUTTON_LEFT))) {
if( currentMillis - jbutton[2].lastChangeTime >= 75)
{
Serial.println("CMD_LEFT\n");
}
jbutton[2].lastChangeTime = currentMillis;
}
if (! (buttons & (1 << BUTTON_UP))) {
if( currentMillis - jbutton[3].lastChangeTime >= 75)
{
Serial.println("CMD_VOL_UP\n");
}
jbutton[3].lastChangeTime = currentMillis;
}
if (! (buttons & (1 << BUTTON_SEL))) {
if( currentMillis- jbutton[4].lastChangeTime >= 75)
{
Serial.println("CMD_SELECT\n");
}
jbutton[4].lastChangeTime = currentMillis;
}
if (! (buttons & (1 << BUTTON_SEL)) && ! (buttons & (1 << BUTTON_DOWN))) {
if( currentMillis- jbutton[5].lastChangeTime >= 50)
{
Serial.println("shutdown-please\n");
}
jbutton[5].lastChangeTime = currentMillis;
}
}
/* Analog Button Read*/
uint32_t t;
bool s;
int i, value, dx, dy;
float a;
uint16_t *buf;
// Read and debounce button inputs...
for(i=0; i<N_BUTTONS; i++) {
s = digitalRead(button[i].pin); // Current button state
if(s != button[i].prevState) { // Changed from before?
t = micros(); // Check time; wait for debounce
if((t - button[i].lastChangeTime) >= DEBOUNCE_US) {
if(s) Keyboard.release(button[i].key); // Button released
else Keyboard.press(button[i].key); // Button pressed
button[i].prevState = state; // Save new button state
button[i].lastChangeTime = t; // and time of change
}
}
}
// Read joystick axes
for(i=0; i<N_AXES; i++) {
// Remap analog reading to 0-1023 range (0=top/left, 1023=down/right)
value = map(ss.analogRead(axis[i].pin), axis[i].lower, axis[i].upper, 0, 1023);
if(axis[i].state == 1) { // Axis previously down/right?
if(value < (1023 * 3 / 5)) { // Moved up/left past hysteresis threshold?
Keyboard.release(axis[i].key2); // Release corresponding key
axis[i].state = 0; // and set state to neutral center zone
}
} else if(axis[i].state == -1) { // Else axis previously up/left?
if(value > (1023 * 2 / 5)) { // Moved down/right past hysteresis threshold?
Keyboard.release(axis[i].key1); // Release corresponding key
axis[i].state = 0; // and set state to neutral center zone
}
} // This is intentionally NOT an 'else' -- state CAN change twice here!
if(!axis[i].state) { // Axis previously in neutral center zone?
if(value > (1023 * 4 / 5)) { // Moved down/right?
Keyboard.press(axis[i].key2); // Press corresponding key
axis[i].state = 1; // and set state to down/right
} else if(value < (1023 / 5)) { // Else axis moved up/left?
Keyboard.press(axis[i].key1); // Press corresponding key
axis[i].state = -1;
}
}
axis[i].value = value; // Save for later
}
if(++state > 3) state = 0;
}
// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t wait) {
for(uint16_t i=0; i<strip.numPixels(); i++) {
strip.setPixelColor(i, c);
strip.show();
delay(wait);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment