Last active
August 24, 2019 15:38
-
-
Save snipsnipsnip/168284 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Meeting Cost Clock | |
// 12 Aug 2009 Keijiro Takahashi | |
/* standard definition */ | |
#include <iostream> | |
namespace Arduino | |
{ | |
typedef unsigned char uint8_t; | |
typedef signed char int8_t; | |
typedef unsigned int uint32_t; | |
typedef signed int int32_t; | |
typedef signed int int16_t; | |
typedef bool boolean; | |
using namespace std; | |
namespace Pin | |
{ | |
const bool LOW = false; | |
const bool HIGH = true; | |
const bool INPUT = false; | |
const bool OUTPUT = true; | |
typedef uint8_t Pin; | |
typedef bool Output; | |
typedef bool IO; | |
void pinMode(Pin pin, IO bit) | |
{ | |
cout << "pinMode(" << (int)pin << ", " << (bit ? "OUT" : "IN") << ")" << endl; | |
} | |
void digitalWrite(Pin pin, Output bit) | |
{ | |
cout << "digitalWrite(" << (int)pin << ", " << bit << ")" << endl; | |
} | |
uint8_t digitalRead(int8_t pin) | |
{ | |
cout << "digitalRead(" << (int)pin << ")" << endl; | |
return 0; | |
} | |
} | |
namespace Clock | |
{ | |
void delay(double duration) | |
{ | |
cout << "delay(" << duration << ")" << endl; | |
} | |
uint32_t millis() | |
{ | |
cout << "millis()" << endl; | |
return 0; | |
} | |
} | |
namespace MsTimer2 | |
{ | |
typedef void (*TimerCallback)(); | |
void set(uint8_t interval, TimerCallback callback) | |
{ | |
cout << "set(" << interval << "," << showbase << hex << callback << ")" << dec << endl; | |
} | |
void start() | |
{ | |
cout << "start()" << endl; | |
} | |
} | |
} | |
using namespace Arduino; | |
using namespace Pin; | |
using namespace Clock; | |
using namespace MsTimer2; | |
// Configuration class - You can modify this. | |
namespace Config | |
{ | |
// Average hourly wage of your company | |
const int kHourlyWage = 5000; | |
// Default number of attendee of meetings | |
const int kDefaultAttendee = 5; | |
} | |
// Used to generate a bit pattern with a given character. | |
// This is also used for character animation. | |
namespace LEDChar | |
{ | |
// * LED bit assignment | |
// 7 6 5 4 3 2 1 0 | |
// digit 1 - F A B P C G D E | |
// digit 2 - B A G F E D C P | |
// digit 3 - F A G B P C D E | |
// Spacial characters | |
const uint8_t kCharBlank = 10; | |
const uint8_t kCharUscore = 11; | |
// Attribute flgas | |
const uint8_t kAttrDP = 0x10; // Decimal point | |
const uint8_t kAttrBlink = 0x20; | |
uint8_t flasher_; // Uninitialized variable but no problem | |
uint8_t getBits(int8_t pos, uint8_t ch) | |
{ | |
// Blinking | |
if ((ch & kAttrBlink) && (flasher_ & 1)) return 0; | |
// Character | |
const uint8_t charPatterns[3][16] = | |
{ | |
{0xeb, 0x28, 0x67, 0x6e, 0xac, 0xce, 0xcf, 0xe8, 0xef, 0xee, 0, 2}, | |
{0xde, 0x82, 0xec, 0xe6, 0xb2, 0x76, 0x7e, 0xd2, 0xfe, 0xf6, 0, 4}, | |
{0xd7, 0x14, 0x73, 0x76, 0xb4, 0xe6, 0xe7, 0xd4, 0xf7, 0xf6, 0, 2} | |
}; | |
uint8_t bits = charPatterns[pos][ch & 0xf]; | |
// Decimal point | |
if (ch & kAttrDP) | |
{ | |
const uint8_t dpPatterns[3] = {0x10, 0x01, 0x08}; | |
bits |= dpPatterns[pos]; | |
} | |
return bits; | |
} | |
void onTick() | |
{ | |
flasher_++; | |
} | |
} | |
// Used to controls the display via digital pins. | |
// This converts numerical values into digit characters. | |
namespace Display | |
{ | |
const int8_t kCathodePin = 2; // 1st cathode pin | |
const int8_t kAnodePin = 10; // 1st anode pin | |
uint8_t digits_[3]; // Digit buffer | |
int8_t refresher_; // Refresh counter | |
void initialize() | |
{ | |
digits_[0] = digits_[1] = digits_[2] = 0; | |
refresher_ = 0; | |
for (int8_t i = 0; i < 8; ++i) digitalWrite(kCathodePin + i, LOW); | |
for (int8_t i = 0; i < 3; ++i) digitalWrite(kAnodePin + i, HIGH); | |
} | |
// Set the digits individually | |
void setDigits(uint8_t digit1, uint8_t digit2, uint8_t digit3) | |
{ | |
digits_[0] = digit1; | |
digits_[1] = digit2; | |
digits_[2] = digit3; | |
} | |
// Show a numerical value on the display | |
void showNumber(int16_t num, uint8_t attr = 0) | |
{ | |
if (num < 10) | |
{ | |
setDigits(attr | (num % 10), | |
LEDChar::kCharBlank, | |
LEDChar::kCharBlank); | |
} | |
else if (num < 100) | |
{ | |
setDigits(attr | (num % 10), | |
attr | (num % 100) / 10, | |
LEDChar::kCharBlank); | |
} | |
else if (num < 1000) | |
{ | |
setDigits(attr | (num % 10), | |
attr | (num % 100 / 10), | |
attr | (num % 1000 / 100)); | |
} | |
else | |
{ | |
uint8_t ch = 9 | LEDChar::kAttrBlink; | |
setDigits(ch, ch, ch); | |
} | |
} | |
// Show an amount of money on the display | |
void showBill(int32_t num) | |
{ | |
if (num < 100L) | |
{ | |
setDigits(num % 10, | |
num % 100 / 10, | |
LEDChar::kCharUscore); | |
} | |
else if (num < 10L * 10000) | |
{ | |
int16_t temp = num / 100; | |
setDigits(temp % 10, | |
temp % 100 / 10, | |
(temp % 1000 / 100) | LEDChar::kAttrDP); | |
} | |
else if (num < 100L * 10000) | |
{ | |
int16_t temp = num / 1000; | |
setDigits(temp % 10, | |
(temp % 100 / 10) | LEDChar::kAttrDP, | |
temp % 1000 / 100); | |
} | |
else if (num < 1000L * 10000) | |
{ | |
int16_t temp = num / 10000; | |
setDigits(temp % 10, | |
temp % 100 / 10, | |
temp % 1000 / 100); | |
} | |
else | |
{ | |
uint8_t ch = 9 | LEDChar::kAttrBlink; | |
setDigits(ch, ch, ch); | |
} | |
} | |
void onRefresh() | |
{ | |
// turn off the current digit | |
pinMode(kAnodePin + refresher_, INPUT); | |
// advance the refresher | |
refresher_ = (refresher_ == 2) ? 0 : refresher_ + 1; | |
// generate a bit pattern for the current digit | |
uint8_t pattern = LEDChar::getBits(refresher_, digits_[refresher_]); | |
// switch the LEDs | |
for (uint8_t i = 0, bit = 1; i < 8; ++i, bit <<= 1) | |
{ | |
pinMode(kCathodePin + i, (pattern & bit) ? OUTPUT : INPUT); | |
} | |
// turn on the current digit | |
pinMode(kAnodePin + refresher_, OUTPUT); | |
// minimum wait | |
delay(1); | |
} | |
} | |
// Used to sum up the cost of meeting. | |
namespace CostCounter | |
{ | |
int8_t numAttendee_; // Number of attendee of the meeting | |
// The variables below are stored as fixed-point value (8-bit fractional) | |
int32_t fpCostPerSec_; // Cost per second | |
int32_t fpTotalCost_; // Total cost | |
void recalcCostPerSec(); | |
void reset(); | |
void initialize() | |
{ | |
numAttendee_ = Config::kDefaultAttendee; | |
recalcCostPerSec(); | |
reset(); | |
} | |
void reset() | |
{ | |
fpTotalCost_ = 0; | |
} | |
void recalcCostPerSec() | |
{ | |
fpCostPerSec_ = 0x100L * Config::kHourlyWage * numAttendee_ / (60 * 60); | |
} | |
int32_t getTotalCost() | |
{ | |
return fpTotalCost_ >> 8; | |
} | |
void incAttendee() | |
{ | |
numAttendee_++; | |
recalcCostPerSec(); | |
} | |
void decAttendee() | |
{ | |
if (numAttendee_ > 0) | |
{ | |
numAttendee_--; | |
recalcCostPerSec(); | |
} | |
} | |
void advanceSecond() | |
{ | |
fpTotalCost_ += fpCostPerSec_; | |
} | |
} | |
// Used to handle timer interrupts and make heartbeat. | |
// Interruption period is set to 0.1 sec for blink animation. | |
namespace Timer | |
{ | |
const int8_t kClockDivider = 10; | |
const int8_t kHeartbeatPin = 13; | |
boolean running_; | |
int8_t prescaler_; | |
void onTick(); | |
void initialize() | |
{ | |
running_ = false; | |
prescaler_ = 0; | |
// Prepare the heartbeat | |
pinMode(kHeartbeatPin, OUTPUT); | |
// Boot up the timer | |
MsTimer2::set(1000 / kClockDivider, onTick); | |
MsTimer2::start(); | |
} | |
void start() | |
{ | |
running_ = true; | |
// The first heartbeat | |
digitalWrite(kHeartbeatPin, HIGH); | |
} | |
void stop() | |
{ | |
running_ = false; | |
// Cancel prescaling | |
prescaler_ = 0; | |
} | |
void onTick() | |
{ | |
LEDChar::onTick(); | |
if (prescaler_ == kClockDivider - 1) | |
{ | |
// One second elapsed | |
CostCounter::advanceSecond(); | |
digitalWrite(kHeartbeatPin, HIGH); | |
prescaler_ = 0; | |
} | |
else | |
{ | |
digitalWrite(kHeartbeatPin, LOW); | |
if (running_) prescaler_++; | |
} | |
} | |
} | |
// Used to handle start/stop button events. | |
namespace StartStopHandler | |
{ | |
const int8_t kButtonPin = 16; | |
const int32_t kHoldTime = 1500; // To detect holding-downs | |
int32_t timeButtonDown_; // Time when the button was pressed down | |
boolean running_; // True while the counter is running | |
void initialize() | |
{ | |
timeButtonDown_ = 0; | |
running_ = false; | |
digitalWrite(kButtonPin, HIGH); | |
} | |
void update() | |
{ | |
if (digitalRead(kButtonPin) == LOW) | |
{ | |
if (timeButtonDown_ == 0) | |
{ | |
// Start pressing | |
timeButtonDown_ = millis(); | |
} | |
else if (millis() - timeButtonDown_ >= kHoldTime) | |
{ | |
// Held down; stop and reset | |
if (running_) | |
{ | |
Timer::stop(); | |
running_ = false; | |
} | |
CostCounter::reset(); | |
} | |
} | |
else if (timeButtonDown_ > 0) | |
{ | |
// Switch the timer if short pressing | |
if (millis() - timeButtonDown_ < kHoldTime) | |
{ | |
if (running_) { Timer::stop(); } else { Timer::start(); } | |
running_ = !running_; | |
} | |
timeButtonDown_ = 0; | |
} | |
} | |
} | |
// Used to handle inc/dec button events. | |
namespace IncDecHandler | |
{ | |
const int8_t kDecButtonPin = 14; | |
const int8_t kIncButtonPin = 15; | |
const int32_t kDisplayDuration = 1500; // Duration of attendee display | |
boolean prevDecButton_; // Previous state of buttons | |
boolean prevIncButton_; | |
int32_t timeDisplay_; // Time when attendee display begins | |
void initialize() | |
{ | |
prevDecButton_ = false; | |
prevIncButton_ = false; | |
timeDisplay_ = 0; | |
digitalWrite(kDecButtonPin, HIGH); | |
digitalWrite(kIncButtonPin, HIGH); | |
} | |
boolean isAttendeeDisplayActive() | |
{ | |
return timeDisplay_ > 0 && millis() - timeDisplay_ < kDisplayDuration; | |
} | |
void update() | |
{ | |
bool decButton = (digitalRead(kDecButtonPin) == LOW); | |
bool incButton = (digitalRead(kIncButtonPin) == LOW); | |
if (decButton && !prevDecButton_) | |
{ | |
// increment | |
CostCounter::incAttendee(); | |
timeDisplay_ = millis(); | |
} | |
else if (incButton && !prevIncButton_) | |
{ | |
// decrement | |
CostCounter::decAttendee(); | |
timeDisplay_ = millis(); | |
} | |
if (timeDisplay_ > 0 && millis() - timeDisplay_ >= kDisplayDuration) | |
{ | |
// Terminate attendee display | |
timeDisplay_ = 0; | |
} | |
prevDecButton_ = decButton; | |
prevIncButton_ = incButton; | |
} | |
} | |
void setup() | |
{ | |
Display::initialize(); | |
CostCounter::initialize(); | |
Timer::initialize(); | |
StartStopHandler::initialize(); | |
IncDecHandler::initialize(); | |
} | |
void loop() | |
{ | |
StartStopHandler::update(); | |
IncDecHandler::update(); | |
if (IncDecHandler::isAttendeeDisplayActive()) | |
{ | |
Display::showNumber(CostCounter::numAttendee_, LEDChar::kAttrBlink); | |
} | |
else | |
{ | |
Display::showBill(CostCounter::getTotalCost()); | |
} | |
Display::onRefresh(); | |
} | |
int main() | |
{ | |
setup(); | |
for (;;) { loop(); } | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment