#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Fonts/FreeSerif24pt7b.h>
#include "InputDebounce.h"
/**
* Source code:
* https://www.italiantechproject.it/tutorial-arduino/sleep-mode-e-interrupt
*/
#include <avr/sleep.h>
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3c ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define BUTTON_DEBOUNCE_DELAY 20 // [ms]
#define ADC_POT_THRESHOLD 10
#define MS_BEFORE_ASLEEP 20000
#define MS_BEFORE_POFF 5000
#define WAKEUP_PIN 2
unsigned int readPotentiometer() {
return analogRead(A0);
}
const int pin = 7;
const int rstPin = 8;
const long RST_PRESS_TIME = 1000;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
enum EnCoinType {
En1C,
En2C,
En5C,
En10C,
En20C,
En50C,
En1E,
En2E
};
int Coins[] = {0, 0, 0, 0, 0, 0, 0, 0}; // 1C, 2C, 5C, 10C, 20C, 50C, 1E, 2E
unsigned long duration, lastActivity;
int prevAdcIn = readPotentiometer();
const int K = 9810 / 2;
auto totalCount = 0.0f;
long rstBtnPressedTime = 0;
static InputDebounce buttonTest; // not enabled yet, setup has to be called later
unsigned long startUS = 0;
bool pinPresence = false;
float coinDiameter = 0.0f;
float coinType = 0.0f;
char strTotals[16];
void initDisplay() {
Serial.print("Initializing display...");
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
Serial.println("DONE");
// Clear the buffer
display.clearDisplay();
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(SSD1306_WHITE); // Draw white text
display.setCursor(0, 0); // Start at top-left corner
display.println("Insert coin...");
display.display();
}
void setup() {
Serial.begin(9600);
while (!Serial);
initDisplay();
pinMode(pin, INPUT_PULLUP);
pinMode(rstPin, INPUT_PULLUP);
// setup input button (debounced)
buttonTest.setup(rstPin, BUTTON_DEBOUNCE_DELAY, InputDebounce::PIM_INT_PULL_UP_RES);
attachInterrupt(digitalPinToInterrupt(WAKEUP_PIN), wakeupCallback, FALLING);
}
void wakeupCallback() {
}
float getCoinType(float diameter) {
float coinType = 0.0;
if(diameter >= 16 && diameter <= 17) {
coinType = 0.01;
} else if(diameter >= 18 && diameter <= 19) {
coinType = 0.02;
} else if(diameter >= 21 && diameter <= 22) {
coinType = 0.05;
} else if(diameter >= 19 && diameter <= 20) {
coinType = 0.1;
} else if(diameter >= 22 && diameter <= 23) {
coinType = 0.2;
} else if(diameter >= 24 && diameter <= 25) {
coinType = 0.5;
} else if(diameter >= 23 && diameter <= 24) {
coinType = 1.0;
} else if(diameter >= 25 && diameter <= 26) {
coinType = 2.0;
}
return coinType;
}
void showCoinType(float coinType) {
switch((int)(coinType * 100)) {
case 1:
display.println("1C");
break;
case 2:
display.println("2C");
break;
case 5:
display.println("5C");
break;
case 10:
display.println("10C");
break;
case 20:
display.println("20C");
break;
case 50:
display.println("50C");
break;
case 100:
display.println("1E");
break;
case 200:
display.println("2E");
break;
}
}
void drawProgress(long buttonTest_OnTime) {
static const auto TOP_PROGBAR_Y = display.height() - 40, LOW_PROGBAR_Y = display.height() - 42;
display.drawRect(0, TOP_PROGBAR_Y, display.width(), LOW_PROGBAR_Y, SSD1306_WHITE);
auto xMax = map(buttonTest_OnTime, 0, 1000, display.width(), 0);
for(auto i=0; i<xMax; i++) {
display.fillRect(0, TOP_PROGBAR_Y, i, LOW_PROGBAR_Y, SSD1306_WHITE);
}
}
void incCoins(float coinType) {
switch((int)(coinType * 100)){
case 1:
Coins[En1C]++;
break;
case 2:
Coins[En2C]++;
break;
case 5:
Coins[En2C]++;
break;
case 10:
Coins[En2C]++;
break;
case 20:
Coins[En2C]++;
break;
case 50:
Coins[En2C]++;
break;
case 100:
Coins[En1E]++;
break;
case 200:
Coins[En2E]++;
break;
}
}
void checkCoinInput() {
//duration = pulseInLong(pin, LOW, 300000);
//duration = random(20000, 1000000);
// presence
if(!pinPresence && !digitalRead(pin)) {
pinPresence = true;
startUS = micros();
}
// end presence
if(pinPresence && digitalRead(pin)) {
pinPresence = false;
duration = micros() - startUS;
auto seconds = (float)duration/(float)1000000.0f;
auto power = seconds * seconds;
coinDiameter = K * power;
coinType = getCoinType(coinDiameter);
if(coinType > 0.0f) {
totalCount += coinType;
incCoins(coinType);
}
}
if(pinPresence) {
displayON();
lastActivity = millis();
}
display.setCursor(0, 0); // Start at top-left corner
display.print("Duration: "); display.print((float)(duration)/1000.0f); display.println(" ms");
display.print("Diameter: "); display.print(coinDiameter); display.println(" mm");
if(coinType > 0.0f) {
showCoinType(coinType);
}
}
void checkButtonState() {
static unsigned int buttonTest_StateOnCount = 0; // to handle new pressed state, and still pressed state
static unsigned int buttonTest_OnTimeLast = 0; // to handle new released state, and remember last on-time (button pressed)
unsigned long now = millis();
// poll button state, return continuous on-time [ms] if pressed (debounced)
unsigned int buttonTest_OnTime = buttonTest.process(now);
// handle input button
if(buttonTest_OnTime) {
// save current on-time (button pressed), to be used when released
buttonTest_OnTimeLast = buttonTest_OnTime;
// check for state change
unsigned int count = buttonTest.getStatePressedCount();
if(buttonTest_StateOnCount != count) {
buttonTest_StateOnCount = count;
// handle pressed state
Serial.print("HIGH");
}
else {
// handle still pressed state
Serial.print("HIGH still pressed");
if(buttonTest_OnTime > RST_PRESS_TIME) {
totalCount = 0.0f;
buttonTest_OnTime = 0;
delay(500);
}
}
Serial.print(" (");
Serial.print(buttonTest_OnTime);
Serial.println("ms)");
displayON();
drawProgress(buttonTest_OnTime);
lastActivity = millis();
}
else {
if(buttonTest_OnTimeLast) {
// handle released state
Serial.print("LOW (last on-time: HIGH ");
Serial.print(buttonTest_OnTimeLast);
Serial.println("ms)");
buttonTest_OnTimeLast = 0; // reset, do not handle this released state again
}
}
}
void showTotals() {
/*
display.setCursor(0, SCREEN_HEIGHT - 40);
display.println(" 1C| 2C| 5C|10C");
sprintf(strTotals, "%3d|%3d|%3d|%3d", Coins[En1C], Coins[En2C], Coins[En5C], Coins[En10C]);
display.println(strTotals);
display.println("20C|50C| 1E| 2E");
sprintf(strTotals, "%3d|%3d|%3d|%3d", Coins[En20C], Coins[En50C], Coins[En1E], Coins[En2E]);
display.println(strTotals);
*/
display.setCursor(0, SCREEN_HEIGHT - 10);
display.print("Tot: "); display.print(totalCount); display.println(" Euro");
}
void checkPotentiometer() {
static const auto MIN_VAL = 15.0f;
static const auto MAX_VAL = 30.0f;
static const auto MIN_ADC = 0;
static const auto MAX_ADC = 512;
auto adcIn = readPotentiometer();
if(abs((int)prevAdcIn - (int)adcIn) > ADC_POT_THRESHOLD) {
displayON();
prevAdcIn = adcIn;
lastActivity = millis();
auto adcInMap = map(adcIn, MIN_ADC, MAX_ADC, (int)MIN_VAL * 10, (int)MAX_VAL * 10);
coinDiameter = (float)adcInMap / 10.0f;
coinType = getCoinType(coinDiameter);
if(coinType > 0.0f) {
totalCount += coinType;
display.setCursor(0, 0); // Start at top-left corner
showCoinType(coinType);
incCoins(coinType);
display.setCursor(0, SCREEN_HEIGHT - 30);
display.print("adcIn: "); display.println(adcIn);
display.setCursor(0, SCREEN_HEIGHT - 20);
display.print("diameter: "); display.print(coinDiameter); display.println(" mm");
delay(1000);
}
}
}
void displayOFF() {
display.ssd1306_command(SSD1306_DISPLAYOFF);
}
void displayON() {
display.ssd1306_command(SSD1306_DISPLAYON);
}
void checkSleep() {
if(millis() - lastActivity >= MS_BEFORE_POFF) {
displayOFF();
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
delay(200);
sleep_cpu();
sleep_disable();
lastActivity = millis();
}
/*
if(millis() - lastActivity >= MS_BEFORE_ASLEEP) {
displayOFF();
lastActivity = millis();
}*/
}
void loop() {
display.clearDisplay();
//checkCoinInput();
checkButtonState();
checkPotentiometer();
checkSleep();
showTotals();
display.display();
delay(10);
}