@@ 39,6 39,8 @@ LiquidCrystal_I2C lcd(0x27,16,2); // LCD address and size.
const byte irrecvPin = 10; // IR receptor pin.
IRrecv irrecv(irrecvPin); // Initialisation.
decode_results results;
+char message[64]; // Displaying message, will be populated from FLASH memory.
+//#define _DEBUG 0 // Uncomment to get debugging messages via serial port in certain steps.
// State-recording variables
volatile bool motion = false; // Is there any movement that should turn on the device?
@@ 48,7 50,7 @@ bool welcomeScreenState = false; // Is the welcome screen playing?
// Timing variables
-const unsigned short secondsToInactive = 7;
+const unsigned short secondsToInactive = 80;
volatile unsigned long prevActiveMillis = 0;
unsigned long prevToneMillis = 0;
unsigned long prevScreenMillis = 0;
@@ 57,6 59,8 @@ unsigned short screenStep = 300; // Not a byte because it is modified to a value
byte toneCounter = 0;
byte screenCounter = 0;
+byte questionCounter = 0;
+byte qaCounter = 0;
// Remote-command variabes
@@ 64,6 68,31 @@ const unsigned long remoteSwitchScroll = 0x1fe48b7;
const unsigned long remotePause = 0x1fee817;
+// Questions and answers
+char buffer[256]; // Maximum length of any question or answer.
+
+const char q_0[] PROGMEM = "What is the number of electrons going through the normal section of a wire whose current is 0.2 A during 16 s?";
+const char a_00[] PROGMEM = "Answer 1: 10^16";
+const char a_01[] PROGMEM = "Answer 2: 10^19";
+const char a_02[] PROGMEM = "Answer 3: 1.6 * 10^19";
+const char a_03[] PROGMEM = "Answer 4: 2.0 * 10^19";
+
+const char q_1[] PROGMEM = "A particle's trajectory is given by the parametric equations x = t, y = t^2 / 2. What is its curvature radius?";
+const char a_10[] PROGMEM = "Answer 1: (1+t^2)^(3/2)";
+const char a_11[] PROGMEM = "Answer 2: (1+t^3)^(1/2)";
+const char a_12[] PROGMEM = "Answer 3: (1+t)^(3/2)";
+const char a_13[] PROGMEM = "Answer 4: (1+t)^(1/2)";
+
+const char* const qa[] PROGMEM = {q_0, a_00, a_01, a_02, a_03, q_1, a_10, a_11, a_12, a_13};
+
+const byte numberOfQuestions = sizeof(qa)/10;
+
+char* getQA(byte qaNumber) {
+ strcpy_P(buffer, (char*)pgm_read_word(&(qa[qaNumber])));
+ return buffer;
+}
+
+
// Scrolling class
class Scroller {
// Receive a message and scroll it through the screen
@@ 71,28 100,34 @@ class Scroller {
// If scrolling horizontally, charStep is the amount of characters in every
// step.
//
- // TODO: Pause.
// TODO: A kind of manual scrolling, using the remote to move the text.
- // TODO: Change in charStep and speed, saving it for every player.
+ // TODO: Change in charStep and speed, saving it for every player (TODO: multi-player support XD ).
unsigned short counter;
const String spaceFilling = " ";
- String message;
unsigned long prevMillis;
unsigned short updateStep;
byte charStep;
char direct;
public:
+ bool completelyDisplayed;
+ String message;
+
Scroller(String msg, char drc) {
- message = spaceFilling + msg + spaceFilling;
- counter = 0;
+ reset(msg);
charStep = 2;
direct = drc;
if (direct == 'H') updateStep = 400;
if (direct == 'V') updateStep = 1000;
}
+
+ void reset(String msg) {
+ message = spaceFilling + msg + spaceFilling;
+ counter = 0;
+ completelyDisplayed = false;
+ }
- void Update() {
+ void scroll() {
unsigned long currentMillis = millis();
if (direct == 'H') { // Horizontal scrolling.
if (counter == 0) {
@@ 108,6 143,7 @@ class Scroller {
prevMillis = millis();
if (counter >= message.length() - 16 + charStep) {
counter = 0;
+ completelyDisplayed = true;
}
}
}
@@ 129,6 165,7 @@ class Scroller {
prevMillis = millis();
if (counter >= message.length()) {
counter = 0;
+ completelyDisplayed = true;
}
}
}
@@ 150,7 187,7 @@ class Scroller {
void pauseOrResume () {
if (isUpperCase(direct)) {
- direct |= B00100000; // To lower-case.
+ direct |= B00100000; // Change direction char to lower-case variant.
} else {
direct &= ~(B00100000); // To upper-case.
}
@@ 158,18 195,25 @@ class Scroller {
};
-// Interruption functions
+// Interruption-related functions
void detectMotion() {
motion = true;
prevActiveMillis = millis();
}
+void updateActiveMillis() {
+ // Update the volatile variable prevActiveMillis securely
+ detachInterrupt(digitalPinToInterrupt(motionPin));
+ prevActiveMillis = millis();
+ attachInterrupt(digitalPinToInterrupt(motionPin), detectMotion, RISING);
+}
+
// Set-up
-Scroller aMessage("This is quite a long message to show in such a tiny screen. =P", 'H');
+Scroller currentQA(getQA(qaCounter), 'H');
void setup() {
pinMode(motionPin, INPUT);
@@ 177,6 221,9 @@ void setup() {
lcd.init();
lcd.noDisplay();
irrecv.enableIRIn();
+ #ifdef _DEBUG
+ Serial.begin(9600);
+ #endif
}
@@ 247,21 294,41 @@ void loop() {
}
}
}
-
+
// Work only if the screen is on.
if (screenState) {
// If you are not welcoming, do your job.
if (!(welcomeToneState || welcomeScreenState)) {
- aMessage.Update();
+ #ifdef _DEBUG
+ Serial.println(currentQA.message);
+ #endif
+ if (!(currentQA.completelyDisplayed)) {
+ currentQA.scroll();
+ #ifdef _DEBUG
+ Serial.println("scroll");
+ #endif
+ } else {
+ qaCounter++;
+ if (qaCounter >= 5*(questionCounter+1)) {
+ qaCounter = 5*questionCounter;
+ #ifdef _DEBUG
+ Serial.println("qaCounter overflow");
+ #endif
+ }
+ currentQA.reset(getQA(qaCounter));
+ #ifdef _DEBUG
+ Serial.println("QAreset");
+ #endif
+ }
if (irrecv.decode(&results)) {
switch (results.value) {
case remoteSwitchScroll:
- aMessage.switchDirection();
- prevActiveMillis = millis();
+ currentQA.switchDirection();
+ updateActiveMillis();
break;
case remotePause:
- aMessage.pauseOrResume();
- prevActiveMillis = millis();
+ currentQA.pauseOrResume();
+ updateActiveMillis();
break;
}
irrecv.resume();