Hello,
I've built myself an electrified coffee grinder. The intended operation is as follows -
No on/off switch, just sleep. Wake up with one button click, another one click for grinding a predetermined time, go back to sleep. Triple click while awake - enter "programming" mode where you hold button for long enough to grind double (for increased accuracy) the intended amount of coffe, after releasing the button new grind time is saved to EEPROM. I got everything to work, but... If I wake up the arduino and don't do anything else, it'll never go back to sleep. I tried btn.reset at wakeup, but that resulted in going back to sleep immediately.
Will also take suggestions as to how to unkludge the grind(); function - the workaround for lag caused by I2C transmission to display (progress bar) is pretty ugly. And of course any other comments about the quality of the code are most welcome.
Thank you!
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <avr/sleep.h>
#include <OneButton.h>
#include <EEPROM.h>
Adafruit_SSD1306 display(4); //default: sda - A4 scl - a5
// 'coffee2253', 32x32px
const unsigned char epd_bitmap_coffee [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x12, 0x40, 0x00,
0x00, 0x12, 0x40, 0x00, 0x00, 0x12, 0x40, 0x00, 0x00, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xe0, 0x04, 0x00, 0x03, 0x30, 0x04, 0x00, 0x02, 0x10,
0x04, 0x00, 0x02, 0x10, 0x04, 0x00, 0x02, 0x10, 0x02, 0x00, 0x02, 0x60, 0x03, 0xff, 0xff, 0xc0,
0x01, 0xff, 0xfc, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x7f, 0xf0, 0x00,
0x00, 0x3f, 0xe0, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
OneButton btn = OneButton(
2, // Input pin for the button
true, // Button is active LOW
true // Enable internal pull-up resistor
);
volatile long grindT=8000;
int eeAddress = 0;
void setup() {
Serial.begin(9600);
Wire.setClock(400000L);
pinMode(2, INPUT); //button pin
digitalWrite(2, HIGH); //button pullup
pinMode(3, OUTPUT); //mosfet out
digitalWrite(3, LOW); //out reset to 0
EEPROM.get(eeAddress, grindT);
display.begin(SSD1306_SWITCHCAPVCC,0x3C);
display.setRotation(1);
display.setTextColor(WHITE);
display.setTextSize(1);
display.setTextWrap(true);
display.clearDisplay();
display.drawBitmap(0, 48, epd_bitmap_coffee, 32, 32, WHITE);
display.display();
delay(1000);
attachInterrupt(digitalPinToInterrupt(2), checkTics, FALLING);
btn.setClickMs(400); // def400 msec Timeout used to distinguish single clicks from double clicks.
btn.setPressMs(500); // def800 msec Duration to hold a button to trigger a long press.
btn.setIdleMs(15000);
btn.attachClick(grind);
btn.attachMultiClick(program);
btn.attachIdle(sleep);
}
void loop() {
btn.tick();
}
void null() {
}
void checkTics() {
btn.tick();
}
void grind() {
btn.attachClick(null);
display.clearDisplay();
display.drawBitmap(0, 0, epd_bitmap_coffee, 32, 32, WHITE);
display.drawRect(0, 33, 32, 95, WHITE);
display.display();
int d=12; //n of steps of the progress bar
int i=0;
int grindTfraction=grindT/d;
unsigned long timerT = 0UL;
unsigned long timerTmax = 0UL;
digitalWrite(3, HIGH);
timerT = millis ();
timerTmax = timerT+grindT;
for (i=0; i<=d; i++) {
display.fillRect(0, 32, 32, i*(96/d), WHITE);
delay(grindTfraction-50); //arbitrary delay reduction to amount for display transmission time
display.display();
if (timerT >= timerTmax) { //turning it off if we still exceed the time too far
digitalWrite(3, LOW);
}
}
digitalWrite(3, LOW);
delay(3000);
display.clearDisplay();
display.drawBitmap(0, 48, epd_bitmap_coffee, 32, 32, WHITE);
display.display();
btn.attachClick(grind);
}
void program() {
display.clearDisplay();
display.display();
display.setCursor(2,2);
display.println("Long press to grind 2x dose of coffe");
display.display();
btn.reset();
btn.attachLongPressStart(startGrind);
display.println("Current time:");
display.println(grindT);
display.display();
}
void startGrind() {
digitalWrite(3, HIGH);
btn.attachLongPressStop(endGrind);
}
void endGrind() {
digitalWrite(3, LOW);
unsigned long doublegrindT = btn.getPressedMs();
grindT = doublegrindT >> 1; // binary divide by 2
EEPROM.put(eeAddress, grindT);
display.clearDisplay();
display.println("New time:");
display.println(grindT);
display.display();
delay(3000);
btn.attachLongPressStart(null);
display.clearDisplay();
display.drawBitmap(0, 48, epd_bitmap_coffee, 32, 32, WHITE);
display.display();
}
void sleep() {
display.clearDisplay();
display.setCursor(0,0);
display.println("falling asleep");
display.display();
delay(3000);
display.clearDisplay();
display.display();
pinMode(3, INPUT); //mosfet out switch off
noInterrupts ();
ADCSRA = 0; // disable ADC
attachInterrupt(0, wake, LOW);
EIFR = bit (INTF0); // clear flag for interrupt 0
// turn off brown-out enable in software
// BODS must be set to one and BODSE must be set to zero within four clock cycles
MCUCR = bit (BODS) | bit (BODSE);
// The BODS bit is automatically cleared after three clock cycles
MCUCR = bit (BODS);
// We are guaranteed that the sleep_cpu call will be done
// as the processor executes the next instruction after
// interrupts are turned on.
interrupts ();
sleep_mode();
}
void wake() {
sleep_disable();
noInterrupts();
attachInterrupt(digitalPinToInterrupt(2), checkTics, FALLING);
interrupts();
display.clearDisplay();
display.setCursor(0,0);
display.println("waking up");
display.display();
delay(2000);
display.clearDisplay();
display.drawBitmap(0, 48, epd_bitmap_coffee, 32, 32, WHITE);
display.display();
pinMode(3, OUTPUT); //mosfet out reactivate
}
3 posts - 2 participants