element14 Presents | Bald Engineer: James Lewis' VCP Profile | Project Videos
Until cryptocurrency replaces money, we have to deal with coins. Instead of just collecting them in a jar, this electronic coin bank lets you know how much you have. The simple part is the count counter module which detects up to 4 coin types. The tricky part is making it battery powered. Thanks to capacitive sensing technology, this coin bank can run off a single charge for months. |
Capacitive Coin Bank
Product Name | Manufacturer | Quantity | Product Link |
---|---|---|---|
MSP-CAPT-FR2633 Evaluation Board CAPTIVATE Capacitive Touch Technology MCU | Texas Instruments | 1 | Buy Now |
Perma-Proto Breadboard Half Size | Adafruit | 1 | NA |
Coin Acceptor | Adafruit | 1 | NA |
2465 - Development Board, PowerBoost 1000. LiPo Battery Charger, 5V, 1A, USB Boost | Adafruit | 1 | Buy Now |
1578 - Rechargeable Battery, 3.7 V, Lithium Polymer, 500 mAh | Adafruit | 1 | Buy Now |
6mm Copper Foil Tape | 3M | 1 | Buy Now |
5v to 12v Boost
Product Name | Manufacturer | Quantity | Product Link |
---|---|---|---|
C0805C106K4PACTU - Multilayer Ceramic Capacitor, C Series, 10 - F, - 10%, X5R, 16 V, 0805 [2012 Metric] | KEMET | 1 | Buy Now |
MBR0540 - Schottky Rectifier, 40 V, 500 mA, Single, SOD-123, 2 Pins, 620 mV | ON | 1 | Buy Now |
4.7uH 0805 Inductor 600mA | Taiyo-Yuden | 1 | Buy Now |
DC-DC Boost, SOT-23-5, 200mA out | Microchip | 1 | Buy Now |
Pyramiduino
Product Name | Manufacturer | Quantity | Product Link |
---|---|---|---|
100nF 0805 Ceramic 16V X7R | KEMET | 1 | Buy Now |
10nF 0805 Ceramic 16V X7R | KEMET | 1 | Buy Now |
22pF 0805 Ceramic 50V C0G | KEMET | 2 | Buy Now |
1uF 0805 Ceramic 16V X7R | KEMET | 1 | Buy Now |
ATmega328 QFN (Or MLF) Package | Microchip | 1 | Buy Now |
LDO 3v3 Regulator, SOT-23-5 | Microchip | 1 | Buy Now |
Crystal 16M FA-238 | EPSON | 1 | Buy Now |
Also Featured:
Product Name | Manufacturer | Quantity | Product Link |
---|---|---|---|
RTM3K-104 - Oscilloscope, RTM3000 Series, 4 Channel, 1 GHz, 2.5 GSPS, 40 Mpts, 350 ps | Rohde & Schwartz | 1 | Buy Now |
Touch Tutorial: Capacitive Touch Tutorial from James Lewis - Bald Engineer
MSP430FR2633 MCU Development Kit: Capacitive touch evaluation and rapid prototyping for MSP430FR2633 microcontroller MCU Development Kit
Autodesk Fusion 360: Autodesk Fusion 360
12 V Boost | Breadboard Circuits |
---|---|
Arduino Code:
// Pins #include <Arduino.h> const byte coinSig = A0; const byte hvSolenoid = A1; // for turning on solenoid/coin counter const byte battControl = 4; const byte battRead = A3; const byte resetButton = 3; bool previousCoinSignal = false; const float coinValue = 0.05; float bankValue = 0.00; unsigned long bankCount = 0; const unsigned long pulseTimeOut = 75; unsigned long armedForCount = 0; unsigned long previousUpdateMillis = 0; unsigned long updateInterval = 110; bool printOnceFlag = false; unsigned long previousEEPROMwrite = 0; // delays writing to EEPROM unsigned long updateEEPROMinterval = 1000; //float EEPROMbankValue = 0.00; unsigned long EEPROMbankCount; unsigned long previousBatteryMillis = 0; const unsigned long batteryInterval = 1000; const float adcResolution = 5.00/1023.0; bool enableCounter = true;
#include <Arduino.h> #include <EEPROM.h> #include <Wire.h> #include <U8g2lib.h> #include "coin-slot-arduino.h" // u8g2 constructor U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R2, /* reset=*/ U8X8_PIN_NONE); void loadEEPROM() { EEPROM.get(0, bankCount); EEPROMbankCount = bankCount; } byte getValueWidth(float displayValue) { // average character width for u8g2_font_logisoso24_tn is 14 const byte charWidth = u8g2.getMaxCharWidth(); byte theWidth = 4 * charWidth; //1.00 if (displayValue >= 10.00) theWidth = 5 * charWidth; if (displayValue >= 100.00) theWidth = 6 * charWidth; //1.00 if (displayValue >= 1000.00) theWidth = 7 * charWidth; //1.00 return ((u8g2.getDisplayWidth()-theWidth)/2); } void printOLED() { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tn); // choose a suitable font byte centerHeight = (u8g2.getDisplayHeight()/2) + (u8g2.getMaxCharHeight()/2); u8g2.setCursor(getValueWidth(bankValue),centerHeight); u8g2.print(bankValue); u8g2.sendBuffer(); // transfer internal memory to the display } void printBank() { bankValue = bankCount * coinValue; printOLED(); Serial.print(F("Bank Value: ")); Serial.println(bankValue); } void printPulses() { Serial.print(F("Pulse coutn: ")); Serial.println(bankCount); } void handleSerial() { char incomingChar = Serial.read(); switch(incomingChar) { case '!': Serial.println(F("Clearing...")); bankCount = 0; bankValue = bankCount * coinValue; printBank(); break; case '.': printPulses(); printBank(); break; } } void checkBattery() { // hceck battery voltage digitalWrite(battControl, HIGH); delay(10); int battReading = analogRead(battRead); digitalWrite(battControl, LOW); // Serial.print(F("Battery: ")); // Serial.println(battReading); if (battReading < 700) { // battery is too low, turn off solenoid enableCounter = false; // turn off enableCounter digitalWrite(hvSolenoid, LOW); //print message // Serial.println(F("Low Battery Disable")); u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tr); // choose a suitable font u8g2.drawStr(0, 50, "lo batt"); u8g2.sendBuffer(); // transfer internal memory to the display while(1); } return; } void handleBankReset() { unsigned long resetCounter = millis(); bool waiting = true; while((digitalRead(resetButton) == HIGH) && waiting) { if (millis() - resetCounter < 1000) { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tr); // choose a suitable font u8g2.drawStr(0, 50, "3"); u8g2.sendBuffer(); // transfer internal memory to the display } if ((millis() - resetCounter > 1000) && (millis() - resetCounter <= 2000)) { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tr); // choose a suitable font u8g2.drawStr(0, 50, "2"); u8g2.sendBuffer(); // transfer internal memory to the display } if ((millis() - resetCounter > 2000) && (millis() - resetCounter <= 3000)) { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tr); // choose a suitable font u8g2.drawStr(0, 50, "1"); u8g2.sendBuffer(); // transfer internal memory to the display } if ((millis() - resetCounter > 3000) && (millis() - resetCounter <= 3500)) { u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tr); // choose a suitable font u8g2.drawStr(0, 50, "0"); u8g2.sendBuffer(); // transfer internal memory to the display } if ((millis() - resetCounter > 3500) && (millis() - resetCounter <= 4000)) { bankCount = 0; } if ((millis() - resetCounter > 4000)) { waiting = false; } } printBank(); } void setup() { pinMode(battControl, OUTPUT); // battery transistor digitalWrite(battControl, LOW); u8g2.begin(); checkBattery(); // init coin counter pinMode(hvSolenoid, OUTPUT); //Serial.print(F("Turning on counter...")); digitalWrite(hvSolenoid, HIGH); pinMode(coinSig, INPUT_PULLUP); u8g2.clearBuffer(); // clear the internal memory u8g2.setFont(u8g2_font_logisoso34_tr); // choose a suitable font u8g2.drawStr(0, 50, "Wait"); u8g2.sendBuffer(); // transfer internal memory to the display Serial.begin(115200); //delay(100); // TODO is there a way to tell when coin acceptor is ready while(digitalRead(coinSig)); // add timeout and error. previousCoinSignal = digitalRead(coinSig); //Serial.println(F("done!")); pinMode(resetButton, INPUT); // init screen //pinMode(hvOLED, OUTPUT); loadEEPROM(); //Serial.print(F("After Setup: ")); //Serial.println(freeMemory()); printBank(); } void loop() { handleSerial(); if (digitalRead(resetButton) == HIGH) handleBankReset(); if (millis() - previousBatteryMillis >= batteryInterval) { previousBatteryMillis = millis(); checkBattery(); } byte currentCoinSignal = digitalRead(coinSig); if (currentCoinSignal == HIGH) { previousUpdateMillis = millis(); // print after pulse train is done previousEEPROMwrite = millis(); // don't write until pulse train is over printOnceFlag = true; } // only print after pulses stop if (printOnceFlag && (millis() - previousUpdateMillis >= updateInterval)) { printBank(); printOnceFlag = false; } // is time to update the EEPROM? if (millis() - previousEEPROMwrite >= updateEEPROMinterval) { // reset the clock previousEEPROMwrite = millis(); // only write to EEPROM when a new value if (bankCount != EEPROMbankCount) { EEPROM.put(0,bankCount); // writes a float to EEPROM Serial.println(F("!!! EEPROM WRITE !!!")); EEPROMbankCount = bankCount; } } if (currentCoinSignal != previousCoinSignal) { // Save the state for next iteration previousCoinSignal = currentCoinSignal; if (currentCoinSignal == HIGH) { // we see an edge, but is it a coin pulse? armedForCount = millis(); } else { // short timeout to make sure it was a full pulse if (millis() - armedForCount < pulseTimeOut) { bankCount++; } } } }
Full source Code, Design Files, and Schematics Attached!
Top Comments