Commit 7330cedf authored by Jacob Priddy's avatar Jacob Priddy 👌

Merge branch '7-add-door-state-detection-module' into 'master'

Resolve "Add door state detection module"

Closes #7

See merge request !5
parents 349c3c84 f181b0fd
Pipeline #9601 passed with stages
in 1 minute and 33 seconds
......@@ -8,6 +8,7 @@ extern "C" {
#define WIFI_CONNECTED_EVENT DOOR_BIT0
#define TIME_SYNCED_EVENT DOOR_BIT1
#define DOOR_OPEN_MODE_EVENT DOOR_BIT2
#define DOOR_LATCH_EVENT DOOR_BIT3
#ifdef __cplusplus
}
......
......@@ -4,16 +4,6 @@
extern "C" {
#endif
// Define keypad pins
//#define ROW0_PIN 14
//#define ROW1_PIN 12
//#define ROW2_PIN 13
//#define ROW3_PIN 15
//
//#define COL0_PIN 4
//#define COL1_PIN 5
//#define COL2_PIN 18
/**
* @brief callback for a keypress event. Passes in the key pressed or released.
*/
......
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_LATCH_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_LATCH_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#define LATCH_CHECK_PERIOD_MS 100
typedef enum {
DOOR_LATCH_CLOSED = 0,
DOOR_LATCH_OPENED = 1,
} door_latch_state_t;
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
///**
// * @brief Function is triggered when the door opens or closes
// * and takes the state the door currently is in as well as how many
// * latch check periods have gone by (currently 1/10'th of a second)
// * since it was last opened/closed/when the thread booted
// */
//typedef void (* door_latch_event_cb)(door_latch_state_t state, uint64_t);
//
//typedef void* door_latch_event_handle_t;
//
//door_latch_event_handle_t door_latch_register_watch(door_latch_event_cb);
//
//void door_latch_unregister_watch(door_latch_event_handle_t);
void door_latch_watch_task(void*);
void door_latch_stop(void);
void door_latch_initialize(void);
#ifdef __cplusplus
}
#endif
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_LATCH_H_
......@@ -4,9 +4,6 @@
extern "C" {
#endif
// Hardware pin that the lock is on
//#define DOOR_LOCK_PIN 21
/**
* @brief initializes the door lock module
*/
......
......@@ -32,14 +32,16 @@
//#define UPDATE_SKIP_VERSION_CHECK
#define ROW0_PIN 14
#define ROW1_PIN 12
#define ROW2_PIN 13
#define ROW3_PIN 15
#define COL0_PIN 4
#define COL1_PIN 5
#define COL2_PIN 18
#define DOOR_LATCH_PIN 19
#define KEYPAD_ROW0_PIN 14
#define KEYPAD_ROW1_PIN 12
#define KEYPAD_ROW2_PIN 13
#define KEYPAD_ROW3_PIN 15
#define KEYPAD_COL0_PIN 4
#define KEYPAD_COL1_PIN 5
#define KEYPAD_COL2_PIN 18
#define DOOR_LOCK_PIN 12
#define DOOR_OPEN_LENGTH_MS (2.5 * 1000)
......
......@@ -9,6 +9,7 @@ extern "C" {
#include <stdint.h>
#include <stddef.h>
#define DOOR_BIT3 ((unsigned)0x00000008)
#define DOOR_BIT2 ((unsigned)0x00000004)
#define DOOR_BIT1 ((unsigned)0x00000002)
#define DOOR_BIT0 ((unsigned)0x00000001)
......
......@@ -24,23 +24,57 @@
#include "app/keypad.h"
#include "app/state.h"
#include "app/status.h"
#include "app/latch.h"
static const char* TAG = "MAIN_MODULE";
static volatile bool door_request_done;
static volatile bool door_access_accepted;
static void code_check_done(int32_t status_code)
{
door_request_done = true;
door_access_accepted = status_code == HTTP_SUCCESS;
}
static void check_code_task(void* pvParameters)
{
(void)pvParameters;
// door_rtos_wait_forever_for_event(WIFI_CONNECTED_EVENT);
ESP_LOGI(TAG, "Started test thread. Opening door...");
door_lock_open();
ESP_LOGI(TAG, "Door unlock request sent to the door lock module");
door_rtos_wait_forever_for_event(WIFI_CONNECTED_EVENT);
ESP_LOGI(TAG, "Connected to AP, begin connecting to DoorCode API");
door_api_verify_code("123866", code_check_done);
while (!door_request_done)
door_rtos_task_delay(100);
if (door_access_accepted)
{
ESP_LOGI(TAG, "We are allowed in the first door!");
door_lock_open();
}
else
ESP_LOGI(TAG, "REJECTED from first door");
ESP_LOGI(TAG, "Putting controller in open mode.");
door_rtos_event_set(DOOR_OPEN_MODE_EVENT);
ESP_LOGI(TAG, "Door is now in open mode. Any registered keypad presses will open door.");
door_request_done = false;
ESP_LOGI(TAG, "Stopping test thread... Other threads are still running meaning that any keypress will still be"
" registered and handled");
door_rtos_task_delay(5000);
ESP_LOGI(TAG, "Making second request");
door_api_verify_code("123867", code_check_done);
while (!door_request_done)
door_rtos_task_delay(100);
ESP_LOGI(TAG, "Done with second request");
if (door_access_accepted)
{
ESP_LOGI(TAG, "We are allowed in the second door!");
door_lock_open();
}
else
ESP_LOGI(TAG, "REJECTED from second door");
// door_api_stop();
// door_wifi_destroy();
ESP_LOGI(TAG, "Finished Requests shutting down the scrublord thread");
door_rtos_task_current_kill();
}
......@@ -49,35 +83,40 @@ void initialize(void)
// Setup non volatile storage
door_nvs_initialize();
// Setup API token (This uses NVS)
// door_token_initialize(API_TOKEN);
door_token_initialize(API_TOKEN);
// Setup the RTOS interfaces such as events
door_rtos_initialize();
// Needs RTOS
// Needs just RTOS
door_latch_initialize();
door_keypad_initialize();
// Start up WiFi (WiFi uses tokens and the RTOS events)
// door_wifi_initialize();
door_wifi_initialize();
// Setup SNTP time syncing (Uses WiFi and the RTOS events)
// door_sntp_initialize();
door_sntp_initialize();
// Needs RTOS and token, possibly WiFi? but not for initialization I don't think.
// door_api_initialize();
door_api_initialize();
// Needs the API
// door_status_initialize();
door_status_initialize();
}
void start(void)
{
// xTaskCreate(door_api_handle_requests_task, "api_task", 4096, NULL, 5, NULL);
// Processing HTTP request and getting codes back is probably the most important
// for noticable responsiveness
xTaskCreate(door_api_handle_requests_task, "api_task", 4096, NULL, 7, NULL);
xTaskCreate(door_latch_watch_task, "latch_task", 4096, NULL, 4, NULL);
xTaskCreate(door_lock_task, "lock_task", 4096, NULL, 6, NULL);
xTaskCreate(door_keypad_scan_task, "keypad_task", 4096, NULL, 6, NULL);
xTaskCreate(door_state_task, "state_task", 4096, NULL, 5, NULL);
// xTaskCreate(update_task, "update_task", 4096, NULL, 2, NULL);
// xTaskCreate(check_code_task, "http_task", 4096, NULL, 3, NULL);
// xTaskCreate(door_status_refresh_task, "refresh_task", 4096, NULL, 5, NULL);
// xTaskCreate(door_status_sync_status_task, "sync_task", 10240, NULL, 4, NULL);
xTaskCreate(update_task, "update_task", 4096, NULL, 2, NULL);
xTaskCreate(check_code_task, "http_task", 4096, NULL, 3, NULL);
xTaskCreate(door_status_refresh_task, "refresh_task", 4096, NULL, 5, NULL);
xTaskCreate(door_status_sync_status_task, "sync_task", 10240, NULL, 4, NULL);
}
void app_main()
......
......@@ -13,6 +13,14 @@
bool door_diagnostic_self_test(void)
{
// For the test branch we won't worry about verifying API connectivity.
return true;
door_http_client_handle_t client = door_http_create_client(door_token_get());
if (!door_rtos_event_wait_ms(WIFI_CONNECTED_EVENT, DOOR_DIAGNOSTIC_CONNECT_TIME_MS) || !client)
return false;
int status = door_http_get(client, API_BASE_PATH DOOR_DIAGNOSTIC_TEST_URL, NULL);
door_http_destroy_client_handle(client);
return status == 200;
}
......@@ -11,14 +11,14 @@
// 1 minute
#define KEYPAD_EVENT_HOOK_WAIT_TIME 1000 * 60
#define KEYPAD_ROW_PIN_MASK (PIN_MASK(ROW0_PIN) \
| PIN_MASK(ROW1_PIN) \
| PIN_MASK(ROW2_PIN) \
| PIN_MASK(ROW3_PIN))
#define KEYPAD_ROW_PIN_MASK (PIN_MASK(KEYPAD_ROW0_PIN) \
| PIN_MASK(KEYPAD_ROW1_PIN) \
| PIN_MASK(KEYPAD_ROW2_PIN) \
| PIN_MASK(KEYPAD_ROW3_PIN))
#define KEYPAD_COL_PIN_MASK (PIN_MASK(COL0_PIN) \
| PIN_MASK(COL1_PIN) \
| PIN_MASK(COL2_PIN))
#define KEYPAD_COL_PIN_MASK (PIN_MASK(KEYPAD_COL0_PIN) \
| PIN_MASK(KEYPAD_COL1_PIN) \
| PIN_MASK(KEYPAD_COL2_PIN))
// These macros are defined for critical sections dealing with a mutex so it is harder to forget
// to unlock the mutex when done, and also so one does not lock improperly. Just wrap anything dealing with
......@@ -32,19 +32,19 @@
#define BEGIN_KEYPAD_CHECK for (unsigned r = 0; r < ROWS; ++r) door_gpio_set_output_pin(key_row_pins[r], DOOR_GPIO_LOW)
#define END_KEYPAD_CHECK for (unsigned r = 0; r < ROWS; ++r) door_gpio_set_output_pin(key_row_pins[r], DOOR_GPIO_HIGH)
door_list_node_t key_handle_list_head;
static door_list_node_t key_handle_list_head;
static const int key_row_pins[ROWS] = {
ROW0_PIN,
ROW1_PIN,
ROW2_PIN,
ROW3_PIN,
KEYPAD_ROW0_PIN,
KEYPAD_ROW1_PIN,
KEYPAD_ROW2_PIN,
KEYPAD_ROW3_PIN,
};
static const int key_column_pins[COLUMNS] = {
COL0_PIN,
COL1_PIN,
COL2_PIN,
KEYPAD_COL0_PIN,
KEYPAD_COL1_PIN,
KEYPAD_COL2_PIN,
};
static const unsigned char keypad_map[ROWS][COLUMNS] = {
......@@ -214,7 +214,8 @@ void door_keypad_unregister_callback(door_keypad_event_handle_t handle)
void door_keypad_initialize(void)
{
mutex = door_rtos_mutex_create();
if (!mutex)
mutex = door_rtos_mutex_create();
door_keypad_scan_stop = false;
door_gpio_configure_input_pins_pull_down(KEYPAD_COL_PIN_MASK);
......
#include <stdbool.h>
#include <stdint.h>
#include "config.h"
#include "app/latch.h"
#include "app/event.h"
#include "connectors/rtos.h"
#include "connectors/gpio.h"
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
//#define LATCH_HANDLE_LIST_WAIT_MS 50
//static mutex_handle_t latch_handle_list_guard;
//static door_list_node_t latch_handle_list_head;
static bool done;
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
//door_latch_event_handle_t door_latch_register_watch(door_latch_event_cb cb)
//{
// door_list_node_t handle = NULL;
// BEGIN_PROTECT_RESOURCE(latch_handle_list_guard, LATCH_HANDLE_LIST_WAIT_MS);
// handle = door_list_add(&latch_handle_list_head, &cb, sizeof(door_latch_event_cb));
// END_PROTECT_RESOURCE(latch_handle_list_guard);
// return handle;
//}
//
//void door_latch_unregister_watch(door_latch_event_handle_t handle)
//{
// BEGIN_PROTECT_RESOURCE(latch_handle_list_guard, LATCH_HANDLE_LIST_WAIT_MS);
// door_list_remove(&latch_handle_list_head, handle);
// END_PROTECT_RESOURCE(latch_handle_list_guard);
//}
void door_latch_watch_task(void* pvParameters)
{
(void)pvParameters;
door_latch_initialize();
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
// uint64_t time_counter = 0;
// door_latch_state_t prev_state = DOOR_LATCH_CLOSED;
do
{
// door_latch_state_t next_state;
door_rtos_task_delay(LATCH_CHECK_PERIOD_MS);
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
// ++time_counter;
// if ((next_state = (door_latch_state_t)door_gpio_get_input_pin(DOOR_LATCH_PIN)) == DOOR_LATCH_OPENED)
if ((door_latch_state_t)door_gpio_get_input_pin(DOOR_LATCH_PIN) == DOOR_LATCH_OPENED)
door_rtos_event_set(DOOR_LATCH_EVENT);
else
door_rtos_event_clear(DOOR_LATCH_EVENT);
// We have changed states!
// if (next_state != prev_state)
// {
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
// BEGIN_PROTECT_RESOURCE(latch_handle_list_guard, LATCH_HANDLE_LIST_WAIT_MS);
// door_list_node_t tmp = latch_handle_list_head;
// while (tmp)
// {
// door_latch_event_cb cb = *(door_latch_event_cb*)tmp->data;
// cb(next_state, time_counter);
// tmp = tmp->next;
// }
// END_PROTECT_RESOURCE(latch_handle_list_guard);
// time_counter = 0;
// }
//
// prev_state = next_state;
} while (!done);
}
void door_latch_stop(void)
{
done = true;
}
void door_latch_initialize(void)
{
/* UNCOMMENT TO ENABLE LATCH CALLBACK FEATURE */
// if (!latch_handle_list_guard)
// latch_handle_list_guard = door_rtos_mutex_create();
door_gpio_configure_input_pins_pull_down(PIN_MASK(DOOR_LATCH_PIN));
}
#include <stddef.h>
#include "config.h"
#include "app/lock.h"
#include "app/latch.h"
#include "app/event.h"
#include "connectors/rtos.h"
#include "connectors/gpio.h"
#include "config.h"
#define DOOR_UNLOCK_LEVEL DOOR_GPIO_HIGH
#define DOOR_LOCK_LEVEL DOOR_GPIO_LOW
......@@ -28,10 +30,11 @@ void door_lock_task(void* pvParameters)
do
{
notify_value = door_rtos_notify_wait_forever();
if (notify_value & UNLOCK_DOOR)
if (notify_value & UNLOCK_DOOR && !door_rtos_event_get(DOOR_LATCH_EVENT))
{
door_gpio_set_output_pin(DOOR_LOCK_PIN, DOOR_UNLOCK_LEVEL);
door_rtos_task_delay(DOOR_OPEN_LENGTH_MS);
// Stop it early if the door opens.
door_rtos_event_wait_ms(DOOR_LATCH_EVENT, DOOR_OPEN_LENGTH_MS);
door_gpio_set_output_pin(DOOR_LOCK_PIN, DOOR_LOCK_LEVEL);
}
} while (!(notify_value & QUIT_TASK));
......
......@@ -66,26 +66,26 @@ TEST_CASE("keypad module scans the keypad")
SECTION("module handles a press")
{
set_key_level(ROW0_PIN, COL0_PIN, true);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, true);
run_test();
REQUIRE(pressed_keys['1']);
}
SECTION("module handles a release")
{
set_key_level(ROW0_PIN, COL0_PIN, true);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, true);
run_test();
set_key_level(ROW0_PIN, COL0_PIN, false);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, false);
run_test();
REQUIRE(released_keys['1']);
}
SECTION("module handles multiple key presses")
{
set_key_level(ROW0_PIN, COL0_PIN, true);
set_key_level(ROW1_PIN, COL1_PIN, true);
set_key_level(ROW2_PIN, COL2_PIN, true);
set_key_level(ROW3_PIN, COL0_PIN, true);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, true);
set_key_level(KEYPAD_ROW1_PIN, KEYPAD_COL1_PIN, true);
set_key_level(KEYPAD_ROW2_PIN, KEYPAD_COL2_PIN, true);
set_key_level(KEYPAD_ROW3_PIN, KEYPAD_COL0_PIN, true);
run_test();
......@@ -97,19 +97,19 @@ TEST_CASE("keypad module scans the keypad")
SECTION("module handles multiple key releases while some keys are still pressed")
{
set_key_level(ROW0_PIN, COL0_PIN, true);
set_key_level(ROW1_PIN, COL1_PIN, true);
set_key_level(ROW2_PIN, COL2_PIN, true);
set_key_level(ROW3_PIN, COL0_PIN, true);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, true);
set_key_level(KEYPAD_ROW1_PIN, KEYPAD_COL1_PIN, true);
set_key_level(KEYPAD_ROW2_PIN, KEYPAD_COL2_PIN, true);
set_key_level(KEYPAD_ROW3_PIN, KEYPAD_COL0_PIN, true);
run_test();
REQUIRE(pressed_keys['1']);
REQUIRE(pressed_keys['5']);
REQUIRE(pressed_keys['9']);
REQUIRE(pressed_keys['*']);
set_key_level(ROW0_PIN, COL0_PIN, false);
set_key_level(ROW1_PIN, COL1_PIN, false);
set_key_level(ROW2_PIN, COL2_PIN, false);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, false);
set_key_level(KEYPAD_ROW1_PIN, KEYPAD_COL1_PIN, false);
set_key_level(KEYPAD_ROW2_PIN, KEYPAD_COL2_PIN, false);
run_test();
REQUIRE(released_keys['1']);
REQUIRE(released_keys['5']);
......@@ -126,9 +126,9 @@ TEST_CASE("keypad module scans the keypad")
door_configured_input_pins = mask;
};
run_test();
REQUIRE(door_configured_input_pins == (PIN_MASK(COL0_PIN) |
PIN_MASK(COL1_PIN) |
PIN_MASK(COL2_PIN)));
REQUIRE(door_configured_input_pins == (PIN_MASK(KEYPAD_COL0_PIN) |
PIN_MASK(KEYPAD_COL1_PIN) |
PIN_MASK(KEYPAD_COL2_PIN)));
}
SECTION("module sets up output pins")
......@@ -139,15 +139,15 @@ TEST_CASE("keypad module scans the keypad")
door_configured_output_pins = mask;
};
run_test();
REQUIRE(door_configured_output_pins == (PIN_MASK(ROW0_PIN) |
PIN_MASK(ROW1_PIN) |
PIN_MASK(ROW2_PIN) |
PIN_MASK(ROW3_PIN)));
REQUIRE(door_configured_output_pins == (PIN_MASK(KEYPAD_ROW0_PIN) |
PIN_MASK(KEYPAD_ROW1_PIN) |
PIN_MASK(KEYPAD_ROW2_PIN) |
PIN_MASK(KEYPAD_ROW3_PIN)));
}
SECTION("module unregisters callbacks")
{
set_key_level(ROW0_PIN, COL0_PIN, true);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, true);
door_keypad_callback_t a = {
.on_press = press,
.on_release = release,
......@@ -158,7 +158,7 @@ TEST_CASE("keypad module scans the keypad")
door_keypad_stop();
door_keypad_scan_task(nullptr);
set_key_level(ROW0_PIN, COL0_PIN, false);
set_key_level(KEYPAD_ROW0_PIN, KEYPAD_COL0_PIN, false);
door_keypad_initialize();
door_keypad_unregister_callback(cb);
door_keypad_stop();
......
#include <catch2/catch.hpp>
#include "test_types.h"
#include "app/event.h"
#include "app/latch.h"
TEST_CASE("the latch does latchy thingies and sees when its open/closed", "[door_latch]")
{
Hooks::gpio = std::make_unique<Hooks::GPIO>();
Hooks::rtos = std::make_unique<Hooks::RTOS>();
door_latch_initialize();
door_latch_stop();
SECTION("latch sets event if door is open")
{
Hooks::gpio->get_pin_hook = [&](uint32_t pin) -> bool
{
return true;
};
REQUIRE_FALSE(door_rtos_event_get(DOOR_LATCH_EVENT));
door_latch_watch_task(nullptr);
REQUIRE(door_rtos_event_get(DOOR_LATCH_EVENT));
}
SECTION("latch clears event if door is closed")
{
Hooks::gpio->get_pin_hook = [&](uint32_t pin) -> bool
{
return false;
};
door_rtos_event_set(DOOR_LATCH_EVENT);
door_latch_watch_task(nullptr);
REQUIRE_FALSE(door_rtos_event_get(DOOR_LATCH_EVENT));
}
}
\ No newline at end of file
......@@ -14,7 +14,7 @@ TEST_CASE("door controller door lock module unlocks", "[door_lock]")
Hooks::rtos->notify_wait_forever_hook = [&]() -> uint32_t
{
return notify_return;
return notify_return;
};
SECTION("lock sets up pin")
......@@ -32,9 +32,10 @@ TEST_CASE("door controller door lock module unlocks", "[door_lock]")
{
uint32_t delay_time = 0;
Hooks::rtos->delay_task_hook = [&](uint32_t ms)
Hooks::rtos->event_wait_ms_hook = [&](unsigned int event, uint32_t ms) -> bool
{
delay_time = ms;
delay_time = ms;
return false;
};
door_lock_task(nullptr);
......@@ -46,7 +47,7 @@ TEST_CASE("door controller door lock module unlocks", "[door_lock]")
unsigned set_pin = 0;
Hooks::gpio->set_pin_hook = [&](uint32_t pin, unsigned level)
{
set_pin = pin;
set_pin = pin;
};
door_lock_task(nullptr);
REQUIRE(set_pin == DOOR_LOCK_PIN);
......@@ -69,14 +70,43 @@ TEST_CASE("door controller door lock module unlocks", "[door_lock]")
bool notified = false;
Hooks::rtos->task_get_current_handle_hook = []()
{
return nullptr;
return nullptr;
};
Hooks::rtos->notify_task_hook = [&](task_handle_t task, uint32_t val)
{
notified = true;
notified = true;
};
door_lock_task(nullptr);
door_lock_open();
REQUIRE(notified == 0);
}
SECTION("lock stops open if door opens")
{
bool wait_called = false;
Hooks::rtos->event_wait_ms_hook = [&](unsigned int event, uint32_t ms) -> bool
{
REQUIRE(event == DOOR_LATCH_EVENT);
wait_called = true;
return true;
};
door_lock_task(nullptr);
REQUIRE(wait_called);
}
SECTION("lock won't open if door is already open")
{
Hooks::rtos->event_get_hook = [&](unsigned int event) -> bool
{
return event == DOOR_LATCH_EVENT;
};
Hooks::gpio->set_pin_hook = [&](uint32_t pin, unsigned int level)
{
// fail the test if we get here
REQUIRE(false);