Stephen Harrison
Published © CC BY-NC

Happy Sad Buttons

Track your hacker space equipment usage and condition with this simple Internet connected buttons solution.

AdvancedFull instructions provided2 hours1,037
Happy Sad Buttons

Things used in this project

Hardware components

Photon
Particle Photon
×1
Illuminating Button
×2
RCR123A Rechargeable Battery
×1
Heatfit Insert - M3
×8
PCB Battery Holder (CR123A)
×1
Molex Connector (3 Pin)
×1

Software apps and online services

Tinamous.com

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Label Printer

Story

Read more

Custom parts and enclosures

Bottom stl file

OpenSCAD design file for enclosure

Main body stl file

Schematics

Schematic

PCB Top Layer

PCB Bottom Layer

PCB All Layers

Eagle Schematic

Eagle PCB

Github project repository

Code

Happy Sad Buttons Firmware

Arduino
//STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
SYSTEM_MODE(SEMI_AUTOMATIC);

#define HappyButtonPin D3
#define HappyLedPin D2

#define SadButtonPin A0
#define SadLedPin A1

#define BatteryMonitorEnablePin A4
#define BatteryMonitorAnalogPin A5

bool happyPressed = false;
uint16_t happyPressedCount;
bool sadPressed = false;
uint16_t sadPressedCount;
bool anyButtonPressed = false;

// Increase this value to keep the Photon
// awake longer (helpful for debugging)
int sleepAfterCounts = 2;

int counter = 0;
bool hasBme = false;

void setup() {
    pinMode(D7, OUTPUT);
    
    setupButtons();
    
    setupBatteryMonitor();

    setupBmeSensor();
    
    readCounts();
    
    hasBme = false;
}

void setupButtons() {
    pinMode(HappyButtonPin, INPUT);
    pinMode(HappyLedPin, OUTPUT);
    digitalWrite(HappyLedPin, HIGH);
    
    pinMode(SadButtonPin, INPUT);
    pinMode(SadLedPin, OUTPUT);
    digitalWrite(SadLedPin, HIGH);
}

void setupBatteryMonitor() {
    pinMode(BatteryMonitorAnalogPin, INPUT);
    pinMode(BatteryMonitorEnablePin, OUTPUT);
    digitalWrite(BatteryMonitorEnablePin, HIGH);    
}

void setupBmeSensor() {
    // Not implemented yet!
    hasBme = false;
}

void checkButton(int buttonPin, int buttonLedPin, int happyIncrement, int sadIncrement, String message) {
    
    if (digitalRead(buttonPin)) {
        
        // Debug helper.
        digitalWrite(D7, HIGH);
        delay(30);
        
        // Debounce to ensure the button is actually pressed.
        if (digitalRead(buttonPin)) {
            anyButtonPressed = true;
            
            // Keep awake for a bit longer to help with 
            // software updates rather than having to get to the device.
            sleepAfterCounts = 60;
            
            // Light the LED to show it's being updated.
            digitalWrite(buttonLedPin, LOW);
            
            // Store & Publish
            happyPressedCount+=happyIncrement;
            sadPressedCount+=sadIncrement;
            storeCounts();
            publish(happyIncrement, sadIncrement, message);
        
            // wait for the button to be released to save
            // repeat messages.
            while(digitalRead(buttonPin)) {
                delay(200);
                digitalWrite(D7, LOW);
                delay(200);
                digitalWrite(D7, HIGH);
            }
        }
      
        // Turn off the LEDs.  
        digitalWrite(D7, LOW);
        digitalWrite(buttonLedPin, HIGH);
    } 
}

void loop() {

    checkButton(HappyButtonPin, HappyLedPin, 1, 0, "Happy button pressed!");
    checkButton(SadButtonPin, SadLedPin, 0, 1, "Sad button pressed!");
    
    if (counter == 0 && !anyButtonPressed) {
        publish(0, 0, "Environental check...");
    }

    delay(1000);

    if (counter > sleepAfterCounts) {
        // Deep sleep for a couple of hours
        // to help preserve battery life.
        System.sleep(SLEEP_MODE_DEEP, 120 * 60);
    }
    
    counter++;
}

float readBatteryLevel() {
    digitalWrite(BatteryMonitorEnablePin, HIGH);
    int adcBits = analogRead(BatteryMonitorAnalogPin);
    // HACK: return to digital input to protect from 5V
    // which will happen when Q1 is disabled and batt monitor pin 
    // becomes connected to the battery/USB input.
    pinMode(BatteryMonitorAnalogPin, INPUT);
    //digitalWrite(BatteryMonitorEnablePin, LOW);
    digitalRead(BatteryMonitorAnalogPin);
    
    float batteryMilliVolts = ((adcBits * 0.8) * 2);
    
    return batteryMilliVolts;
}

void readCounts() {
    uint8_t version;
    EEPROM.get(0, version);
    
    if(version == 0xFF) {
        // EEPROM not used, initialize from blank values.
        happyPressedCount = 0;
        sadPressedCount = 0;
        storeCounts();
        return;
    }
    
    EEPROM.get(1, happyPressedCount);
    EEPROM.get(3, sadPressedCount);
}

void storeCounts() {
    // Address 0 for eeprom data version
    EEPROM.put(0, 1);
    // 16 but ints.
    EEPROM.put(1, happyPressedCount);
    EEPROM.put(3, sadPressedCount);
}

int resetCounts(String args) {
    happyPressedCount = 0;
    sadPressedCount = 0;
    storeCounts();
}

void publish(int happy, int sad, String message) {
    
    float batteryMilliVolts = readBatteryLevel();
    
    Particle.connect();
    delay(1000);
    
    Particle.publish("status", message, 120, PRIVATE);
    delay(1000);
    
    String json = "{'SadPressedCount': " + String(sadPressedCount);
    json+= ", 'HappyPressedCount': " + String(happyPressedCount);
    json+= ", 'Happy': " + String(happy);
    json+= ", 'Sad': " + String(sad);
    json+= ", 'Battery_mv': " + String(batteryMilliVolts);
    json+= ", 'Version': '0.6' ";
    json+= "}";
    // TODO: BME680 details as well if available.
    if (hasBme) {
        //json+= ", 't': " + String(temperature);
        //json+= ", 'h': " + String(humidity);
        //json+= ", 'voc': " + String(vod);
    }
    
    Particle.publish("json",  json, 120, PRIVATE);
    delay(1000);
    
    counter = 0;
}

Credits

Stephen Harrison

Stephen Harrison

18 projects • 51 followers
Founder of Tinamous.com, software developer, hardware tinkerer, dyslexic. @TinamousSteve

Comments