From e82dd3f3a2f57c51657b4d5a7dd7443a11dc63be Mon Sep 17 00:00:00 2001 From: Gustice Date: Sat, 27 Jan 2024 23:32:45 +0100 Subject: [PATCH] Init: Rattenfalle --- .gitignore | 5 + .gitmodules | 3 + .vscode/extensions.json | 11 + .vscode/settings.json | 6 + README | 2 + core | 1 + include/README | 39 ++ include/main.hpp | 36 ++ lib/Device/DcMotor.hpp | 97 ++++ lib/README | 46 ++ lib/Utils/RotatingIndex.hpp | 73 +++ platformio.ini | 43 ++ src/mainControl.cpp | 472 ++++++++++++++++++ src/mainLair.cpp | 116 +++++ test/README | 11 + test/embedded/test_devices/test_statusLed.cpp | 83 +++ test/extra_script.py | 7 + test/native/test_utils/test_main.cpp | 71 +++ 18 files changed, 1122 insertions(+) create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 README create mode 160000 core create mode 100644 include/README create mode 100644 include/main.hpp create mode 100644 lib/Device/DcMotor.hpp create mode 100644 lib/README create mode 100644 lib/Utils/RotatingIndex.hpp create mode 100644 platformio.ini create mode 100644 src/mainControl.cpp create mode 100644 src/mainLair.cpp create mode 100644 test/README create mode 100644 test/embedded/test_devices/test_statusLed.cpp create mode 100644 test/extra_script.py create mode 100644 test/native/test_utils/test_main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..f98794f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "core"] + path = core + url = git@github.com:Gustice/ArduinoDevicePlatform.git diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a91ca8f --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + // 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", + "pioarduino.pioarduino-ide" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0315600 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "C_Cpp.autoAddFileAssociations": false, + + "C_Cpp.clang_format_style": "{BasedOnStyle: Google, IndentWidth: 4, ColumnLimit: 0}", + "C_Cpp.clang_format_fallbackStyle": "{ BasedOnStyle: LLVM, UseTab: Never, IndentWidth: 4, TabWidth: 4, AllowShortIfStatementsOnASingleLine: false, IndentCaseLabels: false, ColumnLimit: 0, AccessModifierOffset: -4, NamespaceIndentation: All, FixNamespaceComments: false }", +} \ No newline at end of file diff --git a/README b/README new file mode 100644 index 0000000..c592e8d --- /dev/null +++ b/README @@ -0,0 +1,2 @@ +# Rattenfalle + diff --git a/core b/core new file mode 160000 index 0000000..4a1e39b --- /dev/null +++ b/core @@ -0,0 +1 @@ +Subproject commit 4a1e39b6aae491fad1c19c72070b888e820c701b diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/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/include/main.hpp b/include/main.hpp new file mode 100644 index 0000000..05de0a3 --- /dev/null +++ b/include/main.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +#include + +#include "AbHal.hpp" +#include "DcMotor.hpp" +#include "Utils.hpp" + + +using TimeStamp = decltype(millis()); + +class ServoGrp { + public: + ServoGrp(int pin) : _pin(pin) {} + + void attach() { + _srv.attach(_pin); + } + + void detach() { + _srv.detach(); + } + + void write(int pos) { + _pos = pos; + _srv.write(_pos); + } + + private: + Servo _srv; + int _pin; + int _pos{}; +}; \ No newline at end of file diff --git a/lib/Device/DcMotor.hpp b/lib/Device/DcMotor.hpp new file mode 100644 index 0000000..a205ec4 --- /dev/null +++ b/lib/Device/DcMotor.hpp @@ -0,0 +1,97 @@ +/** + * @file DcMotor.hpp + * @author Gustice + * @brief Simple Driver implementation for DRVxxxx Derivatives from Texas instruments + * DRV8848 + * + * @copyright Copyright (c) 2024 + */ + +#include + +#include + +class DcMotor { + static constexpr int FullScale = 256; + + public: + struct Pins { + std::array PwmA; // PWM control + int ErrorIn; // Error Feedback from driver + int Sleep; // Sleep control (to enter double mode) + int Ceil; // Maximum speed control + int ErrOut; // Maximum speed control + }; + + enum State { + Startup = 0, + Idle, + Right, + Left, + }; + + DcMotor(Pins pins) : _pins(pins) { + pinMode(_pins.PwmA[0], OUTPUT); + pinMode(_pins.PwmA[1], OUTPUT); + pinMode(_pins.ErrorIn, INPUT); + pinMode(_pins.Sleep, OUTPUT); + pinMode(_pins.Ceil, INPUT); + pinMode(_pins.ErrOut, OUTPUT); + + // sleep to enter double current mode + delay(5); + digitalWrite(_pins.Sleep, LOW); + delay(5); // @todo double after successful operation + digitalWrite(_pins.Sleep, HIGH); + + // set to break + analogWrite(_pins.PwmA[0], FullScale); + analogWrite(_pins.PwmA[1], FullScale); + } + + void stop() { + analogWrite(_pins.PwmA[0], FullScale); + analogWrite(_pins.PwmA[1], FullScale); + + _state = Idle; + } + + void startRight() { + int ceil = analogRead(_pins.Ceil) / 4; + _state = Right; + + analogWrite(_pins.PwmA[0], FullScale); + analogWrite(_pins.PwmA[1], FullScale - ceil); + } + + void startLeft() { + int ceil = analogRead(_pins.Ceil) / 4; + _state = Left; + + analogWrite(_pins.PwmA[0], FullScale - ceil); + analogWrite(_pins.PwmA[1], FullScale); + } + + void tick() { + int ceil = analogRead(_pins.Ceil) / 4; + + switch (_state) { + case Right: + analogWrite(_pins.PwmA[1], FullScale - ceil); + break; + + case Left: + analogWrite(_pins.PwmA[0], FullScale - ceil); + break; + + default: + break; + } + int nErr = digitalRead(_pins.ErrorIn); + digitalWrite(_pins.ErrOut, nErr == 0); + } + + private: + Pins _pins; + State _state = Startup; +}; diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..6debab1 --- /dev/null +++ b/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/lib/Utils/RotatingIndex.hpp b/lib/Utils/RotatingIndex.hpp new file mode 100644 index 0000000..1f6ebf7 --- /dev/null +++ b/lib/Utils/RotatingIndex.hpp @@ -0,0 +1,73 @@ +/** + * @file RotatingIndex.hpp + * @author Gustice + * @brief Classes for typical index manipulation + */ + +#pragma once + +/** + * @brief Index with automatic revolution back to start position + * @details Useful for Buffer-Pointer, or similar tasks + * + * @tparam T Numeric type + */ +template +class RotatingIndex { + public: + /// @brief Value at witch the index rotates back to beginning + const T MaxIndex; + + /** + * @brief Constructor for rotating index + * @brief output index will be set to 0 if incremented value reaches Maximum + * @param max Maximum index for overflow-condition + */ + RotatingIndex(T max) : MaxIndex(max) { + _index = 0; + } + + /** + * @brief Get the current index and increment index afterwards + * @return T current index + */ + T GetIndexAndIncrement() { + auto retVal = _index; + _index++; + if (_index >= MaxIndex) { + _index = 0; + } + return retVal; + }; + + /** + * @brief Get the current index without incrementing the index + * @return T current index + */ + T GetIndex() const { + return _index; + } + + /** + * @brief Increments index and retuns if index has been reset + */ + bool IncrementAndCheckIfRevolves() { + _index++; + if (_index >= MaxIndex) { + _index = 0; + return true; + } + return false; + } + + /** + * @brief Reset Index to 0 + */ + void ResetIndex() { + _index = 0; + } + + private: + /// Current Index + T _index; +}; diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..c05d1b9 --- /dev/null +++ b/platformio.ini @@ -0,0 +1,43 @@ +; 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 + +# Base for embedded environments +[env] +test_framework = unity +test_filter = embedded/test_* +; debug_test = embedded/test_... +lib_extra_dirs = core/lib +; build_unflags = -std=gnu++11 +; build_flags = -std=c++20 + +[env:Lair] +# Board: https://docs.platformio.org/en/latest/boards/atmelsam/seeed_xiao.html +platform = renesas-ra +board = nano_r4 +framework = arduino +build_unflags = -std=gnu++11 +build_flags = -std=c++20 +build_src_filter = + -<*> + + +platform_packages = + toolchain-gccarmnoneeabi@~1.120301.0 +lib_deps = + arduino-libraries/Servo @ ^1.3.0 + +; mainControl.cpp + +# Native environment (Host PC) +[env:native] +# overwrite embedded related setup +test_framework = googletest +platform=native +test_filter = native/test_* +; debug_test = native/test_... diff --git a/src/mainControl.cpp b/src/mainControl.cpp new file mode 100644 index 0000000..9ec20ad --- /dev/null +++ b/src/mainControl.cpp @@ -0,0 +1,472 @@ +#include "main.hpp" + +using namespace abh; + +Input extResetGame(0); + +Output Magnet1(1); +Output Magnet2(3); +Servo Servo1; +Servo Servo2; + +Input endSwitch1(2); +Input endSwitch2(3); + +#define Rfid1 + +Output SLedPin(LED_BUILTIN, abh::Port::LogicType::Inverted); +static StatusLed _statusLed(SLedPin); + +void setup() { + Serial.begin(115200); + _statusLed.set(StatusLed::Mode::DoubleFlash); + + Servo1.attach(2); + Servo2.attach(3); + + Servo1.write(45); + Servo2.write(90); +} + +State state{}; + +void loop() { + static Tick tick(5); + static Tick diagTick(250); + static Tick servoTick(2000); + + if (!tick) { + return; + } + static int servo{}; + + if (servoTick) { + servo++; + if (servo % 2) { + Servo1.write(45); + Servo2.write(0); + } else { + Servo1.write(90); + Servo2.write(180); + } + } + + switch (state) { + default: + case Idle: { + state = Start; + } break; + + case Start: { + state = Running; + } break; + + case Running: { + state = Idle; + } break; + } + + if (diagTick) { + _statusLed.tick(); + Serial.printf("S=%d 0=%d | s=%d \r\n", (bool)true, (bool)false, state); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + +#include + +const int BUFFER_SIZE = 14; // RFID DATA FRAME FORMAT: 1byte head (value: 2), 10byte data (2byte version + 8byte tag), 2byte checksum, 1byte tail (value: 3) +const int DATA_SIZE = 10; // 10byte data (2byte version + 8byte tag) +const int DATA_VERSION_SIZE = 2; // 2byte version (actual meaning of these two bytes may vary) +const int DATA_TAG_SIZE = 8; // 8byte tag +const int CHECKSUM_SIZE = 2; // 2byte checksum +uint8_t buffer[BUFFER_SIZE]; // used to store an incoming data frame +int buffer_index = 0; +String data; +int datainteger; + +Servo tor_servo; // Tor mit Ratte +Servo tor2_servo; // Großes Tor in der Ecke + +#define Reset 52 +#define LED 7 +#define Schloss1 46 +#define Schloss2 47 +#define Tortasteroffen 32 +#define Tortastergeschlossen 28 + +int servo_position, success, tortasterzustand, tortasterzaehler = 0; +int sollID = 9715; +int sollID2 = 22907; +int torzustand = 0; //0=zu | 1=offen +int torFalleTasterOffenZustand, torFalleTasterZuZustand = 0; +int buttonState; // the current reading from the input pin +int lastButtonState = LOW; // the previous reading from the input pin +unsigned long lastDebounceTime = 0; // the last time the output pin was toggled +unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers +unsigned long motorsafetyTime = 0; +unsigned long motorsafetyTimeDelay = 5000; + +void setup() { + Serial.begin(9600); + Serial1.begin(115200); + Serial3.begin(9600); + pinMode(Reset, INPUT); + pinMode(Tortasteroffen, INPUT); + pinMode(Tortastergeschlossen, INPUT); + pinMode(LED, OUTPUT); + pinMode(Schloss1, OUTPUT); + digitalWrite(Schloss1, HIGH); + pinMode(Schloss2, OUTPUT); + digitalWrite(Schloss2, HIGH); + digitalWrite(LED, HIGH); + + //Torzustand erfragen + torFalleTasterOffenZustand = digitalRead(Tortasteroffen); + torFalleTasterZuZustand = digitalRead(Tortastergeschlossen); + Serial.print("Fallentortasteroffen: "); + Serial.println(torFalleTasterOffenZustand); + Serial.print("Fallentortastergeschlossen: "); + Serial.println(torFalleTasterZuZustand); + Serial.println("-------------------------------"); + + if(torFalleTasterOffenZustand == 0) { + Serial.println("FallenTor ist offen"); + torzustand=1; + } else { + Serial.println("FallenTor ist zu"); + digitalWrite(LED, LOW); + tor_offnen(); + } + + Serial.println("INIT DONE"); +} + +void loop() { + int reading = digitalRead(Reset); + if (reading != lastButtonState) { + lastDebounceTime = millis(); + } + + if ((millis() - lastDebounceTime) > debounceDelay) { + if (reading != buttonState) { + buttonState = reading; + if (buttonState == HIGH && torzustand==1) { + Serial.println("Resettaster wurde gedrückt"); + tor_schliessen(); + } + } + } + lastButtonState = reading; + + if (Serial3.available() > 0 && torzustand==0) { + bool call_extract_tag = false; + int ssvalue = Serial3.read(); // read + Serial.println(ssvalue); + if (ssvalue == -1) { // no data was read + return; + } + if (ssvalue == 2) { // RDM630/RDM6300 found a tag => tag incoming + buffer_index = 0; + } else if (ssvalue == 3) { // tag has been fully transmitted + call_extract_tag = true; // extract tag at the end of the function call + } + if (buffer_index >= BUFFER_SIZE) { // checking for a buffer overflow (It's very unlikely that an buffer overflow comes up!) + Serial.println("Error: Buffer overflow detected!"); + return; + } + + buffer[buffer_index++] = ssvalue; // everything is alright => copy current value to buffer + if (call_extract_tag == true && success == 0) { + if (buffer_index == BUFFER_SIZE) { + unsigned tag = extract_tag(); + Serial.println(tag); + if (tag == sollID || tag == sollID2) { + Serial.println("Herzlichen Glückwunsch Sie haben erfolgreich eine Ratte gefangen!"); + tor_offnen(); + } + } else { // something is wrong... start again looking for preamble (value: 2) + buffer_index = 0; + return; + } + } + } else if (Serial3.available() > 0 && torzustand==1) { + while (Serial3.available() > 0) { + Serial3.read(); + } + } +} + +unsigned extract_tag() { + uint8_t msg_head = buffer[0]; + uint8_t *msg_data = buffer + 1; // 10 byte => data contains 2byte version + 8byte tag + uint8_t *msg_data_version = msg_data; + uint8_t *msg_data_tag = msg_data + 2; + uint8_t *msg_checksum = buffer + 11; // 2 byte + uint8_t msg_tail = buffer[13]; + // print message that was sent from RDM630/RDM6300 + Serial.println("--------"); + Serial.print("Message-Head: "); + Serial.println(msg_head); + Serial.println("Message-Data (HEX): "); + for (int i = 0; i < DATA_VERSION_SIZE; ++i) { + Serial.print(char(msg_data_version[i])); + } + Serial.println(" (version)"); + for (int i = 0; i < DATA_TAG_SIZE; ++i) { + Serial.print(char(msg_data_tag[i])); + } + Serial.println(" (tag)"); + Serial.print("Message-Checksum (HEX): "); + for (int i = 0; i < CHECKSUM_SIZE; ++i) { + Serial.print(char(msg_checksum[i])); + } + Serial.println(""); + Serial.print("Message-Tail: "); + Serial.println(msg_tail); + Serial.println("--"); + long tag = hexstr_to_value(msg_data_tag, DATA_TAG_SIZE); + Serial.print("Extracted Tag: "); + Serial.println(tag); + long checksum = 0; + for (int i = 0; i < DATA_SIZE; i += CHECKSUM_SIZE) { + long val = hexstr_to_value(msg_data + i, CHECKSUM_SIZE); + checksum ^= val; + } + Serial.print("Extracted Checksum (HEX): "); + Serial.print(checksum, HEX); + if (checksum == hexstr_to_value(msg_checksum, CHECKSUM_SIZE)) { // compare calculated checksum to retrieved checksum + Serial.print(" (OK)"); // calculated checksum corresponds to transmitted checksum! + } else { + Serial.print(" (NOT OK)"); // checksums do not match + } + Serial.println(""); + Serial.println("--------"); + return tag; +} +long hexstr_to_value(char *str, unsigned int length) { // converts a hexadecimal value (encoded as ASCII string) to a numeric value + char* copy = malloc((sizeof(char) * length) + 1); + memcpy(copy, str, sizeof(char) * length); + copy[length] = '\0'; + // the variable "copy" is a copy of the parameter "str". "copy" has an additional '\0' element to make sure that "str" is null-terminated. + long value = strtol(copy, NULL, 16); // strtol converts a null-terminated string to a long value + free(copy); // clean up + return value; +} +//Hakentor ist bei 76 Grad zu | Rattentor bei 85 Grad +void tor_offnen() { + if(torzustand==0) { + Serial.println("öffnen"); + digitalWrite(LED, HIGH); + //tor_servo.write(0); + + motorsafetyTime = millis(); + digitalWrite(Schloss1, LOW); + + + int servo_position = 200; + //FallenTor + tor2_servo.attach (9); + while (digitalRead(Tortasteroffen) == 1){ + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { + //kill Tor öffnen es dauert zu lang + digitalWrite(Schloss1, HIGH); + Serial.println("Es hat zu lange gedauert. Etwas stimmt nicht. Das Tor öffnen wurde beendet #1"); + break; + } + servo_position--; + tor2_servo.write(servo_position); + delay(10); + } + int test = servo_position+10; + tor2_servo.write(test); + delay(20); + tor2_servo.detach(); + Serial.println("FallenTor offen"); + + digitalWrite(Schloss1, HIGH); + digitalWrite(Schloss2, LOW); + + //RattenTor + tor_servo.attach (4); + for(servo_position = 83; servo_position >=0; servo_position -=1){ + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { + //kill Tor öffnen es dauert zu lang + digitalWrite(Schloss2, HIGH); + Serial.println("Es hat zu lange gedauert. Etwas stimmt nicht. Das Tor öffnen wurde beendet #2"); + break; + } + tor_servo.write(servo_position); + delay(10); + } + tor_servo.detach(); + Serial.println("RattennTor offen"); + + + digitalWrite(Schloss2, HIGH); + Serial.println("Alle Elektroschlösser entspannt"); + + //tor2_servo.write(0); + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { + //kill Tor öffnen es dauert zu lang + Serial.println("Es hat zu lange gedauert. Etwas stimmt nicht. Das Tor öffnen wurde beendet #3"); + } else { + torzustand = 1; + Serial.println("1Torzustand = 1 - Tore wurden geöffnet"); + } + } + while (Serial3.available() > 0) { + Serial3.read(); + } +} +void tor_schliessen() { + if(torzustand==1) { + Serial.println("schliessen"); + digitalWrite(LED, LOW); + //RattenTor + tor_servo.attach (4); + for(servo_position = 0; servo_position <=83; servo_position +=1){ + tor_servo.write(servo_position); + delay(10); + } + tor_servo.detach(); + int servo_position = 0; + //FallenTor + tor2_servo.attach (9); + while (digitalRead(Tortastergeschlossen) == 1){ + servo_position++; + tor2_servo.write(servo_position); + delay(10); + } + + int test = servo_position-3; + tor2_servo.write(test); + delay(20); + tor2_servo.detach(); + + torzustand = 0; + Serial.println("2Torzustand = 0 - Tore wurden geschlossen"); + } + while (Serial3.available() > 0) { + Serial3.read(); + } +} + + + + + + + + + + + +// Hakentor ist bei 76 Grad zu | Rattentor bei 85 Grad +void openDor() { + if (!DoorIsOpen) { + Serial.println("--open"); + + auto motorsafetyTime = millis(); + DoorLock.write(false); // digitalWrite(Schloss1, LOW); + + int servo_position = 200; + // FallenTor + while (digitalRead(Tortasteroffen) == 1) { + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { + // kill Tor öffnen es dauert zu lang + digitalWrite(Schloss1, HIGH); + Serial.println("Es hat zu lange gedauert. Etwas stimmt nicht. Das Tor öffnen wurde beendet #1"); + break; + } + servo_position--; + tor2_servo.write(servo_position); + delay(10); + } + int test = servo_position + 10; + tor2_servo.write(test); + delay(20); + tor2_servo.detach(); + Serial.println("FallenTor offen"); + + digitalWrite(Schloss1, HIGH); + digitalWrite(Schloss2, LOW); + + // RattenTor + for (servo_position = 83; servo_position >= 0; servo_position -= 1) { + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { + // kill Tor öffnen es dauert zu lang + digitalWrite(Schloss2, HIGH); + Serial.println("Es hat zu lange gedauert. Etwas stimmt nicht. Das Tor öffnen wurde beendet #2"); + break; + } + ratDoor.write(servo_position); + delay(10); + } + ratDoor.detach(); + Serial.println("RattennTor offen"); + + digitalWrite(Schloss2, HIGH); + Serial.println("Alle Elektroschlösser entspannt"); + + // tor2_servo.write(0); + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { + // kill Tor öffnen es dauert zu lang + Serial.println("Es hat zu lange gedauert. Etwas stimmt nicht. Das Tor öffnen wurde beendet #3"); + } else { + DoorIsOpen = true; + Serial.println("1DoorIsOpen = 1 - Tore wurden geöffnet"); + } + } +} +void closeDoor() { + if (DoorIsOpen) { + Serial.println("--close"); + // RattenTor + ratDoor.attach(4); + for (servo_position = 0; servo_position <= 83; servo_position += 1) { + ratDoor.write(servo_position); + delay(10); + } + ratDoor.detach(); + int servo_position = 0; + // FallenTor + tor2_servo.attach(9); + while (digitalRead(DoorClodedPin) == 1) { + servo_position++; + tor2_servo.write(servo_position); + delay(10); + } + + int test = servo_position - 3; + tor2_servo.write(test); + delay(20); + tor2_servo.detach(); + + DoorIsOpen = false; + Serial.println(" -- close door"); + } +} diff --git a/src/mainLair.cpp b/src/mainLair.cpp new file mode 100644 index 0000000..e61daf7 --- /dev/null +++ b/src/mainLair.cpp @@ -0,0 +1,116 @@ +#include "main.hpp" + +using namespace abh; + +constexpr TimeStamp motorsafetyTimeDelay = 5000; +constexpr int ServoStartPos = 0; +constexpr int ServoEndPos = 83; + +Output SLedPin(LED_BUILTIN); + +Output DoorLock(14); +Input endSwitchOpen(19); +Input endSwitchClose(21); + +Input openDoorCmd(9); +Output doorOpendRes(8); +ServoGrp DoorServo(3); + +static StatusLed _statusLed(SLedPin); + +void setup() { + Serial.begin(115200); + _statusLed.set(StatusLed::Mode::DoubleFlash); + + doorOpendRes.write(false); +} + +enum State { + WaitForOpenCmd, + Open, + WaitForCloseCmd, + Close +}; + +State state{}; + +void openDor(); +void closeDoor(); + +void loop() { + static Tick tick(5); + static Tick diagTick(250); + if (!tick) { + return; + } + + openDoorCmd.sample(); + endSwitchOpen.sample(); + endSwitchClose.sample(); + + switch (state) { + default: + case WaitForOpenCmd: { + if (openDoorCmd.get() == Input::State::Active) { + state = State::Open; + } + } break; + + case Open: { + DoorLock.write(true); + openDor(); + DoorLock.write(false); + doorOpendRes.write(true); + + state = State::WaitForCloseCmd; + } break; + + case WaitForCloseCmd: { + if (openDoorCmd.get() == Input::State::Passive) { + state = State::Close; + } + } break; + + case Close: { + closeDoor(); + doorOpendRes.write(false); + state = WaitForOpenCmd; + } break; + } + + if (diagTick) { + _statusLed.tick(); + char buff[256]; + sprintf(buff, "S=%d | cI=%d sO=%d sC%d \r\n", + state, openDoorCmd.get(), endSwitchOpen.get(), endSwitchClose.get()); + Serial.print(buff); + } +} + +void openDor() { + Serial.println("--open"); + auto motorsafetyTime = millis(); + DoorServo.attach(); + for (int pos = ServoEndPos; pos >= ServoStartPos; pos -= 1) { + if ((millis() - motorsafetyTime) > motorsafetyTimeDelay) { // prvent overheating + Serial.println("Abort due to timeout"); + DoorServo.detach(); + return; + } + DoorServo.write(pos); + delay(10); + } + DoorServo.detach(); + Serial.println("--isOpen"); +} + +void closeDoor() { + Serial.println("--close"); + DoorServo.attach(); + for (int pos = ServoStartPos; pos <= ServoEndPos; pos += 1) { + DoorServo.write(pos); + delay(10); + } + DoorServo.detach(); + Serial.println("--isClosed"); +} diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner 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/en/latest/advanced/unit-testing/index.html diff --git a/test/embedded/test_devices/test_statusLed.cpp b/test/embedded/test_devices/test_statusLed.cpp new file mode 100644 index 0000000..daa33d6 --- /dev/null +++ b/test/embedded/test_devices/test_statusLed.cpp @@ -0,0 +1,83 @@ +#include "StatusLed.h" +#include +#include + +void setUp(void) {} + +void tearDown(void) {} + +int LED_PIN = LED_BUILTIN; + +void test_LedStaysDark(void) { + StatusLed dut(LED_PIN); + + for (size_t i = 0; i < 30; i++) { + dut.Tick(); + TEST_ASSERT_EQUAL(LOW, digitalRead(LED_PIN)); + delay(10); + } +} + +void test_LedIsConstantOn(void) { + StatusLed dut(LED_PIN); + + dut.Set(StatusLed::Mode::SteadyOn); + for (size_t i = 0; i < 30; i++) { + dut.Tick(); + TEST_ASSERT_EQUAL(HIGH, digitalRead(LED_PIN)); + delay(10); + } +} + +void test_LedBlinks(void) { + StatusLed dut(LED_PIN); + + dut.Set(StatusLed::Mode::Blinking); + for (size_t n = 0; n < 2; n++) { + for (size_t i = 0; i < 8; i++) { + dut.Tick(); + TEST_ASSERT_EQUAL(HIGH, digitalRead(LED_PIN)); + delay(20); + } + for (size_t i = 0; i < 8; i++) { + dut.Tick(); + TEST_ASSERT_EQUAL(LOW, digitalRead(LED_PIN)); + delay(20); + } + } +} + +void test_LedBlinks_InverseLogic(void) { + StatusLed dut(LED_PIN, StatusLed::LedLogic::Inverted); + + dut.Set(StatusLed::Mode::Blinking); + for (size_t n = 0; n < 2; n++) { + for (size_t i = 0; i < 8; i++) { + dut.Tick(); + TEST_ASSERT_EQUAL(LOW, digitalRead(LED_PIN)); + delay(20); + } + for (size_t i = 0; i < 8; i++) { + dut.Tick(); + TEST_ASSERT_EQUAL(HIGH, digitalRead(LED_PIN)); + delay(20); + } + } +} + +void setup() { + // NOTE!!! Wait for >2 secs + // if board doesn't support software reset via Serial.DTR/RTS + delay(2000); + + UNITY_BEGIN(); // IMPORTANT LINE! + RUN_TEST(test_LedStaysDark); + RUN_TEST(test_LedIsConstantOn); + RUN_TEST(test_LedBlinks); + RUN_TEST(test_LedBlinks_InverseLogic); + UNITY_END(); // stop unit testing +} + +void loop() { + delay(500); +} \ No newline at end of file diff --git a/test/extra_script.py b/test/extra_script.py new file mode 100644 index 0000000..45fa7ad --- /dev/null +++ b/test/extra_script.py @@ -0,0 +1,7 @@ +Import("env") + +def after_upload(source, target, env): + import time + time.sleep(2) + +env.AddPostAction("upload", after_upload) diff --git a/test/native/test_utils/test_main.cpp b/test/native/test_utils/test_main.cpp new file mode 100644 index 0000000..b199e37 --- /dev/null +++ b/test/native/test_utils/test_main.cpp @@ -0,0 +1,71 @@ +#include +#include "RotatingIndex.hpp" + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + if (RUN_ALL_TESTS()) + ; + return 0; +} + +TEST(RotatingIndex, Construction) { + RotatingIndex sut(4); + ASSERT_TRUE(true); +} + +TEST(RotatingIndex, InitialState) { + RotatingIndex sut(4); + ASSERT_EQ(sut.GetIndex(), 0); +} + +TEST(RotatingIndex, TestOverflowWithGetIndexMethod) { + RotatingIndex sut(4); + + ASSERT_EQ(sut.GetIndexAndIncrement(), 0); + ASSERT_EQ(sut.GetIndexAndIncrement(), 1); + ASSERT_EQ(sut.GetIndexAndIncrement(), 2); + ASSERT_EQ(sut.GetIndexAndIncrement(), 3); + ASSERT_EQ(sut.GetIndexAndIncrement(), 0); + ASSERT_EQ(sut.GetIndexAndIncrement(), 1); +} + +TEST(RotatingIndex, TestOverflowWithCheckIfRevolvesMethod) { + RotatingIndex sut(4); + + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_TRUE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); +} + +TEST(RotatingIndex, GetIndexReturnsCorrectValue) { + RotatingIndex sut(4); + + ASSERT_EQ(sut.GetIndexAndIncrement(), 0); + ASSERT_EQ(sut.GetIndex(), 1); + ASSERT_EQ(sut.GetIndexAndIncrement(), 1); + ASSERT_EQ(sut.GetIndex(), 2); +} + +TEST(RotatingIndex, TestReset) { + RotatingIndex sut(4); + + sut.GetIndexAndIncrement(); + sut.ResetIndex(); + ASSERT_EQ(sut.GetIndex(), 0); +} + +TEST(RotatingIndex, TestDifferentTypeAndMaximum) { + RotatingIndex sut(6); + + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_TRUE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); + ASSERT_FALSE(sut.IncrementAndCheckIfRevolves()); +} \ No newline at end of file