diff --git a/bitlair_doorduino.code-workspace b/bitlair_doorduino.code-workspace new file mode 100644 index 0000000..2499b26 --- /dev/null +++ b/bitlair_doorduino.code-workspace @@ -0,0 +1,20 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "bitlair_doorduino" + }, + { + "path": "bitlair_doorduino_inner" + }, + { + "path": "bitlair_doorduino_door" + }, + { + "path": "reset_eeprom" + } + ], + "settings": {} +} diff --git a/bitlair_doorduino/.gitignore b/bitlair_doorduino/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/bitlair_doorduino/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/bitlair_doorduino/.vscode/extensions.json b/bitlair_doorduino/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/bitlair_doorduino/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/bitlair_doorduino/include/README b/bitlair_doorduino/include/README new file mode 100644 index 0000000..45496b1 --- /dev/null +++ b/bitlair_doorduino/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/bitlair_doorduino/lib/README b/bitlair_doorduino/lib/README new file mode 100644 index 0000000..8c9c29c --- /dev/null +++ b/bitlair_doorduino/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/bitlair_doorduino/platformio.ini b/bitlair_doorduino/platformio.ini new file mode 100644 index 0000000..e5d0646 --- /dev/null +++ b/bitlair_doorduino/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:nanoatmega328] +platform = atmelavr +board = nanoatmega328new +framework = arduino + +lib_deps = + laurb9/StepperDriver@^1.3.1 + +monitor_speed = 115200 diff --git a/bitlair_doorduino/Entropy.cpp b/bitlair_doorduino/src/Entropy.cpp similarity index 100% rename from bitlair_doorduino/Entropy.cpp rename to bitlair_doorduino/src/Entropy.cpp diff --git a/bitlair_doorduino/Entropy.h b/bitlair_doorduino/src/Entropy.h similarity index 100% rename from bitlair_doorduino/Entropy.h rename to bitlair_doorduino/src/Entropy.h diff --git a/bitlair_doorduino/OneWire.cpp b/bitlair_doorduino/src/OneWire.cpp similarity index 100% rename from bitlair_doorduino/OneWire.cpp rename to bitlair_doorduino/src/OneWire.cpp diff --git a/bitlair_doorduino/OneWire.h b/bitlair_doorduino/src/OneWire.h similarity index 100% rename from bitlair_doorduino/OneWire.h rename to bitlair_doorduino/src/OneWire.h diff --git a/bitlair_doorduino/ds1961.cpp b/bitlair_doorduino/src/ds1961.cpp similarity index 100% rename from bitlair_doorduino/ds1961.cpp rename to bitlair_doorduino/src/ds1961.cpp diff --git a/bitlair_doorduino/ds1961.h b/bitlair_doorduino/src/ds1961.h similarity index 100% rename from bitlair_doorduino/ds1961.h rename to bitlair_doorduino/src/ds1961.h diff --git a/bitlair_doorduino/klemmenstrook.txt b/bitlair_doorduino/src/klemmenstrook.txt similarity index 100% rename from bitlair_doorduino/klemmenstrook.txt rename to bitlair_doorduino/src/klemmenstrook.txt diff --git a/bitlair_doorduino/bitlair_doorduino.ino b/bitlair_doorduino/src/main.cpp similarity index 89% rename from bitlair_doorduino/bitlair_doorduino.ino rename to bitlair_doorduino/src/main.cpp index fc4ceb2..9b31042 100644 --- a/bitlair_doorduino/bitlair_doorduino.ino +++ b/bitlair_doorduino/src/main.cpp @@ -3,21 +3,23 @@ #include #include -#include +// #include #include "Entropy.h" #include "sha1.h" +#include "Wire.h" #include // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step -#define MOTOR_STEPS 100 -#define RPM 120 -#define DIR A0 +#define MOTOR_STEPS 2 +#define RPM 60 +#define DIR A6 +// #define STEP A7 #define STEP 9 #include "A4988.h" A4988 stepper(MOTOR_STEPS, DIR, STEP); -#define INPUT_SOLENOID 4 +#define INPUT_SOLENOID 7 #define INPUT_HORN 3 #define PIN_LEDSOLENOID 6 #define PIN_LEDHORN 5 @@ -27,17 +29,17 @@ uint32_t SolenoidStartTime; -#define PIN_DOORPOWER A3 -#define PIN_SOLENOID A5 -#define PIN_HORN A4 -#define PIN_OPEN 8 -#define PIN_CLOSE 7 +#define PIN_DOORPOWER A1 +#define PIN_SOLENOID A3 +#define PIN_HORN A2 +#define PIN_OPEN 13 +#define PIN_CLOSE A0 -#define PIN_1WIRE 13 +#define PIN_1WIRE 8 #define PIN_LEDGREEN 10 #define PIN_LEDRED 11 -#define PIN_MAINS_POWER A2 +#define PIN_MAINS_POWER 2 #define CMD_BUFSIZE 64 #define CMD_TIMEOUT 10000 //command timeout in milliseconds @@ -45,7 +47,8 @@ uint32_t SolenoidStartTime; #define SECRETSIZE 8 #define ADDRSIZE 8 #define STORAGESIZE (SECRETSIZE + ADDRSIZE) -#define EEPROMSIZE 1024 +#define EEPROMDEVICEADDRESS 0x50 +#define EEPROMSIZE 2048 #define SHA1SIZE 20 #define IBUTTON_SEARCH_TIMEOUT 60000 //timeout searching for ibutton @@ -166,6 +169,7 @@ void setup() { Serial.begin(115200); Serial.println("DEBUG: Board started"); + Wire.begin(); stepper.begin(RPM); stepper.enable(); @@ -179,7 +183,7 @@ void setup() pinMode(PIN_SOLENOID, OUTPUT); pinMode(PIN_OPEN, OUTPUT); pinMode(PIN_CLOSE, OUTPUT); - pinMode(PIN_HORN, OUTPUT); + pinMode(PIN_HORN, OUTPUT); pinMode(PIN_LEDGREEN, OUTPUT); pinMode(PIN_LEDRED, OUTPUT); @@ -188,12 +192,41 @@ void setup() digitalWrite(PIN_OPEN, LOW); digitalWrite(PIN_CLOSE, LOW); digitalWrite(PIN_DOORPOWER, LOW); + digitalWrite(PIN_HORN, LOW); + digitalWrite(PIN_SOLENOID, LOW); SetLEDState(LEDState_Off); Entropy.initialize(); } +void writeEEPROM(unsigned int eeaddress, byte data ) +{ + Wire.beginTransmission(EEPROMDEVICEADDRESS); + Wire.write((int)(eeaddress >> 8)); // MSB + Wire.write((int)(eeaddress & 0xFF)); // LSB + Wire.write(data); + Wire.endTransmission(); + + delay(5); +} + +byte readEEPROM(unsigned int eeaddress ) +{ + byte rdata = 0xFF; + + Wire.beginTransmission(EEPROMDEVICEADDRESS); + Wire.write((int)(eeaddress >> 8)); // MSB + Wire.write((int)(eeaddress & 0xFF)); // LSB + Wire.endTransmission(); + + Wire.requestFrom(EEPROMDEVICEADDRESS,1); + + if (Wire.available()) rdata = Wire.read(); + + return rdata; +} + void AddButton(uint8_t* addr, uint8_t* secret) { for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) @@ -202,7 +235,7 @@ void AddButton(uint8_t* addr, uint8_t* secret) uint16_t startaddr = i * STORAGESIZE; for (uint16_t j = 0; j < ADDRSIZE; j++) { - uint8_t eeprombyte = EEPROM.read(startaddr + j); + uint8_t eeprombyte = readEEPROM(startaddr + j); if (eeprombyte != 0xFF && eeprombyte != addr[j]) { emptyslot = false; @@ -213,10 +246,10 @@ void AddButton(uint8_t* addr, uint8_t* secret) if (emptyslot) { for (uint16_t j = 0; j < ADDRSIZE; j++) - EEPROM.write(startaddr + j, addr[j]); + writeEEPROM(startaddr + j, addr[j]); for (uint16_t j = 0; j < SECRETSIZE; j++) - EEPROM.write(startaddr + j + ADDRSIZE, secret[j]); + writeEEPROM(startaddr + j + ADDRSIZE, secret[j]); Serialprintf("DEBUG: stored button in slot %i\n", i); @@ -235,7 +268,7 @@ void RemoveButton(uint8_t* addr) bool sameaddr = true; for (uint16_t j = 0; j < ADDRSIZE; j++) { - uint8_t eeprombyte = EEPROM.read(startaddr + j); + uint8_t eeprombyte = readEEPROM(startaddr + j); if (eeprombyte != addr[j]) { sameaddr = false; @@ -248,7 +281,7 @@ void RemoveButton(uint8_t* addr) Serialprintf("DEBUG: erasing slot %i\n", i); for (uint16_t j = 0; j < STORAGESIZE; j++) - EEPROM.write(startaddr + j, 0xFF); + writeEEPROM(startaddr + j, 0xFF); } } @@ -261,7 +294,7 @@ bool GetButtonSecret(uint8_t* addr, uint8_t* secret) bool isempty = true; for (uint16_t j = 0; j < ADDRSIZE; j++) { - uint8_t eeprombyte = EEPROM.read(startaddr + j); + uint8_t eeprombyte = readEEPROM(startaddr + j); if (isempty && eeprombyte != 0xFF) isempty = false; @@ -280,7 +313,7 @@ bool GetButtonSecret(uint8_t* addr, uint8_t* secret) Serialprintf("DEBUG: getting secret from slot %i\n", i); for (uint16_t j = 0; j < SECRETSIZE; j++) - secret[j] = EEPROM.read(startaddr + j + ADDRSIZE); + secret[j] = readEEPROM(startaddr + j + ADDRSIZE); return true; } @@ -302,7 +335,7 @@ void ListButtons() bool isempty = true; for (uint16_t j = 0; j < ADDRSIZE; j++) { - uint8_t eeprombyte = EEPROM.read(startaddr + j); + uint8_t eeprombyte = readEEPROM(startaddr + j); if (isempty && eeprombyte != 0xFF) isempty = false; @@ -646,7 +679,7 @@ void loop() stepper.move(MOTOR_STEPS*(RPM/60)*10); } } - if(StateSolenoid == true && ((millis() - SolenoidStartTime) > (10*1000)) ){ + if(StateSolenoid == true && ((millis() - SolenoidStartTime) > (5*1000)) ){ digitalWrite(PIN_SOLENOID, LOW); StateSolenoid = false; } @@ -664,4 +697,4 @@ void loop() } } - + diff --git a/bitlair_doorduino/sha1.cpp b/bitlair_doorduino/src/sha1.cpp similarity index 100% rename from bitlair_doorduino/sha1.cpp rename to bitlair_doorduino/src/sha1.cpp diff --git a/bitlair_doorduino/sha1.h b/bitlair_doorduino/src/sha1.h similarity index 100% rename from bitlair_doorduino/sha1.h rename to bitlair_doorduino/src/sha1.h diff --git a/bitlair_doorduino/test/README b/bitlair_doorduino/test/README new file mode 100644 index 0000000..e7d1588 --- /dev/null +++ b/bitlair_doorduino/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/bitlair_doorduino_door/.gitignore b/bitlair_doorduino_door/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/bitlair_doorduino_door/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/bitlair_doorduino_door/.vscode/extensions.json b/bitlair_doorduino_door/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/bitlair_doorduino_door/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/bitlair_doorduino_door/include/README b/bitlair_doorduino_door/include/README new file mode 100644 index 0000000..45496b1 --- /dev/null +++ b/bitlair_doorduino_door/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/bitlair_doorduino_door/lib/README b/bitlair_doorduino_door/lib/README new file mode 100644 index 0000000..8c9c29c --- /dev/null +++ b/bitlair_doorduino_door/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/bitlair_doorduino_door/platformio.ini b/bitlair_doorduino_door/platformio.ini new file mode 100644 index 0000000..985f8a1 --- /dev/null +++ b/bitlair_doorduino_door/platformio.ini @@ -0,0 +1,20 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:prominiatmega328] +platform = atmelavr +board = pro16MHzatmega328 +framework = arduino + +lib_deps = + laurb9/StepperDriver@^1.4.1 + https://github.com/sosandroid/AMS_AS5048B.git + +monitor_speed = 115200 diff --git a/bitlair_doorduino_door/bitlair_doorduino_door.ino b/bitlair_doorduino_door/src/main.cpp similarity index 94% rename from bitlair_doorduino_door/bitlair_doorduino_door.ino rename to bitlair_doorduino_door/src/main.cpp index 1cab59d..d7a133b 100644 --- a/bitlair_doorduino_door/bitlair_doorduino_door.ino +++ b/bitlair_doorduino_door/src/main.cpp @@ -89,13 +89,13 @@ void setup() { state = STATE_OPEN; Serial.println("RECEIVED OPEN"); stepper.enable(); - stepper.startRotate(-560); + stepper.startRotate(-900); } if (digitalRead(DOOR_CLOSE) == HIGH && state == STATE_IDLE){ state = STATE_CLOSE; Serial.println("RECEIVED CLOSE"); stepper.enable(); - stepper.startRotate(560); + stepper.startRotate(900); } } diff --git a/bitlair_doorduino_door/test/README b/bitlair_doorduino_door/test/README new file mode 100644 index 0000000..e7d1588 --- /dev/null +++ b/bitlair_doorduino_door/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/bitlair_doorduino_inner/.gitignore b/bitlair_doorduino_inner/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/bitlair_doorduino_inner/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/bitlair_doorduino_inner/.vscode/extensions.json b/bitlair_doorduino_inner/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/bitlair_doorduino_inner/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/bitlair_doorduino_inner/include/README b/bitlair_doorduino_inner/include/README new file mode 100644 index 0000000..45496b1 --- /dev/null +++ b/bitlair_doorduino_inner/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/bitlair_doorduino_inner/lib/README b/bitlair_doorduino_inner/lib/README new file mode 100644 index 0000000..8c9c29c --- /dev/null +++ b/bitlair_doorduino_inner/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/bitlair_doorduino_inner/platformio.ini b/bitlair_doorduino_inner/platformio.ini new file mode 100644 index 0000000..e5d0646 --- /dev/null +++ b/bitlair_doorduino_inner/platformio.ini @@ -0,0 +1,19 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:nanoatmega328] +platform = atmelavr +board = nanoatmega328new +framework = arduino + +lib_deps = + laurb9/StepperDriver@^1.3.1 + +monitor_speed = 115200 diff --git a/bitlair_doorduino_inner/src/Entropy.cpp b/bitlair_doorduino_inner/src/Entropy.cpp new file mode 100644 index 0000000..7a83a4d --- /dev/null +++ b/bitlair_doorduino_inner/src/Entropy.cpp @@ -0,0 +1,344 @@ +// Entropy - A entropy (random number) generator for the Arduino +// The latest version of this library will always be stored in the following +// google code repository: +// http://code.google.com/p/avr-hardware-random-number-generation/source/browse/#git%2FEntropy +// with more information available on the libraries wiki page +// http://code.google.com/p/avr-hardware-random-number-generation/wiki/WikiAVRentropy +// +// Copyright 2014 by Walter Anderson +// +// This file is part of Entropy, an Arduino library. +// Entropy is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Entropy is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Entropy. If not, see . + +#include +#include "Entropy.h" + +const uint8_t WDT_MAX_8INT=0xFF; +const uint16_t WDT_MAX_16INT=0xFFFF; +const uint32_t WDT_MAX_32INT=0xFFFFFFFF; +// Since the Due TRNG is so fast we don't need a circular buffer for it +#ifndef ARDUINO_SAM_DUE + const uint8_t gWDT_buffer_SIZE=32; + const uint8_t WDT_POOL_SIZE=8; + uint8_t gWDT_buffer[gWDT_buffer_SIZE]; + uint8_t gWDT_buffer_position; + uint8_t gWDT_loop_counter; + volatile uint8_t gWDT_pool_start; + volatile uint8_t gWDT_pool_end; + volatile uint8_t gWDT_pool_count; + volatile uint32_t gWDT_entropy_pool[WDT_POOL_SIZE]; +#endif + +// This function initializes the global variables needed to implement the circular entropy pool and +// the buffer that holds the raw Timer 1 values that are used to create the entropy pool. It then +// Initializes the Watch Dog Timer (WDT) to perform an interrupt every 2048 clock cycles, (about +// 16 ms) which is as fast as it can be set. +void EntropyClass::initialize(void) +{ +#ifndef ARDUINO_SAM_DUE + gWDT_buffer_position=0; + gWDT_pool_start = 0; + gWDT_pool_end = 0; + gWDT_pool_count = 0; +#endif +#if defined(__AVR__) + cli(); // Temporarily turn off interrupts, until WDT configured + MCUSR = 0; // Use the MCU status register to reset flags for WDR, BOR, EXTR, and POWR + _WD_CONTROL_REG |= (1<<_WD_CHANGE_BIT) | (1<TRNG_IDR = 0xFFFFFFFF; + TRNG->TRNG_CR = TRNG_CR_KEY(0x524e47) | TRNG_CR_ENABLE; +#elif defined(__arm__) && defined(TEENSYDUINO) + SIM_SCGC5 |= SIM_SCGC5_LPTIMER; + LPTMR0_CSR = 0b10000100; + LPTMR0_PSR = 0b00000101; // PCS=01 : 1 kHz clock + LPTMR0_CMR = 0x0006; // smaller number = faster random numbers... + LPTMR0_CSR = 0b01000101; + NVIC_ENABLE_IRQ(IRQ_LPTMR); +#endif +} + +// This function returns a uniformly distributed random integer in the range +// of [0,0xFFFFFFFF] as long as some entropy exists in the pool and a 0 +// otherwise. To ensure a proper random return the available() function +// should be called first to ensure that entropy exists. +// +// The pool is implemented as an 8 value circular buffer +uint32_t EntropyClass::random(void) +{ +#ifdef ARDUINO_SAM_DUE + while (! (TRNG->TRNG_ISR & TRNG_ISR_DATRDY)) + ; + retVal = TRNG->TRNG_ODATA; +#else + uint8_t waiting; + while (gWDT_pool_count < 1) + waiting += 1; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + retVal = gWDT_entropy_pool[gWDT_pool_start]; + gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE; + --gWDT_pool_count; + } +#endif + return(retVal); +} + +// This function returns one byte of a single 32-bit entropy value, while preserving the remaining bytes to +// be returned upon successive calls to the method. This makes best use of the available entropy pool when +// only bytes size chunks of entropy are needed. Not available to public use since there is a method of using +// the default random method for the end-user to achieve the same results. This internal method is for providing +// that capability to the random method, shown below +uint8_t EntropyClass::random8(void) +{ + static uint8_t byte_position=0; + uint8_t retVal8; + + if (byte_position == 0) + share_entropy.int32 = random(); + retVal8 = share_entropy.int8[byte_position++]; + byte_position = byte_position % 4; + return(retVal8); +} + +// This function returns one word of a single 32-bit entropy value, while preserving the remaining word to +// be returned upon successive calls to the method. This makes best use of the available entropy pool when +// only word sized chunks of entropy are needed. Not available to public use since there is a method of using +// the default random method for the end-user to achieve the same results. This internal method is for providing +// that capability to the random method, shown below +uint16_t EntropyClass::random16(void) +{ + static uint8_t word_position=0; + uint16_t retVal16; + + if (word_position == 0) + share_entropy.int32 = random(); + retVal16 = share_entropy.int16[word_position++]; + word_position = word_position % 2; + return(retVal16); +} + +uint8_t EntropyClass::randomByte(void) +{ + return random8(); +} + +uint16_t EntropyClass::randomWord(void) +{ + return random16(); +} + +// This function returns a uniformly distributed integer in the range of +// of [0,max). The added complexity of this function is required to ensure +// a uniform distribution since the naive modulus max (% max) introduces +// bias for all values of max that are not powers of two. +// +// The loops below are needed, because there is a small and non-uniform chance +// That the division below will yield an answer = max, so we just get +// the next random value until answer < max. Which prevents the introduction +// of bias caused by the division process. This is why we can't use the +// simpler modulus operation which introduces significant bias for divisors +// that aren't a power of two +uint32_t EntropyClass::random(uint32_t max) +{ + uint32_t slice; + + if (max < 2) + retVal=0; + else + { + retVal = WDT_MAX_32INT; + if (max <= WDT_MAX_8INT) // If only byte values are needed, make best use of entropy + { // by diving the long into four bytes and using individually + slice = WDT_MAX_8INT / max; + while (retVal >= max) + retVal = random8() / slice; + } + else if (max <= WDT_MAX_16INT) // If only word values are need, make best use of entropy + { // by diving the long into two words and using individually + slice = WDT_MAX_16INT / max; + while (retVal >= max) + retVal = random16() / slice; + } + else + { + slice = WDT_MAX_32INT / max; + while (retVal >= max) + retVal = random() / slice; + } + } + return(retVal); +} + +// This function returns a uniformly distributed integer in the range of +// of [min,max). +uint32_t EntropyClass::random(uint32_t min, uint32_t max) +{ + uint32_t tmp_random, tmax; + + tmax = max - min; + if (tmax < 1) + retVal=min; + else + { + tmp_random = random(tmax); + retVal = min + tmp_random; + } + return(retVal); +} + +// This function returns a uniformly distributed single precision floating point +// in the range of [0.0,1.0) +float EntropyClass::randomf(void) +{ + float fRetVal; + + // Since c++ doesn't allow bit manipulations of floating point types, we are + // using integer type and arrange its bit pattern to follow the IEEE754 bit + // pattern for single precision floating point value in the range of 1.0 - 2.0 + uint32_t tmp_random = random(); + tmp_random = (tmp_random & 0x007FFFFF) | 0x3F800000; + // We then copy that binary representation from the temporary integer to the + // returned floating point value + memcpy((void *) &fRetVal, (void *) &tmp_random, sizeof(fRetVal)); + // Now translate the value back to its intended range by subtracting 1.0 + fRetVal = fRetVal - 1.0; + return (fRetVal); +} + +// This function returns a uniformly distributed single precision floating point +// in the range of [0.0, max) +float EntropyClass::randomf(float max) +{ + float fRetVal; + fRetVal = randomf() * max; + return(fRetVal); +} + +// This function returns a uniformly distributed single precision floating point +// in the range of [min, max) +float EntropyClass::randomf(float min,float max) +{ + float fRetVal; + float tmax; + tmax = max - min; + fRetVal = (randomf() * tmax) + min; + return(fRetVal); +} + +// This function implements the Marsaglia polar method of converting a uniformly +// distributed random numbers to a normaly distributed (bell curve) with the +// mean and standard deviation specified. This type of random number is useful +// for a variety of purposes, like Monte Carlo simulations. +float EntropyClass::rnorm(float mean, float stdDev) +{ + static float spare; + static float u1; + static float u2; + static float s; + static bool isSpareReady = false; + + if (isSpareReady) + { + isSpareReady = false; + return ((spare * stdDev) + mean); + } else { + do { + u1 = (randomf() * 2) - 1; + u2 = (randomf() * 2) - 1; + s = (u1 * u1) + (u2 * u2); + } while (s >= 1.0); + s = sqrt(-2.0 * log(s) / s); + spare = u2 * s; + isSpareReady = true; + return(mean + (stdDev * u1 * s)); + } +} + +// This function returns a unsigned char (8-bit) with the number of unsigned long values +// in the entropy pool +uint8_t EntropyClass::available(void) +{ +#ifdef ARDUINO_SAM_DUE + return(TRNG->TRNG_ISR & TRNG_ISR_DATRDY); +#else + return(gWDT_pool_count); +#endif +} + +// Circular buffer is not needed with the speed of the Arduino Due trng hardware generator +#ifndef ARDUINO_SAM_DUE +// This interrupt service routine is called every time the WDT interrupt is triggered. +// With the default configuration that is approximately once every 16ms, producing +// approximately two 32-bit integer values every second. +// +// The pool is implemented as an 8 value circular buffer +static void isr_hardware_neutral(uint8_t val) +{ + gWDT_buffer[gWDT_buffer_position] = val; + gWDT_buffer_position++; // every time the WDT interrupt is triggered + if (gWDT_buffer_position >= gWDT_buffer_SIZE) + { + gWDT_pool_end = (gWDT_pool_start + gWDT_pool_count) % WDT_POOL_SIZE; + // The following code is an implementation of Jenkin's one at a time hash + // This hash function has had preliminary testing to verify that it + // produces reasonably uniform random results when using WDT jitter + // on a variety of Arduino platforms + for(gWDT_loop_counter = 0; gWDT_loop_counter < gWDT_buffer_SIZE; ++gWDT_loop_counter) + { + gWDT_entropy_pool[gWDT_pool_end] += gWDT_buffer[gWDT_loop_counter]; + gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 10); + gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 6); + } + gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 3); + gWDT_entropy_pool[gWDT_pool_end] ^= (gWDT_entropy_pool[gWDT_pool_end] >> 11); + gWDT_entropy_pool[gWDT_pool_end] += (gWDT_entropy_pool[gWDT_pool_end] << 15); + gWDT_entropy_pool[gWDT_pool_end] = gWDT_entropy_pool[gWDT_pool_end]; + gWDT_buffer_position = 0; // Start collecting the next 32 bytes of Timer 1 counts + if (gWDT_pool_count == WDT_POOL_SIZE) // The entropy pool is full + gWDT_pool_start = (gWDT_pool_start + 1) % WDT_POOL_SIZE; + else // Add another unsigned long (32 bits) to the entropy pool + ++gWDT_pool_count; + } +} +#endif + +#if defined( __AVR_ATtiny25__ ) || defined( __AVR_ATtiny45__ ) || defined( __AVR_ATtiny85__ ) +ISR(WDT_vect) +{ + isr_hardware_neutral(TCNT0); +} + +#elif defined(__AVR__) +ISR(WDT_vect) +{ + isr_hardware_neutral(TCNT1L); // Record the Timer 1 low byte (only one needed) +} + +#elif defined(__arm__) && defined(TEENSYDUINO) +void lptmr_isr(void) +{ + LPTMR0_CSR = 0b10000100; + LPTMR0_CSR = 0b01000101; + isr_hardware_neutral(SYST_CVR); +} +#endif + +// The library implements a single global instance. There is no need, nor will the library +// work properly if multiple instances are created. +EntropyClass Entropy; diff --git a/bitlair_doorduino_inner/src/Entropy.h b/bitlair_doorduino_inner/src/Entropy.h new file mode 100644 index 0000000..0d7d7a6 --- /dev/null +++ b/bitlair_doorduino_inner/src/Entropy.h @@ -0,0 +1,73 @@ +// Entropy - A entropy (random number) generator for the Arduino +// +// Copyright 2014 by Walter Anderson +// +// This file is part of Entropy, an Arduino library. +// Entropy is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Entropy is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Entropy. If not, see . + +#ifndef Entropy_h +#define Entropy_h + +#include + +// Separate the ARM Due headers we use +#ifdef ARDUINO_SAM_DUE +#include +#include +#endif + +// Teensy required headers +#ifdef TEENSYDUINO +#include +#endif + +// Separate AVR headers from ARM headers +#ifdef __AVR__ +#include +#include +#include +#endif + +const uint32_t WDT_RETURN_BYTE=256; +const uint32_t WDT_RETURN_WORD=65536; + +union ENTROPY_LONG_WORD +{ + uint32_t int32; + uint16_t int16[2]; + uint8_t int8[4]; +}; + +class EntropyClass +{ +public: + void initialize(void); + uint32_t random(void); + uint32_t random(uint32_t max); + uint32_t random(uint32_t min, uint32_t max); + uint8_t randomByte(void); + uint16_t randomWord(void); + float randomf(void); + float randomf(float max); + float randomf(float min, float max); + float rnorm(float mean, float stdDev); + uint8_t available(void); + private: + ENTROPY_LONG_WORD share_entropy; + uint32_t retVal; + uint8_t random8(void); + uint16_t random16(void); +}; +extern EntropyClass Entropy; +#endif diff --git a/bitlair_doorduino_inner/src/OneWire.cpp b/bitlair_doorduino_inner/src/OneWire.cpp new file mode 100644 index 0000000..6d55de0 --- /dev/null +++ b/bitlair_doorduino_inner/src/OneWire.cpp @@ -0,0 +1,563 @@ +/* +Copyright (c) 2007, Jim Studt (original old version - many contributors since) + +The latest version of this library may be found at: + http://www.pjrc.com/teensy/td_libs_OneWire.html + +OneWire has been maintained by Paul Stoffregen (paul@pjrc.com) since +January 2010. At the time, it was in need of many bug fixes, but had +been abandoned the original author (Jim Studt). None of the known +contributors were interested in maintaining OneWire. Paul typically +works on OneWire every 6 to 12 months. Patches usually wait that +long. If anyone is interested in more actively maintaining OneWire, +please contact Paul. + +Version 2.3: + Unknonw chip fallback mode, Roger Clark + Teensy-LC compatibility, Paul Stoffregen + Search bug fix, Love Nystrom + +Version 2.2: + Teensy 3.0 compatibility, Paul Stoffregen, paul@pjrc.com + Arduino Due compatibility, http://arduino.cc/forum/index.php?topic=141030 + Fix DS18B20 example negative temperature + Fix DS18B20 example's low res modes, Ken Butcher + Improve reset timing, Mark Tillotson + Add const qualifiers, Bertrik Sikken + Add initial value input to crc16, Bertrik Sikken + Add target_search() function, Scott Roberts + +Version 2.1: + Arduino 1.0 compatibility, Paul Stoffregen + Improve temperature example, Paul Stoffregen + DS250x_PROM example, Guillermo Lovato + PIC32 (chipKit) compatibility, Jason Dangel, dangel.jason AT gmail.com + Improvements from Glenn Trewitt: + - crc16() now works + - check_crc16() does all of calculation/checking work. + - Added read_bytes() and write_bytes(), to reduce tedious loops. + - Added ds2408 example. + Delete very old, out-of-date readme file (info is here) + +Version 2.0: Modifications by Paul Stoffregen, January 2010: +http://www.pjrc.com/teensy/td_libs_OneWire.html + Search fix from Robin James + http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + Use direct optimized I/O in all cases + Disable interrupts during timing critical sections + (this solves many random communication errors) + Disable interrupts during read-modify-write I/O + Reduce RAM consumption by eliminating unnecessary + variables and trimming many to 8 bits + Optimize both crc8 - table version moved to flash + +Modified to work with larger numbers of devices - avoids loop. +Tested in Arduino 11 alpha with 12 sensors. +26 Sept 2008 -- Robin James +http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1238032295/27#27 + +Updated to work with arduino-0008 and to include skip() as of +2007/07/06. --RJL20 + +Modified to calculate the 8-bit CRC directly, avoiding the need for +the 256-byte lookup table to be loaded in RAM. Tested in arduino-0010 +-- Tom Pollard, Jan 23, 2008 + +Jim Studt's original library was modified by Josh Larios. + +Tom Pollard, pollard@alum.mit.edu, contributed around May 20, 2008 + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Much of the code was inspired by Derek Yerger's code, though I don't +think much of that remains. In any event that was.. + (copyleft) 2006 by Derek Yerger - Free to distribute freely. + +The CRC code was excerpted and inspired by the Dallas Semiconductor +sample code bearing this copyright. +//--------------------------------------------------------------------------- +// Copyright (C) 2000 Dallas Semiconductor Corporation, All Rights Reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL DALLAS SEMICONDUCTOR BE LIABLE FOR ANY CLAIM, DAMAGES +// OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of Dallas Semiconductor +// shall not be used except as stated in the Dallas Semiconductor +// Branding Policy. +//-------------------------------------------------------------------------- +*/ + +#include "OneWire.h" + + +OneWire::OneWire(uint8_t pin) +{ + pinMode(pin, INPUT); + bitmask = PIN_TO_BITMASK(pin); + baseReg = PIN_TO_BASEREG(pin); +#if ONEWIRE_SEARCH + reset_search(); +#endif +} + + +// Perform the onewire reset function. We will wait up to 250uS for +// the bus to come high, if it doesn't then it is broken or shorted +// and we return a 0; +// +// Returns 1 if a device asserted a presence pulse, 0 otherwise. +// +uint8_t OneWire::reset(void) +{ + IO_REG_TYPE mask = bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + uint8_t r; + uint8_t retries = 125; + + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); + interrupts(); + // wait until the wire is high... just in case + do { + if (--retries == 0) return 0; + delayMicroseconds(2); + } while ( !DIRECT_READ(reg, mask)); + + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + interrupts(); + delayMicroseconds(480); + noInterrupts(); + DIRECT_MODE_INPUT(reg, mask); // allow it to float + delayMicroseconds(70); + r = !DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(410); + return r; +} + +// +// Write a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +void OneWire::write_bit(uint8_t v) +{ + IO_REG_TYPE mask=bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + + if (v & 1) { + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(10); + DIRECT_WRITE_HIGH(reg, mask); // drive output high + interrupts(); + delayMicroseconds(55); + } else { + noInterrupts(); + DIRECT_WRITE_LOW(reg, mask); + DIRECT_MODE_OUTPUT(reg, mask); // drive output low + delayMicroseconds(65); + DIRECT_WRITE_HIGH(reg, mask); // drive output high + interrupts(); + delayMicroseconds(5); + } +} + +// +// Read a bit. Port and bit is used to cut lookup time and provide +// more certain timing. +// +uint8_t OneWire::read_bit(void) +{ + IO_REG_TYPE mask=bitmask; + volatile IO_REG_TYPE *reg IO_REG_ASM = baseReg; + uint8_t r; + + noInterrupts(); + DIRECT_MODE_OUTPUT(reg, mask); + DIRECT_WRITE_LOW(reg, mask); + delayMicroseconds(3); + DIRECT_MODE_INPUT(reg, mask); // let pin float, pull up will raise + delayMicroseconds(10); + r = DIRECT_READ(reg, mask); + interrupts(); + delayMicroseconds(53); + return r; +} + +// +// Write a byte. The writing code uses the active drivers to raise the +// pin high, if you need power after the write (e.g. DS18S20 in +// parasite power mode) then set 'power' to 1, otherwise the pin will +// go tri-state at the end of the write to avoid heating in a short or +// other mishap. +// +void OneWire::write(uint8_t v, uint8_t power /* = 0 */) { + uint8_t bitMask; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + OneWire::write_bit( (bitMask & v)?1:0); + } + if ( !power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } +} + +void OneWire::write_bytes(const uint8_t *buf, uint16_t count, bool power /* = 0 */) { + for (uint16_t i = 0 ; i < count ; i++) + write(buf[i]); + if (!power) { + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + DIRECT_WRITE_LOW(baseReg, bitmask); + interrupts(); + } +} + +// +// Read a byte +// +uint8_t OneWire::read() { + uint8_t bitMask; + uint8_t r = 0; + + for (bitMask = 0x01; bitMask; bitMask <<= 1) { + if ( OneWire::read_bit()) r |= bitMask; + } + return r; +} + +void OneWire::read_bytes(uint8_t *buf, uint16_t count) { + for (uint16_t i = 0 ; i < count ; i++) + buf[i] = read(); +} + +// +// Do a ROM select +// +void OneWire::select(const uint8_t rom[8]) +{ + uint8_t i; + + write(0x55); // Choose ROM + + for (i = 0; i < 8; i++) write(rom[i]); +} + +// +// Do a ROM skip +// +void OneWire::skip() +{ + write(0xCC); // Skip ROM +} + +void OneWire::depower() +{ + noInterrupts(); + DIRECT_MODE_INPUT(baseReg, bitmask); + interrupts(); +} + +#if ONEWIRE_SEARCH + +// +// You need to use this function to start a search again from the beginning. +// You do not need to do it for the first search, though you could. +// +void OneWire::reset_search() +{ + // reset the search state + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + for(int i = 7; ; i--) { + ROM_NO[i] = 0; + if ( i == 0) break; + } +} + +// Setup the search to find the device type 'family_code' on the next call +// to search(*newAddr) if it is present. +// +void OneWire::target_search(uint8_t family_code) +{ + // set the search state to find SearchFamily type devices + ROM_NO[0] = family_code; + for (uint8_t i = 1; i < 8; i++) + ROM_NO[i] = 0; + LastDiscrepancy = 64; + LastFamilyDiscrepancy = 0; + LastDeviceFlag = FALSE; +} + +// +// Perform a search. If this function returns a '1' then it has +// enumerated the next device and you may retrieve the ROM from the +// OneWire::address variable. If there are no devices, no further +// devices, or something horrible happens in the middle of the +// enumeration then a 0 is returned. If a new device is found then +// its address is copied to newAddr. Use OneWire::reset_search() to +// start over. +// +// --- Replaced by the one from the Dallas Semiconductor web site --- +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return TRUE : device found, ROM number in ROM_NO buffer +// FALSE : device not found, end of search +// +uint8_t OneWire::search(uint8_t *newAddr) +{ + uint8_t id_bit_number; + uint8_t last_zero, rom_byte_number, search_result; + uint8_t id_bit, cmp_id_bit; + + unsigned char rom_byte_mask, search_direction; + + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!LastDeviceFlag) + { + // 1-Wire reset + if (!reset()) + { + // reset the search + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + return FALSE; + } + + // issue the search command + write(0xF0); + + // loop to do the search + do + { + // read a bit and its complement + id_bit = read_bit(); + cmp_id_bit = read_bit(); + + // check for no devices on 1-wire + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else + { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // bit write value for search + else + { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < LastDiscrepancy) + search_direction = ((ROM_NO[rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == LastDiscrepancy); + + // if 0 was picked then record its position in LastZero + if (search_direction == 0) + { + last_zero = id_bit_number; + + // check for Last discrepancy in family + if (last_zero < 9) + LastFamilyDiscrepancy = last_zero; + } + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction == 1) + ROM_NO[rom_byte_number] |= rom_byte_mask; + else + ROM_NO[rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + write_bit(search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) + { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } + while(rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) + { + // search successful so set LastDiscrepancy,LastDeviceFlag,search_result + LastDiscrepancy = last_zero; + + // check for last device + if (LastDiscrepancy == 0) + LastDeviceFlag = TRUE; + + search_result = TRUE; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !ROM_NO[0]) + { + LastDiscrepancy = 0; + LastDeviceFlag = FALSE; + LastFamilyDiscrepancy = 0; + search_result = FALSE; + } else { + for (int i = 0; i < 8; i++) newAddr[i] = ROM_NO[i]; + } + return search_result; + } + +#endif + +#if ONEWIRE_CRC +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" +// + +#if ONEWIRE_CRC8_TABLE +// This table comes from Dallas sample code where it is freely reusable, +// though Copyright (C) 2000 Dallas Semiconductor Corporation +static const uint8_t PROGMEM dscrc_table[] = { + 0, 94,188,226, 97, 63,221,131,194,156,126, 32,163,253, 31, 65, + 157,195, 33,127,252,162, 64, 30, 95, 1,227,189, 62, 96,130,220, + 35,125,159,193, 66, 28,254,160,225,191, 93, 3,128,222, 60, 98, + 190,224, 2, 92,223,129, 99, 61,124, 34,192,158, 29, 67,161,255, + 70, 24,250,164, 39,121,155,197,132,218, 56,102,229,187, 89, 7, + 219,133,103, 57,186,228, 6, 88, 25, 71,165,251,120, 38,196,154, + 101, 59,217,135, 4, 90,184,230,167,249, 27, 69,198,152,122, 36, + 248,166, 68, 26,153,199, 37,123, 58,100,134,216, 91, 5,231,185, + 140,210, 48,110,237,179, 81, 15, 78, 16,242,172, 47,113,147,205, + 17, 79,173,243,112, 46,204,146,211,141,111, 49,178,236, 14, 80, + 175,241, 19, 77,206,144,114, 44,109, 51,209,143, 12, 82,176,238, + 50,108,142,208, 83, 13,239,177,240,174, 76, 18,145,207, 45,115, + 202,148,118, 40,171,245, 23, 73, 8, 86,180,234,105, 55,213,139, + 87, 9,235,181, 54,104,138,212,149,203, 41,119,244,170, 72, 22, + 233,183, 85, 11,136,214, 52,106, 43,117,151,201, 74, 20,246,168, + 116, 42,200,150, 21, 75,169,247,182,232, 10, 84,215,137,107, 53}; + +// +// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM +// and the registers. (note: this might better be done without to +// table, it would probably be smaller and certainly fast enough +// compared to all those delayMicrosecond() calls. But I got +// confused, so I use this table from the examples.) +// +uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + crc = pgm_read_byte(dscrc_table + (crc ^ *addr++)); + } + return crc; +} +#else +// +// Compute a Dallas Semiconductor 8 bit CRC directly. +// this is much slower, but much smaller, than the lookup table. +// +uint8_t OneWire::crc8(const uint8_t *addr, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) { + uint8_t inbyte = *addr++; + for (uint8_t i = 8; i; i--) { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} +#endif + +#if ONEWIRE_CRC16 +bool OneWire::check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc) +{ + crc = ~crc16(input, len, crc); + return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +} + +uint16_t OneWire::crc16(const uint8_t* input, uint16_t len, uint16_t crc) +{ + static const uint8_t oddparity[16] = + { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + + for (uint16_t i = 0 ; i < len ; i++) { + // Even though we're just copying a byte from the input, + // we'll be doing 16-bit computation with it. + uint16_t cdata = input[i]; + cdata = (cdata ^ crc) & 0xff; + crc >>= 8; + + if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) + crc ^= 0xC001; + + cdata <<= 6; + crc ^= cdata; + cdata <<= 1; + crc ^= cdata; + } + return crc; +} +#endif + +#endif diff --git a/bitlair_doorduino_inner/src/OneWire.h b/bitlair_doorduino_inner/src/OneWire.h new file mode 100644 index 0000000..be62737 --- /dev/null +++ b/bitlair_doorduino_inner/src/OneWire.h @@ -0,0 +1,250 @@ +#ifndef OneWire_h +#define OneWire_h + +#include + +#if ARDUINO >= 100 +#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc +#else +#include "WProgram.h" // for delayMicroseconds +#include "pins_arduino.h" // for digitalPinToBitMask, etc +#endif + +// You can exclude certain features from OneWire. In theory, this +// might save some space. In practice, the compiler automatically +// removes unused code (technically, the linker, using -fdata-sections +// and -ffunction-sections when compiling, and Wl,--gc-sections +// when linking), so most of these will not result in any code size +// reduction. Well, unless you try to use the missing features +// and redesign your program to not need them! ONEWIRE_CRC8_TABLE +// is the exception, because it selects a fast but large algorithm +// or a small but slow algorithm. + +// you can exclude onewire_search by defining that to 0 +#ifndef ONEWIRE_SEARCH +#define ONEWIRE_SEARCH 1 +#endif + +// You can exclude CRC checks altogether by defining this to 0 +#ifndef ONEWIRE_CRC +#define ONEWIRE_CRC 1 +#endif + +// Select the table-lookup method of computing the 8-bit CRC +// by setting this to 1. The lookup table enlarges code size by +// about 250 bytes. It does NOT consume RAM (but did in very +// old versions of OneWire). If you disable this, a slower +// but very compact algorithm is used. +#ifndef ONEWIRE_CRC8_TABLE +#define ONEWIRE_CRC8_TABLE 1 +#endif + +// You can allow 16-bit CRC checks by defining this to 1 +// (Note that ONEWIRE_CRC must also be 1.) +#ifndef ONEWIRE_CRC16 +#define ONEWIRE_CRC16 1 +#endif + +#define FALSE 0 +#define TRUE 1 + +// Platform specific I/O definitions + +#if defined(__AVR__) +#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM asm("r30") +#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) + +#elif defined(__MK20DX128__) || defined(__MK20DX256__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (1) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (*((base)+512)) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1) + +#elif defined(__MKL26Z64__) +#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint8_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask)) +#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask)) +#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask)) + +#elif defined(__SAM3X8E__) +// Arduino 1.5.1 may have a bug in delayMicroseconds() on Arduino Due. +// http://arduino.cc/forum/index.php/topic,141030.msg1076268.html#msg1076268 +// If you have trouble with OneWire on Arduino Due, please check the +// status of delayMicroseconds() before reporting a bug in OneWire! +#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER)) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0) +#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask)) +#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask)) +#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask)) +#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask)) +#ifndef PROGMEM +#define PROGMEM +#endif +#ifndef pgm_read_byte +#define pgm_read_byte(addr) (*(const uint8_t *)(addr)) +#endif + +#elif defined(__PIC32MX__) +#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin))) +#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) +#define IO_REG_TYPE uint32_t +#define IO_REG_ASM +#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10 +#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08 +#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04 +#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24 +#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28 + +#else +#define PIN_TO_BASEREG(pin) (0) +#define PIN_TO_BITMASK(pin) (pin) +#define IO_REG_TYPE unsigned int +#define IO_REG_ASM +#define DIRECT_READ(base, pin) digitalRead(pin) +#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW) +#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH) +#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT) +#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT) +#warning "OneWire. Fallback mode. Using API calls for pinMode,digitalRead and digitalWrite. Operation of this library is not guaranteed on this architecture." + +#endif + + +class OneWire +{ + private: + IO_REG_TYPE bitmask; + volatile IO_REG_TYPE *baseReg; + +#if ONEWIRE_SEARCH + // global search state + unsigned char ROM_NO[8]; + uint8_t LastDiscrepancy; + uint8_t LastFamilyDiscrepancy; + uint8_t LastDeviceFlag; +#endif + + public: + OneWire( uint8_t pin); + + // Perform a 1-Wire reset cycle. Returns 1 if a device responds + // with a presence pulse. Returns 0 if there is no device or the + // bus is shorted or otherwise held low for more than 250uS + uint8_t reset(void); + + // Issue a 1-Wire rom select command, you do the reset first. + void select(const uint8_t rom[8]); + + // Issue a 1-Wire rom skip command, to address all on bus. + void skip(void); + + // Write a byte. If 'power' is one then the wire is held high at + // the end for parasitically powered devices. You are responsible + // for eventually depowering it by calling depower() or doing + // another read or write. + void write(uint8_t v, uint8_t power = 0); + + void write_bytes(const uint8_t *buf, uint16_t count, bool power = 0); + + // Read a byte. + uint8_t read(void); + + void read_bytes(uint8_t *buf, uint16_t count); + + // Write a bit. The bus is always left powered at the end, see + // note in write() about that. + void write_bit(uint8_t v); + + // Read a bit. + uint8_t read_bit(void); + + // Stop forcing power onto the bus. You only need to do this if + // you used the 'power' flag to write() or used a write_bit() call + // and aren't about to do another read or write. You would rather + // not leave this powered if you don't have to, just in case + // someone shorts your bus. + void depower(void); + +#if ONEWIRE_SEARCH + // Clear the search state so that if will start from the beginning again. + void reset_search(); + + // Setup the search to find the device type 'family_code' on the next call + // to search(*newAddr) if it is present. + void target_search(uint8_t family_code); + + // Look for the next device. Returns 1 if a new address has been + // returned. A zero might mean that the bus is shorted, there are + // no devices, or you have already retrieved all of them. It + // might be a good idea to check the CRC to make sure you didn't + // get garbage. The order is deterministic. You will always get + // the same devices in the same order. + uint8_t search(uint8_t *newAddr); +#endif + +#if ONEWIRE_CRC + // Compute a Dallas Semiconductor 8 bit CRC, these are used in the + // ROM and scratchpad registers. + static uint8_t crc8(const uint8_t *addr, uint8_t len); + +#if ONEWIRE_CRC16 + // Compute the 1-Wire CRC16 and compare it against the received CRC. + // Example usage (reading a DS2408): + // // Put everything in a buffer so we can compute the CRC easily. + // uint8_t buf[13]; + // buf[0] = 0xF0; // Read PIO Registers + // buf[1] = 0x88; // LSB address + // buf[2] = 0x00; // MSB address + // WriteBytes(net, buf, 3); // Write 3 cmd bytes + // ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + // if (!CheckCRC16(buf, 11, &buf[11])) { + // // Handle error. + // } + // + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param inverted_crc - The two CRC16 bytes in the received data. + // This should just point into the received data, + // *not* at a 16-bit integer. + // @param crc - The crc starting value (optional) + // @return True, iff the CRC matches. + static bool check_crc16(const uint8_t* input, uint16_t len, const uint8_t* inverted_crc, uint16_t crc = 0); + + // Compute a Dallas Semiconductor 16 bit CRC. This is required to check + // the integrity of data received from many 1-Wire devices. Note that the + // CRC computed here is *not* what you'll get from the 1-Wire network, + // for two reasons: + // 1) The CRC is transmitted bitwise inverted. + // 2) Depending on the endian-ness of your processor, the binary + // representation of the two-byte return value may have a different + // byte order than the two bytes you get from 1-Wire. + // @param input - Array of bytes to checksum. + // @param len - How many bytes to use. + // @param crc - The crc starting value (optional) + // @return The CRC16, as defined by Dallas Semiconductor. + static uint16_t crc16(const uint8_t* input, uint16_t len, uint16_t crc = 0); +#endif +#endif +}; + +#endif diff --git a/bitlair_doorduino_inner/src/ds1961.cpp b/bitlair_doorduino_inner/src/ds1961.cpp new file mode 100644 index 0000000..37f521e --- /dev/null +++ b/bitlair_doorduino_inner/src/ds1961.cpp @@ -0,0 +1,325 @@ +#include +#include +#include + +#include "OneWire.h" +#include "ds1961.h" + +// commands used in the DS1961 standard +#define CMD_WRITE_SCRATCHPAD 0x0F +#define CMD_COMPUTE_NEXT_SECRET 0x33 +#define CMD_COPY_SCRATCHPAD 0x55 +#define CMD_LOAD_FIRST_SECRET 0x5A +#define CMD_REFRESH_SCRATCHPAD 0xA3 +#define CMD_READ_AUTH_PAGE 0xA5 +#define CMD_READ_SCRATCHPAD 0xAA +#define CMD_READ_MEMORY 0xF0 + +// memory ranges +#define MEM_DATA_PAGE_0 0x00 +#define MEM_DATA_PAGE_1 0x20 +#define MEM_DATA_PAGE_2 0x40 +#define MEM_DATA_PAGE_3 0x60 +#define MEM_SECRET 0x80 +#define MEM_IDENTITY 0x90 + +// timing (ms) +#define T_CSHA 2 // actually 1.5 +#define T_PROG 10 + + +DS1961::DS1961(OneWire *oneWire) +{ + ow = oneWire; +} + +static bool ResetAndSelect(OneWire *ow, const uint8_t id[8]) +{ + if (!ow->reset()) { + return false; + } + ow->select((uint8_t *) id); + + return true; +} + +static bool WriteScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, const uint8_t data[8]) +{ + uint8_t buf[11]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // perform write scratchpad command + buf[len++] = CMD_WRITE_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + memcpy(buf + len, data, 8); + len += 8; + ow->write_bytes(buf, len); + ow->read_bytes(crc, 2); + + return ow->check_crc16(buf, len, crc); +} + +static bool RefreshScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, const uint8_t data[8]) +{ + uint8_t buf[11]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // perform refresh scratchpad command + buf[len++] = CMD_REFRESH_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + memcpy(buf + len, data, 8); + len += 8; + ow->write_bytes(buf, len); + ow->read_bytes(crc, 2); + + return ow->check_crc16(buf, len, crc); +} + +static bool ReadScratchPad(OneWire *ow, const uint8_t id[8], uint16_t *addr, uint8_t *es, uint8_t data[8]) +{ + uint8_t buf[12]; + uint8_t crc[2]; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send read scratchpad command + buf[len++] = CMD_READ_SCRATCHPAD; + ow->write_bytes(buf, len); + + // get TA0/1 and ES + ow->read_bytes(buf + len, 3); + len += 3; + *addr = (buf[2] << 8) | buf[1]; + *es = buf[3]; + + // get data + ow->read_bytes(buf + len, 8); + len += 8; + memcpy(data, buf + 4, 8); + + // check CRC + ow->read_bytes(crc, 2); + return ow->check_crc16(buf, len, crc); +} + +static bool CopyScratchPad(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t es, const uint8_t mac[20]) +{ + uint8_t buf[4]; + int len = 0; + uint8_t status; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send copy scratchpad command + arguments + buf[len++] = CMD_COPY_SCRATCHPAD; + buf[len++] = (addr >> 0) & 0xFF; // 2 byte target address + buf[len++] = (addr >> 8) & 0xFF; // 2 byte target address + buf[len++] = es; // es + ow->write_bytes(buf, len, 1); // write and keep powered + + // wait while MAC is calculated + delay(T_CSHA); + + // send MAC + ow->write_bytes(mac, 20); + + // wait 10 ms + delay(T_PROG); + ow->depower(); + + // check final status byte + status = ow->read(); + return (status == 0xAA); +} + +static bool ReadAuthPage(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t data[32], uint8_t mac[20]) +{ + uint8_t buf[36]; + uint8_t crc[2]; + uint8_t status; + int len = 0; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // send command + buf[len++] = CMD_READ_AUTH_PAGE; + buf[len++] = (addr >> 0) & 0xFF; + buf[len++] = (addr >> 8) & 0xFF; + ow->write_bytes(buf, len); + + // read data part + 0xFF + ow->read_bytes(buf + len, 33); + len += 33; + if (buf[35] != 0xFF) { + return false; + } + ow->read_bytes(crc, 2); + if (!ow->check_crc16(buf, len, crc)) { + return false; + } + memcpy(data, buf + 3, 32); + + // read mac part + delay(T_CSHA); + ow->read_bytes(mac, 20); + ow->read_bytes(crc, 2); + if (!ow->check_crc16(mac, 20, crc)) { + return false; + } + + // check final status byte + status = ow->read(); + return (status == 0xAA); +} + +static bool LoadFirstSecret(OneWire *ow, const uint8_t id[8], uint16_t addr, uint8_t es) +{ + uint8_t status; + + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // write auth code + ow->write(CMD_LOAD_FIRST_SECRET); + ow->write((addr >> 0) & 0xFF); + ow->write((addr >> 8) & 0xFF); + ow->write(es, 1); + delay(T_PROG); + ow->depower(); + + status = ow->read(); + return (status == 0xAA); +} + +static bool ReadMemory(OneWire *ow, const uint8_t id[8], int addr, int len, uint8_t data[]) +{ + // reset and select + if (!ResetAndSelect(ow, id)) { + return false; + } + + // write command/addr + ow->write(CMD_READ_MEMORY); + ow->write((addr >> 0) & 0xFF); + ow->write((addr >> 8) & 0xFF); + + // read data + ow->read_bytes(data, len); + + return true; +} + +bool DS1961::ReadAuthWithChallenge(const uint8_t id[8], uint16_t addr, const uint8_t challenge[3], uint8_t data[32], uint8_t mac[20]) +{ + uint8_t scratchpad[8]; + + // put the challenge in the scratchpad + memset(scratchpad, 0, sizeof(scratchpad)); + memcpy(scratchpad + 4, challenge, 3); + if (!WriteScratchPad(ow, id, addr, scratchpad)) { +// Serial.println("WriteScratchPad failed!"); + return false; + } + + // perform the authenticated read + if (!ReadAuthPage(ow, id, addr, data, mac)) { +// Serial.println("ReadAuthPage failed!"); + return false; + } + + return true; +} + +bool DS1961::WriteSecret(const uint8_t id[8], const uint8_t secret[8]) +{ + uint16_t addr; + uint8_t es; + uint8_t data[8]; + + // write secret to scratch pad + if (!WriteScratchPad(ow, id, MEM_SECRET, secret)) { +// Serial.println("WriteScratchPad failed!"); + return false; + } + + // read scratch pad for auth code + if (!ReadScratchPad(ow, id, &addr, &es, data)) { +// Serial.println("ReadScratchPad failed!"); + return false; + } + if (!LoadFirstSecret(ow, id, addr, es)) { +// Serial.println("LoadFirstSecret failed!"); + return false; + } + + return true; +} + +/* + * Writes 8 bytes of data to specified address + */ +bool DS1961::WriteData(const uint8_t id[8], int addr, const uint8_t data[8], const uint8_t mac[20]) +{ + uint8_t spad[8]; + uint16_t ad; + uint8_t es; + + // write data into scratchpad + if (!WriteScratchPad(ow, id, addr, data)) { + Serial.println("WriteScratchPad failed!"); + return false; + } + + // read scratch pad for auth code + if (!ReadScratchPad(ow, id, &ad, &es, spad)) { + Serial.println("ReadScratchPad failed!"); + return false; + } + + // copy scratchpad to EEPROM + if (!CopyScratchPad(ow, id, ad, es, mac)) { + Serial.println("CopyScratchPad failed!"); + return false; + } + + // refresh scratchpad + if (!RefreshScratchPad(ow, id, addr, data)) { + Serial.println("RefreshScratchPad failed!"); + return false; + } + + // re-write with load first secret + if (!LoadFirstSecret(ow, id, addr, es)) { + Serial.println("LoadFirstSecret failed!"); + return false; + } + + return true; +} + diff --git a/bitlair_doorduino_inner/src/ds1961.h b/bitlair_doorduino_inner/src/ds1961.h new file mode 100644 index 0000000..27179df --- /dev/null +++ b/bitlair_doorduino_inner/src/ds1961.h @@ -0,0 +1,23 @@ +#ifndef _DS1961_H_ +#define _DS1961_H_ + +#include +#include + +#include "OneWire.h" + +class DS1961 { + +public: + DS1961(OneWire *oneWire); + + bool WriteSecret(const uint8_t id[8], const uint8_t secret[8]); + bool ReadAuthWithChallenge(const uint8_t id[8], uint16_t addr, const uint8_t challenge[3], uint8_t data[32], uint8_t mac[20]); + bool WriteData(const uint8_t id[8], int addr, const uint8_t data[8], const uint8_t mac[20]); + +private: + OneWire *ow; + +}; + +#endif /* _DS1961_H_ */ diff --git a/bitlair_doorduino_inner/src/main.cpp b/bitlair_doorduino_inner/src/main.cpp new file mode 100644 index 0000000..94f4094 --- /dev/null +++ b/bitlair_doorduino_inner/src/main.cpp @@ -0,0 +1,741 @@ +#include "OneWire.h" +#include "ds1961.h" + +#include +#include +// #include +#include "Entropy.h" +#include "sha1.h" +#include "Wire.h" + + +#include +// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step +#define MOTOR_STEPS 2 +#define RPM 60 +#define DIR A6 +// #define STEP A7 +#define STEP 9 +#include "A4988.h" +A4988 stepper(MOTOR_STEPS, DIR, STEP); + +#define INPUT_SOLENOID 7 +#define INPUT_HORN 3 +#define PIN_LEDSOLENOID 6 +#define PIN_LEDHORN 5 +bool StateSolenoid = false; +bool StateHorn = false; +uint32_t SolenoidStartTime; +bool StateSolenoidInactive = false; +uint32_t SolenoidInactiveStartTime; + + + +#define PIN_DOORPOWER A1 +#define PIN_SOLENOID A3 +#define PIN_HORN A2 +#define PIN_OPEN 13 +#define PIN_CLOSE A0 + +#define PIN_1WIRE 8 +#define PIN_LEDGREEN 10 +#define PIN_LEDRED 11 + +#define PIN_MAINS_POWER 2 + +#define CMD_BUFSIZE 64 +#define CMD_TIMEOUT 10000 //command timeout in milliseconds + +#define SECRETSIZE 8 +#define ADDRSIZE 8 +#define STORAGESIZE (SECRETSIZE + ADDRSIZE) +#define EEPROMDEVICEADDRESS 0x50 +#define EEPROMSIZE 2048 +#define SHA1SIZE 20 + +#define IBUTTON_SEARCH_TIMEOUT 60000 //timeout searching for ibutton + +#define LEDState_Off 0 +#define LEDState_Reading 1 +#define LEDState_Authorized 2 +#define LEDState_Busy 3 + +#define SPACEState_Open 1 +#define SPACEState_Closed 0 + +#define htons(x) ( ((x)<<8) | (((x)>>8)&0xFF) ) +#define ntohs(x) htons(x) + +#define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \ + ((x)<< 8 & 0x00FF0000UL) | \ + ((x)>> 8 & 0x0000FF00UL) | \ + ((x)>>24 & 0x000000FFUL) ) +#define ntohl(x) htonl(x) + +OneWire ds(PIN_1WIRE); +DS1961 ibutton(&ds); + +bool HasMainsPower(); + +int Serialprintf (const char* fmt, ...) __attribute__ ((format (printf, 1, 2))); + +int Serialprintf (const char* fmt, ...) +{ + char buf[256]; + + va_list args; + va_start(args, fmt); + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + + Serial.print(buf); +} + +uint8_t g_ledstate = LEDState_Off; +uint32_t g_ledtimestart; +bool g_fade; +bool g_lockopen; +bool g_spacestate = SPACEState_Closed; + +#define LED_PERIOD 1024 + +void ProcessLEDs() +{ + if (g_ledstate == LEDState_Off) + { + analogWrite(PIN_LEDGREEN, 0); + analogWrite(PIN_LEDRED, 0); + } + else if (g_ledstate == LEDState_Reading) + { + uint32_t timesincesetting = millis() + LED_PERIOD / 2 - g_ledtimestart; + uint32_t ledtime = timesincesetting % LED_PERIOD; + uint32_t ledval; + if (ledtime < LED_PERIOD / 2) + ledval = ledtime; + else + ledval = (LED_PERIOD - 1) - ledtime; + + ledval = (ledval * ledval) / LED_PERIOD; + if (!HasMainsPower()) + ledval = (ledval + 5) / 10; + + if (g_lockopen) + { + analogWrite(PIN_LEDGREEN, ledval); + analogWrite(PIN_LEDRED, 0); + } + else + { + analogWrite(PIN_LEDGREEN, 0); + analogWrite(PIN_LEDRED, ledval); + } + } + else if (g_ledstate == LEDState_Authorized) + { + if (g_lockopen) + { + analogWrite(PIN_LEDGREEN, 255); + analogWrite(PIN_LEDRED, 0); + } + else + { + analogWrite(PIN_LEDGREEN, 0); + analogWrite(PIN_LEDRED, 255); + } + } + else if (g_ledstate == LEDState_Busy) + { + analogWrite(PIN_LEDGREEN, 255); + analogWrite(PIN_LEDRED, 255); + } +} + +void SetLEDState(uint8_t ledstate) +{ + if (ledstate == LEDState_Reading && g_ledstate != LEDState_Reading) + { + g_ledtimestart = millis(); + g_fade = true; + } + + g_ledstate = ledstate; + + ProcessLEDs(); +} + +void DelayLEDs(uint32_t delayms) +{ + uint32_t now = millis(); + while (millis() - now < delayms) + ProcessLEDs(); +} + +void setup() +{ + Serial.begin(115200); + Serial.println("DEBUG: Board started"); + Wire.begin(); + + stepper.begin(RPM); + stepper.enable(); + stepper.setMicrostep(1); // Set microstep mode to 1:1 + + pinMode(INPUT_SOLENOID, INPUT_PULLUP); + pinMode(INPUT_HORN, INPUT_PULLUP); + pinMode(PIN_LEDSOLENOID, OUTPUT); + pinMode(PIN_LEDHORN, OUTPUT); + pinMode(PIN_DOORPOWER, OUTPUT); + pinMode(PIN_SOLENOID, OUTPUT); + pinMode(PIN_OPEN, OUTPUT); + pinMode(PIN_CLOSE, OUTPUT); + pinMode(PIN_HORN, OUTPUT); + + pinMode(PIN_LEDGREEN, OUTPUT); + pinMode(PIN_LEDRED, OUTPUT); + pinMode(PIN_MAINS_POWER, INPUT); + + digitalWrite(PIN_OPEN, LOW); + digitalWrite(PIN_CLOSE, LOW); + digitalWrite(PIN_DOORPOWER, LOW); + digitalWrite(PIN_HORN, LOW); + digitalWrite(PIN_SOLENOID, LOW); + + SetLEDState(LEDState_Off); + + Entropy.initialize(); +} + +void writeEEPROM(unsigned int eeaddress, byte data ) +{ + Wire.beginTransmission(EEPROMDEVICEADDRESS); + Wire.write((int)(eeaddress >> 8)); // MSB + Wire.write((int)(eeaddress & 0xFF)); // LSB + Wire.write(data); + Wire.endTransmission(); + + delay(5); +} + +byte readEEPROM(unsigned int eeaddress ) +{ + byte rdata = 0xFF; + + Wire.beginTransmission(EEPROMDEVICEADDRESS); + Wire.write((int)(eeaddress >> 8)); // MSB + Wire.write((int)(eeaddress & 0xFF)); // LSB + Wire.endTransmission(); + + Wire.requestFrom(EEPROMDEVICEADDRESS,1); + + if (Wire.available()) rdata = Wire.read(); + + return rdata; +} + +void AddButton(uint8_t* addr, uint8_t* secret) +{ + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + bool emptyslot = true; + uint16_t startaddr = i * STORAGESIZE; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = readEEPROM(startaddr + j); + if (eeprombyte != 0xFF && eeprombyte != addr[j]) + { + emptyslot = false; + break; + } + } + + if (emptyslot) + { + for (uint16_t j = 0; j < ADDRSIZE; j++) + writeEEPROM(startaddr + j, addr[j]); + + for (uint16_t j = 0; j < SECRETSIZE; j++) + writeEEPROM(startaddr + j + ADDRSIZE, secret[j]); + + Serialprintf("DEBUG: stored button in slot %i\n", i); + + return; + } + } + + Serial.println("ERROR: no room in eeprom to store button"); +} + +void RemoveButton(uint8_t* addr) +{ + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + uint16_t startaddr = i * STORAGESIZE; + bool sameaddr = true; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = readEEPROM(startaddr + j); + if (eeprombyte != addr[j]) + { + sameaddr = false; + break; + } + } + if (!sameaddr) + continue; + + Serialprintf("DEBUG: erasing slot %i\n", i); + + for (uint16_t j = 0; j < STORAGESIZE; j++) + writeEEPROM(startaddr + j, 0xFF); + } +} + +bool GetButtonSecret(uint8_t* addr, uint8_t* secret) +{ + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + uint16_t startaddr = i * STORAGESIZE; + bool sameaddr = true; + bool isempty = true; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = readEEPROM(startaddr + j); + if (isempty && eeprombyte != 0xFF) + isempty = false; + + if (eeprombyte != addr[j]) + { + sameaddr = false; + break; + } + } + + if (isempty) + continue; + + if (sameaddr) + { + Serialprintf("DEBUG: getting secret from slot %i\n", i); + + for (uint16_t j = 0; j < SECRETSIZE; j++) + secret[j] = readEEPROM(startaddr + j + ADDRSIZE); + + return true; + } + } + + Serial.println("DEBUG: can't find secret for button"); + + return false; +} + +void ListButtons() +{ + Serial.println("button list start"); + + for (uint16_t i = 0; i < EEPROMSIZE / STORAGESIZE; i++) + { + uint16_t startaddr = i * STORAGESIZE; + uint8_t buttonid[ADDRSIZE]; + bool isempty = true; + for (uint16_t j = 0; j < ADDRSIZE; j++) + { + uint8_t eeprombyte = readEEPROM(startaddr + j); + if (isempty && eeprombyte != 0xFF) + isempty = false; + + buttonid[j] = eeprombyte; + } + + if (isempty) + continue; + + Serialprintf("button: "); + for (uint16_t j = 0; j < ADDRSIZE; j++) + Serialprintf("%02x", buttonid[j]); + Serialprintf("\n"); + } +} + +#define RANDOMDELAY_MIN 50 +#define RANDOMDELAY_MAX 200 + +bool AuthenticateButton(uint8_t* addr) +{ + uint8_t secret[SECRETSIZE]; + if (!GetButtonSecret(addr, secret)) + return false; + + uint8_t mac_from_ibutton[SHA1SIZE]; + uint8_t data[32]; + uint8_t nonce[3]; + + for (uint8_t i = 0; i < sizeof(nonce); i++) + nonce[i] = Entropy.randomByte(); + + if (!ibutton.ReadAuthWithChallenge(addr, 0, nonce, data, mac_from_ibutton)) + return false; + + sha1::sha1nfo sha1data = {}; + sha1::sha1_init(&sha1data); + sha1::sha1_write(&sha1data, (const char*)secret, 4); + sha1::sha1_write(&sha1data, (const char*)data, sizeof(data)); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0xff); + sha1::sha1_writebyte(&sha1data, 0x40); + sha1::sha1_write(&sha1data, (const char*)addr, ADDRSIZE - 1); + sha1::sha1_write(&sha1data, (const char*)secret + 4, 4); + sha1::sha1_write(&sha1data, (const char*)nonce, sizeof(nonce)); + + uint8_t* sha_computed = sha1::sha1_result(&sha1data); + + uint8_t mac_computed[SHA1SIZE]; + ((uint32_t*)mac_computed)[0] = htonl(ntohl(*(uint32_t *)sha_computed) - 0x67452301); + ((uint32_t*)mac_computed)[1] = htonl(ntohl(*(uint32_t *)(sha_computed+4)) - 0xefcdab89); + ((uint32_t*)mac_computed)[2] = htonl(ntohl(*(uint32_t *)(sha_computed+8)) - 0x98badcfe); + ((uint32_t*)mac_computed)[3] = htonl(ntohl(*(uint32_t *)(sha_computed+12)) - 0x10325476); + ((uint32_t*)mac_computed)[4] = htonl(ntohl(*(uint32_t *)(sha_computed+16)) - 0xc3d2e1f0); + + //this check should always take the same amount of time, to prevent a timing attack + bool macvalid = true; + for (uint8_t i = 0; i < SHA1SIZE; i++) + { + if (mac_from_ibutton[i] != mac_computed[SHA1SIZE - 1 - i]) + macvalid = false; + } + + //add a random delay + delayMicroseconds(Entropy.random(RANDOMDELAY_MIN, RANDOMDELAY_MAX)); + + return macvalid; +} + +bool ReadCMD(char* cmdbuf, uint8_t* cmdbuffill) +{ + uint32_t cmdstarttime = millis(); + *cmdbuffill = 0; + for(;;) + { + if (Serial.available()) + { + char input = Serial.read(); + if (input == '\n') + { + cmdbuf[*cmdbuffill] = 0; + return true; + } + else if (*cmdbuffill < CMD_BUFSIZE - 1) + { + cmdbuf[*cmdbuffill] = input; + (*cmdbuffill)++; + } + } + + if (millis() - cmdstarttime >= CMD_TIMEOUT) + { + Serial.println("ERROR: timeout receiving command"); + return false; + } + } +} + +uint8_t NextWordPos(char* cmdbuf, uint8_t cmdbuffill, uint8_t index) +{ + bool foundwhitespace = false; + for (uint8_t i = index; i < cmdbuffill; i++) + { + if (!foundwhitespace) + { + if (cmdbuf[i] == ' ') + foundwhitespace = true; + } + else + { + if (cmdbuf[i] != ' ') + { + return i; + } + } + } + + return 0; +} + +bool GetHexWordFromCMD(char* cmdbuf, uint8_t cmdbuffill, uint8_t* wordpos, uint8_t* wordbuf, uint8_t wordsize, char* wordname) +{ + *wordpos = NextWordPos(cmdbuf, cmdbuffill, *wordpos); + + if (*wordpos == 0) + { + Serialprintf("ERROR: no %s found in command\n", wordname); + return false; + } + else if (cmdbuffill - *wordpos < wordsize * 2) + { + Serialprintf("ERROR: %s is too short\n", wordname); + return false; + } + + for (uint8_t i = 0; i < wordsize; i++) + { + if ((cmdbuf[*wordpos + i * 2] == ' ') || (cmdbuf[*wordpos + i * 2 + 1] == ' ')) + { + Serialprintf("ERROR: %s is too short\n", wordname); + return false; + } + + int numread = sscanf(cmdbuf + *wordpos + i * 2, "%2hhx", wordbuf + i); + if (numread != 1) + { + Serialprintf("ERROR: %s is invalid\n", wordname); + return false; + } + } + + return true; +} + +#define CMD_ADD_BUTTON "add_button" +#define CMD_REMOVE_BUTTON "remove_button" +#define CMD_LIST_BUTTONS "list_buttons" +#define CMD_SPACESTATE "spacestate" + +void ParseCMD(char* cmdbuf, uint8_t cmdbuffill) +{ + Serial.print("DEBUG: Received cmd: "); + Serial.println(cmdbuf); + + bool isadd = strncmp(CMD_ADD_BUTTON, cmdbuf, strlen(CMD_ADD_BUTTON)) == 0; + bool isremove = strncmp(CMD_REMOVE_BUTTON, cmdbuf, strlen(CMD_REMOVE_BUTTON)) == 0; + bool islist = strncmp(CMD_LIST_BUTTONS, cmdbuf, strlen(CMD_LIST_BUTTONS)) == 0; + bool isspacestate = strncmp(CMD_SPACESTATE, cmdbuf, strlen(CMD_SPACESTATE)) == 0; + + if (isadd || isremove) + { + uint8_t wordpos = 0; + uint8_t addr[ADDRSIZE]; + if (!GetHexWordFromCMD(cmdbuf, cmdbuffill, &wordpos, addr, ADDRSIZE, "address")) + return; + + Serial.print("DEBUG: Received address "); + for (uint8_t i = 0; i < ADDRSIZE; i++) + Serialprintf("%02x", addr[i]); + Serial.print("\n"); + + bool addrvalid = false; + for (uint8_t i = 0; i < ADDRSIZE; i++) + { + if (addr[i] != 0xFF) + { + addrvalid = true; + break; + } + } + if (!addrvalid) + { + Serial.println("ERROR: address FFFFFFFFFFFFFFFF is invalid"); + return; + } + + if (isadd) + { + uint8_t secret[SECRETSIZE]; + if (!GetHexWordFromCMD(cmdbuf, cmdbuffill, &wordpos, secret, SECRETSIZE, "secret")) + return; + + Serial.print("DEBUG: Received secret "); + for (uint8_t i = 0; i < ADDRSIZE; i++) + Serialprintf("%02x", secret[i]); + Serial.print("\n"); + + AddButton(addr, secret); + } + else + { + Serialprintf("DEBUG: removing button\n"); + RemoveButton(addr); + } + } + else if (islist) + { + ListButtons(); + } + else if (isspacestate) + { + uint8_t wordpos = 0; + wordpos = NextWordPos(cmdbuf, cmdbuffill, wordpos); + bool isopen = strncmp("open", &cmdbuf[wordpos], strlen("open")) == 0; + bool isclosed = strncmp("closed", &cmdbuf[wordpos], strlen("closed")) == 0; + if(isopen || isclosed){ + Serial.print("Old state: "); + Serial.println(g_spacestate == SPACEState_Open ? "open" : "closed"); + g_spacestate = isopen ? SPACEState_Open : SPACEState_Closed; + } + Serial.print("Current state: "); + Serial.println(g_spacestate == SPACEState_Open ? "open" : "closed"); + } + else + { + Serial.println("Unknown command"); + } +} + +#define TOGGLE_TIME 2500 +#define BUTTON_TIME 250 + +void ToggleLock() +{ + if (g_lockopen) + { + g_lockopen = false; + Serial.println("closing lock"); + digitalWrite(PIN_DOORPOWER, HIGH); + digitalWrite(PIN_CLOSE, HIGH); + DelayLEDs(BUTTON_TIME); + DelayLEDs(TOGGLE_TIME - BUTTON_TIME); + } + else + { + g_lockopen = true; + Serial.println("opening lock"); + digitalWrite(PIN_DOORPOWER, HIGH); + digitalWrite(PIN_OPEN, HIGH); + DelayLEDs(BUTTON_TIME); + DelayLEDs(TOGGLE_TIME - BUTTON_TIME); + } + + DelayLEDs(4000); + digitalWrite(PIN_OPEN, LOW); + digitalWrite(PIN_CLOSE, LOW); + digitalWrite(PIN_DOORPOWER, LOW); + + Serial.println("finished lock action"); +} + +bool HasMainsPower() +{ + return digitalRead(PIN_MAINS_POWER) == HIGH; +} + +void loop() +{ + uint8_t addr[ADDRSIZE]; + uint32_t deniedcount = 0; + + for(;;) + { + if (Serial.available()) + { + uint8_t input = Serial.read(); + if (input == '\n') + { + SetLEDState(LEDState_Busy); + Serial.println("ready"); + + char cmdbuf[CMD_BUFSIZE] = {}; + uint8_t cmdbuffill; + if (ReadCMD(cmdbuf, &cmdbuffill)) + ParseCMD(cmdbuf, cmdbuffill); + } + } + + SetLEDState(LEDState_Reading); + + ds.reset_search(); + if (ds.search(addr) && OneWire::crc8(addr, 7) == addr[7]) + { + Serial.print("DEBUG: Found iButton with address: "); + for (uint8_t i = 0; i < sizeof(addr); i++) + Serialprintf("%02x", addr[i]); + Serial.print('\n'); + + if (AuthenticateButton(addr)) + { + SetLEDState(LEDState_Authorized); + Serial.print("iButton authenticated\n"); + g_lockopen = true; + // DelayLEDs(5000); + // ToggleLock(); + deniedcount = 0; + + // if(g_lockopen == true){ + StateSolenoid = true; + SolenoidStartTime = millis(); + Serial.print("Solenoid activated\n"); + digitalWrite(PIN_SOLENOID, HIGH); + // stepper.move(MOTOR_STEPS*(RPM/60)*10); + // } + + } + else + { + deniedcount++; + if (deniedcount == 3) + { + Serial.print("iButton not authenticated\n"); + SetLEDState(LEDState_Busy); + //disabled because sounding the horn resets the arduino + //digitalWrite(PIN_HORN, HIGH); + //DelayLEDs(500); + //digitalWrite(PIN_HORN, LOW); + deniedcount = 0; + } + } + } + else + { + deniedcount = 0; + } + + ProcessLEDs(); + + if(g_spacestate == SPACEState_Open){ + digitalWrite(PIN_LEDSOLENOID, HIGH); + }else{ + digitalWrite(PIN_LEDSOLENOID, LOW); + } + digitalWrite(PIN_LEDHORN, HIGH); + if (digitalRead(INPUT_SOLENOID) == LOW) { + if(g_spacestate == SPACEState_Open){ + if(StateSolenoid == false){ + StateSolenoid = true; + SolenoidStartTime = millis(); + Serial.print("Solenoid activated\n"); + digitalWrite(PIN_SOLENOID, HIGH); + g_lockopen = true; + // stepper.move(MOTOR_STEPS*(RPM/60)*10); + } + }else{ + if(StateSolenoidInactive == false){ + StateSolenoidInactive = true; + SolenoidInactiveStartTime = millis(); + Serial.print("Spacestate closed, Solenoid button not active\n"); + } + } + } + if(StateSolenoid == true && ((millis() - SolenoidStartTime) > (5*1000)) ){ + digitalWrite(PIN_SOLENOID, LOW); + StateSolenoid = false; + g_lockopen = false; + } + if(StateSolenoidInactive == true && ((millis() - SolenoidInactiveStartTime) > (1*1000)) ){ + StateSolenoidInactive = false; + } + if (digitalRead(INPUT_HORN) == LOW) { + if(StateHorn == false){ + StateHorn = true; + Serial.print("Horn activated\n"); + digitalWrite(PIN_HORN, HIGH); + } + }else{ + StateHorn = false; + digitalWrite(PIN_HORN, LOW); + } + + + } +} + diff --git a/bitlair_doorduino_inner/src/sha1.cpp b/bitlair_doorduino_inner/src/sha1.cpp new file mode 100644 index 0000000..e6dace5 --- /dev/null +++ b/bitlair_doorduino_inner/src/sha1.cpp @@ -0,0 +1,156 @@ +#include "sha1.h" +#include + +namespace sha1 +{ + /* code */ + #define SHA1_K0 0x5a827999 + #define SHA1_K20 0x6ed9eba1 + #define SHA1_K40 0x8f1bbcdc + #define SHA1_K60 0xca62c1d6 + + void sha1_init(sha1nfo *s) { + s->state[0] = 0x67452301; + s->state[1] = 0xefcdab89; + s->state[2] = 0x98badcfe; + s->state[3] = 0x10325476; + s->state[4] = 0xc3d2e1f0; + s->byteCount = 0; + s->bufferOffset = 0; + } + + uint32_t sha1_rol32(uint32_t number, uint8_t bits) { + return ((number << bits) | (number >> (32-bits))); + } + + void sha1_hashBlock(sha1nfo *s) { + uint8_t i; + uint32_t a,b,c,d,e,t; + + a=s->state[0]; + b=s->state[1]; + c=s->state[2]; + d=s->state[3]; + e=s->state[4]; + for (i=0; i<80; i++) { + if (i>=16) { + t = s->buffer[(i+13)&15] ^ s->buffer[(i+8)&15] ^ s->buffer[(i+2)&15] ^ s->buffer[i&15]; + s->buffer[i&15] = sha1_rol32(t,1); + } + if (i<20) { + t = (d ^ (b & (c ^ d))) + SHA1_K0; + } else if (i<40) { + t = (b ^ c ^ d) + SHA1_K20; + } else if (i<60) { + t = ((b & c) | (d & (b | c))) + SHA1_K40; + } else { + t = (b ^ c ^ d) + SHA1_K60; + } + t+=sha1_rol32(a,5) + e + s->buffer[i&15]; + e=d; + d=c; + c=sha1_rol32(b,30); + b=a; + a=t; + } + s->state[0] += a; + s->state[1] += b; + s->state[2] += c; + s->state[3] += d; + s->state[4] += e; + } + + void sha1_addUncounted(sha1nfo *s, uint8_t data) { + uint8_t * const b = (uint8_t*) s->buffer; + #ifdef SHA_BIG_ENDIAN + b[s->bufferOffset] = data; + #else + b[s->bufferOffset ^ 3] = data; + #endif + s->bufferOffset++; + if (s->bufferOffset == BLOCK_LENGTH) { + sha1_hashBlock(s); + s->bufferOffset = 0; + } + } + + void sha1_writebyte(sha1nfo *s, uint8_t data) { + ++s->byteCount; + sha1_addUncounted(s, data); + } + + void sha1_write(sha1nfo *s, const char *data, size_t len) { + for (;len--;) sha1_writebyte(s, (uint8_t) *data++); + } + + void sha1_pad(sha1nfo *s) { + // Implement SHA-1 padding (fips180-2 §5.1.1) + + // Pad with 0x80 followed by 0x00 until the end of the block + sha1_addUncounted(s, 0x80); + while (s->bufferOffset != 56) sha1_addUncounted(s, 0x00); + + // Append length in the last 8 bytes + sha1_addUncounted(s, 0); // We're only using 32 bit lengths + sha1_addUncounted(s, 0); // But SHA-1 supports 64 bit lengths + sha1_addUncounted(s, 0); // So zero pad the top bits + sha1_addUncounted(s, s->byteCount >> 29); // Shifting to multiply by 8 + sha1_addUncounted(s, s->byteCount >> 21); // as SHA-1 supports bitstreams as well as + sha1_addUncounted(s, s->byteCount >> 13); // byte. + sha1_addUncounted(s, s->byteCount >> 5); + sha1_addUncounted(s, s->byteCount << 3); + } + + uint8_t* sha1_result(sha1nfo *s) { + // Pad to complete the last block + sha1_pad(s); + + #ifndef SHA_BIG_ENDIAN + // Swap byte order back + int i; + for (i=0; i<5; i++) { + s->state[i]= + (((s->state[i])<<24)& 0xff000000) + | (((s->state[i])<<8) & 0x00ff0000) + | (((s->state[i])>>8) & 0x0000ff00) + | (((s->state[i])>>24)& 0x000000ff); + } + #endif + + // Return pointer to hash (20 characters) + return (uint8_t*) s->state; + } + + #define HMAC_IPAD 0x36 + #define HMAC_OPAD 0x5c + + void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength) { + uint8_t i; + memset(s->keyBuffer, 0, BLOCK_LENGTH); + if (keyLength > BLOCK_LENGTH) { + // Hash long keys + sha1_init(s); + for (;keyLength--;) sha1_writebyte(s, *key++); + memcpy(s->keyBuffer, sha1_result(s), HASH_LENGTH); + } else { + // Block length keys are used as is + memcpy(s->keyBuffer, key, keyLength); + } + // Start inner hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_IPAD); + } + } + + uint8_t* sha1_resultHmac(sha1nfo *s) { + uint8_t i; + // Complete inner hash + memcpy(s->innerHash,sha1_result(s),HASH_LENGTH); + // Calculate outer hash + sha1_init(s); + for (i=0; ikeyBuffer[i] ^ HMAC_OPAD); + for (i=0; iinnerHash[i]); + return sha1_result(s); + } +} diff --git a/bitlair_doorduino_inner/src/sha1.h b/bitlair_doorduino_inner/src/sha1.h new file mode 100644 index 0000000..2b1ddf9 --- /dev/null +++ b/bitlair_doorduino_inner/src/sha1.h @@ -0,0 +1,69 @@ +#ifndef SHA1_H +#define SHA1_H + +#include +#include + +#define __LITTLE_ENDIAN__ +//#define __BIG_ENDIAN__ + +namespace sha1 +{ + /* This code is public-domain - it is based on libcrypt + * placed in the public domain by Wei Dai and other contributors. + */ + // gcc -Wall -DSHA1TEST -o sha1test sha1.c && ./sha1test + + #ifdef __BIG_ENDIAN__ + # define SHA_BIG_ENDIAN + #elif defined __LITTLE_ENDIAN__ + /* override */ + #elif defined __BYTE_ORDER + # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + # define SHA_BIG_ENDIAN + # endif + #else // ! defined __LITTLE_ENDIAN__ + # include // machine/endian.h + # if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + # define SHA_BIG_ENDIAN + # endif + #endif + + + /* header */ + + #define HASH_LENGTH 20 + #define BLOCK_LENGTH 64 + + typedef struct sha1nfo { + uint32_t buffer[BLOCK_LENGTH/4]; + uint32_t state[HASH_LENGTH/4]; + uint32_t byteCount; + uint8_t bufferOffset; + uint8_t keyBuffer[BLOCK_LENGTH]; + uint8_t innerHash[HASH_LENGTH]; + } sha1nfo; + + /* public API - prototypes - TODO: doxygen*/ + + /** + */ + void sha1_init(sha1nfo *s); + /** + */ + void sha1_writebyte(sha1nfo *s, uint8_t data); + /** + */ + void sha1_write(sha1nfo *s, const char *data, size_t len); + /** + */ + uint8_t* sha1_result(sha1nfo *s); + /** + */ + void sha1_initHmac(sha1nfo *s, const uint8_t* key, int keyLength); + /** + */ + uint8_t* sha1_resultHmac(sha1nfo *s); +} + +#endif //SHA1_H diff --git a/bitlair_doorduino_inner/test/README b/bitlair_doorduino_inner/test/README new file mode 100644 index 0000000..e7d1588 --- /dev/null +++ b/bitlair_doorduino_inner/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html diff --git a/manage.py b/manage.py index ee9cbc9..dea27fb 100755 --- a/manage.py +++ b/manage.py @@ -4,7 +4,7 @@ import serial import sys import time -SERIAL_PORTS = [ '/dev/ttyS1', '/dev/ttyS2' ] +SERIAL_PORTS = [ '/dev/ttyS2' ] def open_port(tty): @@ -56,7 +56,7 @@ def remove_button(ports, button): break print(line) -def add_button(ser, button, secret): +def add_button(ports, button, secret): for port in ports: with open_port(port) as ser: ser.write(b'\r\n') diff --git a/pi-config/.gitignore b/pi-config/.gitignore new file mode 100644 index 0000000..344e05d --- /dev/null +++ b/pi-config/.gitignore @@ -0,0 +1,2 @@ +toegang +config/settings diff --git a/pi-config/config/settings-example b/pi-config/config/settings-example new file mode 100644 index 0000000..8334d6e --- /dev/null +++ b/pi-config/config/settings-example @@ -0,0 +1,13 @@ +[mqtt] +doorbell.subject=bitlair/doorduino/doorbell +dooropen.subject=bitlair/doorduino/dooropen +lockstate.subject=bitlair/doorduino/lockstate +server=mqtt.bitlair.nl +mqtt-simple=/usr/local/bin/mqtt-simple +client-id=doorpi +state-bitlair.subject=bitlair/state +state-djo.subject=bitlair/state/djo + +[serial] +device=/dev/ttyS2 + diff --git a/pi-config/doorduino-init b/pi-config/doorduino-init new file mode 100755 index 0000000..19f1acc --- /dev/null +++ b/pi-config/doorduino-init @@ -0,0 +1,111 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: generic-prog +# Required-Start: $local_fs $remote_fs $network +# Required-Stop: $local_fs $remote_fs $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Generic Program +# Description: Generic Program is a generic program to do generic things with +### END INIT INFO + + +# Documentation available at +# http://refspecs.linuxfoundation.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptfunc.html +# Debian provides some extra functions though +. /lib/lsb/init-functions + + +DAEMON_NAME="root" +DAEMON_USER="root" +DAEMON_PATH="/root/bitlair_doorduino/pi-config/doorduino.py" +DAEMON_OPTS="-c /root/bitlair_doorduino/pi-config/config" +DAEMON_PWD="${PWD}" +DAEMON_DESC=$(get_lsb_header_val $0 "Short-Description") +DAEMON_PID="/var/run/${DAEMON_NAME}.pid" +DAEMON_NICE=0 +DAEMON_LOG='/var/log/doorduino' + +[ -r "/etc/default/${DAEMON_NAME}" ] && . "/etc/default/${DAEMON_NAME}" + +do_start() { + local result + + pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null + if [ $? -eq 0 ]; then + log_warning_msg "${DAEMON_NAME} is already started" + result=0 + else + log_daemon_msg "Starting ${DAEMON_DESC}" "${DAEMON_NAME}" + touch "${DAEMON_LOG}" + chown $DAEMON_USER "${DAEMON_LOG}" + chmod u+rw "${DAEMON_LOG}" + if [ -z "${DAEMON_USER}" ]; then + start-stop-daemon --start --quiet --oknodo --background \ + --nicelevel $DAEMON_NICE \ + --chdir "${DAEMON_PWD}" \ + --pidfile "${DAEMON_PID}" --make-pidfile \ + --exec "${DAEMON_PATH}" -- $DAEMON_OPTS + result=$? + else + start-stop-daemon --start --quiet --oknodo --background \ + --nicelevel $DAEMON_NICE \ + --chdir "${DAEMON_PWD}" \ + --pidfile "${DAEMON_PID}" --make-pidfile \ + --chuid "${DAEMON_USER}" \ + --exec "${DAEMON_PATH}" -- $DAEMON_OPTS + result=$? + fi + log_end_msg $result + fi + return $result +} + +do_stop() { + local result + + pidofproc -p "${DAEMON_PID}" "${DAEMON_PATH}" > /dev/null + if [ $? -ne 0 ]; then + log_warning_msg "${DAEMON_NAME} is not started" + result=0 + else + log_daemon_msg "Stopping ${DAEMON_DESC}" "${DAEMON_NAME}" + killproc -p "${DAEMON_PID}" "${DAEMON_PATH}" + result=$? + log_end_msg $result + rm "${DAEMON_PID}" + fi + return $result +} + +do_restart() { + local result + do_stop + result=$? + if [ $result = 0 ]; then + do_start + result=$? + fi + return $result +} + +do_status() { + local result + status_of_proc -p "${DAEMON_PID}" "${DAEMON_PATH}" "${DAEMON_NAME}" + result=$? + return $result +} + +do_usage() { + echo $"Usage: $0 {start | stop | restart | status}" + exit 1 +} + +case "$1" in +start) do_start; exit $? ;; +stop) do_stop; exit $? ;; +restart) do_restart; exit $? ;; +status) do_status; exit $? ;; +*) do_usage; exit 1 ;; +esac + diff --git a/pi-config/doorduino.py b/pi-config/doorduino.py new file mode 100755 index 0000000..1abfa0f --- /dev/null +++ b/pi-config/doorduino.py @@ -0,0 +1,320 @@ +#!/usr/bin/python3 + +from serial.tools import list_ports +import serial +import io +import time +import subprocess +import csv +import getopt +import sys +import os +import math +import paramiko +from pathlib import Path +import configparser +import re +import threading +import syslog +import csv +import git + +import logging +from paho.mqtt import client as mqtt_client + +def connect_mqtt(config, broker, port, client_id): + # def on_connect(client, userdata, flags, rc): + # For paho-mqtt 2.0.0, you need to add the properties parameter. + def on_connect(client, userdata, flags, rc, properties): + if rc == 0: + print("Connected to MQTT Broker!") + subscribe(client, config.get('mqtt', 'state-bitlair.subject')) + subscribe(client, config.get('mqtt', 'state-djo.subject')) + else: + print("Failed to connect, return code %d\n", rc) + # Set Connecting Client ID + # For paho-mqtt 2.0.0, you need to set callback_api_version. + client = mqtt_client.Client(client_id=client_id, callback_api_version=mqtt_client.CallbackAPIVersion.VERSION2) + + # client.username_pw_set(username, password) + client.on_connect = on_connect + client.connect(broker, port) + return client + +FIRST_RECONNECT_DELAY = 1 +RECONNECT_RATE = 2 +MAX_RECONNECT_COUNT = 12 +MAX_RECONNECT_DELAY = 60 + +def on_disconnect(client, userdata, rc): + logging.info("Disconnected with result code: %s", rc) + reconnect_count, reconnect_delay = 0, FIRST_RECONNECT_DELAY + while reconnect_count < MAX_RECONNECT_COUNT: + logging.info("Reconnecting in %d seconds...", reconnect_delay) + time.sleep(reconnect_delay) + + try: + client.reconnect() + logging.info("Reconnected successfully!") + return + except Exception as err: + logging.error("%s. Reconnect failed. Retrying...", err) + + reconnect_delay *= RECONNECT_RATE + reconnect_delay = min(reconnect_delay, MAX_RECONNECT_DELAY) + reconnect_count += 1 + logging.info("Reconnect failed after %s attempts. Exiting...", reconnect_count) + +states = {} +def update_spacestate(): + global ser + open = False + for key in states: + if states[key] == "open": + open = True + try: + if open: + print("Send spacestate open") + ser.write(b"\n") + ser.write(b"spacestate open\n"); + else: + print("Send spacestate closed") + ser.write(b"\n") + ser.write(b"spacestate closed\n") + except serial.SerialException: + print("Serial connection error") + time.sleep(2) + + +def subscribe(client: mqtt_client, topic): + def on_message(client, userdata, msg): + states[msg.topic] = msg.payload.decode() + print(f"Received `{msg.payload.decode()}` from `{msg.topic}` topic") + update_spacestate() + + client.subscribe(topic) + client.on_message = on_message + + +def read_configuration(configdir): + if (configdir == ''): + print("Missing configdir.") + sys.exit(2) + + if not os.path.exists(configdir): + print("Directory ", configdir, " does not exist"); + sys.exit(2) + + configfile = Path(os.path.join(configdir, 'settings')) + if not configfile.is_file(): + print(configfile, " does not exist") + sys.exit(2) + + config = configparser.ConfigParser() + config.read_file(configfile.open()) + + expected_config_options = { 'mqtt': [ 'doorbell.subject', 'dooropen.subject', 'lockstate.subject', 'server', 'mqtt-simple' ], + 'serial': [ 'device' ] } + + for section, options in expected_config_options.items(): + for option in options: + if not config.has_option(section, option): + print("Missing config option ", option, "in section", section) + sys.exit(2) + + return config + +def mqtt_send_thread(config, subject, value, persistent): + if persistent: + subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-r", "-p", subject, "-m", value]) + else: + subprocess.call([config.get('mqtt', 'mqtt-simple'), "-h", config.get('mqtt', 'server'), "-p", subject, "-m", value]) + +def mqtt(config, subject, value, persistent=False): + threading.Thread(target = mqtt_send_thread, args = (config, subject, value, persistent)).start() + +def log(message): + print("LOG " + message) + syslog.syslog(message) + +def mqtt_thread(): + global client + global config + client = connect_mqtt(config, config.get('mqtt', 'server'), 1883, config.get('mqtt', 'client-id')) + + client.loop_forever() + +buttons = [] +def serial_monitor_thread(): + global ser + global config + global buttons + while True: + try: + ser = serial.Serial(config.get('serial', 'device'), 115200, rtscts=False, dsrdtr=True) + + time.sleep(2); + print("Doorduino started"); + + while True: + data = ser.readline() + action = data.decode("iso-8859-1").strip() + + print("Data:" + action) + if action == "Horn activated": + print("Horn activated") + log("Horn activated") + mqtt(config, config.get('mqtt','doorbell.subject'), '1', False) + time.sleep(2) + mqtt(config, config.get('mqtt','doorbell.subject'), '0', False) + elif action == "Solenoid activated": + print("Solenoid activated") + log("Solenoid activated") + mqtt(config, config.get('mqtt','dooropen.subject'), '1', False) + time.sleep(2) + mqtt(config, config.get('mqtt','dooropen.subject'), '0', False) + elif action == "iButton authenticated": + print("iButton authenticated") + log("iButton authenticated") + elif action == "opening lock": + print("lock open") + log("lock open") + mqtt(config, config.get('mqtt','lockstate.subject'), 'open', True) + elif action == "closing lock": + print("lock closed") + log("lock closed") + mqtt(config, config.get('mqtt','lockstate.subject'), 'closed', True) + elif action[:7] == "button:": + # print("got button ") + # print(action[8:]) + if not action[8:] in buttons: + buttons.append(action[8:]) + elif action == "DEBUG: Board started": + print("Arduino was reset, sending spacestate") + update_spacestate() + + except serial.SerialException: + print("Serial connection error") + time.sleep(2) + except StopIteration: + print("No device found") + time.sleep(2) + + +def git_update(git_dir): + log("Updating git") + # subprocess.call(["git", "-C", git_dir, "pull"]) + repo = git.Repo(git_dir) + # print('Remotes:') + # for remote in repo.remotes: + # print(f'- {remote.name} {remote.url}') + try: + pull = repo.remotes.origin.pull() + except git.exc.GitCommandError: + print("Git pull failed") + return False + + # if pull[0].flags == 128: + # print("Git pull failed") + # return False + return True + + +def update_buttons(): + global ser + global buttons + print("GIT init") + if(not git_update("toegang")): + print("Aborting GIT update thread") + return False + + git_buttons = {} + with open('toegang/toegang.csv', newline='') as csvfile: + data = csvfile.read() + data = data.replace(' ', '') + reader = csv.DictReader(data.splitlines(), delimiter=',') + for row in reader: + ibutton = row['ibutton'].split(':') + git_buttons[ibutton[0].lower()] = ibutton[1].lower() + # print(row['naam'] + " " + row['ibutton']) + + if len(git_buttons) < 25: + print("Something wrong, not enough buttons in git") + return + + buttons = [] + ser.write(b"\n") + ser.write(b"list_buttons\n"); + time.sleep(10) + if len(buttons) < 5: + print("Something wrong, not enough buttons in doorduino") + return + + print(buttons) + for button in git_buttons: + if button not in buttons: + print("should add " + button) + ser.write(b"\n") + ser.write(b"add_button "+button.encode('ascii')+b" "+git_buttons[button].encode('ascii')+b"\n") + time.sleep(2) + # else: + # print("already there " + button) + + for button in buttons: + if button not in git_buttons: + print("should remove " + button) + ser.write(b"\n") + ser.write(b"remove_button "+button.encode('ascii')+b"\n") + time.sleep(2) + # else: + # print("should be there " + button) + + print("Update buttons finished") + + +def git_thread(): + print("Updating buttons") + update_buttons() + print("Update buttons finished, sleeping for 3600 seconds") + time.sleep(3600) + + +def threadwrap(threadfunc): + def wrapper(): + while True: + try: + threadfunc() + except BaseException as e: + print('{!r}; restarting thread'.format(e)) + else: + print('exited normally, bad thread; restarting') + return wrapper + + +def main(argv): + global config + configdir = '' + + syslog.openlog('doorduino') + + try: + opts, args = getopt.getopt(argv,"c:",["config="]) + except getopt.GetoptError: + print('doorduino.py -c ') + sys.exit(2) + + for opt, arg in opts: + if opt == "-c" or opt == "--config": + configdir = arg + + config = read_configuration(configdir) + + threading.Thread(target = threadwrap(serial_monitor_thread)).start() + time.sleep(5) # Give doorduino time to start before sending spacestate data + threading.Thread(target = threadwrap(mqtt_thread)).start() + threading.Thread(target = threadwrap(git_thread)).start() + + +if __name__ == "__main__": + main(sys.argv[1:]) + diff --git a/pi-config/doorduino.service b/pi-config/doorduino.service new file mode 100644 index 0000000..c06cd09 --- /dev/null +++ b/pi-config/doorduino.service @@ -0,0 +1,13 @@ +[Unit] +Description=Doorduino +After=network.target + +[Service] +Type=idle +Restart=on-failure +User=root +WorkingDirectory=/root/bitlair_doorduino/pi-config +ExecStart=/usr/bin/python3 -u doorduino.py -c config + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/pi-config/requirements.txt b/pi-config/requirements.txt new file mode 100644 index 0000000..2b40728 --- /dev/null +++ b/pi-config/requirements.txt @@ -0,0 +1,3 @@ +paho-mqtt +paramiko +GitPython diff --git a/pi-config/reset_arduino.sh b/pi-config/reset_arduino.sh new file mode 100755 index 0000000..efd7823 --- /dev/null +++ b/pi-config/reset_arduino.sh @@ -0,0 +1,8 @@ +#!/bin/bash +cd /sys/class/gpio/ +echo 6 > export +echo out > gpio6/direction +echo 0 > gpio6/value +echo 1 > gpio6/value +echo 6 > unexport + diff --git a/reset_eeprom/.gitignore b/reset_eeprom/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/reset_eeprom/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/reset_eeprom/.vscode/extensions.json b/reset_eeprom/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/reset_eeprom/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/reset_eeprom/include/README b/reset_eeprom/include/README new file mode 100644 index 0000000..45496b1 --- /dev/null +++ b/reset_eeprom/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/reset_eeprom/lib/README b/reset_eeprom/lib/README new file mode 100644 index 0000000..8c9c29c --- /dev/null +++ b/reset_eeprom/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into executable file. + +The source code of each library should be placed in a an own separate directory +("lib/your_library_name/[here are source files]"). + +For example, see a structure of the following two libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +and a contents of `src/main.c`: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +PlatformIO Library Dependency Finder will find automatically dependent +libraries scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/reset_eeprom/platformio.ini b/reset_eeprom/platformio.ini new file mode 100644 index 0000000..176ed79 --- /dev/null +++ b/reset_eeprom/platformio.ini @@ -0,0 +1,18 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:nanoatmega328] +platform = atmelavr +board = nanoatmega328new +framework = arduino + +lib_deps = + +monitor_speed = 115200 diff --git a/reset_eeprom/src/main.cpp b/reset_eeprom/src/main.cpp new file mode 100644 index 0000000..1f74652 --- /dev/null +++ b/reset_eeprom/src/main.cpp @@ -0,0 +1,78 @@ +/* + * EEPROM Clear + * + * Sets all of the bytes of the EEPROM to 0. + * Please see eeprom_iteration for a more in depth + * look at how to traverse the EEPROM. + * + * This example code is in the public domain. + */ + +#include "Arduino.h" +//#include +#include "Wire.h" + +#define EEPROMDEVICEADDRESS 0x50 +#define EEPROMSIZE 2048 + + +void writeEEPROM(unsigned int eeaddress, byte data ) +{ + Wire.beginTransmission(EEPROMDEVICEADDRESS); + Wire.write((int)(eeaddress >> 8)); // MSB + Wire.write((int)(eeaddress & 0xFF)); // LSB + Wire.write(data); + Wire.endTransmission(); + + delay(5); +} + +byte readEEPROM(unsigned int eeaddress ) +{ + byte rdata = 0xFF; + + Wire.beginTransmission(EEPROMDEVICEADDRESS); + Wire.write((int)(eeaddress >> 8)); // MSB + Wire.write((int)(eeaddress & 0xFF)); // LSB + Wire.endTransmission(); + + Wire.requestFrom(EEPROMDEVICEADDRESS,1); + + if (Wire.available()) rdata = Wire.read(); + + return rdata; +} + +void setup() { + // initialize the LED pin as an output. + pinMode(13, OUTPUT); + Serial.begin(115200); + + /*** + Iterate through each byte of the EEPROM storage. + Larger AVR processors have larger EEPROM sizes, E.g: + - Arduino Duemilanove: 512 B EEPROM storage. + - Arduino Uno: 1 kB EEPROM storage. + - Arduino Mega: 4 kB EEPROM storage. + Rather than hard-coding the length, you should use the pre-provided length function. + This will make your code portable to all AVR processors. + ***/ + + delay(1000); + Serial.println("BEGIN"); + + for (int i = 0 ; i < EEPROMSIZE ; i++) { + // writeEEPROM(i, 0xFF); + Serial.print(readEEPROM(i),HEX); + } + + // turn the LED on when we're done + digitalWrite(13, HIGH); + + Serial.println(""); + Serial.println("END"); +} + +void loop() { + /** Empty loop. **/ +} \ No newline at end of file diff --git a/reset_eeprom/test/README b/reset_eeprom/test/README new file mode 100644 index 0000000..e7d1588 --- /dev/null +++ b/reset_eeprom/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Unit Testing and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/page/plus/unit-testing.html