VFD modular clock firmware

Dependencies:   DipCortex-EEprom RTC flw mbed

Committer:
Backstr?m
Date:
Tue Feb 24 23:01:40 2015 +0900
Revision:
12:dfb422107412
Parent:
11:34b344fdec98
Added tag v1.0.2 for changeset 34b344fdec98

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Backstrom 0:f6e68b4ce169 1 /*
Backstrom 0:f6e68b4ce169 2 * VFD Modular Clock - mbed
Backstrom 0:f6e68b4ce169 3 * (C) 2011-14 Akafugu Corporation
Backstrom 0:f6e68b4ce169 4 *
Backstrom 0:f6e68b4ce169 5 * This program is free software; you can redistribute it and/or modify it under the
Backstrom 0:f6e68b4ce169 6 * terms of the GNU General Public License as published by the Free Software
Backstrom 0:f6e68b4ce169 7 * Foundation; either version 2 of the License, or (at your option) any later
Backstrom 0:f6e68b4ce169 8 * version.
Backstrom 0:f6e68b4ce169 9 *
Backstrom 0:f6e68b4ce169 10 * This program is distributed in the hope that it will be useful, but WITHOUT ANY
Backstrom 0:f6e68b4ce169 11 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
Backstrom 0:f6e68b4ce169 12 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
Backstrom 0:f6e68b4ce169 13 *
Backstrom 0:f6e68b4ce169 14 */
Backstrom 0:f6e68b4ce169 15
Backstrom 0:f6e68b4ce169 16 #include "global.h"
Backstrom 0:f6e68b4ce169 17 #include "mbed.h"
Backstrom 0:f6e68b4ce169 18
Backstrom 0:f6e68b4ce169 19 #include "VFDDisplay.h"
Backstrom 0:f6e68b4ce169 20 #include "IV18Display.h"
Backstrom 0:f6e68b4ce169 21
Backstrom 0:f6e68b4ce169 22 #include "prefs.h"
Backstrom 0:f6e68b4ce169 23 #include "gps.h"
Backstrom 0:f6e68b4ce169 24 #include "menu.h"
Backstrom 0:f6e68b4ce169 25 #include "button.h"
Backstrom 0:f6e68b4ce169 26 #include "rtc.h"
Backstrom 0:f6e68b4ce169 27 #include "ds3231m.h"
Backstrom 0:f6e68b4ce169 28 #include "beep.h"
Backstrom 0:f6e68b4ce169 29 #include "flw.h"
Backstrom 0:f6e68b4ce169 30
Backstrom 0:f6e68b4ce169 31 IV18Display display(PinMap::data, PinMap::clock, PinMap::latch, PinMap::blank);
Backstrom 0:f6e68b4ce169 32
Backstrom 0:f6e68b4ce169 33 Menu menu;
Backstrom 0:f6e68b4ce169 34
Backstrom 0:f6e68b4ce169 35 I2C i2c(PinMap::sda, PinMap::scl);
Backstrom 0:f6e68b4ce169 36 DS3231M rtc(i2c);
Backstrom 0:f6e68b4ce169 37 //RTC rtc;
Backstrom 0:f6e68b4ce169 38
Backstrom 0:f6e68b4ce169 39 DigitalOut led(PinMap::led);
Backstrom 0:f6e68b4ce169 40 Beep piezo(PinMap::piezo);
Backstrom 0:f6e68b4ce169 41
Backstrom 0:f6e68b4ce169 42 FourLetterWord flw(&i2c);
Backstrom 0:f6e68b4ce169 43 char flwWord[5];
Backstrom 0:f6e68b4ce169 44 int flwOffset;
Backstrom 0:f6e68b4ce169 45 int flwOffsetDirection = 1;
Backstrom 0:f6e68b4ce169 46 volatile bool haveEEPROM;
Backstrom 0:f6e68b4ce169 47
Backstrom 0:f6e68b4ce169 48 Ticker blanker;
Backstrom 0:f6e68b4ce169 49 Ticker multiplexer;
Backstrom 0:f6e68b4ce169 50 Ticker button_ticker;
Backstrom 0:f6e68b4ce169 51 Ticker rtc_ticker;
Backstrom 0:f6e68b4ce169 52 Ticker tenth_ticker;
Backstrom 0:f6e68b4ce169 53
Backstrom 0:f6e68b4ce169 54 volatile state_t g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 55 uint32_t gps_last_update = 0xffff;
Backstrom 0:f6e68b4ce169 56
Backstrom 0:f6e68b4ce169 57 uint8_t calculate_segments_7(uint8_t character);
Backstrom 0:f6e68b4ce169 58
Backstrom 0:f6e68b4ce169 59 // Alarm
Backstrom 0:f6e68b4ce169 60 volatile bool g_alarm_on;
Backstrom 0:f6e68b4ce169 61 volatile bool g_alarming;
Backstrom 11:34b344fdec98 62 volatile bool g_updateFLW = false;
Backstrom 0:f6e68b4ce169 63
Backstrom 0:f6e68b4ce169 64 void write_vfd_8bit(uint8_t data);
Backstrom 0:f6e68b4ce169 65 void write_vfd_iv18(uint8_t digit, uint8_t segments);
Backstrom 0:f6e68b4ce169 66 void write_vfd(uint8_t digit, uint8_t segments);
Backstrom 0:f6e68b4ce169 67 void demo_cycle(char* buf);
Backstrom 0:f6e68b4ce169 68
Backstrom 0:f6e68b4ce169 69 void blank_tick()
Backstrom 0:f6e68b4ce169 70 {
Backstrom 0:f6e68b4ce169 71 static uint32_t cnt = 0;
Backstrom 0:f6e68b4ce169 72
Backstrom 0:f6e68b4ce169 73 if ((cnt%10) > (10-display.getBrightness())) {
Backstrom 0:f6e68b4ce169 74 display.blank(false);
Backstrom 0:f6e68b4ce169 75 }
Backstrom 0:f6e68b4ce169 76 else {
Backstrom 0:f6e68b4ce169 77 display.blank(true);
Backstrom 0:f6e68b4ce169 78 }
Backstrom 0:f6e68b4ce169 79
Backstrom 0:f6e68b4ce169 80 cnt++;
Backstrom 0:f6e68b4ce169 81 }
Backstrom 0:f6e68b4ce169 82
Backstrom 0:f6e68b4ce169 83 void multiplex_tick()
Backstrom 0:f6e68b4ce169 84 {
Backstrom 0:f6e68b4ce169 85 display.multiplexTick();
Backstrom 0:f6e68b4ce169 86 }
Backstrom 0:f6e68b4ce169 87
Backstrom 0:f6e68b4ce169 88 void rtc_tick()
Backstrom 0:f6e68b4ce169 89 {
Backstrom 0:f6e68b4ce169 90 if (haveEEPROM)
Backstrom 11:34b344fdec98 91 g_updateFLW = true;
Backstrom 0:f6e68b4ce169 92
Backstrom 0:f6e68b4ce169 93 rtc.tick();
Backstrom 0:f6e68b4ce169 94 }
Backstrom 0:f6e68b4ce169 95
Backstrom 0:f6e68b4ce169 96 void tenth_tick()
Backstrom 0:f6e68b4ce169 97 {
Backstrom 0:f6e68b4ce169 98 rtc.tenth_tick();
Backstrom 0:f6e68b4ce169 99 }
Backstrom 0:f6e68b4ce169 100
Backstrom 0:f6e68b4ce169 101 void counterTest()
Backstrom 0:f6e68b4ce169 102 {
Backstrom 0:f6e68b4ce169 103 display.cls();
Backstrom 0:f6e68b4ce169 104 for (uint16_t i = 0; i < 200; i++) {
Backstrom 0:f6e68b4ce169 105 display.printf("cnt %3d", i);
Backstrom 0:f6e68b4ce169 106 wait(0.01);
Backstrom 0:f6e68b4ce169 107 }
Backstrom 0:f6e68b4ce169 108 }
Backstrom 0:f6e68b4ce169 109
Backstrom 0:f6e68b4ce169 110 void timeAndDateTest()
Backstrom 0:f6e68b4ce169 111 {
Backstrom 0:f6e68b4ce169 112 uint8_t hour = 15;
Backstrom 0:f6e68b4ce169 113 uint8_t min = 59;
Backstrom 0:f6e68b4ce169 114 uint8_t sec = 55;
Backstrom 0:f6e68b4ce169 115
Backstrom 0:f6e68b4ce169 116 display.cls();
Backstrom 0:f6e68b4ce169 117 for (uint8_t i = 0; i < 7; i++) {
Backstrom 0:f6e68b4ce169 118 display.printf("%02d-%02d-%02d", hour, min, sec);
Backstrom 0:f6e68b4ce169 119 sec++;
Backstrom 0:f6e68b4ce169 120 if (sec == 60) { sec = 0; min++; }
Backstrom 0:f6e68b4ce169 121 if (min == 60) { min = 0; hour++; }
Backstrom 0:f6e68b4ce169 122 wait(1.0);
Backstrom 0:f6e68b4ce169 123 }
Backstrom 0:f6e68b4ce169 124
Backstrom 0:f6e68b4ce169 125 display.printf("%02d-%02d-%02d Monday 2014-05-12", hour, min, sec);
Backstrom 0:f6e68b4ce169 126
Backstrom 0:f6e68b4ce169 127 while (!display.scrollFinished()) {
Backstrom 0:f6e68b4ce169 128 display.scroll();
Backstrom 0:f6e68b4ce169 129 wait(0.25);
Backstrom 0:f6e68b4ce169 130 }
Backstrom 0:f6e68b4ce169 131
Backstrom 0:f6e68b4ce169 132 display.resetScroll();
Backstrom 0:f6e68b4ce169 133
Backstrom 0:f6e68b4ce169 134 for (uint8_t i = 0; i < 5; i++) {
Backstrom 0:f6e68b4ce169 135 display.printf("%2d-%02d-%02d", hour, min, sec);
Backstrom 0:f6e68b4ce169 136 sec++;
Backstrom 0:f6e68b4ce169 137 if (sec == 60) { sec = 0; min++; }
Backstrom 0:f6e68b4ce169 138 if (min == 60) { min = 0; hour++; }
Backstrom 0:f6e68b4ce169 139 wait(1.0);
Backstrom 0:f6e68b4ce169 140 }
Backstrom 0:f6e68b4ce169 141
Backstrom 0:f6e68b4ce169 142 }
Backstrom 0:f6e68b4ce169 143
Backstrom 0:f6e68b4ce169 144 int dayOfWeek(int y, int m, int d)
Backstrom 0:f6e68b4ce169 145 {
Backstrom 0:f6e68b4ce169 146 static const int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
Backstrom 0:f6e68b4ce169 147 y -= m < 3;
Backstrom 0:f6e68b4ce169 148 return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
Backstrom 0:f6e68b4ce169 149 }
Backstrom 0:f6e68b4ce169 150
Backstrom 0:f6e68b4ce169 151 void welcomeMessage()
Backstrom 0:f6e68b4ce169 152 {
Backstrom 0:f6e68b4ce169 153 const char* buf = " Akafugu VFD Modular Clock";
Backstrom 0:f6e68b4ce169 154
Backstrom 0:f6e68b4ce169 155 display.cls();
Backstrom 0:f6e68b4ce169 156 display.printf(buf);
Backstrom 0:f6e68b4ce169 157
Backstrom 0:f6e68b4ce169 158 // scroll forward
Backstrom 0:f6e68b4ce169 159 while (!display.scrollFinished()) {
Backstrom 0:f6e68b4ce169 160 display.scroll();
Backstrom 0:f6e68b4ce169 161 led = !led;
Backstrom 0:f6e68b4ce169 162 wait(0.20);
Backstrom 0:f6e68b4ce169 163 }
Backstrom 0:f6e68b4ce169 164
Backstrom 0:f6e68b4ce169 165 display.cls();
Backstrom 0:f6e68b4ce169 166 }
Backstrom 0:f6e68b4ce169 167
Backstrom 0:f6e68b4ce169 168 // helper function for handling time/alarm setting
Backstrom 0:f6e68b4ce169 169 uint8_t handleTimeSetting(uint8_t setting, uint8_t min, uint8_t max,
Backstrom 0:f6e68b4ce169 170 bool backButton, bool nextButton)
Backstrom 0:f6e68b4ce169 171 {
Backstrom 0:f6e68b4ce169 172 if (backButton) {
Backstrom 0:f6e68b4ce169 173 if (setting == 0) {
Backstrom 0:f6e68b4ce169 174 setting = max;
Backstrom 0:f6e68b4ce169 175 }
Backstrom 0:f6e68b4ce169 176 else {
Backstrom 0:f6e68b4ce169 177 setting--;
Backstrom 0:f6e68b4ce169 178 }
Backstrom 0:f6e68b4ce169 179 }
Backstrom 0:f6e68b4ce169 180 else if (nextButton) {
Backstrom 0:f6e68b4ce169 181 if (setting == max) {
Backstrom 0:f6e68b4ce169 182 setting = 0;
Backstrom 0:f6e68b4ce169 183 }
Backstrom 0:f6e68b4ce169 184 else {
Backstrom 0:f6e68b4ce169 185 setting++;
Backstrom 0:f6e68b4ce169 186 }
Backstrom 0:f6e68b4ce169 187 }
Backstrom 0:f6e68b4ce169 188
Backstrom 0:f6e68b4ce169 189 return setting;
Backstrom 0:f6e68b4ce169 190 }
Backstrom 0:f6e68b4ce169 191
Backstrom 0:f6e68b4ce169 192 const uint32_t MENU_TIMEOUT = 3 * 10; // each cycle 0.1s, 3 secs total
Backstrom 0:f6e68b4ce169 193 struct tm* tm_;
Backstrom 0:f6e68b4ce169 194
Backstrom 0:f6e68b4ce169 195 int main()
Backstrom 0:f6e68b4ce169 196 {
Backstrom 0:f6e68b4ce169 197 bool suppress_button_sound = false;
Backstrom 0:f6e68b4ce169 198 uint8_t button3_holdcounter = 0;
Backstrom 0:f6e68b4ce169 199 uint32_t menu_countdown = MENU_TIMEOUT;
Backstrom 0:f6e68b4ce169 200
Backstrom 0:f6e68b4ce169 201 // Enable extra memory banks
Backstrom 0:f6e68b4ce169 202 LPC_SYSCON->SYSAHBCLKCTRL |= 0x1 << 26; // RAM1
Backstrom 0:f6e68b4ce169 203 LPC_SYSCON->SYSAHBCLKCTRL |= 0x1 << 27; // USBSRAM
Backstrom 0:f6e68b4ce169 204
Backstrom 0:f6e68b4ce169 205 // PWM
Backstrom 0:f6e68b4ce169 206 // See: http://developer.mbed.org/forum/mbed/topic/3276/
Backstrom 0:f6e68b4ce169 207 /*
Backstrom 0:f6e68b4ce169 208 LPC_CT16B0->TCR = 2;
Backstrom 0:f6e68b4ce169 209 LPC_CT16B0->PR = 19;
Backstrom 0:f6e68b4ce169 210 LPC_CT16B0->MR0 = 48000; // 1ms
Backstrom 0:f6e68b4ce169 211 LPC_CT16B0->TCR = 1;
Backstrom 0:f6e68b4ce169 212 */
Backstrom 0:f6e68b4ce169 213
Backstrom 0:f6e68b4ce169 214 BUTTON_STATE buttons;
Backstrom 0:f6e68b4ce169 215
Backstrom 0:f6e68b4ce169 216 rtc.begin();
Backstrom 0:f6e68b4ce169 217 init_prefs();
Backstrom 0:f6e68b4ce169 218
Backstrom 0:f6e68b4ce169 219 // FLW initialization
Backstrom 0:f6e68b4ce169 220 tm_ = rtc.getTime();
Backstrom 0:f6e68b4ce169 221
Backstrom 0:f6e68b4ce169 222 flw.begin(tm_->tm_min * 60 + tm_->tm_sec);
Backstrom 0:f6e68b4ce169 223 flw.setCensored(true);
Backstrom 0:f6e68b4ce169 224
Backstrom 0:f6e68b4ce169 225 memset(flwWord, 0, 5);
Backstrom 0:f6e68b4ce169 226 haveEEPROM = flw.hasEeprom();
Backstrom 0:f6e68b4ce169 227 if (haveEEPROM)
Backstrom 0:f6e68b4ce169 228 strncpy(flwWord, flw.getWord(), 4);
Backstrom 0:f6e68b4ce169 229 else
Backstrom 0:f6e68b4ce169 230 memset(flwWord, 0, 4);
Backstrom 0:f6e68b4ce169 231
Backstrom 0:f6e68b4ce169 232 led = 0;
Backstrom 0:f6e68b4ce169 233
Backstrom 0:f6e68b4ce169 234 initialize_buttons();
Backstrom 0:f6e68b4ce169 235 menu.setDigits(display.digits());
Backstrom 0:f6e68b4ce169 236
Backstrom 0:f6e68b4ce169 237 blanker.attach_us(&blank_tick, 100);
Backstrom 0:f6e68b4ce169 238 multiplexer.attach_us(&multiplex_tick, 2000);
Backstrom 0:f6e68b4ce169 239 button_ticker.attach_us(&button_tick, 5000);
Backstrom 0:f6e68b4ce169 240 rtc_ticker.attach(&rtc_tick, 1.0);
Backstrom 0:f6e68b4ce169 241 tenth_ticker.attach(&tenth_tick, 0.1);
Backstrom 0:f6e68b4ce169 242
Backstrom 0:f6e68b4ce169 243 PrefsData* prefs = get_prefs();
Backstrom 0:f6e68b4ce169 244 display.setBrightness(prefs->prefs.brightness);
Backstrom 0:f6e68b4ce169 245
Backstrom 0:f6e68b4ce169 246 wait(1.0);
Backstrom 0:f6e68b4ce169 247 //welcomeMessage();
Backstrom 0:f6e68b4ce169 248 led = 0;
Backstrom 0:f6e68b4ce169 249
Backstrom 0:f6e68b4ce169 250 #ifdef HAVE_GPS
Backstrom 0:f6e68b4ce169 251 gps_init();
Backstrom 0:f6e68b4ce169 252 #endif // HAVE_GPS
Backstrom 0:f6e68b4ce169 253
Backstrom 0:f6e68b4ce169 254 /*
Backstrom 0:f6e68b4ce169 255 // button test
Backstrom 0:f6e68b4ce169 256 while (1) {
Backstrom 0:f6e68b4ce169 257 get_button_state(&buttons);
Backstrom 0:f6e68b4ce169 258
Backstrom 0:f6e68b4ce169 259 display.printf("%d-%d-%d %d", buttons.b1_keydown, buttons.b2_keydown, buttons.b3_keydown, get_keystatus());
Backstrom 0:f6e68b4ce169 260 wait(0.1);
Backstrom 0:f6e68b4ce169 261 }
Backstrom 0:f6e68b4ce169 262 */
Backstrom 0:f6e68b4ce169 263
Backstrom 0:f6e68b4ce169 264 while (1) {
Backstrom 0:f6e68b4ce169 265 get_button_state(&buttons);
Backstrom 0:f6e68b4ce169 266 prefs = get_prefs();
Backstrom 0:f6e68b4ce169 267
Backstrom 0:f6e68b4ce169 268 led = 0;
Backstrom 0:f6e68b4ce169 269
Backstrom 0:f6e68b4ce169 270 #ifdef HAVE_GPS
Backstrom 0:f6e68b4ce169 271 // Read GPS and update RTC if needed
Backstrom 0:f6e68b4ce169 272 if (gpsDataReady()) {
Backstrom 0:f6e68b4ce169 273 char* nmea = gpsNMEA();
Backstrom 0:f6e68b4ce169 274 bool error = false;
Backstrom 0:f6e68b4ce169 275 bool fix = false;
Backstrom 0:f6e68b4ce169 276
Backstrom 0:f6e68b4ce169 277 if ( prefs->prefs.gps && strncmp( nmea, "$GPRMC,", 7 ) == 0 ) {
Backstrom 0:f6e68b4ce169 278 time_t t = parseGPSdata(nmea, error, fix, prefs->prefs.gps_tzh, prefs->prefs.gps_tzm);
Backstrom 0:f6e68b4ce169 279 time_t delta = t - gps_last_update;
Backstrom 0:f6e68b4ce169 280
Backstrom 0:f6e68b4ce169 281 if (fix && !error && delta > 60) {
Backstrom 0:f6e68b4ce169 282 led = 1;
Backstrom 0:f6e68b4ce169 283 struct tm *tmp = localtime(&t);
Backstrom 0:f6e68b4ce169 284 rtc.setTime(tmp);
Backstrom 0:f6e68b4ce169 285 gps_last_update = t;
Backstrom 0:f6e68b4ce169 286 }
Backstrom 0:f6e68b4ce169 287 }
Backstrom 0:f6e68b4ce169 288 }
Backstrom 0:f6e68b4ce169 289 #endif // HAVE_GPS
Backstrom 0:f6e68b4ce169 290
Backstrom 0:f6e68b4ce169 291 if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) {
Backstrom 0:f6e68b4ce169 292 if (suppress_button_sound || g_alarming) {
Backstrom 0:f6e68b4ce169 293 suppress_button_sound = false;
Backstrom 0:f6e68b4ce169 294 buttons.b1_keyup = buttons.b2_keyup = buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 295 }
Backstrom 0:f6e68b4ce169 296 else {
Backstrom 5:5c073029c416 297 piezo.play('g');
Backstrom 0:f6e68b4ce169 298 }
Backstrom 0:f6e68b4ce169 299
Backstrom 0:f6e68b4ce169 300 button3_holdcounter = 0;
Backstrom 0:f6e68b4ce169 301 g_alarming = false;
Backstrom 0:f6e68b4ce169 302 }
Backstrom 0:f6e68b4ce169 303
Backstrom 0:f6e68b4ce169 304 switch (g_clock_state) {
Backstrom 0:f6e68b4ce169 305 case STATE_CLOCK:
Backstrom 0:f6e68b4ce169 306 {
Backstrom 0:f6e68b4ce169 307 tm_ = rtc.getTime();
Backstrom 0:f6e68b4ce169 308
Backstrom 0:f6e68b4ce169 309 // button 1 triggers menu
Backstrom 0:f6e68b4ce169 310 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 311 //display.incBrightness();
Backstrom 0:f6e68b4ce169 312 display.printf("%s ", menu.reset());
Backstrom 0:f6e68b4ce169 313 display.setAlarmIndicator(false);
Backstrom 0:f6e68b4ce169 314 display.setGPSIndicator(false);
Backstrom 0:f6e68b4ce169 315 set_extra_prefs(tm_->tm_year+1900, tm_->tm_mon+1, tm_->tm_mday);
Backstrom 0:f6e68b4ce169 316 g_clock_state = STATE_MENU;
Backstrom 0:f6e68b4ce169 317 menu_countdown = MENU_TIMEOUT;
Backstrom 0:f6e68b4ce169 318 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 319 break;
Backstrom 0:f6e68b4ce169 320 }
Backstrom 0:f6e68b4ce169 321
Backstrom 0:f6e68b4ce169 322 // Button 2 cycles through display mode
Backstrom 0:f6e68b4ce169 323 if (buttons.b2_keyup) {
Backstrom 0:f6e68b4ce169 324 display.toggleTimeMode();
Backstrom 0:f6e68b4ce169 325 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 326 }
Backstrom 0:f6e68b4ce169 327
Backstrom 0:f6e68b4ce169 328 // Button 3 toggles FLW mode
Backstrom 0:f6e68b4ce169 329 if (buttons.b3_keyup && haveEEPROM) {
Backstrom 0:f6e68b4ce169 330 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 331 button3_holdcounter = 0;
Backstrom 0:f6e68b4ce169 332 g_clock_state = STATE_FLW;
Backstrom 0:f6e68b4ce169 333 }
Backstrom 0:f6e68b4ce169 334
Backstrom 0:f6e68b4ce169 335 // Hold button 3 to set alarm
Backstrom 0:f6e68b4ce169 336 if (buttons.b3_repeat) {
Backstrom 0:f6e68b4ce169 337 button3_holdcounter++;
Backstrom 0:f6e68b4ce169 338 buttons.b3_repeat = 0;
Backstrom 0:f6e68b4ce169 339 suppress_button_sound = true;
Backstrom 0:f6e68b4ce169 340
Backstrom 0:f6e68b4ce169 341 if (button3_holdcounter == 6) {
Backstrom 0:f6e68b4ce169 342 g_alarm_on = !g_alarm_on;
Backstrom 0:f6e68b4ce169 343 piezo.play('a');
Backstrom 0:f6e68b4ce169 344 }
Backstrom 0:f6e68b4ce169 345 }
Backstrom 0:f6e68b4ce169 346
Backstrom 0:f6e68b4ce169 347 // Enter auto date
Backstrom 0:f6e68b4ce169 348 if (prefs->prefs.auto_date && tm_->tm_sec == 50) {
Backstrom 0:f6e68b4ce169 349 display.cls();
Backstrom 0:f6e68b4ce169 350 display.printTimeLong(tm_, rtc.getTenths());
Backstrom 0:f6e68b4ce169 351 g_clock_state = STATE_AUTO_DATE;
Backstrom 0:f6e68b4ce169 352 }
Backstrom 0:f6e68b4ce169 353 else if (haveEEPROM && prefs->prefs.flw > 0 && tm_->tm_sec == 30) {
Backstrom 0:f6e68b4ce169 354 g_clock_state = STATE_AUTO_FLW;
Backstrom 0:f6e68b4ce169 355 }
Backstrom 0:f6e68b4ce169 356 else {
Backstrom 0:f6e68b4ce169 357 display.printTime(tm_, rtc.getTenths());
Backstrom 0:f6e68b4ce169 358 }
Backstrom 0:f6e68b4ce169 359 }
Backstrom 0:f6e68b4ce169 360 break;
Backstrom 0:f6e68b4ce169 361 case STATE_MENU:
Backstrom 0:f6e68b4ce169 362 {
Backstrom 0:f6e68b4ce169 363 // button 1 goes to next menu item
Backstrom 0:f6e68b4ce169 364 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 365 display.printf("%s ", menu.next());
Backstrom 0:f6e68b4ce169 366 menu_countdown = MENU_TIMEOUT;
Backstrom 0:f6e68b4ce169 367 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 368 }
Backstrom 0:f6e68b4ce169 369
Backstrom 0:f6e68b4ce169 370 // Button 2 selects menu item
Backstrom 0:f6e68b4ce169 371 if (buttons.b2_keyup || buttons.b2_repeat) {
Backstrom 0:f6e68b4ce169 372 bool setTime, setAlarm;
Backstrom 0:f6e68b4ce169 373
Backstrom 0:f6e68b4ce169 374 display.printf("%8s ", menu.select(setTime, setAlarm));
Backstrom 0:f6e68b4ce169 375
Backstrom 0:f6e68b4ce169 376 if (setTime) {
Backstrom 0:f6e68b4ce169 377 menu.leave();
Backstrom 0:f6e68b4ce169 378 display.setBlinkMode(VFDDisplay::Hours);
Backstrom 0:f6e68b4ce169 379 display.blink(true);
Backstrom 0:f6e68b4ce169 380 tm_ = rtc.getTime(); // get time from RTC to use as basis for settings
Backstrom 0:f6e68b4ce169 381 tm_->tm_sec = 0;
Backstrom 0:f6e68b4ce169 382 g_clock_state = STATE_SET_CLOCK_HH;
Backstrom 0:f6e68b4ce169 383 }
Backstrom 0:f6e68b4ce169 384 else if (setAlarm) {
Backstrom 0:f6e68b4ce169 385 menu.leave();
Backstrom 0:f6e68b4ce169 386 display.setBlinkMode(VFDDisplay::Minutes);
Backstrom 0:f6e68b4ce169 387 display.blink(true);
Backstrom 0:f6e68b4ce169 388 tm_ = rtc.getAlarm();
Backstrom 0:f6e68b4ce169 389 g_clock_state = STATE_SET_ALARM_HH;
Backstrom 0:f6e68b4ce169 390 }
Backstrom 0:f6e68b4ce169 391
Backstrom 0:f6e68b4ce169 392 menu_countdown = MENU_TIMEOUT;
Backstrom 0:f6e68b4ce169 393 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 394 }
Backstrom 0:f6e68b4ce169 395 // Button 3 leaves menu
Backstrom 0:f6e68b4ce169 396 else if (buttons.b3_keyup) {
Backstrom 0:f6e68b4ce169 397 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 398 menu.leave();
Backstrom 0:f6e68b4ce169 399 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 400 }
Backstrom 0:f6e68b4ce169 401
Backstrom 0:f6e68b4ce169 402 menu_countdown--;
Backstrom 0:f6e68b4ce169 403
Backstrom 0:f6e68b4ce169 404 // exit menu if unused for preset time
Backstrom 0:f6e68b4ce169 405 if (menu_countdown == 0) {
Backstrom 0:f6e68b4ce169 406 menu.leave();
Backstrom 0:f6e68b4ce169 407 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 408 }
Backstrom 0:f6e68b4ce169 409
Backstrom 0:f6e68b4ce169 410 // If g_clock_state has changed, we are leaving the menu, update parameters
Backstrom 0:f6e68b4ce169 411 if (g_clock_state == STATE_CLOCK) {
Backstrom 0:f6e68b4ce169 412 display.setGPSIndicator(true);
Backstrom 0:f6e68b4ce169 413
Backstrom 0:f6e68b4ce169 414 tm_ = rtc.getTime();
Backstrom 0:f6e68b4ce169 415 int year, month, day;
Backstrom 0:f6e68b4ce169 416
Backstrom 0:f6e68b4ce169 417 get_extra_prefs(year, month, day);
Backstrom 0:f6e68b4ce169 418 tm_->tm_year = year - 1900;
Backstrom 0:f6e68b4ce169 419 tm_->tm_mon = month-1;
Backstrom 0:f6e68b4ce169 420 tm_->tm_mday = day;
Backstrom 0:f6e68b4ce169 421
Backstrom 0:f6e68b4ce169 422 tm_->tm_wday = dayOfWeek(year, month, day);
Backstrom 0:f6e68b4ce169 423
Backstrom 0:f6e68b4ce169 424 rtc.setTime(tm_);
Backstrom 0:f6e68b4ce169 425
Backstrom 0:f6e68b4ce169 426 wait(0.05);
Backstrom 0:f6e68b4ce169 427 display.setGPSIndicator(false);
Backstrom 0:f6e68b4ce169 428 }
Backstrom 0:f6e68b4ce169 429 }
Backstrom 0:f6e68b4ce169 430 break;
Backstrom 0:f6e68b4ce169 431 case STATE_AUTO_DATE:
Backstrom 0:f6e68b4ce169 432 {
Backstrom 0:f6e68b4ce169 433 // exit scroll if any button is pressed
Backstrom 0:f6e68b4ce169 434 if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) {
Backstrom 0:f6e68b4ce169 435 display.cls();
Backstrom 0:f6e68b4ce169 436 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 437
Backstrom 0:f6e68b4ce169 438 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 439 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 440 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 441 }
Backstrom 0:f6e68b4ce169 442
Backstrom 0:f6e68b4ce169 443 static uint8_t scroll_cnt = 0;
Backstrom 0:f6e68b4ce169 444
Backstrom 0:f6e68b4ce169 445 if (display.scrollFinished()) {
Backstrom 0:f6e68b4ce169 446 display.cls();
Backstrom 0:f6e68b4ce169 447 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 448 }
Backstrom 0:f6e68b4ce169 449 else {
Backstrom 0:f6e68b4ce169 450 if (++scroll_cnt == 2) {
Backstrom 0:f6e68b4ce169 451 display.scroll();
Backstrom 0:f6e68b4ce169 452 scroll_cnt = 0;
Backstrom 0:f6e68b4ce169 453 }
Backstrom 0:f6e68b4ce169 454 }
Backstrom 0:f6e68b4ce169 455 }
Backstrom 0:f6e68b4ce169 456 break;
Backstrom 0:f6e68b4ce169 457 case STATE_SET_CLOCK_HH:
Backstrom 0:f6e68b4ce169 458 {
Backstrom 0:f6e68b4ce169 459 display.printTimeSet(tm_);
Backstrom 0:f6e68b4ce169 460
Backstrom 0:f6e68b4ce169 461 if (buttons.b1_repeat || buttons.b2_repeat) {
Backstrom 0:f6e68b4ce169 462 display.blink(false);
Backstrom 0:f6e68b4ce169 463 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, buttons.b1_repeat, buttons.b2_repeat);
Backstrom 0:f6e68b4ce169 464 buttons.b1_repeat = 0;
Backstrom 0:f6e68b4ce169 465 buttons.b2_repeat = 0;
Backstrom 0:f6e68b4ce169 466 }
Backstrom 0:f6e68b4ce169 467 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 468 display.blink(true);
Backstrom 0:f6e68b4ce169 469 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, true, false);
Backstrom 0:f6e68b4ce169 470 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 471 }
Backstrom 0:f6e68b4ce169 472 else if (buttons.b2_keyup) { // Button 2
Backstrom 0:f6e68b4ce169 473 display.blink(true);
Backstrom 0:f6e68b4ce169 474 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, false, true);
Backstrom 0:f6e68b4ce169 475 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 476 }
Backstrom 0:f6e68b4ce169 477 else if (buttons.b3_keyup) { // Button 3 moves to minute setting
Backstrom 0:f6e68b4ce169 478 display.setBlinkMode(VFDDisplay::Minutes);
Backstrom 0:f6e68b4ce169 479 g_clock_state = STATE_SET_CLOCK_MM;
Backstrom 0:f6e68b4ce169 480 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 481 }
Backstrom 0:f6e68b4ce169 482 }
Backstrom 0:f6e68b4ce169 483 break;
Backstrom 0:f6e68b4ce169 484 case STATE_SET_CLOCK_MM:
Backstrom 0:f6e68b4ce169 485 {
Backstrom 0:f6e68b4ce169 486 display.printTimeSet(tm_);
Backstrom 0:f6e68b4ce169 487
Backstrom 0:f6e68b4ce169 488 if (buttons.b1_repeat || buttons.b2_repeat) {
Backstrom 0:f6e68b4ce169 489 display.blink(false);
Backstrom 0:f6e68b4ce169 490 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, buttons.b1_repeat, buttons.b2_repeat);
Backstrom 0:f6e68b4ce169 491 buttons.b1_repeat = 0;
Backstrom 0:f6e68b4ce169 492 buttons.b2_repeat = 0;
Backstrom 0:f6e68b4ce169 493 }
Backstrom 0:f6e68b4ce169 494 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 495 display.blink(true);
Backstrom 0:f6e68b4ce169 496 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, true, false);
Backstrom 0:f6e68b4ce169 497 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 498 }
Backstrom 0:f6e68b4ce169 499 else if (buttons.b2_keyup) { // Button 2
Backstrom 0:f6e68b4ce169 500 display.blink(true);
Backstrom 0:f6e68b4ce169 501 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, false, true);
Backstrom 0:f6e68b4ce169 502 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 503 }
Backstrom 0:f6e68b4ce169 504 else if (buttons.b3_keyup) { // Button 3 moves to second setting
Backstrom 0:f6e68b4ce169 505 display.setBlinkMode(VFDDisplay::Seconds);
Backstrom 0:f6e68b4ce169 506 g_clock_state = STATE_SET_CLOCK_SS;
Backstrom 0:f6e68b4ce169 507 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 508 }
Backstrom 0:f6e68b4ce169 509 }
Backstrom 0:f6e68b4ce169 510 break;
Backstrom 0:f6e68b4ce169 511 case STATE_SET_CLOCK_SS:
Backstrom 0:f6e68b4ce169 512 {
Backstrom 0:f6e68b4ce169 513 display.printTimeSet(tm_);
Backstrom 0:f6e68b4ce169 514
Backstrom 0:f6e68b4ce169 515 if (buttons.b1_repeat || buttons.b2_repeat) {
Backstrom 0:f6e68b4ce169 516 display.blink(false);
Backstrom 0:f6e68b4ce169 517 tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, buttons.b1_repeat, buttons.b2_repeat);
Backstrom 0:f6e68b4ce169 518 buttons.b1_repeat = 0;
Backstrom 0:f6e68b4ce169 519 buttons.b2_repeat = 0;
Backstrom 0:f6e68b4ce169 520 }
Backstrom 0:f6e68b4ce169 521 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 522 display.blink(true);
Backstrom 0:f6e68b4ce169 523 tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, true, false);
Backstrom 0:f6e68b4ce169 524 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 525 }
Backstrom 0:f6e68b4ce169 526 else if (buttons.b2_keyup) { // Button 2
Backstrom 0:f6e68b4ce169 527 display.blink(true);
Backstrom 0:f6e68b4ce169 528 tm_->tm_sec = handleTimeSetting(tm_->tm_sec, 0, 59, false, true);
Backstrom 0:f6e68b4ce169 529 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 530 }
Backstrom 0:f6e68b4ce169 531 else if (buttons.b3_keyup) { // Button 3 moves to minute setting
Backstrom 0:f6e68b4ce169 532 display.setBlinkMode(VFDDisplay::Full);
Backstrom 0:f6e68b4ce169 533 display.blink(false);
Backstrom 0:f6e68b4ce169 534 rtc.setTime(tm_);
Backstrom 0:f6e68b4ce169 535 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 536 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 537 }
Backstrom 0:f6e68b4ce169 538 }
Backstrom 0:f6e68b4ce169 539 break;
Backstrom 0:f6e68b4ce169 540 case STATE_SET_ALARM_HH:
Backstrom 0:f6e68b4ce169 541 {
Backstrom 0:f6e68b4ce169 542 display.printTimeSet(tm_, false);
Backstrom 0:f6e68b4ce169 543
Backstrom 0:f6e68b4ce169 544 if (buttons.b1_repeat || buttons.b2_repeat) {
Backstrom 0:f6e68b4ce169 545 display.blink(false);
Backstrom 0:f6e68b4ce169 546 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, buttons.b1_repeat, buttons.b2_repeat);
Backstrom 0:f6e68b4ce169 547 buttons.b1_repeat = 0;
Backstrom 0:f6e68b4ce169 548 buttons.b2_repeat = 0;
Backstrom 0:f6e68b4ce169 549 }
Backstrom 0:f6e68b4ce169 550 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 551 display.blink(true);
Backstrom 0:f6e68b4ce169 552 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, true, false);
Backstrom 0:f6e68b4ce169 553 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 554 }
Backstrom 0:f6e68b4ce169 555 else if (buttons.b2_keyup) { // Button 2
Backstrom 0:f6e68b4ce169 556 display.blink(true);
Backstrom 0:f6e68b4ce169 557 tm_->tm_hour = handleTimeSetting(tm_->tm_hour, 0, 23, false, true);
Backstrom 0:f6e68b4ce169 558 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 559 }
Backstrom 0:f6e68b4ce169 560 else if (buttons.b3_keyup) { // Button 3 moves to minute setting
Backstrom 0:f6e68b4ce169 561 display.setBlinkMode(VFDDisplay::Seconds);
Backstrom 0:f6e68b4ce169 562 g_clock_state = STATE_SET_ALARM_MM;
Backstrom 0:f6e68b4ce169 563 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 564 }
Backstrom 0:f6e68b4ce169 565 }
Backstrom 0:f6e68b4ce169 566 break;
Backstrom 0:f6e68b4ce169 567 case STATE_SET_ALARM_MM:
Backstrom 0:f6e68b4ce169 568 {
Backstrom 0:f6e68b4ce169 569 display.printTimeSet(tm_, false);
Backstrom 0:f6e68b4ce169 570
Backstrom 0:f6e68b4ce169 571 if (buttons.b1_repeat || buttons.b2_repeat) {
Backstrom 0:f6e68b4ce169 572 display.blink(false);
Backstrom 0:f6e68b4ce169 573 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, buttons.b1_repeat, buttons.b2_repeat);
Backstrom 0:f6e68b4ce169 574 buttons.b1_repeat = 0;
Backstrom 0:f6e68b4ce169 575 buttons.b2_repeat = 0;
Backstrom 0:f6e68b4ce169 576 }
Backstrom 0:f6e68b4ce169 577 if (buttons.b1_keyup) {
Backstrom 0:f6e68b4ce169 578 display.blink(true);
Backstrom 0:f6e68b4ce169 579 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, true, false);
Backstrom 0:f6e68b4ce169 580 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 581 }
Backstrom 0:f6e68b4ce169 582 else if (buttons.b2_keyup) { // Button 2
Backstrom 0:f6e68b4ce169 583 display.blink(true);
Backstrom 0:f6e68b4ce169 584 tm_->tm_min = handleTimeSetting(tm_->tm_min, 0, 59, false, true);
Backstrom 0:f6e68b4ce169 585 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 586 }
Backstrom 0:f6e68b4ce169 587 else if (buttons.b3_keyup) { // Button 3 moves to minute setting
Backstrom 0:f6e68b4ce169 588 display.setBlinkMode(VFDDisplay::Full);
Backstrom 0:f6e68b4ce169 589 display.blink(false);
Backstrom 0:f6e68b4ce169 590 rtc.setAlarm(tm_->tm_hour, tm_->tm_min, 0);
Backstrom 0:f6e68b4ce169 591 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 592 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 593 }
Backstrom 0:f6e68b4ce169 594 }
Backstrom 0:f6e68b4ce169 595 break;
Backstrom 0:f6e68b4ce169 596 case STATE_AUTO_FLW:
Backstrom 0:f6e68b4ce169 597 {
Backstrom 0:f6e68b4ce169 598 tm_ = rtc.getTime();
Backstrom 0:f6e68b4ce169 599
Backstrom 0:f6e68b4ce169 600 if (tm_->tm_sec >= 37) {
Backstrom 0:f6e68b4ce169 601 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 602 }
Backstrom 0:f6e68b4ce169 603 }
Backstrom 0:f6e68b4ce169 604 // fall-through
Backstrom 0:f6e68b4ce169 605 case STATE_FLW:
Backstrom 0:f6e68b4ce169 606 {
Backstrom 0:f6e68b4ce169 607 // exit if any button is pressed
Backstrom 0:f6e68b4ce169 608 if (buttons.b1_keyup || buttons.b2_keyup || buttons.b3_keyup) {
Backstrom 0:f6e68b4ce169 609 display.cls();
Backstrom 0:f6e68b4ce169 610 g_clock_state = STATE_CLOCK;
Backstrom 0:f6e68b4ce169 611
Backstrom 0:f6e68b4ce169 612 buttons.b1_keyup = 0;
Backstrom 0:f6e68b4ce169 613 buttons.b2_keyup = 0;
Backstrom 0:f6e68b4ce169 614 buttons.b3_keyup = 0;
Backstrom 0:f6e68b4ce169 615 }
Backstrom 0:f6e68b4ce169 616
Backstrom 0:f6e68b4ce169 617 if (flwOffset == 0)
Backstrom 0:f6e68b4ce169 618 display.printf("%s ", flwWord);
Backstrom 0:f6e68b4ce169 619 else
Backstrom 0:f6e68b4ce169 620 display.printf("%*s" "%s ", flwOffset, " ", flwWord);
Backstrom 0:f6e68b4ce169 621 }
Backstrom 0:f6e68b4ce169 622 break;
Backstrom 0:f6e68b4ce169 623 default:
Backstrom 0:f6e68b4ce169 624 break;
Backstrom 0:f6e68b4ce169 625 }
Backstrom 0:f6e68b4ce169 626
Backstrom 0:f6e68b4ce169 627 if (g_alarm_on && rtc.checkAlarm()) {
Backstrom 0:f6e68b4ce169 628 g_alarming = true;
Backstrom 0:f6e68b4ce169 629 }
Backstrom 0:f6e68b4ce169 630
Backstrom 0:f6e68b4ce169 631 display.setAlarmIndicator(g_alarm_on);
Backstrom 0:f6e68b4ce169 632 display.setGPSIndicator(g_alarming);
Backstrom 0:f6e68b4ce169 633
Backstrom 0:f6e68b4ce169 634 if (g_alarming) {
Backstrom 0:f6e68b4ce169 635 piezo.play('g');
Backstrom 0:f6e68b4ce169 636 }
Backstrom 11:34b344fdec98 637
Backstrom 11:34b344fdec98 638 if (g_updateFLW)
Backstrom 11:34b344fdec98 639 {
Backstrom 11:34b344fdec98 640 g_updateFLW = false;
Backstrom 11:34b344fdec98 641
Backstrom 11:34b344fdec98 642 strncpy(flwWord, flw.getWord(), 4);
Backstrom 11:34b344fdec98 643 flwOffset += flwOffsetDirection;
Backstrom 11:34b344fdec98 644
Backstrom 11:34b344fdec98 645 if (flwOffset <= 0) {
Backstrom 11:34b344fdec98 646 flwOffset = 0;
Backstrom 11:34b344fdec98 647 flwOffsetDirection = 1;
Backstrom 11:34b344fdec98 648 }
Backstrom 11:34b344fdec98 649 else if (flwOffset > display.digits() -4) {
Backstrom 11:34b344fdec98 650 flwOffset = 3;
Backstrom 11:34b344fdec98 651 flwOffsetDirection = -1;
Backstrom 11:34b344fdec98 652 }
Backstrom 11:34b344fdec98 653 }
Backstrom 0:f6e68b4ce169 654
Backstrom 0:f6e68b4ce169 655 wait(0.1);
Backstrom 0:f6e68b4ce169 656 }
Backstrom 0:f6e68b4ce169 657 }