├── .gitignore ├── DSCPanel ├── DSC.cpp ├── DSC.h ├── DSC_Constants.h ├── DSC_Globals.h ├── README.md ├── archive │ └── DSC_Example.ino └── examples │ ├── DSCPanelExample │ └── DSCPanelExample.ino │ ├── DSCPanelNoEthernet │ └── DSCPanelNoEthernet.ino │ └── DSCPanelSimple │ └── DSCPanelSimple.ino ├── README.md ├── TextBuffer ├── README.md ├── TextBuffer.cpp ├── TextBuffer.h └── keywords.txt ├── Time ├── DateStrings.cpp ├── Readme.txt ├── Time.cpp ├── Time.h ├── TimeLib.h ├── examples │ ├── Processing │ │ └── SyncArduinoClock │ │ │ ├── SyncArduinoClock.pde │ │ │ └── readme.txt │ ├── TimeArduinoDue │ │ └── TimeArduinoDue.ino │ ├── TimeGPS │ │ └── TimeGPS.ino │ ├── TimeNTP │ │ └── TimeNTP.ino │ ├── TimeNTP_ESP8266WiFi │ │ └── TimeNTP_ESP8266WiFi.ino │ ├── TimeRTC │ │ └── TimeRTC.ino │ ├── TimeRTCLog │ │ └── TimeRTCLog.ino │ ├── TimeRTCSet │ │ └── TimeRTCSet.ino │ ├── TimeSerial │ │ └── TimeSerial.ino │ ├── TimeSerialDateStrings │ │ └── TimeSerialDateStrings.ino │ └── TimeTeensy3 │ │ └── TimeTeensy3.ino ├── keywords.txt ├── library.json └── library.properties ├── readserial.py └── schematic.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Folder view configuration files 2 | .DS_Store 3 | Desktop.ini 4 | 5 | # Thumbnail cache files 6 | ._* 7 | Thumbs.db 8 | 9 | # Files that might appear on external disks 10 | .Spotlight-V100 11 | .Trashes 12 | 13 | # Compiled Python files 14 | *.pyc 15 | 16 | # Compiled C++ files 17 | *.out 18 | 19 | # Application specific files 20 | venv 21 | node_modules 22 | .sass-cache 23 | -------------------------------------------------------------------------------- /DSCPanel/DSC.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "DSC.h" 3 | #include "DSC_Constants.h" 4 | #include "DSC_Globals.h" 5 | #include 6 | 7 | /// ----- GLOBAL VARIABLES ----- 8 | /* 9 | * The existence of global variables are "declared" in DSC_Global.h, so that each 10 | * source file that includes the header knows about them. The variables must then be 11 | * “defined” once in one of the source files (this one). 12 | 13 | * The following structure contains all of the global variables used by the ISR to 14 | * communicate with the DSC.clkCalled() object. You cannot pass parameters to an 15 | * ISR so these values must be global. The fields are defined in DSC_Globals.h 16 | */ 17 | dscGlobal_t dscGlobal; 18 | 19 | // ----- Input/Output Pins (Global) ----- 20 | byte CLK; // Keybus Yellow (Clock Line) 21 | byte DTA_IN; // Keybus Green (Data Line via V divider) 22 | byte DTA_OUT; // Keybus Green Output (Data Line through driver) 23 | byte LED; // LED pin on the arduino 24 | 25 | void clkCalled_Handler(); // Prototype for interrupt handler, called on clock line change 26 | 27 | TextBuffer tempByte(12); // Initialize TextBuffer.h for temp byte buffer 28 | TextBuffer pInfo(WORD_BITS); // Initialize TextBuffer.h for panel info 29 | TextBuffer kInfo(WORD_BITS); // Initialize TextBuffer.h for keypad info 30 | 31 | /// --- END GLOBAL VARIABLES --- 32 | 33 | DSC::DSC(void) 34 | { 35 | // ----- Time Variables ----- 36 | // Volatile variables, modified within ISR, based on micros() 37 | dscGlobal.intervalTimer = 0; 38 | dscGlobal.clockChange = 0; 39 | dscGlobal.lastChange = 0; 40 | dscGlobal.lastRise = 0; // NOT USED YET 41 | dscGlobal.lastFall = 0; // NOT USED YET 42 | dscGlobal.newWord = false; // NOT USED YET 43 | 44 | // Time variables, based on millis() 45 | dscGlobal.lastStatus = 0; 46 | dscGlobal.lastData = 0; 47 | 48 | // Class level variables to hold time elements 49 | int yy = 0, mm = 0, dd = 0, HH = 0, MM = 0, SS = 0; 50 | bool timeAvailable = false; // Changes to true when kCmd == 0xa5 to 51 | // indicate that the time elements are valid 52 | 53 | // ----- Input/Output Pins (DEFAULTS) ------ 54 | // These can be changed prior to DSC.begin() using functions below 55 | CLK = 3; // Keybus Yellow (Clock Line) 56 | DTA_IN = 4; // Keybus Green (Data Line via V divider) 57 | DTA_OUT = 8; // Keybus Green Output (Data Line through driver) 58 | LED = 13; // LED pin on the arduino 59 | 60 | // ----- Keybus Word String Vars ----- 61 | dscGlobal.pBuild="", dscGlobal.pWord=""; 62 | dscGlobal.oldPWord="", dscGlobal.pMsg=""; 63 | dscGlobal.kBuild="", dscGlobal.kWord=""; 64 | dscGlobal.oldKWord="", dscGlobal.kMsg=""; 65 | dscGlobal.pCmd = 0, dscGlobal.kCmd = 0; 66 | 67 | // ----- Byte Array Variables ----- 68 | //dscGlobal.pBytes[ARR_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // NOT USED 69 | //dscGlobal.kBytes[ARR_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // NOT USED 70 | } 71 | 72 | int DSC::addSerial(void) 73 | { 74 | } 75 | 76 | void DSC::begin(void) 77 | { 78 | pinMode(CLK, INPUT); 79 | pinMode(DTA_IN, INPUT); 80 | pinMode(DTA_OUT, OUTPUT); 81 | pinMode(LED, OUTPUT); 82 | 83 | tempByte.begin(); // Begin the tempByte buffer, allocate memory 84 | pInfo.begin(); // Begin the panel info buffer, allocate memory 85 | kInfo.begin(); // Begin the keypad info buffer, allocate memory 86 | 87 | // Set the interrupt pin 88 | intrNum = digitalPinToInterrupt(CLK); 89 | 90 | // Attach interrupt on the CLK pin 91 | attachInterrupt(intrNum, clkCalled_Handler, CHANGE); 92 | // Changed from RISING to CHANGE to read both panel and keypad data 93 | } 94 | 95 | /* This is the interrupt handler used by this class. It is called every time the input 96 | * pin changes from high to low or from low to high. 97 | * 98 | * The function is not a member of the DSC class, it must be in the global scope in order 99 | * to be called by attachInterrupt() from within the DSC class. 100 | */ 101 | void clkCalled_Handler() 102 | { 103 | dscGlobal.clockChange = micros(); // Save the current clock change time 104 | dscGlobal.intervalTimer = 105 | (dscGlobal.clockChange - dscGlobal.lastChange); // Determine interval since last clock change 106 | 107 | // If the interval is longer than the required amount (NEW_WORD_INTV - 200 us) 108 | if (dscGlobal.intervalTimer > (NEW_WORD_INTV - 200)) { 109 | dscGlobal.kWord = dscGlobal.kBuild; // Save the complete keypad raw data bytes sentence 110 | dscGlobal.kBuild = ""; // Reset the raw data bytes keypad word being built 111 | } 112 | dscGlobal.lastChange = dscGlobal.clockChange; // Re-save the current change time as last change time 113 | 114 | // If clock line is going HIGH, this is PANEL data 115 | if (digitalRead(CLK)) { 116 | dscGlobal.lastRise = dscGlobal.lastChange; // Set the lastRise time 117 | if (dscGlobal.pBuild.length() <= MAX_BITS) { // Limit the string size to something manageable 118 | //delayMicroseconds(120); // Delay for 120 us to get a valid data line read 119 | if (digitalRead(DTA_IN)) dscGlobal.pBuild += "1"; 120 | else dscGlobal.pBuild += "0"; 121 | } 122 | } 123 | // Otherwise, it's going LOW, this is KEYPAD data 124 | else { 125 | dscGlobal.lastFall = dscGlobal.lastChange; // Set the lastFall time 126 | if (dscGlobal.kBuild.length() <= MAX_BITS) { // Limit the string size to something manageable 127 | //delayMicroseconds(200); // Delay for 300 us to get a valid data line read 128 | if (digitalRead(DTA_IN)) dscGlobal.kBuild += "1"; 129 | else dscGlobal.kBuild += "0"; 130 | } 131 | } 132 | } 133 | 134 | int DSC::process(void) 135 | { 136 | // ------------ Get/process incoming data ------------- 137 | dscGlobal.pCmd = 0, 138 | dscGlobal.kCmd = 0; 139 | timeAvailable = false; // Set the time element status to invalid 140 | 141 | // ----------------- Turn on/off LED ------------------ 142 | if ((millis() - dscGlobal.lastStatus) > 500) 143 | digitalWrite(LED, 0); // Turn LED OFF (no recent status command [0x05]) 144 | else 145 | digitalWrite(LED, 1); // Turn LED ON (recent status command [0x05]) 146 | 147 | /* 148 | * The normal clock frequency is 1 Hz or one cycle every ms (1000 us) 149 | * The new word marker is clock high for about 15 ms (15000 us) 150 | * If the interval is longer than the required amount (NEW_WORD_INTV + 200 us), 151 | * and the panel word in progress (pBuild) is more than 8 characters long, 152 | * process the panel and keypad words, otherwise return failure (0). 153 | */ 154 | if ((dscGlobal.intervalTimer < (NEW_WORD_INTV + 200)) || 155 | (dscGlobal.pBuild.length() < 8)) return 0; // Return failure 156 | 157 | dscGlobal.pWord = dscGlobal.pBuild; // Save the complete panel raw data bytes sentence 158 | dscGlobal.pBuild = ""; // Reset the raw data panel word being built 159 | dscGlobal.pMsg = ""; // Initialize panel message for output 160 | //dscGlobal.pCmd = 0; 161 | 162 | dscGlobal.kMsg = ""; // Initialize keypad message for output 163 | //dscGlobal.kCmd = 0; 164 | 165 | dscGlobal.pCmd = decodePanel(); // Decode the panel binary, return command byte, or 0 166 | dscGlobal.kCmd = decodeKeypad(); // Decode the keypad binary, return command byte, or 0 167 | 168 | if (dscGlobal.pCmd && dscGlobal.kCmd) return 3; // Return 3 if both were decoded 169 | else if (dscGlobal.kCmd) return 2; // Return 2 if keypad word was decoded 170 | else if (dscGlobal.pCmd) return 1; // Return 1 if panel word was decoded 171 | else return 0; // Return failure if none were decoded 172 | } 173 | 174 | byte DSC::decodePanel(void) 175 | { 176 | // ------------- Process the Panel Data Word --------------- 177 | byte cmd = binToInt(dscGlobal.pWord,0,8); // Get the panel pCmd (data word type/command) 178 | 179 | if (dscGlobal.pWord == dscGlobal.oldPWord || cmd == 0x00) { 180 | // Skip this word if the data hasn't changed, or pCmd is empty (0x00) 181 | return 0; // Return failure 182 | } 183 | else { 184 | // This seems to be a valid word, try to process it 185 | dscGlobal.lastData = millis(); // Record the time (last data word was received) 186 | dscGlobal.oldPWord = dscGlobal.pWord; // This is a new/good word, save it 187 | 188 | // Interpret the data 189 | if (cmd == 0x05) 190 | { 191 | dscGlobal.lastStatus = millis(); // Record the time for LED logic 192 | dscGlobal.pMsg += F("[Status] "); 193 | if (binToInt(dscGlobal.pWord,16,1)) { 194 | dscGlobal.pMsg += F("Ready"); 195 | } 196 | else { 197 | dscGlobal.pMsg += F("Not Ready"); 198 | } 199 | if (binToInt(dscGlobal.pWord,12,1)) dscGlobal.pMsg += F(", Error"); 200 | if (binToInt(dscGlobal.pWord,13,1)) dscGlobal.pMsg += F(", Bypass"); 201 | if (binToInt(dscGlobal.pWord,14,1)) dscGlobal.pMsg += F(", Memory"); 202 | if (binToInt(dscGlobal.pWord,15,1)) dscGlobal.pMsg += F(", Armed"); 203 | if (binToInt(dscGlobal.pWord,17,1)) dscGlobal.pMsg += F(", Program"); 204 | if (binToInt(dscGlobal.pWord,29,1)) dscGlobal.pMsg += F(", Power Fail"); // ??? - maybe 28 or 20? 205 | } 206 | 207 | if (cmd == 0xa5) 208 | { 209 | dscGlobal.pMsg += F("[Info] "); 210 | int y3 = binToInt(dscGlobal.pWord,9,4); 211 | int y4 = binToInt(dscGlobal.pWord,13,4); 212 | yy = (String(y3) + String(y4)).toInt(); 213 | mm = binToInt(dscGlobal.pWord,19,4); 214 | dd = binToInt(dscGlobal.pWord,23,5); 215 | HH = binToInt(dscGlobal.pWord,28,5); 216 | MM = binToInt(dscGlobal.pWord,33,6); 217 | 218 | timeAvailable = true; // Set the time element status to valid 219 | 220 | byte arm = binToInt(dscGlobal.pWord,41,2); 221 | byte master = binToInt(dscGlobal.pWord,43,1); 222 | byte user = binToInt(dscGlobal.pWord,43,6); // 0-36 223 | if (arm == 0x02) { 224 | dscGlobal.pMsg += F(", Armed"); 225 | user = user - 0x19; 226 | } 227 | if (arm == 0x03) { 228 | dscGlobal.pMsg += F(", Disarmed"); 229 | } 230 | if (arm > 0) { 231 | if (master) dscGlobal.pMsg += F(", Master Code"); 232 | else dscGlobal.pMsg += F(", User Code"); 233 | user += 1; // shift to 1-32, 33, 34 234 | if (user > 34) user += 5; // convert to system code 40, 41, 42 235 | dscGlobal.pMsg += " " + String(user); 236 | } 237 | } 238 | 239 | if (cmd == 0x27) 240 | { 241 | dscGlobal.pMsg += F("[Zones A] "); 242 | int zones = binToInt(dscGlobal.pWord,8+1+8+8+8+8,8); 243 | if (zones & 1) dscGlobal.pMsg += "1"; 244 | if (zones & 2) dscGlobal.pMsg += "2"; 245 | if (zones & 4) dscGlobal.pMsg += "3"; 246 | if (zones & 8) dscGlobal.pMsg += "4"; 247 | if (zones & 16) dscGlobal.pMsg += "5"; 248 | if (zones & 32) dscGlobal.pMsg += "6"; 249 | if (zones & 64) dscGlobal.pMsg += "7"; 250 | if (zones & 128) dscGlobal.pMsg += "8"; 251 | if (zones == 0) dscGlobal.pMsg += "Ready "; 252 | } 253 | 254 | if (cmd == 0x2d) 255 | { 256 | dscGlobal.pMsg += F("[Zones B] "); 257 | int zones = binToInt(dscGlobal.pWord,8+1+8+8+8+8,8); 258 | if (zones & 1) dscGlobal.pMsg += "9"; 259 | if (zones & 2) dscGlobal.pMsg += "10"; 260 | if (zones & 4) dscGlobal.pMsg += "11"; 261 | if (zones & 8) dscGlobal.pMsg += "12"; 262 | if (zones & 16) dscGlobal.pMsg += "13"; 263 | if (zones & 32) dscGlobal.pMsg += "14"; 264 | if (zones & 64) dscGlobal.pMsg += "15"; 265 | if (zones & 128) dscGlobal.pMsg += "16"; 266 | if (zones == 0) dscGlobal.pMsg += "Ready "; 267 | } 268 | 269 | if (cmd == 0x34) 270 | { 271 | dscGlobal.pMsg += F("[Zones C] "); 272 | int zones = binToInt(dscGlobal.pWord,8+1+8+8+8+8,8); 273 | if (zones & 1) dscGlobal.pMsg += "17"; 274 | if (zones & 2) dscGlobal.pMsg += "18"; 275 | if (zones & 4) dscGlobal.pMsg += "19"; 276 | if (zones & 8) dscGlobal.pMsg += "20"; 277 | if (zones & 16) dscGlobal.pMsg += "21"; 278 | if (zones & 32) dscGlobal.pMsg += "22"; 279 | if (zones & 64) dscGlobal.pMsg += "23"; 280 | if (zones & 128) dscGlobal.pMsg += "24"; 281 | if (zones == 0) dscGlobal.pMsg += "Ready "; 282 | } 283 | 284 | if (cmd == 0x3e) 285 | { 286 | dscGlobal.pMsg += F("[Zones D] "); 287 | int zones = binToInt(dscGlobal.pWord,8+1+8+8+8+8,8); 288 | if (zones & 1) dscGlobal.pMsg += "25"; 289 | if (zones & 2) dscGlobal.pMsg += "26"; 290 | if (zones & 4) dscGlobal.pMsg += "27"; 291 | if (zones & 8) dscGlobal.pMsg += "28"; 292 | if (zones & 16) dscGlobal.pMsg += "29"; 293 | if (zones & 32) dscGlobal.pMsg += "30"; 294 | if (zones & 64) dscGlobal.pMsg += "31"; 295 | if (zones & 128) dscGlobal.pMsg += "32"; 296 | if (zones == 0) dscGlobal.pMsg += "Ready "; 297 | } 298 | // --- The other 32 zones for a 1864 panel need to be added after this --- 299 | 300 | if (cmd == 0x11) { 301 | dscGlobal.pMsg += F("[Keypad Query] "); 302 | } 303 | if (cmd == 0x0a) { 304 | dscGlobal.pMsg += F("[Panel Program Mode] "); 305 | } 306 | if (cmd == 0x5d) { 307 | dscGlobal.pMsg += F("[Alarm Memory Group 1] "); 308 | } 309 | if (cmd == 0x63) { 310 | dscGlobal.pMsg += F("[Alarm Memory Group 2] "); 311 | } 312 | if (cmd == 0x64) { 313 | dscGlobal.pMsg += F("[Beep Command Group 1] "); 314 | } 315 | if (cmd == 0x69) { 316 | dscGlobal.pMsg += F("[Beep Command Group 2] "); 317 | } 318 | if (cmd == 0x39) { 319 | dscGlobal.pMsg += F("[Undefined command from panel] "); 320 | } 321 | if (cmd == 0xb1) { 322 | dscGlobal.pMsg += F("[Zone Configuration] "); 323 | } 324 | return cmd; // Return success 325 | } 326 | } 327 | 328 | byte DSC::decodeKeypad(void) 329 | { 330 | // ------------- Process the Keypad Data Word --------------- 331 | byte cmd = binToInt(dscGlobal.kWord,0,8); // Get the keypad pCmd (data word type/command) 332 | String btnStr = F("[Button] "); 333 | 334 | if (dscGlobal.kWord.indexOf("0") == -1) { 335 | // Skip this word if kWord is all 1's 336 | return 0; // Return failure 337 | } 338 | else { 339 | // This seems to be a valid word, try to process it 340 | dscGlobal.lastData = millis(); // Record the time (last data word was received) 341 | dscGlobal.oldKWord = dscGlobal.kWord; // This is a new/good word, save it 342 | 343 | byte kByte2 = binToInt(dscGlobal.kWord,8,8); 344 | 345 | // Interpret the data 346 | if (cmd == kOut) { 347 | if (kByte2 == one) 348 | dscGlobal.kMsg += btnStr + "1"; 349 | else if (kByte2 == two) 350 | dscGlobal.kMsg += btnStr + "2"; 351 | else if (kByte2 == three) 352 | dscGlobal.kMsg += btnStr + "3"; 353 | else if (kByte2 == four) 354 | dscGlobal.kMsg += btnStr + "4"; 355 | else if (kByte2 == five) 356 | dscGlobal.kMsg += btnStr + "5"; 357 | else if (kByte2 == six) 358 | dscGlobal.kMsg += btnStr + "6"; 359 | else if (kByte2 == seven) 360 | dscGlobal.kMsg += btnStr + "7"; 361 | else if (kByte2 == eight) 362 | dscGlobal.kMsg += btnStr + "8"; 363 | else if (kByte2 == nine) 364 | dscGlobal.kMsg += btnStr + "9"; 365 | else if (kByte2 == aster) 366 | dscGlobal.kMsg += btnStr + "*"; 367 | else if (kByte2 == zero) 368 | dscGlobal.kMsg += btnStr + "0"; 369 | else if (kByte2 == pound) 370 | dscGlobal.kMsg += btnStr + "#"; 371 | else if (kByte2 == stay) 372 | dscGlobal.kMsg += btnStr + F("Stay"); 373 | else if (kByte2 == away) 374 | dscGlobal.kMsg += btnStr + F("Away"); 375 | else if (kByte2 == chime) 376 | dscGlobal.kMsg += btnStr + F("Chime"); 377 | else if (kByte2 == reset) 378 | dscGlobal.kMsg += btnStr + F("Reset"); 379 | else if (kByte2 == kExit) 380 | dscGlobal.kMsg += btnStr + F("Exit"); 381 | else if (kByte2 == lArrow) // These arrow commands don't work every time 382 | dscGlobal.kMsg += btnStr + F("<"); 383 | else if (kByte2 == rArrow) // They are often reverse for unknown reasons 384 | dscGlobal.kMsg += btnStr + F(">"); 385 | else if (kByte2 == kOut) 386 | dscGlobal.kMsg += F("[Keypad Response]"); 387 | else { 388 | dscGlobal.kMsg += "[Keypad] 0x" + String(kByte2, HEX) + " (Unknown)"; 389 | } 390 | } 391 | 392 | if (cmd == fire) 393 | dscGlobal.kMsg += btnStr + F("Fire"); 394 | if (cmd == aux) 395 | dscGlobal.kMsg += btnStr + F("Auxillary"); 396 | if (cmd == panic) 397 | dscGlobal.kMsg += btnStr + F("Panic"); 398 | 399 | return cmd; // Return success 400 | } 401 | } 402 | 403 | const char* DSC::pnlFormat(void) 404 | { 405 | if (!dscGlobal.pCmd) return NULL; // return failure 406 | // Formats the panel binary string into bytes of binary data in the form: 407 | // 8 1 8 8 8 8 8 etc, and returns a pointer to the buffer 408 | pInfo.clear(); 409 | pInfo.print("[Panel] "); 410 | 411 | if (dscGlobal.pWord.length() > 8) { 412 | pInfo.print(binToChar(dscGlobal.pWord, 0, 8)); 413 | pInfo.print(" "); 414 | pInfo.print(binToChar(dscGlobal.pWord, 8, 9)); 415 | pInfo.print(" "); 416 | int grps = (dscGlobal.pWord.length() - 9) / 8; 417 | for(int i=0;i ((grps*8)+9)) 422 | pInfo.print(binToChar(dscGlobal.pWord, (grps*8)+9, dscGlobal.pWord.length())); 423 | } 424 | else 425 | pInfo.print(binToChar(dscGlobal.pWord, 0, dscGlobal.pWord.length())); 426 | 427 | if (pnlChkSum(dscGlobal.pWord)) pInfo.print(" (OK)"); 428 | 429 | return pInfo.getBuffer(); // return the pointer 430 | } 431 | 432 | const char* DSC::pnlRaw(void) 433 | { 434 | if (!dscGlobal.pCmd) return NULL; // return failure 435 | // Puts the raw word into a buffer and returns a pointer to the buffer 436 | pInfo.clear(); 437 | pInfo.print("[Panel] "); 438 | 439 | for(int i=0;i 8) { 471 | int grps = dscGlobal.kWord.length() / 8; 472 | for(int i=0;i (grps*8)) 477 | kInfo.print(binToChar(dscGlobal.kWord, (grps*8),dscGlobal.kWord.length())); 478 | } 479 | else 480 | kInfo.print(binToChar(dscGlobal.kWord, 0, dscGlobal.kWord.length())); 481 | 482 | return kInfo.getBuffer(); // return the pointer 483 | } 484 | 485 | int DSC::pnlChkSum(String &dataStr) 486 | { 487 | // Sums all but the last full byte (minus padding) and compares to last byte 488 | // returns 0 if not valid, and 1 if checksum valid 489 | int cSum = 0; 490 | if (dataStr.length() > 8) { 491 | cSum += binToInt(dataStr,0,8); 492 | int grps = (dataStr.length() - 9) / 8; 493 | for(int i=0;i= 100 26 | #include "Arduino.h" 27 | #else 28 | #include "WProgram.h" 29 | #endif 30 | 31 | class DSC : public Print // Initialize DSC as an extension of the print class 32 | { 33 | public: 34 | // Class to call to initialize the DSC Class 35 | // for example... DSC dsc; 36 | DSC(void); 37 | 38 | // Used to add the serial instance to the DSC Class 39 | int addSerial(void); 40 | 41 | // Included in the setup function of the user's sketch 42 | // Begins the the class, sets the pin modes, attaches the interrupt 43 | void begin(void); 44 | 45 | // Included in the main loop of user's sketch, checks and processes 46 | // the current panel and keypad words if able 47 | // Returns: 0 (No 48 | int process(void); 49 | 50 | // Decodes the panel and keypad words, returns 0 for failure and the command 51 | // byte for success 52 | byte decodePanel(void); 53 | byte decodeKeypad(void); 54 | 55 | // Ends, or de-constructs the class - NOT USED 56 | //int end(); 57 | 58 | // Returns the panel and keypad word in formatted binary (returns NULL if failure) 59 | const char* pnlFormat(void); 60 | const char* kpdFormat(void); 61 | 62 | // Returns the panel and keypad word in raw binary (returns NULL if failure) 63 | const char* pnlRaw(void); 64 | const char* kpdRaw(void); 65 | 66 | // Returns 1 if there is a valid checksum, 0 if not 67 | int pnlChkSum(String &dataStr); 68 | 69 | unsigned int binToInt(String &dataStr, int offset, int dataLen); 70 | const char* binToChar(String &dataStr, int offset, int endData); 71 | String byteToBin(byte b); 72 | 73 | void zeroArr(byte byteArr[]); 74 | void setCLK(int p); 75 | void setDTA_IN(int p); 76 | void setDTA_OUT(int p); 77 | void setLED(int p); 78 | 79 | // ----- Print class extension variables ----- 80 | virtual size_t write(uint8_t); 81 | virtual size_t write(const char *str); 82 | virtual size_t write(const uint8_t *buffer, size_t size); 83 | 84 | // Class level variables to hold time elements 85 | int yy, mm, dd, HH, MM, SS; 86 | bool timeAvailable; 87 | 88 | private: 89 | uint8_t intrNum; 90 | }; 91 | 92 | #endif 93 | -------------------------------------------------------------------------------- /DSCPanel/DSC_Constants.h: -------------------------------------------------------------------------------- 1 | /* DSC_Constants.h 2 | * Part of DSC Library 3 | * See COPYRIGHT.txt and LICENSE.txt for more information. 4 | * 5 | * It contains all of the constants used by the DSC class. 6 | * They have to be declared global in scope because they may be accessed by 7 | * the ISR and you cannot pass parameters nor objects to an ISR routine. 8 | * 9 | * In general, applications would not include this file. 10 | */ 11 | 12 | #ifndef DSC_Constants_h 13 | #define DSC_Constants_h 14 | 15 | // ----- Word/Timing Constants ----- 16 | const byte MAX_BITS = 128; // The length at which to overflow (max 255) 17 | const byte WORD_BITS = 108; // The expected length of a word (max 255) 18 | const int NEW_WORD_INTV = 5200; // New word indicator interval in us (Microseconds) 19 | const byte ARR_SIZE = 12; // (max 255) // NOT USED 20 | 21 | // ------ HEX LOOK-UP ARRAY ------ 22 | const char hex[] = "0123456789abcdef"; // HEX alphanumerics look-up array 23 | 24 | // ----- KEYPAD BUTTON VALUES ----- 25 | const byte kOut = 0xff; // 11111111 Usual 1st byte from keypad 26 | const byte k_ff = 0xff; // 11111111 Keypad CRC checksum 1? 27 | const byte k_7f = 0x7f; // 01111111 Keypad CRC checksum 2? 28 | // The following buttons data are in the 2nd byte: 29 | const byte one = 0x82; // 10000010 30 | const byte two = 0x85; // 10000101 31 | const byte three = 0x87; // 10000111 32 | const byte four = 0x88; // 10001000 33 | const byte five = 0x8b; // 10001011 34 | const byte six = 0x8d; // 10001101 35 | const byte seven = 0x8e; // 10001110 36 | const byte eight = 0x91; // 10010001 37 | const byte nine = 0x93; // 10010011 38 | const byte aster = 0x94; // 10010100 39 | const byte zero = 0x80; // 10000000 40 | const byte pound = 0x96; // 10010110 41 | const byte stay = 0xd7; // 11010111 42 | const byte away = 0xd8; // 11011000 43 | const byte chime = 0xdd; // 11011101 44 | const byte reset = 0xed; // 11101101 45 | const byte kExit = 0xf0; // 11110000 46 | const byte lArrow = 0xfb; // 11111011 47 | const byte rArrow = 0xf7; // 11110111 48 | // The following button's data are in the 1st byte, and these 49 | // seem to be sent twice, with a panel response in between: 50 | const byte fire = 0xbb; // 10111011 51 | const byte aux = 0xdd; // 11011101 52 | const byte panic = 0xee; // 11101110 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /DSCPanel/DSC_Globals.h: -------------------------------------------------------------------------------- 1 | /* DSC_Globals.h 2 | * Part of DSC Library 3 | * See COPYRIGHT.txt and LICENSE.txt for more information. 4 | * 5 | * It contains definition of global items which are used by the DSC class. 6 | * They have to be declared global in scope because they are accessed by 7 | * the ISR and you cannot pass parameters nor objects to an ISR routine. 8 | * 9 | * In general, applications would not include this file. 10 | */ 11 | #ifndef DSC_Globals_h 12 | #define DSC_Globals_h 13 | #include 14 | 15 | /* Timing data is stored in a buffer by the receiver object. It is an array of 16 | * uint16_t that should be at least 100 entries as defined by this default below. 17 | * However some IR sequences will require longer buffers especially those used for 18 | * air conditioner controls. In general we recommend you keep this value below 255 19 | * so that the index into the array can remain 8 bits. This library can handle larger 20 | * arrays however it will make your code longer in addition to taking more RAM. 21 | 22 | #define PNL_BUF_LENGTH 128 23 | #define KPD_BUF_LENGTH 128 24 | #if (PNL_BUF_LENGTH > 255) 25 | typedef uint16_t bufIndex_t; 26 | #else 27 | typedef uint8_t bufIndex_t; 28 | #endif 29 | #if (KPD_BUF_LENGTH > 255) 30 | typedef uint16_t bufIndex_t; 31 | #else 32 | typedef uint8_t bufIndex_t; 33 | #endif 34 | */ 35 | 36 | // ----- Input/Output Pins ----- 37 | extern byte CLK; // Keybus Yellow (Clock Line) 38 | extern byte DTA_IN; // Keybus Green (Data Line via V divider) 39 | extern byte DTA_OUT; // Keybus Green Output (Data Line through driver) 40 | extern byte LED; // LED pin on the arduino 41 | 42 | /* 43 | // Receiver states. This previously was enum but changed it to uint8_t 44 | // to guarantee it was a single atomic 8-bit value. 45 | #define STATE_UNKNOWN 0 46 | #define STATE_NEW_WORD_MARK 1 47 | #define STATE_TIMING_MARK 2 48 | #define STATE_TIMING_SPACE 3 49 | #define STATE_FINISHED 4 50 | #define STATE_RUNNING 5 51 | typedef uint8_t currentState_t; 52 | */ 53 | 54 | /* The structure contains information used by the ISR routine. Because we cannot 55 | * pass parameters to an ISR, vars must be global. Values which can be changed by 56 | * the ISR but are accessed outside the ISR must be volatile (for the most part) 57 | */ 58 | 59 | typedef struct 60 | { 61 | // ----- Keybus Word String Vars ----- 62 | String pBuild, pWord, oldPWord, pMsg; 63 | String kBuild, kWord, oldKWord, kMsg; 64 | byte pCmd, kCmd; 65 | 66 | // ----- Time Variables ----- 67 | unsigned long lastStatus; 68 | unsigned long lastData; 69 | // Volatile time variables, modified within ISR 70 | volatile unsigned long intervalTimer; 71 | volatile unsigned long clockChange; 72 | volatile unsigned long lastChange; 73 | volatile unsigned long lastRise; // NOT USED 74 | volatile unsigned long lastFall; // NOT USED 75 | 76 | volatile bool newWord; // NOT USED 77 | 78 | // ----- Byte Arrays ----- 79 | byte pBytes[]; // NOT USED 80 | byte kBytes[]; // NOT USED 81 | } 82 | dscGlobal_t; 83 | extern dscGlobal_t dscGlobal; //declared in DSC.cpp 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /DSCPanel/README.md: -------------------------------------------------------------------------------- 1 | # DSC_Panel 2 | Sketch and libraries to allow Arduino to act as a virtual Keypad for a DSC Powerseries 18XX alarm panel 3 | -------------------------------------------------------------------------------- /DSCPanel/archive/DSC_Example.ino: -------------------------------------------------------------------------------- 1 | // DSC_18XX Arduino Interface - Final 2 | // 3 | // This is the final version of this sketch prior to creation of the DSC library 4 | // It is recomended to use the new .ino sketches as examples with the DSC library 5 | // 6 | // Sketch to decode the keybus protocol on DSC PowerSeries 1816, 1832 and 1864 panels 7 | // -- Use the schematic at https://github.com/emcniece/Arduino-Keybus to connect the 8 | // keybus lines to the arduino via voltage divider circuits. Don't forget to 9 | // connect the Keybus Ground to Arduino Ground (not depicted on the circuit)! You 10 | // can also power your arduino from the keybus (+12 VDC, positive), depending on the 11 | // the type arduino board you have. 12 | // -- The initial code from the github website, while claiming to use the CRC32 Library 13 | // did not seem to reference that library. This sketch [may] use it, so be sure to 14 | // download the CRC32 library from the github above and include it in your sketch 15 | // (Though up till this point [29 Sep 16] I am not sure what it does... 16 | // 17 | // 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // ----- Input/Output Pins ----- 26 | const byte CLK = 3; // Keybus Yellow (Clock Line) 27 | const byte DTA_IN = 4; // Keybus Green (Data Line via V divider) 28 | const byte DTA_OUT = 8; // Keybus Green Output (Data Line through driver) 29 | const byte LED_PIN = 13; // LED pin on the arduino 30 | 31 | const char hex[] = "0123456789abcdef"; // HEX alphanumerics look-up array 32 | 33 | // ----- KEYPAD BUTTON VALUES ----- 34 | const byte kOut = 0xff; // 11111111 Usual 1st byte from keypad 35 | const byte k_ff = 0xff; // 11111111 Keypad CRC checksum 1? 36 | const byte k_7f = 0x7f; // 01111111 Keypad CRC checksum 2? 37 | // The following buttons data are in the 2nd byte: 38 | const byte one = 0x82; // 10000010 39 | const byte two = 0x85; // 10000101 40 | const byte three = 0x87; // 10000111 41 | const byte four = 0x88; // 10001000 42 | const byte five = 0x8b; // 10001011 43 | const byte six = 0x8d; // 10001101 44 | const byte seven = 0x8e; // 10001110 45 | const byte eight = 0x91; // 10010001 46 | const byte nine = 0x93; // 10010011 47 | const byte aster = 0x94; // 10010100 48 | const byte zero = 0x80; // 10000000 49 | const byte pound = 0x96; // 10010110 50 | const byte stay = 0xd7; // 11010111 51 | const byte away = 0xd8; // 11011000 52 | const byte chime = 0xdd; // 11011101 53 | const byte reset = 0xed; // 11101101 54 | const byte kExit = 0xf0; // 11110000 55 | const byte lArrow = 0xfb; // 11111011 56 | const byte rArrow = 0xf7; // 11110111 57 | // The following button's data are in the 1st byte, and these 58 | // seem to be sent twice, with a panel response in between: 59 | const byte fire = 0xbb; // 10111011 60 | const byte aux = 0xdd; // 11011101 61 | const byte panic = 0xee; // 11101110 62 | 63 | // ----- Word/Timing Constants ----- 64 | const byte MAX_BITS = 200; // Max of 254 65 | const int NEW_WORD_INTV = 5200; // New word indicator interval in us (Microseconds) 66 | 67 | // ----- Keybus Word String Vars ----- 68 | String pBuild="", pWord="", oldPWord="", pMsg=""; 69 | String kBuild="", kWord="", oldKWord="", kMsg=""; 70 | 71 | // ----- Byte Array Variables ----- 72 | const byte ARR_SIZE = 14; // Max of 254 // NOT USED 73 | byte pBytes[ARR_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // NOT USED 74 | byte kBytes[ARR_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // NOT USED 75 | 76 | // ----- Time Variables ----- 77 | time_t t = now(); // Initialize time (TimeLib.h) 78 | unsigned long lastStatus = 0; 79 | unsigned long lastData = 0; 80 | // Volatile variables, modified within ISR 81 | volatile unsigned long intervalTimer = 0; // NOT USED 82 | volatile unsigned long clockChange = 0; 83 | volatile unsigned long lastChange = 0; 84 | volatile unsigned long lastRise = 0; // NOT USED 85 | volatile unsigned long lastFall = 0; // NOT USED 86 | volatile bool newWord = false; // NOT USED 87 | 88 | // ----- Ethernet/WiFi Variables ----- 89 | bool newClient = false; // Whether the client is new or not 90 | bool streamData = false; // Was the request to stream data? (/STREAM) 91 | // Enter a MAC address and IP address for the controller: 92 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 93 | // Set a manual IP address in case DHCP Fails: 94 | IPAddress ip(192, 168, 1, 169); 95 | 96 | char buf[100]; // NOT USED 97 | TextBuffer message(128); // Initialize TextBuffer.h for print/client message 98 | CRC32 crc32; // Initialize CRC // NOT USED 99 | 100 | EthernetServer server(80); // Start Ethernet Server on Port 80 101 | EthernetClient client; // Start Ethernet Client 102 | 103 | void setup() 104 | { 105 | pinMode(CLK, INPUT); 106 | pinMode(DTA_IN, INPUT); 107 | pinMode(DTA_OUT, OUTPUT); 108 | Serial.begin(115200); 109 | Serial.flush(); 110 | Serial.println(F("DSC Powerseries 18XX")); 111 | Serial.println(F("Key Bus Monitor")); 112 | Serial.println(F("Initializing")); 113 | 114 | message.begin(); // Begin the message buffer, allocate memory 115 | 116 | // Start the Ethernet connection and the server (Try to use DHCP): 117 | Serial.println(F("Trying to get an IP address using DHCP...")); 118 | if (Ethernet.begin(mac) == 0) { 119 | Serial.println(F("Trying to manually set IP address...")); 120 | // Initialize the ethernet device using manual IP address: 121 | if (1 == 0) { // This logic is "shut off", doesn't work with Ethernet client 122 | Serial.println(F("No ethernet connection")); 123 | } 124 | else { 125 | Ethernet.begin(mac, ip); 126 | server.begin(); 127 | } 128 | } 129 | Serial.print(F("Server is at: ")); 130 | Serial.println(Ethernet.localIP()); 131 | Serial.println(); 132 | 133 | // Attach interrupt on the CLK pin 134 | attachInterrupt(digitalPinToInterrupt(CLK), clkCalled, CHANGE); 135 | // Moved to the last line in setup, otherwise may interrupt init message 136 | // Changed from RISING to CHANGE to read both panel and keypad data 137 | } 138 | 139 | // ----------------------------------------------------------------------------------------------------------------------- 140 | // ------------------------------------------------------- MAIN LOOP --------------------------------------------------- 141 | // ----------------------------------------------------------------------------------------------------------------------- 142 | 143 | void loop() 144 | { 145 | // -------------- Check for a new connection -------------- 146 | if (!client.connected()) { 147 | client = server.available(); 148 | newClient = true; 149 | streamData = false; 150 | } 151 | if (client and newClient) { 152 | float connTimeout = millis() + 500; // Set the timeout 500 ms from now 153 | while(true) { 154 | // If there is available data 155 | if (client.available()) { 156 | // Read the first two words of the request 157 | String request_type = client.readStringUntil(' '); // Read till the first space 158 | String request = client.readStringUntil(' '); // Read till the next space 159 | Serial.print(F("Request: ")); 160 | Serial.print(request_type); 161 | Serial.print(" "); 162 | Serial.println(request); 163 | //client.flush(); 164 | if (request == "/STREAM") { 165 | streamData = true; 166 | newClient = false; 167 | for (int i=0; i <= 30; i++){ 168 | client.println(F("Empty --------------------------------------------------")); 169 | } 170 | } 171 | else { 172 | streamData = false; 173 | client.stop(); 174 | newClient = true; 175 | } 176 | break; 177 | } 178 | // If not, wait up to [connTimeout] ms for data 179 | if (millis() >= connTimeout) { 180 | Serial.println(F("Connection Timeout")); 181 | streamData = false; 182 | client.stop(); 183 | newClient = true; 184 | } 185 | delay(5); 186 | } 187 | } 188 | 189 | // ----------------- Turn on/off LED ------------------ 190 | if ((millis() - lastStatus) > 500) 191 | digitalWrite(LED_PIN,0); // Turn LED OFF (no recent status command [0x05]) 192 | else 193 | digitalWrite(LED_PIN,1); // Turn LED ON (recent status command [0x05]) 194 | 195 | // --------------- Print No Data Message -------------- (FOR DEBUG PURPOSES) 196 | if ((millis() - lastData) > 20000) { 197 | // Print no data message if there is no new data in XX time (ms) 198 | Serial.println(F("--- No data for 20 seconds ---")); 199 | if (client.connected() and streamData) { 200 | client.println(F("--- No data for 20 seconds ---")); 201 | } 202 | lastData = millis(); // Reset the timer 203 | } 204 | 205 | // ---------------- Get/process incoming data ---------------- 206 | 207 | // The normal clock frequency is 1 Hz or one cycle every ms (1000 us) 208 | // The new word marker is clock high for about 15 ms (15000 us) 209 | 210 | //if (micros() < (lastChange + NEW_WORD_INTV)) return; 211 | if ((intervalTimer < (NEW_WORD_INTV + 200)) || (pBuild.length() < 8)) return; 212 | 213 | // If it takes more than 1500 us (1.5 ms), the data word is complete, and it is now time 214 | // to process the words. The new word marker is about 15 ms long, which means we have 215 | // about 13.5 ms left to process and output the panel and keypad words before the next 216 | // words begin. 217 | 218 | pWord = pBuild; // Save the complete panel raw data bytes sentence 219 | pBuild = ""; // Reset the raw data panel word being built 220 | pMsg = ""; // Initialize panel message for output 221 | //kWord = kBuild; // Save the complete keypad raw data bytes sentence 222 | //kBuild = ""; // Reset the raw data bytes keypad word being built 223 | kMsg = ""; // Initialize keypad message for output 224 | byte pCmd = decodePanel(); // Decode the panel binary, return command byte, or 0 225 | byte kCmd = decodeKeypad(); // Decode the keypad binary, return command byte, or 0 226 | 227 | if (pCmd) { 228 | // ------------ Write the panel message to buffer ------------ 229 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 230 | message.write(F("[Panel] ")); 231 | pnlBinary(pWord); // Writes formatted raw data to the message buffer 232 | Serial.print(message.getBuffer()); 233 | 234 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 235 | message.write(formatTime(now())); // Add the time stamp 236 | message.write(" "); 237 | if (String(pCmd,HEX).length() == 1) 238 | message.write("0"); // Write a leading zero to a single digit HEX 239 | message.write(String(pCmd,HEX)); 240 | message.write("("); 241 | message.write(pCmd); 242 | message.write("): "); 243 | message.writeln(pMsg); 244 | 245 | // ------------ Print the message ------------ 246 | Serial.print(message.getBuffer()); 247 | if (client.connected() and streamData) { 248 | client.write(message.getBuffer(), message.getSize()); 249 | } 250 | } 251 | 252 | if (kCmd) { 253 | // ------------ Write the keypad message to buffer ------------ 254 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 255 | message.write(F("[Keypad] ")); 256 | kpdBinary(kWord); // Writes formatted raw data to the message buffer 257 | Serial.print(message.getBuffer()); 258 | 259 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 260 | message.write(formatTime(now())); // Add the time stamp 261 | message.write(" "); 262 | if (String(kCmd,HEX).length() == 1) 263 | message.write("0"); // Write a leading zero to a single digit HEX 264 | message.write(String(kCmd,HEX)); 265 | message.write("("); 266 | message.write(kCmd); 267 | message.write("): "); 268 | message.writeln(kMsg); 269 | 270 | // ------------ Print the message ------------ 271 | Serial.print(message.getBuffer()); 272 | if (client.connected() and streamData) { 273 | client.write(message.getBuffer(), message.getSize()); 274 | } 275 | } 276 | } 277 | 278 | // ----------------------------------------------------------------------------------------------------------------------- 279 | // ------------------------------------------------------- FUNCTIONS --------------------------------------------------- 280 | // ----------------------------------------------------------------------------------------------------------------------- 281 | 282 | void clkCalled() 283 | { 284 | clockChange = micros(); // Save the current clock change time 285 | intervalTimer = (clockChange - lastChange); // Determine interval since last clock change 286 | //if (intervalTimer > NEW_WORD_INTV) newWord = true; 287 | //else newWord = false; 288 | if (intervalTimer > (NEW_WORD_INTV - 200)) { 289 | kWord = kBuild; // Save the complete keypad raw data bytes sentence 290 | kBuild = ""; // Reset the raw data bytes keypad word being built 291 | } 292 | lastChange = clockChange; // Re-save the current change time as last change time 293 | 294 | if (digitalRead(CLK)) { // If clock line is going HIGH, this is PANEL data 295 | lastRise = lastChange; // Set the lastRise time 296 | if (pBuild.length() <= MAX_BITS) { // Limit the string size to something manageable 297 | //delayMicroseconds(120); // Delay for 120 us to get a valid data line read 298 | if (digitalRead(DTA_IN)) pBuild += "1"; else pBuild += "0"; 299 | } 300 | } 301 | else { // Otherwise, it's going LOW, this is KEYPAD data 302 | lastFall = lastChange; // Set the lastFall time 303 | if (kBuild.length() <= MAX_BITS) { // Limit the string size to something manageable 304 | //delayMicroseconds(200); // Delay for 300 us to get a valid data line read 305 | if (digitalRead(DTA_IN)) kBuild += "1"; else kBuild += "0"; 306 | } 307 | } 308 | } 309 | 310 | void pnlBinary(String &dataStr) 311 | { 312 | // Formats the referenced string into bytes of binary data in the form: 313 | // 8 1 8 8 8 8 8 etc, and then prints each segment 314 | if (dataStr.length() > 8) { 315 | message.write(dataStr.substring(0,8)); 316 | message.write(" "); 317 | message.write(dataStr.substring(8,9)); 318 | message.write(" "); 319 | int grps = (dataStr.length() - 9) / 8; 320 | for(int i=0;i ((grps*8)+9)) 325 | message.write(dataStr.substring((grps*8)+9,dataStr.length())); 326 | } 327 | else { 328 | message.write(dataStr); 329 | } 330 | if (pnlChkSum(dataStr)) message.write(" (OK)"); 331 | message.writeln(); 332 | } 333 | 334 | bool pnlChkSum(String &dataStr) 335 | { 336 | // Sums all but the last full byte (minus padding) and compares to last byte 337 | // returns 0 if not valid, and 1 if checksum valid 338 | int cSum = 0; 339 | if (dataStr.length() > 8) { 340 | cSum += binToInt(dataStr,0,8); 341 | int grps = (dataStr.length() - 9) / 8; 342 | for(int i=0;i 8) { 367 | int grps = dataStr.length() / 8; 368 | for(int i=0;i (grps*8)) 373 | message.write(dataStr.substring((grps*8),dataStr.length())); 374 | } 375 | else { 376 | message.write(dataStr); 377 | } 378 | message.writeln(); 379 | } 380 | 381 | unsigned int binToInt(String &dataStr, int offset, int dataLen) 382 | { 383 | // Returns the value of the binary data in the String from "offset" to "dataLen" as an int 384 | int iBuf = 0; 385 | for(int j=0;j 0) { 485 | if (master) pMsg += F(", Master Code"); 486 | else pMsg += F(", User Code"); 487 | user += 1; // shift to 1-32, 33, 34 488 | if (user > 34) user += 5; // convert to system code 40, 41, 42 489 | pMsg += " " + String(user); 490 | } 491 | } 492 | 493 | if (cmd == 0x27) 494 | { 495 | pMsg += F("[Zones A] "); 496 | int zones = binToInt(pWord,8+1+8+8+8+8,8); 497 | if (zones & 1) pMsg += "1"; 498 | if (zones & 2) pMsg += "2"; 499 | if (zones & 4) pMsg += "3"; 500 | if (zones & 8) pMsg += "4"; 501 | if (zones & 16) pMsg += "5"; 502 | if (zones & 32) pMsg += "6"; 503 | if (zones & 64) pMsg += "7"; 504 | if (zones & 128) pMsg += "8"; 505 | if (zones == 0) pMsg += "Ready "; 506 | } 507 | 508 | if (cmd == 0x2d) 509 | { 510 | pMsg += F("[Zones B] "); 511 | int zones = binToInt(pWord,8+1+8+8+8+8,8); 512 | if (zones & 1) pMsg += "9"; 513 | if (zones & 2) pMsg += "10"; 514 | if (zones & 4) pMsg += "11"; 515 | if (zones & 8) pMsg += "12"; 516 | if (zones & 16) pMsg += "13"; 517 | if (zones & 32) pMsg += "14"; 518 | if (zones & 64) pMsg += "15"; 519 | if (zones & 128) pMsg += "16"; 520 | if (zones == 0) pMsg += "Ready "; 521 | } 522 | 523 | if (cmd == 0x34) 524 | { 525 | pMsg += F("[Zones C] "); 526 | int zones = binToInt(pWord,8+1+8+8+8+8,8); 527 | if (zones & 1) pMsg += "17"; 528 | if (zones & 2) pMsg += "18"; 529 | if (zones & 4) pMsg += "19"; 530 | if (zones & 8) pMsg += "20"; 531 | if (zones & 16) pMsg += "21"; 532 | if (zones & 32) pMsg += "22"; 533 | if (zones & 64) pMsg += "23"; 534 | if (zones & 128) pMsg += "24"; 535 | if (zones == 0) pMsg += "Ready "; 536 | } 537 | 538 | if (cmd == 0x3e) 539 | { 540 | pMsg += F("[Zones D] "); 541 | int zones = binToInt(pWord,8+1+8+8+8+8,8); 542 | if (zones & 1) pMsg += "25"; 543 | if (zones & 2) pMsg += "26"; 544 | if (zones & 4) pMsg += "27"; 545 | if (zones & 8) pMsg += "28"; 546 | if (zones & 16) pMsg += "29"; 547 | if (zones & 32) pMsg += "30"; 548 | if (zones & 64) pMsg += "31"; 549 | if (zones & 128) pMsg += "32"; 550 | if (zones == 0) pMsg += "Ready "; 551 | } 552 | // --- The other 32 zones for a 1864 panel need to be added after this --- 553 | 554 | if (cmd == 0x11) { 555 | pMsg += F("[Keypad Query] "); 556 | } 557 | if (cmd == 0x0a) { 558 | pMsg += F("[Panel Program Mode] "); 559 | } 560 | if (cmd == 0x5d) { 561 | pMsg += F("[Alarm Memory Group 1] "); 562 | } 563 | if (cmd == 0x63) { 564 | pMsg += F("[Alarm Memory Group 2] "); 565 | } 566 | if (cmd == 0x64) { 567 | pMsg += F("[Beep Command Group 1] "); 568 | } 569 | if (cmd == 0x69) { 570 | pMsg += F("[Beep Command Group 2] "); 571 | } 572 | if (cmd == 0x39) { 573 | pMsg += F("[Undefined command from panel] "); 574 | } 575 | if (cmd == 0xb1) { 576 | pMsg += F("[Zone Configuration] "); 577 | } 578 | return cmd; // Return success 579 | } 580 | } 581 | 582 | static byte decodeKeypad() 583 | { 584 | // ------------- Process the Keypad Data Word --------------- 585 | byte cmd = binToInt(kWord,0,8); // Get the keypad pCmd (data word type/command) 586 | String btnStr = F("[Button] "); 587 | 588 | if (kWord.indexOf("0") == -1) { 589 | // Skip this word if kWord is all 1's 590 | return 0; // Return failure 591 | } 592 | else { 593 | // This seems to be a valid word, try to process it 594 | lastData = millis(); // Record the time (last data word was received) 595 | oldKWord = kWord; // This is a new/good word, save it 596 | 597 | byte kByte2 = binToInt(kWord,8,8); 598 | 599 | // Interpret the data 600 | if (cmd == kOut) { 601 | if (kByte2 == one) 602 | kMsg += btnStr + "1"; 603 | else if (kByte2 == two) 604 | kMsg += btnStr + "2"; 605 | else if (kByte2 == three) 606 | kMsg += btnStr + "3"; 607 | else if (kByte2 == four) 608 | kMsg += btnStr + "4"; 609 | else if (kByte2 == five) 610 | kMsg += btnStr + "5"; 611 | else if (kByte2 == six) 612 | kMsg += btnStr + "6"; 613 | else if (kByte2 == seven) 614 | kMsg += btnStr + "7"; 615 | else if (kByte2 == eight) 616 | kMsg += btnStr + "8"; 617 | else if (kByte2 == nine) 618 | kMsg += btnStr + "9"; 619 | else if (kByte2 == aster) 620 | kMsg += btnStr + "*"; 621 | else if (kByte2 == zero) 622 | kMsg += btnStr + "0"; 623 | else if (kByte2 == pound) 624 | kMsg += btnStr + "#"; 625 | else if (kByte2 == stay) 626 | kMsg += btnStr + F("Stay"); 627 | else if (kByte2 == away) 628 | kMsg += btnStr + F("Away"); 629 | else if (kByte2 == chime) 630 | kMsg += btnStr + F("Chime"); 631 | else if (kByte2 == reset) 632 | kMsg += btnStr + F("Reset"); 633 | else if (kByte2 == kExit) 634 | kMsg += btnStr + F("Exit"); 635 | else if (kByte2 == lArrow) // These arrow commands don't work every time 636 | kMsg += btnStr + F("<"); 637 | else if (kByte2 == rArrow) // They are often reverse for unknown reasons 638 | kMsg += btnStr + F(">"); 639 | else if (kByte2 == kOut) 640 | kMsg += F("[Keypad Response]"); 641 | else { 642 | kMsg += "[Keypad] 0x" + String(kByte2, HEX) + " (Unknown)"; 643 | } 644 | } 645 | 646 | if (cmd == fire) 647 | kMsg += btnStr + F("Fire"); 648 | if (cmd == aux) 649 | kMsg += btnStr + F("Auxillary"); 650 | if (cmd == panic) 651 | kMsg += btnStr + F("Panic"); 652 | 653 | return cmd; // Return success 654 | } 655 | } 656 | // ----------------------------------------------------------------------------------------------------------------------- 657 | // ---------------------------------------------------------- END ------------------------------------------------------ 658 | // ----------------------------------------------------------------------------------------------------------------------- 659 | -------------------------------------------------------------------------------- /DSCPanel/examples/DSCPanelExample/DSCPanelExample.ino: -------------------------------------------------------------------------------- 1 | // DSC_18XX Arduino Interface - Example with Ethernet Functionality 2 | // 3 | // Sketch to decode the keybus protocol on DSC PowerSeries 1816, 1832 and 1864 panels 4 | // -- Use the schematic at https://github.com/emcniece/Arduino-Keybus to connect the 5 | // keybus lines to the arduino via voltage divider circuits. Don't forget to 6 | // connect the Keybus Ground to Arduino Ground (not depicted on the circuit)! You 7 | // can also power your arduino from the keybus (+12 VDC, positive), depending on the 8 | // the type arduino board you have. 9 | // 10 | // 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | // ----- Ethernet/WiFi Variables ----- 19 | bool newClient = false; // Whether the client is new or not 20 | bool streamData = false; // Was the request to stream data? (/STREAM) 21 | // Enter a MAC address and IP address for the controller: 22 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 23 | // Set a manual IP address in case DHCP Fails: 24 | IPAddress ip(192, 168, 1, 169); 25 | 26 | DSC dsc; // Initialize DSC.h library as "dsc" 27 | TextBuffer message(128); // Initialize TextBuffer.h for print/client message 28 | TextBuffer timeBuf(24); // Initialize TextBuffer.h for formatted time message 29 | 30 | EthernetServer server(80); // Start Ethernet Server on Port 80 31 | EthernetClient client; // Start Ethernet Client 32 | 33 | // -------------------------------------------------------------------------------------------------------- 34 | // ----------------------------------------------- SETUP ------------------------------------------------ 35 | // -------------------------------------------------------------------------------------------------------- 36 | 37 | void setup() 38 | { 39 | Serial.begin(115200); 40 | Serial.flush(); 41 | Serial.println(F("DSC Powerseries 18XX")); 42 | Serial.println(F("Key Bus Monitor")); 43 | Serial.println(F("Initializing")); 44 | 45 | message.begin(); // Begin the message buffer, allocate memory 46 | timeBuf.begin(); // Begin the formatted time buffer, allocate memory 47 | 48 | // Start the Ethernet connection and the server (Try to use DHCP): 49 | Serial.println(F("Trying to get an IP address using DHCP...")); 50 | if (Ethernet.begin(mac) == 0) { 51 | Serial.println(F("Trying to manually set IP address...")); 52 | // Initialize the ethernet device using manual IP address: 53 | if (1 == 0) { // This logic is "shut off", doesn't work with Ethernet client 54 | Serial.println(F("No ethernet connection")); 55 | } 56 | else { 57 | Ethernet.begin(mac, ip); 58 | server.begin(); 59 | } 60 | } 61 | Serial.print(F("Server is at: ")); 62 | Serial.println(Ethernet.localIP()); 63 | Serial.println(); 64 | 65 | dsc.setCLK(3); // Sets the clock pin to 3 (example, this is also the default) 66 | // setDTA_IN( ), setDTA_OUT( ) and setLED( ) can also be called 67 | dsc.begin(); // Start the dsc library (Sets the pin modes) 68 | } 69 | 70 | // -------------------------------------------------------------------------------------------------------- 71 | // --------------------------------------------- MAIN LOOP ---------------------------------------------- 72 | // -------------------------------------------------------------------------------------------------------- 73 | 74 | void loop() 75 | { 76 | // -------------- Check for a new connection -------------- 77 | if (!client.connected()) { 78 | client = server.available(); 79 | newClient = true; 80 | streamData = false; 81 | } 82 | if (client and newClient) { 83 | float connTimeout = millis() + 500; // Set the timeout 500 ms from now 84 | while(true) { 85 | // If there is available data 86 | if (client.available()) { 87 | // Read the first two words of the request 88 | String request_type = client.readStringUntil(' '); // Read till the first space 89 | String request = client.readStringUntil(' '); // Read till the next space 90 | Serial.print(F("Request: ")); 91 | Serial.print(request_type); 92 | Serial.print(" "); 93 | Serial.println(request); 94 | //client.flush(); 95 | if (request == "/STREAM") { 96 | streamData = true; 97 | newClient = false; 98 | for (int i=0; i <= 30; i++){ 99 | client.println(F("Empty --------------------------------------------------")); 100 | } 101 | } 102 | else { 103 | streamData = false; 104 | client.stop(); 105 | newClient = true; 106 | } 107 | break; 108 | } 109 | // If not, wait up to [connTimeout] ms for data 110 | if (millis() >= connTimeout) { 111 | Serial.println(F("Connection Timeout")); 112 | streamData = false; 113 | client.stop(); 114 | newClient = true; 115 | } 116 | delay(5); 117 | } 118 | } 119 | 120 | // --------------- Print No Data Message -------------- (FOR DEBUG PURPOSES) 121 | if ((millis() - dscGlobal.lastData) > 20000) { 122 | // Print no data message if there is no new data in XX time (ms) 123 | Serial.println(F("--- No data for 20 seconds ---")); 124 | if (client.connected() and streamData) { 125 | client.println(F("--- No data for 20 seconds ---")); 126 | } 127 | dscGlobal.lastData = millis(); // Reset the timer 128 | } 129 | 130 | // ---------------- Get/process incoming data ---------------- 131 | if (!dsc.process()) return; 132 | 133 | if (dsc.timeAvailable) setDscTime(); // Attempt to update the system time 134 | 135 | if (dscGlobal.pCmd) { 136 | // ------------ Print the formatted raw data ------------ 137 | //Serial.print(message.getBuffer()); // Prints unformatted word to serial 138 | Serial.println(dsc.pnlFormat()); 139 | 140 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 141 | message.print(formatTime(now())); // Add the time stamp 142 | message.print(" "); 143 | if (String(dscGlobal.pCmd,HEX).length() == 1) 144 | message.print("0"); // Write a leading zero to a single digit HEX 145 | message.print(String(dscGlobal.pCmd,HEX)); 146 | message.print("("); 147 | message.print(dscGlobal.pCmd); 148 | message.print("): "); 149 | message.println(dscGlobal.pMsg); 150 | 151 | // ------------ Print the message ------------ 152 | Serial.print(message.getBuffer()); 153 | if (client.connected() and streamData) { 154 | client.write(message.getBuffer(), message.getSize()); 155 | } 156 | } 157 | 158 | if (dscGlobal.kCmd) { 159 | // ------------ Print the formatted raw data ------------ 160 | //Serial.print(message.getBuffer()); // Prints unformatted word to serial 161 | Serial.println(dsc.kpdFormat()); 162 | 163 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 164 | message.print(formatTime(now())); // Add the time stamp 165 | message.print(" "); 166 | if (String(dscGlobal.kCmd,HEX).length() == 1) 167 | message.print("0"); // Write a leading zero to a single digit HEX 168 | message.print(String(dscGlobal.kCmd,HEX)); 169 | message.print("("); 170 | message.print(dscGlobal.kCmd); 171 | message.print("): "); 172 | message.println(dscGlobal.kMsg); 173 | 174 | // ------------ Print the message ------------ 175 | Serial.print(message.getBuffer()); 176 | if (client.connected() and streamData) { 177 | client.write(message.getBuffer(), message.getSize()); 178 | } 179 | } 180 | } 181 | 182 | // -------------------------------------------------------------------------------------------------------- 183 | // --------------------------------------------- FUNCTIONS ---------------------------------------------- 184 | // -------------------------------------------------------------------------------------------------------- 185 | 186 | const char* formatTime(time_t cTime) 187 | { 188 | timeBuf.clear(); 189 | timeBuf.print(digits(hour(cTime))); timeBuf.print(":"); 190 | timeBuf.print(digits(minute(cTime))); timeBuf.print(":"); 191 | timeBuf.print(digits(second(cTime))); timeBuf.print(", "); 192 | timeBuf.print(month(cTime)); timeBuf.print("/"); 193 | timeBuf.print(day(cTime)); timeBuf.print("/"); 194 | timeBuf.print(year(cTime)); 195 | 196 | return timeBuf.getBuffer(); 197 | } 198 | 199 | /* // OLD formatTime - String was unstable 200 | String formatTime(time_t cTime) 201 | { 202 | return digits(hour(cTime)) + ":" + digits(minute(cTime)) + ":" + digits(second(cTime)) + ", " + 203 | String(month(cTime)) + "/" + String(day(cTime)) + "/" + String(year(cTime)); //+ "/20" 204 | } 205 | */ 206 | 207 | String digits(unsigned int val) 208 | { 209 | // Take an unsigned int and convert to a string with appropriate leading zeros 210 | if (val < 10) return "0" + String(val); 211 | else return String(val); 212 | } 213 | 214 | void setDscTime() 215 | { 216 | setTime(dsc.HH,dsc.MM,dsc.SS,dsc.dd,dsc.mm,dsc.yy); 217 | if (timeStatus() == timeSet) { 218 | Serial.println(F("Time Synchronized")); 219 | } 220 | else { 221 | Serial.println(F("Time Sync Error")); 222 | } 223 | } 224 | 225 | // -------------------------------------------------------------------------------------------------------- 226 | // ------------------------------------------------ END ------------------------------------------------- 227 | // -------------------------------------------------------------------------------------------------------- 228 | -------------------------------------------------------------------------------- /DSCPanel/examples/DSCPanelNoEthernet/DSCPanelNoEthernet.ino: -------------------------------------------------------------------------------- 1 | // DSC_18XX Arduino Interface - Example with no Ethernet functionality 2 | // 3 | // Sketch to decode the keybus protocol on DSC PowerSeries 1816, 1832 and 1864 panels 4 | // -- Use the schematic at https://github.com/emcniece/Arduino-Keybus to connect the 5 | // keybus lines to the arduino via voltage divider circuits. Don't forget to 6 | // connect the Keybus Ground to Arduino Ground (not depicted on the circuit)! You 7 | // can also power your arduino from the keybus (+12 VDC, positive), depending on the 8 | // the type arduino board you have. 9 | // 10 | // 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | DSC dsc; // Initialize DSC.h library as "dsc" 17 | TextBuffer message(128); // Initialize TextBuffer.h for print/client message 18 | TextBuffer timeBuf(24); // Initialize TextBuffer.h for formatted time message 19 | 20 | // -------------------------------------------------------------------------------------------------------- 21 | // ----------------------------------------------- SETUP ------------------------------------------------ 22 | // -------------------------------------------------------------------------------------------------------- 23 | 24 | void setup() 25 | { 26 | Serial.begin(115200); 27 | Serial.flush(); 28 | Serial.println(F("DSC Powerseries 18XX")); 29 | Serial.println(F("Key Bus Interface")); 30 | Serial.println(F("Initializing")); 31 | 32 | message.begin(); // Begin the message buffer, allocate memory 33 | timeBuf.begin(); // Begin the formatted time buffer, allocate memory 34 | 35 | dsc.setCLK(3); // Sets the clock pin to 3 (example, this is also the default) 36 | // setDTA_IN( ), setDTA_OUT( ) and setLED( ) can also be called 37 | dsc.begin(); // Start the dsc library (Sets the pin modes) 38 | } 39 | 40 | // -------------------------------------------------------------------------------------------------------- 41 | // --------------------------------------------- MAIN LOOP ---------------------------------------------- 42 | // -------------------------------------------------------------------------------------------------------- 43 | 44 | void loop() 45 | { 46 | 47 | // --------------- Print No Data Message -------------- (FOR DEBUG PURPOSES) 48 | if ((millis() - dscGlobal.lastData) > 20000) { 49 | // Print no data message if there is no new data in XX time (ms) 50 | Serial.println(F("--- No data for 20 seconds ---")); 51 | dscGlobal.lastData = millis(); // Reset the timer 52 | } 53 | 54 | // ---------------- Get/process incoming data ---------------- 55 | if (!dsc.process()) return; 56 | 57 | if (dsc.timeAvailable) setDscTime(); // Attempt to update the system time 58 | 59 | if (dscGlobal.pCmd) { 60 | // ------------ Print the formatted raw data ------------ 61 | //Serial.print(message.getBuffer()); // Prints unformatted word to serial 62 | Serial.println(dsc.pnlFormat()); 63 | 64 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 65 | message.print(formatTime(now())); // Add the time stamp 66 | message.print(" "); 67 | if (String(dscGlobal.pCmd,HEX).length() == 1) 68 | message.print("0"); // Write a leading zero to a single digit HEX 69 | message.print(String(dscGlobal.pCmd,HEX)); 70 | message.print("("); 71 | message.print(dscGlobal.pCmd); 72 | message.print("): "); 73 | message.println(dscGlobal.pMsg); 74 | 75 | // ------------ Print the message ------------ 76 | Serial.print(message.getBuffer()); 77 | } 78 | 79 | if (dscGlobal.kCmd) { 80 | // ------------ Print the formatted raw data ------------ 81 | //Serial.print(message.getBuffer()); // Prints unformatted word to serial 82 | Serial.println(dsc.kpdFormat()); 83 | 84 | message.clear(); // Clear the message Buffer (this sets first byte to 0) 85 | message.print(formatTime(now())); // Add the time stamp 86 | message.print(" "); 87 | if (String(dscGlobal.kCmd,HEX).length() == 1) 88 | message.print("0"); // Write a leading zero to a single digit HEX 89 | message.print(String(dscGlobal.kCmd,HEX)); 90 | message.print("("); 91 | message.print(dscGlobal.kCmd); 92 | message.print("): "); 93 | message.println(dscGlobal.kMsg); 94 | 95 | // ------------ Print the message ------------ 96 | Serial.print(message.getBuffer()); 97 | } 98 | } 99 | 100 | // -------------------------------------------------------------------------------------------------------- 101 | // --------------------------------------------- FUNCTIONS ---------------------------------------------- 102 | // -------------------------------------------------------------------------------------------------------- 103 | 104 | 105 | const char* formatTime(time_t cTime) 106 | { 107 | timeBuf.clear(); 108 | timeBuf.print(digits(hour(cTime))); timeBuf.print(":"); 109 | timeBuf.print(digits(minute(cTime))); timeBuf.print(":"); 110 | timeBuf.print(digits(second(cTime))); timeBuf.print(", "); 111 | timeBuf.print(month(cTime)); timeBuf.print("/"); 112 | timeBuf.print(day(cTime)); timeBuf.print("/"); 113 | timeBuf.print(year(cTime)); 114 | 115 | return timeBuf.getBuffer(); 116 | } 117 | 118 | /* // OLD formatTime - String was unstable 119 | String formatTime(time_t cTime) 120 | { 121 | return digits(hour(cTime)) + ":" + digits(minute(cTime)) + ":" + digits(second(cTime)) + ", " + 122 | String(month(cTime)) + "/" + String(day(cTime)) + "/" + String(year(cTime)); //+ "/20" 123 | } 124 | */ 125 | 126 | String digits(unsigned int val) 127 | { 128 | // Take an unsigned int and convert to a string with appropriate leading zeros 129 | if (val < 10) return "0" + String(val); 130 | else return String(val); 131 | } 132 | 133 | void setDscTime() 134 | { 135 | setTime(dsc.HH,dsc.MM,dsc.SS,dsc.dd,dsc.mm,dsc.yy); 136 | if (timeStatus() == timeSet) { 137 | Serial.println(F("Time Synchronized")); 138 | } 139 | else { 140 | Serial.println(F("Time Sync Error")); 141 | } 142 | } 143 | 144 | // -------------------------------------------------------------------------------------------------------- 145 | // ------------------------------------------------ END ------------------------------------------------- 146 | // -------------------------------------------------------------------------------------------------------- 147 | -------------------------------------------------------------------------------- /DSCPanel/examples/DSCPanelSimple/DSCPanelSimple.ino: -------------------------------------------------------------------------------- 1 | // DSC_18XX Arduino Interface - Simple Example 2 | // 3 | // - Demonstrates the use of the DSC library, prints the raw binary words and 4 | // minimally formats and then prints the decoded panel message 5 | // 6 | // Sketch to decode the keybus protocol on DSC PowerSeries 1816, 1832 and 1864 panels 7 | // -- Use the schematic at https://github.com/emcniece/Arduino-Keybus to connect the 8 | // keybus lines to the arduino via voltage divider circuits. Don't forget to 9 | // connect the Keybus Ground to Arduino Ground (not depicted on the circuit)! You 10 | // can also power your arduino from the keybus (+12 VDC, positive), depending on the 11 | // the type arduino board you have. 12 | // 13 | // 14 | 15 | #include 16 | 17 | DSC dsc; // Initialize DSC.h library as "dsc" 18 | 19 | // -------------------------------------------------------------------------------------------------------- 20 | // ----------------------------------------------- SETUP ------------------------------------------------ 21 | // -------------------------------------------------------------------------------------------------------- 22 | 23 | void setup() 24 | { 25 | Serial.begin(115200); 26 | Serial.flush(); 27 | Serial.println(F("DSC Powerseries 18XX")); 28 | Serial.println(F("Key Bus Interface")); 29 | Serial.println(F("Initializing")); 30 | 31 | dsc.setCLK(3); // Sets the clock pin to 3 (example, this is also the default) 32 | // setDTA_IN( ), setDTA_OUT( ) and setLED( ) can also be called 33 | dsc.begin(); // Start the dsc library (Sets the pin modes) 34 | } 35 | 36 | // -------------------------------------------------------------------------------------------------------- 37 | // --------------------------------------------- MAIN LOOP ---------------------------------------------- 38 | // -------------------------------------------------------------------------------------------------------- 39 | 40 | void loop() 41 | { 42 | // ---------------- Get/process incoming data ---------------- 43 | if (!dsc.process()) return; 44 | 45 | if (dscGlobal.pCmd) { 46 | // ------------ Print the Binary Panel Word ------------ 47 | //Serial.print(F("[Panel] ")); 48 | Serial.println(dsc.pnlRaw()); 49 | 50 | // ------------ Print the Formatted Panel Word ------------ 51 | Serial.println(dsc.pnlFormat()); 52 | 53 | // ------------ Print the decoded Panel Message ------------ 54 | Serial.print("---> "); 55 | if (String(dscGlobal.pCmd,HEX).length() == 1) 56 | Serial.print("0"); // Write a leading zero to a single digit HEX 57 | Serial.print(String(dscGlobal.pCmd,HEX)); 58 | Serial.print("("); 59 | Serial.print(dscGlobal.pCmd); 60 | Serial.print("): "); 61 | Serial.println(dscGlobal.pMsg); 62 | } 63 | 64 | if (dscGlobal.kCmd) { 65 | // ------------ Print the Binary Keypad Word ------------ 66 | //Serial.print(F("[Keypad] ")); 67 | Serial.println(dsc.kpdRaw()); 68 | 69 | // ------------ Print the Formatted Keypad Word ------------ 70 | Serial.println(dsc.kpdFormat()); 71 | 72 | // ------------ Print the decoded Keypad Message ------------ 73 | Serial.print("---> "); 74 | if (String(dscGlobal.kCmd,HEX).length() == 1) 75 | Serial.print("0"); // Write a leading zero to a single digit HEX 76 | Serial.print(String(dscGlobal.kCmd,HEX)); 77 | Serial.print("("); 78 | Serial.print(dscGlobal.kCmd); 79 | Serial.print("): "); 80 | Serial.println(dscGlobal.kMsg); 81 | } 82 | } 83 | 84 | // -------------------------------------------------------------------------------------------------------- 85 | // --------------------------------------------- FUNCTIONS ---------------------------------------------- 86 | // -------------------------------------------------------------------------------------------------------- 87 | 88 | // None 89 | 90 | // -------------------------------------------------------------------------------------------------------- 91 | // ------------------------------------------------ END ------------------------------------------------- 92 | // -------------------------------------------------------------------------------------------------------- 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Arduino sketches and libraries to allow it to act as a virtual Keypad for a DSC Powerseries 18XX alarm panel: 2 | ------------------------------------------------------------------------------------------------------------- 3 | - Original DSC Keybus reverse engineering thread http://www.avrfreaks.net/comment/864193 4 | - Arduino Libraries from **stagf15's** [repositories] (https://github.com/stagf15) 5 | - Original "how to connect" shematic from **emcniece's** [Arduino-Keybus repository] (https://github.com/emcniece/Arduino-Keybus) 6 | 7 | ### How to connect Arduino to DSC Panel 8 | 9 | ![alt text][logo] 10 | 11 | [logo]: https://raw.githubusercontent.com/sjlouw/dsc-alarm-arduino/master/schematic.png "Connect DSC Keybus to Arduino" 12 | 13 | ### How to use 14 | 1. Clone this repository 15 | 2. Zip the three directories in three separate zip files (DSCPanel.zip, TextBuffer.zip and Time.zip) 16 | 3. Install it as Arduino libraries 17 | 4. From within the Arduino IDE, choose File --> Examples --> DSCPanel 18 | 5. Upload to the Arduino and watch the Serial Monitor as Panel and Keypad data stream in 19 | 20 | `readserial.py` can be used if Arduino is connected via USB to Raspberry Pi, to read the serial data from Arduino. 21 | -------------------------------------------------------------------------------- /TextBuffer/README.md: -------------------------------------------------------------------------------- 1 | # TextBuffer -------------------------------------------------------------------------------- /TextBuffer/TextBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | #include "TextBuffer.h" 3 | 4 | TextBuffer::TextBuffer(unsigned int bufSize) 5 | { 6 | _bufSize = (bufSize + 3) & (~3); // Makes sure size is a multiple of 4 7 | // - required for the ESP8266 boards 8 | } 9 | 10 | int TextBuffer::begin() 11 | { 12 | buffer = (byte*)malloc(sizeof(byte)*_bufSize); 13 | 14 | if (!buffer) return 0; // return failure if malloc fails 15 | capacity = _bufSize; 16 | memset(buffer, 0, capacity); // Initialize by zeroing the entire array 17 | position = 0; // -- Not currently used -- 18 | return capacity; // return buffer capacity if successful 19 | } 20 | 21 | size_t TextBuffer::write(uint8_t character) 22 | { 23 | if (!buffer) return 0; // return failure 24 | if((getSize() + 1) < capacity) { 25 | // Save the character to the end of the buffer, if there is room 26 | buffer[getSize()] = character; 27 | return 1; // return success (1 byte) 28 | } 29 | return 0; // return failure (buffer full) 30 | } 31 | 32 | size_t TextBuffer::write(const char *str) 33 | { 34 | // Code to display/add string when given a pointer to the beginning 35 | // The last character will be null, so a while(*str) is used 36 | // Increment str (str++) to get the next letter 37 | if (!buffer) return 0; // return failure 38 | if (str == NULL) return 0; 39 | return write((const uint8_t *)str, strlen(str)); 40 | } 41 | 42 | size_t TextBuffer::write(const uint8_t *wBuffer, size_t size) 43 | { 44 | // Code to display/add array of chars when given a pointer to the 45 | // beginning of the array and a size. This will not end with the null character 46 | if (!buffer) return 0; // return failure 47 | size_t n = 0; 48 | while (size--) { 49 | if (write(*wBuffer++)) n++; 50 | else break; 51 | } 52 | return n; // Return the number of bytes written 53 | } 54 | 55 | int TextBuffer::clear() 56 | { 57 | if (!buffer) return 0; // return failure 58 | memset(buffer, 0, capacity); // Zero the entire array 59 | position = 0; 60 | return 1; // return success 61 | } 62 | 63 | int TextBuffer::end() 64 | { 65 | if (!buffer) return 0; // return failure 66 | free(buffer); 67 | return 1; // return success 68 | } 69 | 70 | const char* TextBuffer::getBuffer() 71 | { 72 | if (!buffer) return (char)0; // return 0 byte (null terminator) 73 | return (const char*)buffer; // return const char array buffer pointer 74 | } 75 | 76 | char* TextBuffer::getBufPointer() 77 | { 78 | if (!buffer) return 0; // return failure 79 | clear(); // clear the buffer 80 | return (char*)buffer; // return char array buffer pointer 81 | } 82 | 83 | int TextBuffer::getSize() 84 | { 85 | if (!buffer) return 0; // return failure 86 | return strlen((const char*)buffer); 87 | } 88 | 89 | int TextBuffer::getCapacity() 90 | { 91 | if (!buffer) return 0; // return failure 92 | return capacity; 93 | } 94 | 95 | int TextBuffer::getCheckSum() 96 | { 97 | if (!buffer) return 0; // return failure 98 | // Create Checksum 99 | int checkSum = 0; 100 | int csCount = 1; 101 | while (buffer[csCount + 1] != 0) 102 | { 103 | checkSum ^= buffer[csCount]; 104 | csCount++; 105 | } 106 | /* 107 | // Change the checksum to a string, in HEX form, convert to upper case, and return 108 | String checkSumStr = String(checkSum, HEX); 109 | checkSumStr.toUpperCase(); 110 | return checkSumStr; 111 | */ 112 | return checkSum; // Return the checksum as type int 113 | } 114 | -------------------------------------------------------------------------------- /TextBuffer/TextBuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | TextBuffer.h - Library for writing text/characters to a buffer 3 | that will store them in a char array of predetermined length 4 | 5 | Inherits from the Print.h class and uses the predefined print 6 | methods to feed this class 7 | 8 | Created by stagf15, 11 Oct 16. 9 | Released into the public domain. 10 | 11 | */ 12 | 13 | #ifndef TextBuffer_h 14 | #define TextBuffer_h 15 | 16 | #if defined(ARDUINO) && ARDUINO >= 100 17 | #include "Arduino.h" 18 | #else 19 | #include "WProgram.h" 20 | #endif 21 | 22 | 23 | class TextBuffer : public Print 24 | { 25 | public: 26 | 27 | // In general, functions will return the following: 28 | // If failure, return int 0, or (char)0 29 | // If success, return int 1, or the byte size of 30 | // what was added/changed/created 31 | 32 | // If malloc fails in begin() then it will return 0 33 | // - all other functions will then return failure 34 | // - if this would cause failure in the sketch, recommend 35 | // checking begin() for failure and handling there 36 | 37 | // Class to call to initialize the buffer (before Setup). Requires the 38 | // desired size of the buffer in bytes. The size is then checked and 39 | // converted, if required, to a multiple of 4. (for ESP8266 boards) 40 | TextBuffer(unsigned int bufSize); 41 | 42 | // Begins the buffer; allocates the memory (in Setup) 43 | int begin(); 44 | 45 | // Functions used by the extension of print class 46 | virtual size_t write(uint8_t); 47 | virtual size_t write(const char *str); 48 | virtual size_t write(const uint8_t *wBuffer, size_t size); 49 | 50 | // Clears the buffer, writes 0 bytes to all elements (memset) 51 | // and resets position to 0 52 | int clear(); 53 | 54 | // Ends, or de-allocates the buffer and frees the memory 55 | // Requires a begin() to create a new buffer if needed 56 | int end(); 57 | 58 | // Returns a pointer to the buffer as a const char array 59 | // Used to return the buffer itself 60 | const char* getBuffer(); 61 | 62 | // Returns a pointer to the buffer as a char array 63 | // Used to modify the buffer directly from the calling sketch 64 | // - this could be dangerous, if the capacity is not checked prior! 65 | // - calling this will overwrite anything in the buffer 66 | char* getBufPointer(); 67 | 68 | // Returns the size (length) of the buffer, not including null terminator 69 | int getSize(); 70 | 71 | // Returns the capacity of the buffer, equal to the bufsize passed initially 72 | int getCapacity(); 73 | 74 | // Creates a checksum from the sequential XOR of the buffer characters 75 | // - This is the NMEA0183 standard checksum 76 | int getCheckSum(); 77 | 78 | private: 79 | 80 | // Pointer to the buffer char array 81 | byte* buffer; 82 | 83 | // Variable to hold the aligned buffer max capacity 84 | // - Aligned to multiples of 4 bytes for the ESP8266 boards 85 | unsigned int _bufSize; 86 | 87 | // Same as _bufSize, the max capacity, including null terminator, of the buffer 88 | unsigned int capacity; 89 | 90 | // The position of the "cursor", or reference point in the buffer (usually 0) 91 | // - Not currently used 92 | unsigned int position; 93 | }; 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /TextBuffer/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For TextBuffer 3 | ####################################### 4 | 5 | ####################################### 6 | # Library (KEYWORD3) 7 | ####################################### 8 | 9 | TextBuffer KEYWORD3 10 | 11 | ####################################### 12 | # Datatypes (KEYWORD1) 13 | ####################################### 14 | 15 | TextBuffer KEYWORD1 16 | 17 | ####################################### 18 | # Methods and Functions (KEYWORD2) 19 | ####################################### 20 | 21 | begin KEYWORD2 22 | write KEYWORD2 23 | write KEYWORD2 24 | clear KEYWORD2 25 | end KEYWORD2 26 | getBuffer KEYWORD2 27 | getBufPointer KEYWORD2 28 | getSize KEYWORD2 29 | getCapacity KEYWORD2 30 | getCheckSum KEYWORD2 31 | 32 | ####################################### 33 | # Constants (LITERAL1) 34 | ####################################### 35 | #Example1 LITERAL1 36 | -------------------------------------------------------------------------------- /Time/DateStrings.cpp: -------------------------------------------------------------------------------- 1 | /* DateStrings.cpp 2 | * Definitions for date strings for use with the Time library 3 | * 4 | * Updated for Arduino 1.5.7 18 July 2014 5 | * 6 | * No memory is consumed in the sketch if your code does not call any of the string methods 7 | * You can change the text of the strings, make sure the short strings are each exactly 3 characters 8 | * the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in TimeLib.h 9 | * 10 | */ 11 | 12 | #if defined(__AVR__) 13 | #include 14 | #else 15 | // for compatiblity with Arduino Due and Teensy 3.0 and maybe others? 16 | #define PROGMEM 17 | #define PGM_P const char * 18 | #define pgm_read_byte(addr) (*(const unsigned char *)(addr)) 19 | #define pgm_read_word(addr) (*(const unsigned char **)(addr)) 20 | #define strcpy_P(dest, src) strcpy((dest), (src)) 21 | #endif 22 | #include // for strcpy_P or strcpy 23 | #include "TimeLib.h" 24 | 25 | // the short strings for each day or month must be exactly dt_SHORT_STR_LEN 26 | #define dt_SHORT_STR_LEN 3 // the length of short strings 27 | 28 | static char buffer[dt_MAX_STRING_LEN+1]; // must be big enough for longest string and the terminating null 29 | 30 | const char monthStr0[] PROGMEM = ""; 31 | const char monthStr1[] PROGMEM = "January"; 32 | const char monthStr2[] PROGMEM = "February"; 33 | const char monthStr3[] PROGMEM = "March"; 34 | const char monthStr4[] PROGMEM = "April"; 35 | const char monthStr5[] PROGMEM = "May"; 36 | const char monthStr6[] PROGMEM = "June"; 37 | const char monthStr7[] PROGMEM = "July"; 38 | const char monthStr8[] PROGMEM = "August"; 39 | const char monthStr9[] PROGMEM = "September"; 40 | const char monthStr10[] PROGMEM = "October"; 41 | const char monthStr11[] PROGMEM = "November"; 42 | const char monthStr12[] PROGMEM = "December"; 43 | 44 | const PROGMEM char * const PROGMEM monthNames_P[] = 45 | { 46 | monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6, 47 | monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12 48 | }; 49 | 50 | const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec"; 51 | 52 | const char dayStr0[] PROGMEM = "Err"; 53 | const char dayStr1[] PROGMEM = "Sunday"; 54 | const char dayStr2[] PROGMEM = "Monday"; 55 | const char dayStr3[] PROGMEM = "Tuesday"; 56 | const char dayStr4[] PROGMEM = "Wednesday"; 57 | const char dayStr5[] PROGMEM = "Thursday"; 58 | const char dayStr6[] PROGMEM = "Friday"; 59 | const char dayStr7[] PROGMEM = "Saturday"; 60 | 61 | const PROGMEM char * const PROGMEM dayNames_P[] = 62 | { 63 | dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7 64 | }; 65 | 66 | const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat"; 67 | 68 | /* functions to return date strings */ 69 | 70 | char* monthStr(uint8_t month) 71 | { 72 | strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month]))); 73 | return buffer; 74 | } 75 | 76 | char* monthShortStr(uint8_t month) 77 | { 78 | for (int i=0; i < dt_SHORT_STR_LEN; i++) 79 | buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)])); 80 | buffer[dt_SHORT_STR_LEN] = 0; 81 | return buffer; 82 | } 83 | 84 | char* dayStr(uint8_t day) 85 | { 86 | strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day]))); 87 | return buffer; 88 | } 89 | 90 | char* dayShortStr(uint8_t day) 91 | { 92 | uint8_t index = day*dt_SHORT_STR_LEN; 93 | for (int i=0; i < dt_SHORT_STR_LEN; i++) 94 | buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i])); 95 | buffer[dt_SHORT_STR_LEN] = 0; 96 | return buffer; 97 | } 98 | -------------------------------------------------------------------------------- /Time/Readme.txt: -------------------------------------------------------------------------------- 1 | Readme file for Arduino Time Library 2 | 3 | Time is a library that provides timekeeping functionality for Arduino. 4 | 5 | The code is derived from the Playground DateTime library but is updated 6 | to provide an API that is more flexable and easier to use. 7 | 8 | A primary goal was to enable date and time functionality that can be used with 9 | a variety of external time sources with minimum differences required in sketch logic. 10 | 11 | Example sketches illustrate how similar sketch code can be used with: a Real Time Clock, 12 | internet NTP time service, GPS time data, and Serial time messages from a computer 13 | for time synchronization. 14 | 15 | The functions available in the library include: 16 | 17 | hour(); // the hour now (0-23) 18 | minute(); // the minute now (0-59) 19 | second(); // the second now (0-59) 20 | day(); // the day now (1-31) 21 | weekday(); // day of the week, Sunday is day 0 22 | month(); // the month now (1-12) 23 | year(); // the full four digit year: (2009, 2010 etc) 24 | 25 | there are also functions to return the hour in 12 hour format 26 | hourFormat12(); // the hour now in 12 hour format 27 | isAM(); // returns true if time now is AM 28 | isPM(); // returns true if time now is PM 29 | 30 | now(); // returns the current time as seconds since Jan 1 1970 31 | 32 | The time and date functions can take an optional parameter for the time. This prevents 33 | errors if the time rolls over between elements. For example, if a new minute begins 34 | between getting the minute and second, the values will be inconsistent. Using the 35 | following functions eliminates this probglem 36 | time_t t = now(); // store the current time in time variable t 37 | hour(t); // returns the hour for the given time t 38 | minute(t); // returns the minute for the given time t 39 | second(t); // returns the second for the given time t 40 | day(t); // the day for the given time t 41 | weekday(t); // day of the week for the given time t 42 | month(t); // the month for the given time t 43 | year(t); // the year for the given time t 44 | 45 | 46 | Functions for managing the timer services are: 47 | setTime(t); // set the system time to the give time t 48 | setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr (2010 or 10 sets year to 2010) 49 | adjustTime(adjustment); // adjust system time by adding the adjustment value 50 | 51 | timeStatus(); // indicates if time has been set and recently synchronized 52 | // returns one of the following enumerations: 53 | timeNotSet // the time has never been set, the clock started at Jan 1 1970 54 | timeNeedsSync // the time had been set but a sync attempt did not succeed 55 | timeSet // the time is set and is synced 56 | Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but 57 | the returned time may have drifted if the status is timeNeedsSync. 58 | 59 | setSyncProvider(getTimeFunction); // set the external time provider 60 | setSyncInterval(interval); // set the number of seconds between re-sync 61 | 62 | 63 | There are many convenience macros in the time.h file for time constants and conversion of time units. 64 | 65 | To use the library, copy the download to the Library directory. 66 | 67 | The Time directory contains the Time library and some example sketches 68 | illustrating how the library can be used with various time sources: 69 | 70 | - TimeSerial.pde shows Arduino as a clock without external hardware. 71 | It is synchronized by time messages sent over the serial port. 72 | A companion Processing sketch will automatically provide these messages 73 | if it is running and connected to the Arduino serial port. 74 | 75 | - TimeSerialDateStrings.pde adds day and month name strings to the sketch above 76 | Short (3 character) and long strings are available to print the days of 77 | the week and names of the months. 78 | 79 | - TimeRTC uses a DS1307 real time clock to provide time synchronization. 80 | A basic RTC library named DS1307RTC is included in the download. 81 | To run this sketch the DS1307RTC library must be installed. 82 | 83 | - TimeRTCSet is similar to the above and adds the ability to set the Real Time Clock 84 | 85 | - TimeRTCLog demonstrates how to calculate the difference between times. 86 | It is a vary simple logger application that monitors events on digtial pins 87 | and prints (to the serial port) the time of an event and the time period since the previous event. 88 | 89 | - TimeNTP uses the Arduino Ethernet shield to access time using the internet NTP time service. 90 | The NTP protocol uses UDP and the UdpBytewise library is required, see: 91 | http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/ 92 | 93 | - TimeGPS gets time from a GPS 94 | This requires the TinyGPS library from Mikal Hart: 95 | http://arduiniana.org/libraries/TinyGPS 96 | 97 | Differences between this code and the playground DateTime library 98 | although the Time library is based on the DateTime codebase, the API has changed. 99 | Changes in the Time library API: 100 | - time elements are functions returning int (they are variables in DateTime) 101 | - Years start from 1970 102 | - days of the week and months start from 1 (they start from 0 in DateTime) 103 | - DateStrings do not require a seperate library 104 | - time elements can be accessed non-atomically (in DateTime they are always atomic) 105 | - function added to automatically sync time with extrnal source 106 | - localTime and maketime parameters changed, localTime renamed to breakTime 107 | 108 | Technical notes: 109 | 110 | Internal system time is based on the standard Unix time_t. 111 | The value is the number of seconds since Jan 1 1970. 112 | System time begins at zero when the sketch starts. 113 | 114 | The internal time can be automatically synchronized at regular intervals to an external time source. 115 | This is enabled by calling the setSyncProvider(provider) function - the provider argument is 116 | the address of a function that returns the current time as a time_t. 117 | See the sketches in the examples directory for usage. 118 | 119 | The default interval for re-syncing the time is 5 minutes but can be changed by calling the 120 | setSyncInterval( interval) method to set the number of seconds between re-sync attempts. 121 | 122 | The Time library defines a structure for holding time elements that is a compact version of the C tm structure. 123 | All the members of the Arduino tm structure are bytes and the year is offset from 1970. 124 | Convenience macros provide conversion to and from the Arduino format. 125 | 126 | Low level functions to convert between system time and individual time elements are provided: 127 | breakTime( time, &tm); // break time_t into elements stored in tm struct 128 | makeTime( &tm); // return time_t from elements stored in tm struct 129 | 130 | The DS1307RTC library included in the download provides an example of how a time provider 131 | can use the low level functions to interface with the Time library. 132 | -------------------------------------------------------------------------------- /Time/Time.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | time.c - low level time and date functions 3 | Copyright (c) Michael Margolis 2009-2014 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | 19 | 1.0 6 Jan 2010 - initial release 20 | 1.1 12 Feb 2010 - fixed leap year calculation error 21 | 1.2 1 Nov 2010 - fixed setTime bug (thanks to Korman for this) 22 | 1.3 24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update 23 | status, updated examples for Arduino 1.0, fixed ARM 24 | compatibility issues, added TimeArduinoDue and TimeTeensy3 25 | examples, add error checking and messages to RTC examples, 26 | add examples to DS1307RTC library. 27 | 1.4 5 Sep 2014 - compatibility with Arduino 1.5.7 28 | */ 29 | 30 | #if ARDUINO >= 100 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | #include "TimeLib.h" 37 | 38 | static tmElements_t tm; // a cache of time elements 39 | static time_t cacheTime; // the time the cache was updated 40 | static uint32_t syncInterval = 300; // time sync will be attempted after this many seconds 41 | 42 | void refreshCache(time_t t) { 43 | if (t != cacheTime) { 44 | breakTime(t, tm); 45 | cacheTime = t; 46 | } 47 | } 48 | 49 | int hour() { // the hour now 50 | return hour(now()); 51 | } 52 | 53 | int hour(time_t t) { // the hour for the given time 54 | refreshCache(t); 55 | return tm.Hour; 56 | } 57 | 58 | int hourFormat12() { // the hour now in 12 hour format 59 | return hourFormat12(now()); 60 | } 61 | 62 | int hourFormat12(time_t t) { // the hour for the given time in 12 hour format 63 | refreshCache(t); 64 | if( tm.Hour == 0 ) 65 | return 12; // 12 midnight 66 | else if( tm.Hour > 12) 67 | return tm.Hour - 12 ; 68 | else 69 | return tm.Hour ; 70 | } 71 | 72 | uint8_t isAM() { // returns true if time now is AM 73 | return !isPM(now()); 74 | } 75 | 76 | uint8_t isAM(time_t t) { // returns true if given time is AM 77 | return !isPM(t); 78 | } 79 | 80 | uint8_t isPM() { // returns true if PM 81 | return isPM(now()); 82 | } 83 | 84 | uint8_t isPM(time_t t) { // returns true if PM 85 | return (hour(t) >= 12); 86 | } 87 | 88 | int minute() { 89 | return minute(now()); 90 | } 91 | 92 | int minute(time_t t) { // the minute for the given time 93 | refreshCache(t); 94 | return tm.Minute; 95 | } 96 | 97 | int second() { 98 | return second(now()); 99 | } 100 | 101 | int second(time_t t) { // the second for the given time 102 | refreshCache(t); 103 | return tm.Second; 104 | } 105 | 106 | int day(){ 107 | return(day(now())); 108 | } 109 | 110 | int day(time_t t) { // the day for the given time (0-6) 111 | refreshCache(t); 112 | return tm.Day; 113 | } 114 | 115 | int weekday() { // Sunday is day 1 116 | return weekday(now()); 117 | } 118 | 119 | int weekday(time_t t) { 120 | refreshCache(t); 121 | return tm.Wday; 122 | } 123 | 124 | int month(){ 125 | return month(now()); 126 | } 127 | 128 | int month(time_t t) { // the month for the given time 129 | refreshCache(t); 130 | return tm.Month; 131 | } 132 | 133 | int year() { // as in Processing, the full four digit year: (2009, 2010 etc) 134 | return year(now()); 135 | } 136 | 137 | int year(time_t t) { // the year for the given time 138 | refreshCache(t); 139 | return tmYearToCalendar(tm.Year); 140 | } 141 | 142 | /*============================================================================*/ 143 | /* functions to convert to and from system time */ 144 | /* These are for interfacing with time serivces and are not normally needed in a sketch */ 145 | 146 | // leap year calulator expects year argument as years offset from 1970 147 | #define LEAP_YEAR(Y) ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) ) 148 | 149 | static const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0 150 | 151 | void breakTime(time_t timeInput, tmElements_t &tm){ 152 | // break the given time_t into time components 153 | // this is a more compact version of the C library localtime function 154 | // note that year is offset from 1970 !!! 155 | 156 | uint8_t year; 157 | uint8_t month, monthLength; 158 | uint32_t time; 159 | unsigned long days; 160 | 161 | time = (uint32_t)timeInput; 162 | tm.Second = time % 60; 163 | time /= 60; // now it is minutes 164 | tm.Minute = time % 60; 165 | time /= 60; // now it is hours 166 | tm.Hour = time % 24; 167 | time /= 24; // now it is days 168 | tm.Wday = ((time + 4) % 7) + 1; // Sunday is day 1 169 | 170 | year = 0; 171 | days = 0; 172 | while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) { 173 | year++; 174 | } 175 | tm.Year = year; // year is offset from 1970 176 | 177 | days -= LEAP_YEAR(year) ? 366 : 365; 178 | time -= days; // now it is days in this year, starting at 0 179 | 180 | days=0; 181 | month=0; 182 | monthLength=0; 183 | for (month=0; month<12; month++) { 184 | if (month==1) { // february 185 | if (LEAP_YEAR(year)) { 186 | monthLength=29; 187 | } else { 188 | monthLength=28; 189 | } 190 | } else { 191 | monthLength = monthDays[month]; 192 | } 193 | 194 | if (time >= monthLength) { 195 | time -= monthLength; 196 | } else { 197 | break; 198 | } 199 | } 200 | tm.Month = month + 1; // jan is month 1 201 | tm.Day = time + 1; // day of month 202 | } 203 | 204 | time_t makeTime(tmElements_t &tm){ 205 | // assemble time elements into time_t 206 | // note year argument is offset from 1970 (see macros in time.h to convert to other formats) 207 | // previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9 208 | 209 | int i; 210 | uint32_t seconds; 211 | 212 | // seconds from 1970 till 1 jan 00:00:00 of the given year 213 | seconds= tm.Year*(SECS_PER_DAY * 365); 214 | for (i = 0; i < tm.Year; i++) { 215 | if (LEAP_YEAR(i)) { 216 | seconds += SECS_PER_DAY; // add extra days for leap years 217 | } 218 | } 219 | 220 | // add days for this year, months start from 1 221 | for (i = 1; i < tm.Month; i++) { 222 | if ( (i == 2) && LEAP_YEAR(tm.Year)) { 223 | seconds += SECS_PER_DAY * 29; 224 | } else { 225 | seconds += SECS_PER_DAY * monthDays[i-1]; //monthDay array starts from 0 226 | } 227 | } 228 | seconds+= (tm.Day-1) * SECS_PER_DAY; 229 | seconds+= tm.Hour * SECS_PER_HOUR; 230 | seconds+= tm.Minute * SECS_PER_MIN; 231 | seconds+= tm.Second; 232 | return (time_t)seconds; 233 | } 234 | /*=====================================================*/ 235 | /* Low level system time functions */ 236 | 237 | static uint32_t sysTime = 0; 238 | static uint32_t prevMillis = 0; 239 | static uint32_t nextSyncTime = 0; 240 | static timeStatus_t Status = timeNotSet; 241 | 242 | getExternalTime getTimePtr; // pointer to external sync function 243 | //setExternalTime setTimePtr; // not used in this version 244 | 245 | #ifdef TIME_DRIFT_INFO // define this to get drift data 246 | time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync 247 | #endif 248 | 249 | 250 | time_t now() { 251 | // calculate number of seconds passed since last call to now() 252 | while (millis() - prevMillis >= 1000) { 253 | // millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference 254 | sysTime++; 255 | prevMillis += 1000; 256 | #ifdef TIME_DRIFT_INFO 257 | sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift 258 | #endif 259 | } 260 | if (nextSyncTime <= sysTime) { 261 | if (getTimePtr != 0) { 262 | time_t t = getTimePtr(); 263 | if (t != 0) { 264 | setTime(t); 265 | } else { 266 | nextSyncTime = sysTime + syncInterval; 267 | Status = (Status == timeNotSet) ? timeNotSet : timeNeedsSync; 268 | } 269 | } 270 | } 271 | return (time_t)sysTime; 272 | } 273 | 274 | void setTime(time_t t) { 275 | #ifdef TIME_DRIFT_INFO 276 | if(sysUnsyncedTime == 0) 277 | sysUnsyncedTime = t; // store the time of the first call to set a valid Time 278 | #endif 279 | 280 | sysTime = (uint32_t)t; 281 | nextSyncTime = (uint32_t)t + syncInterval; 282 | Status = timeSet; 283 | prevMillis = millis(); // restart counting from now (thanks to Korman for this fix) 284 | } 285 | 286 | void setTime(int hr,int min,int sec,int dy, int mnth, int yr){ 287 | // year can be given as full four digit year or two digts (2010 or 10 for 2010); 288 | //it is converted to years since 1970 289 | if( yr > 99) 290 | yr = yr - 1970; 291 | else 292 | yr += 30; 293 | tm.Year = yr; 294 | tm.Month = mnth; 295 | tm.Day = dy; 296 | tm.Hour = hr; 297 | tm.Minute = min; 298 | tm.Second = sec; 299 | setTime(makeTime(tm)); 300 | } 301 | 302 | void adjustTime(long adjustment) { 303 | sysTime += adjustment; 304 | } 305 | 306 | // indicates if time has been set and recently synchronized 307 | timeStatus_t timeStatus() { 308 | now(); // required to actually update the status 309 | return Status; 310 | } 311 | 312 | void setSyncProvider( getExternalTime getTimeFunction){ 313 | getTimePtr = getTimeFunction; 314 | nextSyncTime = sysTime; 315 | now(); // this will sync the clock 316 | } 317 | 318 | void setSyncInterval(time_t interval){ // set the number of seconds between re-sync 319 | syncInterval = (uint32_t)interval; 320 | nextSyncTime = sysTime + syncInterval; 321 | } 322 | -------------------------------------------------------------------------------- /Time/Time.h: -------------------------------------------------------------------------------- 1 | #include "TimeLib.h" 2 | -------------------------------------------------------------------------------- /Time/TimeLib.h: -------------------------------------------------------------------------------- 1 | /* 2 | time.h - low level time and date functions 3 | */ 4 | 5 | /* 6 | July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this) 7 | - fixed daysToTime_t macro (thanks maniacbug) 8 | */ 9 | 10 | #ifndef _Time_h 11 | #ifdef __cplusplus 12 | #define _Time_h 13 | 14 | #include 15 | #ifndef __AVR__ 16 | #include // for __time_t_defined, but avr libc lacks sys/types.h 17 | #endif 18 | 19 | 20 | #if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc 21 | typedef unsigned long time_t; 22 | #endif 23 | 24 | 25 | // This ugly hack allows us to define C++ overloaded functions, when included 26 | // from within an extern "C", as newlib's sys/stat.h does. Actually it is 27 | // intended to include "time.h" from the C library (on ARM, but AVR does not 28 | // have that file at all). On Mac and Windows, the compiler will find this 29 | // "Time.h" instead of the C library "time.h", so we may cause other weird 30 | // and unpredictable effects by conflicting with the C library header "time.h", 31 | // but at least this hack lets us define C++ functions as intended. Hopefully 32 | // nothing too terrible will result from overriding the C library header?! 33 | extern "C++" { 34 | typedef enum {timeNotSet, timeNeedsSync, timeSet 35 | } timeStatus_t ; 36 | 37 | typedef enum { 38 | dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday 39 | } timeDayOfWeek_t; 40 | 41 | typedef enum { 42 | tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields 43 | } tmByteFields; 44 | 45 | typedef struct { 46 | uint8_t Second; 47 | uint8_t Minute; 48 | uint8_t Hour; 49 | uint8_t Wday; // day of week, sunday is day 1 50 | uint8_t Day; 51 | uint8_t Month; 52 | uint8_t Year; // offset from 1970; 53 | } tmElements_t, TimeElements, *tmElementsPtr_t; 54 | 55 | //convenience macros to convert to and from tm years 56 | #define tmYearToCalendar(Y) ((Y) + 1970) // full four digit year 57 | #define CalendarYrToTm(Y) ((Y) - 1970) 58 | #define tmYearToY2k(Y) ((Y) - 30) // offset is from 2000 59 | #define y2kYearToTm(Y) ((Y) + 30) 60 | 61 | typedef time_t(*getExternalTime)(); 62 | //typedef void (*setExternalTime)(const time_t); // not used in this version 63 | 64 | 65 | /*==============================================================================*/ 66 | /* Useful Constants */ 67 | #define SECS_PER_MIN ((time_t)(60UL)) 68 | #define SECS_PER_HOUR ((time_t)(3600UL)) 69 | #define SECS_PER_DAY ((time_t)(SECS_PER_HOUR * 24UL)) 70 | #define DAYS_PER_WEEK ((time_t)(7UL)) 71 | #define SECS_PER_WEEK ((time_t)(SECS_PER_DAY * DAYS_PER_WEEK)) 72 | #define SECS_PER_YEAR ((time_t)(SECS_PER_WEEK * 52UL)) 73 | #define SECS_YR_2000 ((time_t)(946684800UL)) // the time at the start of y2k 74 | 75 | /* Useful Macros for getting elapsed time */ 76 | #define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN) 77 | #define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 78 | #define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR) 79 | #define dayOfWeek(_time_) ((( _time_ / SECS_PER_DAY + 4) % DAYS_PER_WEEK)+1) // 1 = Sunday 80 | #define elapsedDays(_time_) ( _time_ / SECS_PER_DAY) // this is number of days since Jan 1 1970 81 | #define elapsedSecsToday(_time_) (_time_ % SECS_PER_DAY) // the number of seconds since last midnight 82 | // The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971 83 | // Always set the correct time before settting alarms 84 | #define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY) // time at the start of the given day 85 | #define nextMidnight(_time_) ( previousMidnight(_time_) + SECS_PER_DAY ) // time at the end of the given day 86 | #define elapsedSecsThisWeek(_time_) (elapsedSecsToday(_time_) + ((dayOfWeek(_time_)-1) * SECS_PER_DAY) ) // note that week starts on day 1 87 | #define previousSunday(_time_) (_time_ - elapsedSecsThisWeek(_time_)) // time at the start of the week for the given time 88 | #define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK) // time at the end of the week for the given time 89 | 90 | 91 | /* Useful Macros for converting elapsed time to a time_t */ 92 | #define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN) 93 | #define hoursToTime_t ((H)) ( (H) * SECS_PER_HOUR) 94 | #define daysToTime_t ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011 95 | #define weeksToTime_t ((W)) ( (W) * SECS_PER_WEEK) 96 | 97 | /*============================================================================*/ 98 | /* time and date functions */ 99 | int hour(); // the hour now 100 | int hour(time_t t); // the hour for the given time 101 | int hourFormat12(); // the hour now in 12 hour format 102 | int hourFormat12(time_t t); // the hour for the given time in 12 hour format 103 | uint8_t isAM(); // returns true if time now is AM 104 | uint8_t isAM(time_t t); // returns true the given time is AM 105 | uint8_t isPM(); // returns true if time now is PM 106 | uint8_t isPM(time_t t); // returns true the given time is PM 107 | int minute(); // the minute now 108 | int minute(time_t t); // the minute for the given time 109 | int second(); // the second now 110 | int second(time_t t); // the second for the given time 111 | int day(); // the day now 112 | int day(time_t t); // the day for the given time 113 | int weekday(); // the weekday now (Sunday is day 1) 114 | int weekday(time_t t); // the weekday for the given time 115 | int month(); // the month now (Jan is month 1) 116 | int month(time_t t); // the month for the given time 117 | int year(); // the full four digit year: (2009, 2010 etc) 118 | int year(time_t t); // the year for the given time 119 | 120 | time_t now(); // return the current time as seconds since Jan 1 1970 121 | void setTime(time_t t); 122 | void setTime(int hr,int min,int sec,int day, int month, int yr); 123 | void adjustTime(long adjustment); 124 | 125 | /* date strings */ 126 | #define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null) 127 | char* monthStr(uint8_t month); 128 | char* dayStr(uint8_t day); 129 | char* monthShortStr(uint8_t month); 130 | char* dayShortStr(uint8_t day); 131 | 132 | /* time sync functions */ 133 | timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized 134 | void setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider 135 | void setSyncInterval(time_t interval); // set the number of seconds between re-sync 136 | 137 | /* low level functions to convert to and from system time */ 138 | void breakTime(time_t time, tmElements_t &tm); // break time_t into elements 139 | time_t makeTime(tmElements_t &tm); // convert time elements into time_t 140 | 141 | } // extern "C++" 142 | #endif // __cplusplus 143 | #endif /* _Time_h */ 144 | 145 | -------------------------------------------------------------------------------- /Time/examples/Processing/SyncArduinoClock/SyncArduinoClock.pde: -------------------------------------------------------------------------------- 1 | /** 2 | * SyncArduinoClock. 3 | * 4 | * portIndex must be set to the port connected to the Arduino 5 | * 6 | * The current time is sent in response to request message from Arduino 7 | * or by clicking the display window 8 | * 9 | * The time message is 11 ASCII text characters; a header (the letter 'T') 10 | * followed by the ten digit system time (unix time) 11 | */ 12 | 13 | 14 | import processing.serial.*; 15 | import java.util.Date; 16 | import java.util.Calendar; 17 | import java.util.GregorianCalendar; 18 | 19 | public static final short portIndex = 0; // select the com port, 0 is the first port 20 | public static final String TIME_HEADER = "T"; //header for arduino serial time message 21 | public static final char TIME_REQUEST = 7; // ASCII bell character 22 | public static final char LF = 10; // ASCII linefeed 23 | public static final char CR = 13; // ASCII linefeed 24 | Serial myPort; // Create object from Serial class 25 | 26 | void setup() { 27 | size(200, 200); 28 | println(Serial.list()); 29 | println(" Connecting to -> " + Serial.list()[portIndex]); 30 | myPort = new Serial(this,Serial.list()[portIndex], 9600); 31 | println(getTimeNow()); 32 | } 33 | 34 | void draw() 35 | { 36 | textSize(20); 37 | textAlign(CENTER); 38 | fill(0); 39 | text("Click to send\nTime Sync", 0, 75, 200, 175); 40 | if ( myPort.available() > 0) { // If data is available, 41 | char val = char(myPort.read()); // read it and store it in val 42 | if(val == TIME_REQUEST){ 43 | long t = getTimeNow(); 44 | sendTimeMessage(TIME_HEADER, t); 45 | } 46 | else 47 | { 48 | if(val == LF) 49 | ; //igonore 50 | else if(val == CR) 51 | println(); 52 | else 53 | print(val); // echo everying but time request 54 | } 55 | } 56 | } 57 | 58 | void mousePressed() { 59 | sendTimeMessage( TIME_HEADER, getTimeNow()); 60 | } 61 | 62 | 63 | void sendTimeMessage(String header, long time) { 64 | String timeStr = String.valueOf(time); 65 | myPort.write(header); // send header and time to arduino 66 | myPort.write(timeStr); 67 | myPort.write('\n'); 68 | } 69 | 70 | long getTimeNow(){ 71 | // java time is in ms, we want secs 72 | Date d = new Date(); 73 | Calendar cal = new GregorianCalendar(); 74 | long current = d.getTime()/1000; 75 | long timezone = cal.get(cal.ZONE_OFFSET)/1000; 76 | long daylight = cal.get(cal.DST_OFFSET)/1000; 77 | return current + timezone + daylight; 78 | } 79 | -------------------------------------------------------------------------------- /Time/examples/Processing/SyncArduinoClock/readme.txt: -------------------------------------------------------------------------------- 1 | SyncArduinoClock is a Processing sketch that responds to Arduino requests for 2 | time synchronization messages. 3 | 4 | The portIndex must be set the Serial port connected to Arduino. 5 | 6 | Download TimeSerial.pde onto Arduino and you should see the time 7 | message displayed when you run SyncArduinoClock in Processing. 8 | The Arduino time is set from the time on your computer through the 9 | Processing sketch. 10 | -------------------------------------------------------------------------------- /Time/examples/TimeArduinoDue/TimeArduinoDue.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeRTC.pde 3 | * example code illustrating Time library with Real Time Clock. 4 | * 5 | * This example requires Markus Lange's Arduino Due RTC Library 6 | * https://github.com/MarkusLange/Arduino-Due-RTC-Library 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | // Select the Slowclock source 13 | //RTC_clock rtc_clock(RC); 14 | RTC_clock rtc_clock(XTAL); 15 | 16 | void setup() { 17 | Serial.begin(9600); 18 | rtc_clock.init(); 19 | if (rtc_clock.date_already_set() == 0) { 20 | // Unfortunately, the Arduino Due hardware does not seem to 21 | // be designed to maintain the RTC clock state when the 22 | // board resets. Markus described it thusly: "Uhh the Due 23 | // does reset with the NRSTB pin. This resets the full chip 24 | // with all backup regions including RTC, RTT and SC. Only 25 | // if the reset is done with the NRST pin will these regions 26 | // stay with their old values." 27 | rtc_clock.set_time(__TIME__); 28 | rtc_clock.set_date(__DATE__); 29 | // However, this might work on other unofficial SAM3X boards 30 | // with different reset circuitry than Arduino Due? 31 | } 32 | setSyncProvider(getArduinoDueTime); 33 | if(timeStatus()!= timeSet) 34 | Serial.println("Unable to sync with the RTC"); 35 | else 36 | Serial.println("RTC has set the system time"); 37 | } 38 | 39 | time_t getArduinoDueTime() 40 | { 41 | return rtc_clock.unixtime(); 42 | } 43 | 44 | void loop() 45 | { 46 | digitalClockDisplay(); 47 | delay(1000); 48 | } 49 | 50 | void digitalClockDisplay(){ 51 | // digital clock display of the time 52 | Serial.print(hour()); 53 | printDigits(minute()); 54 | printDigits(second()); 55 | Serial.print(" "); 56 | Serial.print(day()); 57 | Serial.print(" "); 58 | Serial.print(month()); 59 | Serial.print(" "); 60 | Serial.print(year()); 61 | Serial.println(); 62 | } 63 | 64 | void printDigits(int digits){ 65 | // utility function for digital clock display: prints preceding colon and leading 0 66 | Serial.print(":"); 67 | if(digits < 10) 68 | Serial.print('0'); 69 | Serial.print(digits); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /Time/examples/TimeGPS/TimeGPS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeGPS.pde 3 | * example code illustrating time synced from a GPS 4 | * 5 | */ 6 | 7 | #include 8 | #include // http://arduiniana.org/libraries/TinyGPS/ 9 | #include 10 | // TinyGPS and SoftwareSerial libraries are the work of Mikal Hart 11 | 12 | SoftwareSerial SerialGPS = SoftwareSerial(10, 11); // receive on pin 10 13 | TinyGPS gps; 14 | 15 | // To use a hardware serial port, which is far more efficient than 16 | // SoftwareSerial, uncomment this line and remove SoftwareSerial 17 | //#define SerialGPS Serial1 18 | 19 | // Offset hours from gps time (UTC) 20 | const int offset = 1; // Central European Time 21 | //const int offset = -5; // Eastern Standard Time (USA) 22 | //const int offset = -4; // Eastern Daylight Time (USA) 23 | //const int offset = -8; // Pacific Standard Time (USA) 24 | //const int offset = -7; // Pacific Daylight Time (USA) 25 | 26 | // Ideally, it should be possible to learn the time zone 27 | // based on the GPS position data. However, that would 28 | // require a complex library, probably incorporating some 29 | // sort of database using Eric Muller's time zone shape 30 | // maps, at http://efele.net/maps/tz/ 31 | 32 | time_t prevDisplay = 0; // when the digital clock was displayed 33 | 34 | void setup() 35 | { 36 | Serial.begin(9600); 37 | while (!Serial) ; // Needed for Leonardo only 38 | SerialGPS.begin(4800); 39 | Serial.println("Waiting for GPS time ... "); 40 | } 41 | 42 | void loop() 43 | { 44 | while (SerialGPS.available()) { 45 | if (gps.encode(SerialGPS.read())) { // process gps messages 46 | // when TinyGPS reports new data... 47 | unsigned long age; 48 | int Year; 49 | byte Month, Day, Hour, Minute, Second; 50 | gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age); 51 | if (age < 500) { 52 | // set the Time to the latest GPS reading 53 | setTime(Hour, Minute, Second, Day, Month, Year); 54 | adjustTime(offset * SECS_PER_HOUR); 55 | } 56 | } 57 | } 58 | if (timeStatus()!= timeNotSet) { 59 | if (now() != prevDisplay) { //update the display only if the time has changed 60 | prevDisplay = now(); 61 | digitalClockDisplay(); 62 | } 63 | } 64 | } 65 | 66 | void digitalClockDisplay(){ 67 | // digital clock display of the time 68 | Serial.print(hour()); 69 | printDigits(minute()); 70 | printDigits(second()); 71 | Serial.print(" "); 72 | Serial.print(day()); 73 | Serial.print(" "); 74 | Serial.print(month()); 75 | Serial.print(" "); 76 | Serial.print(year()); 77 | Serial.println(); 78 | } 79 | 80 | void printDigits(int digits) { 81 | // utility function for digital clock display: prints preceding colon and leading 0 82 | Serial.print(":"); 83 | if(digits < 10) 84 | Serial.print('0'); 85 | Serial.print(digits); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /Time/examples/TimeNTP/TimeNTP.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Time_NTP.pde 3 | * Example showing time sync to NTP time source 4 | * 5 | * This sketch uses the Ethernet library 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 14 | // NTP Servers: 15 | IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov 16 | // IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov 17 | // IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov 18 | 19 | 20 | const int timeZone = 1; // Central European Time 21 | //const int timeZone = -5; // Eastern Standard Time (USA) 22 | //const int timeZone = -4; // Eastern Daylight Time (USA) 23 | //const int timeZone = -8; // Pacific Standard Time (USA) 24 | //const int timeZone = -7; // Pacific Daylight Time (USA) 25 | 26 | 27 | EthernetUDP Udp; 28 | unsigned int localPort = 8888; // local port to listen for UDP packets 29 | 30 | void setup() 31 | { 32 | Serial.begin(9600); 33 | while (!Serial) ; // Needed for Leonardo only 34 | delay(250); 35 | Serial.println("TimeNTP Example"); 36 | if (Ethernet.begin(mac) == 0) { 37 | // no point in carrying on, so do nothing forevermore: 38 | while (1) { 39 | Serial.println("Failed to configure Ethernet using DHCP"); 40 | delay(10000); 41 | } 42 | } 43 | Serial.print("IP number assigned by DHCP is "); 44 | Serial.println(Ethernet.localIP()); 45 | Udp.begin(localPort); 46 | Serial.println("waiting for sync"); 47 | setSyncProvider(getNtpTime); 48 | } 49 | 50 | time_t prevDisplay = 0; // when the digital clock was displayed 51 | 52 | void loop() 53 | { 54 | if (timeStatus() != timeNotSet) { 55 | if (now() != prevDisplay) { //update the display only if time has changed 56 | prevDisplay = now(); 57 | digitalClockDisplay(); 58 | } 59 | } 60 | } 61 | 62 | void digitalClockDisplay(){ 63 | // digital clock display of the time 64 | Serial.print(hour()); 65 | printDigits(minute()); 66 | printDigits(second()); 67 | Serial.print(" "); 68 | Serial.print(day()); 69 | Serial.print(" "); 70 | Serial.print(month()); 71 | Serial.print(" "); 72 | Serial.print(year()); 73 | Serial.println(); 74 | } 75 | 76 | void printDigits(int digits){ 77 | // utility for digital clock display: prints preceding colon and leading 0 78 | Serial.print(":"); 79 | if(digits < 10) 80 | Serial.print('0'); 81 | Serial.print(digits); 82 | } 83 | 84 | /*-------- NTP code ----------*/ 85 | 86 | const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message 87 | byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets 88 | 89 | time_t getNtpTime() 90 | { 91 | while (Udp.parsePacket() > 0) ; // discard any previously received packets 92 | Serial.println("Transmit NTP Request"); 93 | sendNTPpacket(timeServer); 94 | uint32_t beginWait = millis(); 95 | while (millis() - beginWait < 1500) { 96 | int size = Udp.parsePacket(); 97 | if (size >= NTP_PACKET_SIZE) { 98 | Serial.println("Receive NTP Response"); 99 | Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer 100 | unsigned long secsSince1900; 101 | // convert four bytes starting at location 40 to a long integer 102 | secsSince1900 = (unsigned long)packetBuffer[40] << 24; 103 | secsSince1900 |= (unsigned long)packetBuffer[41] << 16; 104 | secsSince1900 |= (unsigned long)packetBuffer[42] << 8; 105 | secsSince1900 |= (unsigned long)packetBuffer[43]; 106 | return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; 107 | } 108 | } 109 | Serial.println("No NTP Response :-("); 110 | return 0; // return 0 if unable to get the time 111 | } 112 | 113 | // send an NTP request to the time server at the given address 114 | void sendNTPpacket(IPAddress &address) 115 | { 116 | // set all bytes in the buffer to 0 117 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 118 | // Initialize values needed to form NTP request 119 | // (see URL above for details on the packets) 120 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 121 | packetBuffer[1] = 0; // Stratum, or type of clock 122 | packetBuffer[2] = 6; // Polling Interval 123 | packetBuffer[3] = 0xEC; // Peer Clock Precision 124 | // 8 bytes of zero for Root Delay & Root Dispersion 125 | packetBuffer[12] = 49; 126 | packetBuffer[13] = 0x4E; 127 | packetBuffer[14] = 49; 128 | packetBuffer[15] = 52; 129 | // all NTP fields have been given values, now 130 | // you can send a packet requesting a timestamp: 131 | Udp.beginPacket(address, 123); //NTP requests are to port 123 132 | Udp.write(packetBuffer, NTP_PACKET_SIZE); 133 | Udp.endPacket(); 134 | } 135 | 136 | -------------------------------------------------------------------------------- /Time/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeNTP_ESP8266WiFi.ino 3 | * Example showing time sync to NTP time source 4 | * 5 | * This sketch uses the ESP8266WiFi library 6 | */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | const char ssid[] = "*************"; // your network SSID (name) 13 | const char pass[] = "********"; // your network password 14 | 15 | // NTP Servers: 16 | static const char ntpServerName[] = "us.pool.ntp.org"; 17 | //static const char ntpServerName[] = "time.nist.gov"; 18 | //static const char ntpServerName[] = "time-a.timefreq.bldrdoc.gov"; 19 | //static const char ntpServerName[] = "time-b.timefreq.bldrdoc.gov"; 20 | //static const char ntpServerName[] = "time-c.timefreq.bldrdoc.gov"; 21 | 22 | const int timeZone = 1; // Central European Time 23 | //const int timeZone = -5; // Eastern Standard Time (USA) 24 | //const int timeZone = -4; // Eastern Daylight Time (USA) 25 | //const int timeZone = -8; // Pacific Standard Time (USA) 26 | //const int timeZone = -7; // Pacific Daylight Time (USA) 27 | 28 | 29 | WiFiUDP Udp; 30 | unsigned int localPort = 8888; // local port to listen for UDP packets 31 | 32 | time_t getNtpTime(); 33 | void digitalClockDisplay(); 34 | void printDigits(int digits); 35 | void sendNTPpacket(IPAddress &address); 36 | 37 | void setup() 38 | { 39 | Serial.begin(9600); 40 | while (!Serial) ; // Needed for Leonardo only 41 | delay(250); 42 | Serial.println("TimeNTP Example"); 43 | Serial.print("Connecting to "); 44 | Serial.println(ssid); 45 | WiFi.begin(ssid, pass); 46 | 47 | while (WiFi.status() != WL_CONNECTED) { 48 | delay(500); 49 | Serial.print("."); 50 | } 51 | 52 | Serial.print("IP number assigned by DHCP is "); 53 | Serial.println(WiFi.localIP()); 54 | Serial.println("Starting UDP"); 55 | Udp.begin(localPort); 56 | Serial.print("Local port: "); 57 | Serial.println(Udp.localPort()); 58 | Serial.println("waiting for sync"); 59 | setSyncProvider(getNtpTime); 60 | setSyncInterval(300); 61 | } 62 | 63 | time_t prevDisplay = 0; // when the digital clock was displayed 64 | 65 | void loop() 66 | { 67 | if (timeStatus() != timeNotSet) { 68 | if (now() != prevDisplay) { //update the display only if time has changed 69 | prevDisplay = now(); 70 | digitalClockDisplay(); 71 | } 72 | } 73 | } 74 | 75 | void digitalClockDisplay() 76 | { 77 | // digital clock display of the time 78 | Serial.print(hour()); 79 | printDigits(minute()); 80 | printDigits(second()); 81 | Serial.print(" "); 82 | Serial.print(day()); 83 | Serial.print("."); 84 | Serial.print(month()); 85 | Serial.print("."); 86 | Serial.print(year()); 87 | Serial.println(); 88 | } 89 | 90 | void printDigits(int digits) 91 | { 92 | // utility for digital clock display: prints preceding colon and leading 0 93 | Serial.print(":"); 94 | if (digits < 10) 95 | Serial.print('0'); 96 | Serial.print(digits); 97 | } 98 | 99 | /*-------- NTP code ----------*/ 100 | 101 | const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message 102 | byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets 103 | 104 | time_t getNtpTime() 105 | { 106 | IPAddress ntpServerIP; // NTP server's ip address 107 | 108 | while (Udp.parsePacket() > 0) ; // discard any previously received packets 109 | Serial.println("Transmit NTP Request"); 110 | // get a random server from the pool 111 | WiFi.hostByName(ntpServerName, ntpServerIP); 112 | Serial.print(ntpServerName); 113 | Serial.print(": "); 114 | Serial.println(ntpServerIP); 115 | sendNTPpacket(ntpServerIP); 116 | uint32_t beginWait = millis(); 117 | while (millis() - beginWait < 1500) { 118 | int size = Udp.parsePacket(); 119 | if (size >= NTP_PACKET_SIZE) { 120 | Serial.println("Receive NTP Response"); 121 | Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer 122 | unsigned long secsSince1900; 123 | // convert four bytes starting at location 40 to a long integer 124 | secsSince1900 = (unsigned long)packetBuffer[40] << 24; 125 | secsSince1900 |= (unsigned long)packetBuffer[41] << 16; 126 | secsSince1900 |= (unsigned long)packetBuffer[42] << 8; 127 | secsSince1900 |= (unsigned long)packetBuffer[43]; 128 | return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR; 129 | } 130 | } 131 | Serial.println("No NTP Response :-("); 132 | return 0; // return 0 if unable to get the time 133 | } 134 | 135 | // send an NTP request to the time server at the given address 136 | void sendNTPpacket(IPAddress &address) 137 | { 138 | // set all bytes in the buffer to 0 139 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 140 | // Initialize values needed to form NTP request 141 | // (see URL above for details on the packets) 142 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 143 | packetBuffer[1] = 0; // Stratum, or type of clock 144 | packetBuffer[2] = 6; // Polling Interval 145 | packetBuffer[3] = 0xEC; // Peer Clock Precision 146 | // 8 bytes of zero for Root Delay & Root Dispersion 147 | packetBuffer[12] = 49; 148 | packetBuffer[13] = 0x4E; 149 | packetBuffer[14] = 49; 150 | packetBuffer[15] = 52; 151 | // all NTP fields have been given values, now 152 | // you can send a packet requesting a timestamp: 153 | Udp.beginPacket(address, 123); //NTP requests are to port 123 154 | Udp.write(packetBuffer, NTP_PACKET_SIZE); 155 | Udp.endPacket(); 156 | } 157 | -------------------------------------------------------------------------------- /Time/examples/TimeRTC/TimeRTC.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeRTC.pde 3 | * example code illustrating Time library with Real Time Clock. 4 | * 5 | */ 6 | 7 | #include 8 | #include 9 | #include // a basic DS1307 library that returns time as a time_t 10 | 11 | void setup() { 12 | Serial.begin(9600); 13 | while (!Serial) ; // wait until Arduino Serial Monitor opens 14 | setSyncProvider(RTC.get); // the function to get the time from the RTC 15 | if(timeStatus()!= timeSet) 16 | Serial.println("Unable to sync with the RTC"); 17 | else 18 | Serial.println("RTC has set the system time"); 19 | } 20 | 21 | void loop() 22 | { 23 | if (timeStatus() == timeSet) { 24 | digitalClockDisplay(); 25 | } else { 26 | Serial.println("The time has not been set. Please run the Time"); 27 | Serial.println("TimeRTCSet example, or DS1307RTC SetTime example."); 28 | Serial.println(); 29 | delay(4000); 30 | } 31 | delay(1000); 32 | } 33 | 34 | void digitalClockDisplay(){ 35 | // digital clock display of the time 36 | Serial.print(hour()); 37 | printDigits(minute()); 38 | printDigits(second()); 39 | Serial.print(" "); 40 | Serial.print(day()); 41 | Serial.print(" "); 42 | Serial.print(month()); 43 | Serial.print(" "); 44 | Serial.print(year()); 45 | Serial.println(); 46 | } 47 | 48 | void printDigits(int digits){ 49 | // utility function for digital clock display: prints preceding colon and leading 0 50 | Serial.print(":"); 51 | if(digits < 10) 52 | Serial.print('0'); 53 | Serial.print(digits); 54 | } 55 | 56 | -------------------------------------------------------------------------------- /Time/examples/TimeRTCLog/TimeRTCLog.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeRTCLogger.pde 3 | * example code illustrating adding and subtracting Time. 4 | * 5 | * this sketch logs pin state change events 6 | * the time of the event and time since the previous event is calculated and sent to the serial port. 7 | */ 8 | 9 | #include 10 | #include 11 | #include // a basic DS1307 library that returns time as a time_t 12 | 13 | const int nbrInputPins = 6; // monitor 6 digital pins 14 | const int inputPins[nbrInputPins] = {2,3,4,5,6,7}; // pins to monitor 15 | boolean state[nbrInputPins] ; // the state of the monitored pins 16 | time_t prevEventTime[nbrInputPins] ; // the time of the previous event 17 | 18 | void setup() { 19 | Serial.begin(9600); 20 | setSyncProvider(RTC.get); // the function to sync the time from the RTC 21 | for(int i=0; i < nbrInputPins; i++){ 22 | pinMode( inputPins[i], INPUT); 23 | // uncomment these lines if pull-up resistors are wanted 24 | // pinMode( inputPins[i], INPUT_PULLUP); 25 | // state[i] = HIGH; 26 | } 27 | } 28 | 29 | void loop() 30 | { 31 | for(int i=0; i < nbrInputPins; i++) 32 | { 33 | boolean val = digitalRead(inputPins[i]); 34 | if(val != state[i]) 35 | { 36 | time_t duration = 0; // the time since the previous event 37 | state[i] = val; 38 | time_t timeNow = now(); 39 | if(prevEventTime[i] > 0) 40 | // if this was not the first state change, calculate the time from the previous change 41 | duration = duration = timeNow - prevEventTime[i]; 42 | logEvent(inputPins[i], val, timeNow, duration ); // log the event 43 | prevEventTime[i] = timeNow; // store the time for this event 44 | } 45 | } 46 | } 47 | 48 | void logEvent( int pin, boolean state, time_t timeNow, time_t duration) 49 | { 50 | Serial.print("Pin "); 51 | Serial.print(pin); 52 | if( state == HIGH) 53 | Serial.print(" went High at "); 54 | else 55 | Serial.print(" went Low at "); 56 | showTime(timeNow); 57 | if(duration > 0){ 58 | // only display duration if greater than 0 59 | Serial.print(", Duration was "); 60 | showDuration(duration); 61 | } 62 | Serial.println(); 63 | } 64 | 65 | 66 | void showTime(time_t t){ 67 | // display the given time 68 | Serial.print(hour(t)); 69 | printDigits(minute(t)); 70 | printDigits(second(t)); 71 | Serial.print(" "); 72 | Serial.print(day(t)); 73 | Serial.print(" "); 74 | Serial.print(month(t)); 75 | Serial.print(" "); 76 | Serial.print(year(t)); 77 | } 78 | 79 | void printDigits(int digits){ 80 | // utility function for digital clock display: prints preceding colon and leading 0 81 | Serial.print(":"); 82 | if(digits < 10) 83 | Serial.print('0'); 84 | Serial.print(digits); 85 | } 86 | 87 | void showDuration(time_t duration){ 88 | // prints the duration in days, hours, minutes and seconds 89 | if(duration >= SECS_PER_DAY){ 90 | Serial.print(duration / SECS_PER_DAY); 91 | Serial.print(" day(s) "); 92 | duration = duration % SECS_PER_DAY; 93 | } 94 | if(duration >= SECS_PER_HOUR){ 95 | Serial.print(duration / SECS_PER_HOUR); 96 | Serial.print(" hour(s) "); 97 | duration = duration % SECS_PER_HOUR; 98 | } 99 | if(duration >= SECS_PER_MIN){ 100 | Serial.print(duration / SECS_PER_MIN); 101 | Serial.print(" minute(s) "); 102 | duration = duration % SECS_PER_MIN; 103 | } 104 | Serial.print(duration); 105 | Serial.print(" second(s) "); 106 | } 107 | 108 | -------------------------------------------------------------------------------- /Time/examples/TimeRTCSet/TimeRTCSet.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeRTCSet.pde 3 | * example code illustrating Time library with Real Time Clock. 4 | * 5 | * RTC clock is set in response to serial port time message 6 | * A Processing example sketch to set the time is included in the download 7 | * On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone) 8 | */ 9 | 10 | #include 11 | #include 12 | #include // a basic DS1307 library that returns time as a time_t 13 | 14 | 15 | void setup() { 16 | Serial.begin(9600); 17 | while (!Serial) ; // Needed for Leonardo only 18 | setSyncProvider(RTC.get); // the function to get the time from the RTC 19 | if (timeStatus() != timeSet) 20 | Serial.println("Unable to sync with the RTC"); 21 | else 22 | Serial.println("RTC has set the system time"); 23 | } 24 | 25 | void loop() 26 | { 27 | if (Serial.available()) { 28 | time_t t = processSyncMessage(); 29 | if (t != 0) { 30 | RTC.set(t); // set the RTC and the system time to the received value 31 | setTime(t); 32 | } 33 | } 34 | digitalClockDisplay(); 35 | delay(1000); 36 | } 37 | 38 | void digitalClockDisplay(){ 39 | // digital clock display of the time 40 | Serial.print(hour()); 41 | printDigits(minute()); 42 | printDigits(second()); 43 | Serial.print(" "); 44 | Serial.print(day()); 45 | Serial.print(" "); 46 | Serial.print(month()); 47 | Serial.print(" "); 48 | Serial.print(year()); 49 | Serial.println(); 50 | } 51 | 52 | void printDigits(int digits){ 53 | // utility function for digital clock display: prints preceding colon and leading 0 54 | Serial.print(":"); 55 | if(digits < 10) 56 | Serial.print('0'); 57 | Serial.print(digits); 58 | } 59 | 60 | /* code to process time sync messages from the serial port */ 61 | #define TIME_HEADER "T" // Header tag for serial time sync message 62 | 63 | unsigned long processSyncMessage() { 64 | unsigned long pctime = 0L; 65 | const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 66 | 67 | if(Serial.find(TIME_HEADER)) { 68 | pctime = Serial.parseInt(); 69 | return pctime; 70 | if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013) 71 | pctime = 0L; // return 0 to indicate that the time is not valid 72 | } 73 | } 74 | return pctime; 75 | } 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /Time/examples/TimeSerial/TimeSerial.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeSerial.pde 3 | * example code illustrating Time library set through serial port messages. 4 | * 5 | * Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970) 6 | * you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2013 7 | T1357041600 8 | * 9 | * A Processing example sketch to automatically send the messages is included in the download 10 | * On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone) 11 | */ 12 | 13 | #include 14 | 15 | #define TIME_HEADER "T" // Header tag for serial time sync message 16 | #define TIME_REQUEST 7 // ASCII bell character requests a time sync message 17 | 18 | void setup() { 19 | Serial.begin(9600); 20 | while (!Serial) ; // Needed for Leonardo only 21 | pinMode(13, OUTPUT); 22 | setSyncProvider( requestSync); //set function to call when sync required 23 | Serial.println("Waiting for sync message"); 24 | } 25 | 26 | void loop(){ 27 | if (Serial.available()) { 28 | processSyncMessage(); 29 | } 30 | if (timeStatus()!= timeNotSet) { 31 | digitalClockDisplay(); 32 | } 33 | if (timeStatus() == timeSet) { 34 | digitalWrite(13, HIGH); // LED on if synced 35 | } else { 36 | digitalWrite(13, LOW); // LED off if needs refresh 37 | } 38 | delay(1000); 39 | } 40 | 41 | void digitalClockDisplay(){ 42 | // digital clock display of the time 43 | Serial.print(hour()); 44 | printDigits(minute()); 45 | printDigits(second()); 46 | Serial.print(" "); 47 | Serial.print(day()); 48 | Serial.print(" "); 49 | Serial.print(month()); 50 | Serial.print(" "); 51 | Serial.print(year()); 52 | Serial.println(); 53 | } 54 | 55 | void printDigits(int digits){ 56 | // utility function for digital clock display: prints preceding colon and leading 0 57 | Serial.print(":"); 58 | if(digits < 10) 59 | Serial.print('0'); 60 | Serial.print(digits); 61 | } 62 | 63 | 64 | void processSyncMessage() { 65 | unsigned long pctime; 66 | const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 67 | 68 | if(Serial.find(TIME_HEADER)) { 69 | pctime = Serial.parseInt(); 70 | if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013) 71 | setTime(pctime); // Sync Arduino clock to the time received on the serial port 72 | } 73 | } 74 | } 75 | 76 | time_t requestSync() 77 | { 78 | Serial.write(TIME_REQUEST); 79 | return 0; // the time will be sent later in response to serial mesg 80 | } 81 | 82 | -------------------------------------------------------------------------------- /Time/examples/TimeSerialDateStrings/TimeSerialDateStrings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeSerialDateStrings.pde 3 | * example code illustrating Time library date strings 4 | * 5 | * This sketch adds date string functionality to TimeSerial sketch 6 | * Also shows how to handle different messages 7 | * 8 | * A message starting with a time header sets the time 9 | * A Processing example sketch to automatically send the messages is inclided in the download 10 | * On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone) 11 | * 12 | * A message starting with a format header sets the date format 13 | 14 | * send: Fs\n for short date format 15 | * send: Fl\n for long date format 16 | */ 17 | 18 | #include 19 | 20 | // single character message tags 21 | #define TIME_HEADER 'T' // Header tag for serial time sync message 22 | #define FORMAT_HEADER 'F' // Header tag indicating a date format message 23 | #define FORMAT_SHORT 's' // short month and day strings 24 | #define FORMAT_LONG 'l' // (lower case l) long month and day strings 25 | 26 | #define TIME_REQUEST 7 // ASCII bell character requests a time sync message 27 | 28 | static boolean isLongFormat = true; 29 | 30 | void setup() { 31 | Serial.begin(9600); 32 | while (!Serial) ; // Needed for Leonardo only 33 | setSyncProvider( requestSync); //set function to call when sync required 34 | Serial.println("Waiting for sync message"); 35 | } 36 | 37 | void loop(){ 38 | if (Serial.available() > 1) { // wait for at least two characters 39 | char c = Serial.read(); 40 | if( c == TIME_HEADER) { 41 | processSyncMessage(); 42 | } 43 | else if( c== FORMAT_HEADER) { 44 | processFormatMessage(); 45 | } 46 | } 47 | if (timeStatus()!= timeNotSet) { 48 | digitalClockDisplay(); 49 | } 50 | delay(1000); 51 | } 52 | 53 | void digitalClockDisplay() { 54 | // digital clock display of the time 55 | Serial.print(hour()); 56 | printDigits(minute()); 57 | printDigits(second()); 58 | Serial.print(" "); 59 | if(isLongFormat) 60 | Serial.print(dayStr(weekday())); 61 | else 62 | Serial.print(dayShortStr(weekday())); 63 | Serial.print(" "); 64 | Serial.print(day()); 65 | Serial.print(" "); 66 | if(isLongFormat) 67 | Serial.print(monthStr(month())); 68 | else 69 | Serial.print(monthShortStr(month())); 70 | Serial.print(" "); 71 | Serial.print(year()); 72 | Serial.println(); 73 | } 74 | 75 | void printDigits(int digits) { 76 | // utility function for digital clock display: prints preceding colon and leading 0 77 | Serial.print(":"); 78 | if(digits < 10) 79 | Serial.print('0'); 80 | Serial.print(digits); 81 | } 82 | 83 | void processFormatMessage() { 84 | char c = Serial.read(); 85 | if( c == FORMAT_LONG){ 86 | isLongFormat = true; 87 | Serial.println(F("Setting long format")); 88 | } 89 | else if( c == FORMAT_SHORT) { 90 | isLongFormat = false; 91 | Serial.println(F("Setting short format")); 92 | } 93 | } 94 | 95 | void processSyncMessage() { 96 | unsigned long pctime; 97 | const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 - paul, perhaps we define in time.h? 98 | 99 | pctime = Serial.parseInt(); 100 | if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013) 101 | setTime(pctime); // Sync Arduino clock to the time received on the serial port 102 | } 103 | } 104 | 105 | time_t requestSync() { 106 | Serial.write(TIME_REQUEST); 107 | return 0; // the time will be sent later in response to serial mesg 108 | } 109 | -------------------------------------------------------------------------------- /Time/examples/TimeTeensy3/TimeTeensy3.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * TimeRTC.pde 3 | * example code illustrating Time library with Real Time Clock. 4 | * 5 | */ 6 | 7 | #include 8 | 9 | void setup() { 10 | // set the Time library to use Teensy 3.0's RTC to keep time 11 | setSyncProvider(getTeensy3Time); 12 | 13 | Serial.begin(115200); 14 | while (!Serial); // Wait for Arduino Serial Monitor to open 15 | delay(100); 16 | if (timeStatus()!= timeSet) { 17 | Serial.println("Unable to sync with the RTC"); 18 | } else { 19 | Serial.println("RTC has set the system time"); 20 | } 21 | } 22 | 23 | void loop() { 24 | if (Serial.available()) { 25 | time_t t = processSyncMessage(); 26 | if (t != 0) { 27 | Teensy3Clock.set(t); // set the RTC 28 | setTime(t); 29 | } 30 | } 31 | digitalClockDisplay(); 32 | delay(1000); 33 | } 34 | 35 | void digitalClockDisplay() { 36 | // digital clock display of the time 37 | Serial.print(hour()); 38 | printDigits(minute()); 39 | printDigits(second()); 40 | Serial.print(" "); 41 | Serial.print(day()); 42 | Serial.print(" "); 43 | Serial.print(month()); 44 | Serial.print(" "); 45 | Serial.print(year()); 46 | Serial.println(); 47 | } 48 | 49 | time_t getTeensy3Time() 50 | { 51 | return Teensy3Clock.get(); 52 | } 53 | 54 | /* code to process time sync messages from the serial port */ 55 | #define TIME_HEADER "T" // Header tag for serial time sync message 56 | 57 | unsigned long processSyncMessage() { 58 | unsigned long pctime = 0L; 59 | const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 60 | 61 | if(Serial.find(TIME_HEADER)) { 62 | pctime = Serial.parseInt(); 63 | return pctime; 64 | if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013) 65 | pctime = 0L; // return 0 to indicate that the time is not valid 66 | } 67 | } 68 | return pctime; 69 | } 70 | 71 | void printDigits(int digits){ 72 | // utility function for digital clock display: prints preceding colon and leading 0 73 | Serial.print(":"); 74 | if(digits < 10) 75 | Serial.print('0'); 76 | Serial.print(digits); 77 | } 78 | 79 | -------------------------------------------------------------------------------- /Time/keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Time 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | time_t KEYWORD1 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | now KEYWORD2 13 | second KEYWORD2 14 | minute KEYWORD2 15 | hour KEYWORD2 16 | day KEYWORD2 17 | month KEYWORD2 18 | year KEYWORD2 19 | isAM KEYWORD2 20 | isPM KEYWORD2 21 | weekday KEYWORD2 22 | setTime KEYWORD2 23 | adjustTime KEYWORD2 24 | setSyncProvider KEYWORD2 25 | setSyncInterval KEYWORD2 26 | timeStatus KEYWORD2 27 | TimeLib KEYWORD2 28 | ####################################### 29 | # Instances (KEYWORD2) 30 | ####################################### 31 | 32 | ####################################### 33 | # Constants (LITERAL1) 34 | ####################################### 35 | -------------------------------------------------------------------------------- /Time/library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Time", 3 | "description": "Time keeping library", 4 | "keywords": "Time, date, hour, minute, second, day, week, month, year, RTC", 5 | "authors": [ 6 | { 7 | "name": "Michael Margolis" 8 | }, 9 | { 10 | "name": "Paul Stoffregen", 11 | "email": "paul@pjrc.com", 12 | "url": "http://www.pjrc.com", 13 | "maintainer": true 14 | } 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/PaulStoffregen/Time" 19 | }, 20 | "version": "1.5", 21 | "homepage": "http://playground.arduino.cc/Code/Time", 22 | "frameworks": "Arduino", 23 | "examples": [ 24 | "examples/*/*.ino" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /Time/library.properties: -------------------------------------------------------------------------------- 1 | name=Time 2 | version=1.5 3 | author=Michael Margolis 4 | maintainer=Paul Stoffregen 5 | sentence=Timekeeping functionality for Arduino 6 | paragraph=Date and Time functions, with provisions to synchronize to external time sources like GPS and NTP (Internet). This library is often used together with TimeAlarms and DS1307RTC. 7 | category=Timing 8 | url=http://playground.arduino.cc/code/time 9 | architectures=* 10 | 11 | -------------------------------------------------------------------------------- /readserial.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # Reading the serial data in realtime 4 | 5 | import serial 6 | 7 | ser = serial.Serial("/dev/ttyACM0", 115200) 8 | while True: 9 | print ser.readline() 10 | -------------------------------------------------------------------------------- /schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjlouw/dsc-alarm-arduino/a4574ab01dbd38cadd07b2a897b45e724bcf419a/schematic.png --------------------------------------------------------------------------------