Commit 07479dff authored by Jacob Priddy's avatar Jacob Priddy 👌

Add initial files

parents
build/
cmake-build*
sdkconfig
sdkconfig.old
cmake_minimum_required(VERSION 3.5)
if(COMPILE_UNIVERSAL_TESTS)
set(PROJECT_NAME door-controller-universal-tests)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
project(${PROJECT_NAME} C CXX)
find_package(Catch2 REQUIRED)
set(INCLUDE_DIR main/include)
set(APP_SRC main/src/app)
set(CONNECTOR_TEST main/src/connectors/tests)
set(TEST_SRC main/src/test)
file(GLOB_RECURSE FILES
${INCLUDE_DIR}/*.h
${APP_SRC}/*.c
${CONNECTOR_TEST}/*.c
${TEST_SRC}/*.cpp
)
add_executable(${PROJECT_NAME} ${FILES})
target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} Catch2::Catch2)
else()
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(door-controller)
endif()
FROM espressif/idf:latest
RUN apt-get update && apt-get install -y \
build-essential \
gcc \
g++ \
gdb \
cmake \
&& apt-get clean \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/*
# Install catch2
RUN git clone https://github.com/catchorg/Catch2.git /catch2 \
&& cd /catch2 && cmake -Bbuild -H. -DBUILD_TESTING=OFF \
&& cmake --build build/ --target install \
&& cd / \
&& rm -rf /catch2
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
CMD [ "/bin/bash" ]
Code in this repository is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := app-template
include $(IDF_PATH)/make/project.mk
# Useful commands
- Building
- `idf build`
- To flash
- On my local machine run idfinit which runs `source /opt/esp-idf/export.sh`
- `idf -p /dev/ttyUSB0 flash`
- To Monitor:
- `idf -p /dev/ttyUSB0 monitor`
# CLion Development Setup
Disclaimer, I set this up before clion has full docker integration, so once that comes out, these steps
will need to be changed.
## Toolchain Setup
Add a new toolchain and point the c compiler, c++ compiler, and gdb to the installed xtensa esp idf executables.
For me these were located in `~/.espressif/tools/xtensa-esp32-elf/esp-2020r1-8.2.0/xtensa-esp32-elf/bin/`
## CMake Setup
Make sure you followed the esp-idf setup instructions. Set the `IDF_PATH` and `PYTHON` environment variables
in the cmake configuration you want to use. `IDF_PATH` should be set to the base esp-idf install directory.
`PYTHON` should point to the python executable that the esp-idf installation setup for you.
For me `ESP_IDF` was `/opt/esp-idf`
`PATH` should also be overwritten to contain the tools directory of the base esp-idf installation,
as well as the xtensa toolchain bin that was installed to your home directory (mentioned earlier in toolchain setup).
config.h
*.pem
\ No newline at end of file
set(INCLUDE_DIR include)
set(APP_SRC src/app)
set(CONNECTOR_SRC src/connectors/esp32)
file(GLOB_RECURSE FILES ${APP_SRC}/*.c ${CONNECTOR_SRC}/*.c)
idf_component_register(SRCS ${FILES} "main.c"
INCLUDE_DIRS ${INCLUDE_DIR}
EMBED_TXTFILES "doorcode_root_cert.pem")
# put here your custom config value
menu "Example Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
help
SSID (network name) for the example to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
endmenu
#ifndef DOOR_CONTROLLER_MAIN_API_H_
#define DOOR_CONTROLLER_MAIN_API_H_
#include <stdbool.h>
#include "connectors/http.h"
#define HTTP_SUCCESS 200
// Requires door_wifi to be initialized
/**
* @param code MUST be null terminated
* @return
*/
bool door_api_verify_code(const char* code);
#endif //DOOR_CONTROLLER_MAIN_API_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_DIAGNOSTIC_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_DIAGNOSTIC_H_
#include <stdbool.h>
bool door_diagnostic_self_test(void);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_DIAGNOSTIC_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_EVENT_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_EVENT_H_
#include "connectors/rtos.h"
#define WIFI_CONNECTED_EVENT BIT0
#define TIME_SYNCED_EVENT BIT1
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_EVENT_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_RANDOM_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_RANDOM_H_
#include <stdint.h>
uint32_t door_random_get_random_between(uint32_t min, uint32_t max);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_RANDOM_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_STATE_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_STATE_H_
void state_task(void* parameters);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_STATE_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_TIME_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_TIME_H_
int door_time_get_current_hour(void);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_TIME_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_TOKEN_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_TOKEN_H_
void door_token_initialize(const char* default_token);
void door_token_set(const char* token);
const char* door_token_get(void);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_TOKEN_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_APP_UPDATE_H
#define DOOR_CONTROLLER_MAIN_INCLUDE_APP_UPDATE_H_
void update_task(void* parameters);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_APP_UPDATE_H_
#ifndef DOOR_CONFIG_H
#define DOOR_CONFIG_H
#define DOOR_WIFI_SSID "ssid"
#define DOOR_WIFI_PASSWORD "password"
#define API_HOST "host or ip"
#define API_PORT 443
#define API_TOKEN "door_1_api_token"
#define API_BASE_PATH "/api/door"
#define API_UPDATE_URL API_BASE_PATH "/update"
#define API_TIMEOUT_MS 10000
#define API_USE_SSL
#define API_NO_NAME_VERIFY
#define NTP_SERVER "pool.ntp.org"
#define TIMEZONE "PST8PDT,M3.2.0/2,M11.1.0"
#define UPDATE_HOUR_START 2
// 3 hours
#define UPDATE_MAX_FUDGE_FACTOR_MS ((1000*60*60)*3)
// Every hour
#define UPDATE_CHECK_PERIOD_MS (1000*60*60)
//#define UPDATE_SKIP_VERSION_CHECK
#endif //DOOR_CONFIG_H
#ifndef DOOR_CONTROLLER_MAIN_HTTP_H_
#define DOOR_CONTROLLER_MAIN_HTTP_H_
#include <stdbool.h>
#define DOOR_HTTP_SUCCESS 0
#define DOOR_HTTP_ERR_FAIL -1
#define DOOR_HTTP_ERR_TIMEOUT -2
/**
* @brief return false if something goes wrong. Can be called multiple times on one request because of chunked data
*/
typedef bool (* door_http_process_data_cb)(const void* data, const int len);
typedef struct
{
door_http_process_data_cb data_handler;
} door_http_callback_t;
/**
* @brief Initializes the door http system
* @param[in] token The API token to use
* @return < 0 on failure
*/
int door_http_initialize(const char* token);
/**
* @brief De-initalizes the http system. If it is to be used again, door_http_init must be called again
*/
void door_http_destroy(void);
/**
* @brief Performs a get request to the door code server. door_http_free_buffer must be called
* when the callee is done with the data before the next request
* @param[in] path the path of the url to make the request to
* @param[in] callbacks http request callbacks to handle getting data etc...
* @return
* - status code on success
* - < 0 on fail
*/
int door_http_get(const char* path, door_http_callback_t* callbacks);
/**
* @brief Downloads and updates the next OTA partition from the server.
* Will return false if not connected to WiFi
* @param[in] api_token the api token to use for authentication/authorization/identification
* @return
* - true on success
* - false otherwise
*/
bool door_http_ota_update(const char* api_token);
#endif //DOOR_CONTROLLER_MAIN_HTTP_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_NVS_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_NVS_H_
#define DOOR_NVS_SUCCESS 0
#define DOOR_NVS_ERR_KEY_NOT_FOUND -1
#define DOOR_NVS_ERR_NO_HANDLE -2
#define DOOR_NVS_ERR_NO_MEM -3
#define DOOR_NVS_ERR_NOT_ENOUGH_SPACE -4
#define DOOR_NVS_ERR_VALUE_TOO_LONG -5
#define DOOR_NVS_ERR_OTHER -6
/**
* @brief Initializes the NVS system on the chip. If this fails the application will stop
*/
void door_nvs_initialize(void);
/**
* @brief Gets a string from NVS
* @param[in] key
* @param[out] string pointer to allocated memory for string. This must be freed.
* @return
*/
int door_nvs_get_string(const char* key, const char** string);
/**
* @brief Sets a string value.
* @param key The key to save the value under
* @param value The value to be saved in NVS
* @return
*/
int door_nvs_set_string(const char* key, const char* value);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_NVS_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_RANDOM_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_RANDOM_H_
#include <stdint.h>
uint32_t door_random_get_random_number(void);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_RANDOM_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_RTOS_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_RTOS_H_
#include <stdbool.h>
#include <stdint.h>
#define BIT1 0x00000002
#define BIT0 0x00000001
void door_rtos_initialize(void);
//void door_rtos_destroy(void);
void door_rtos_wait_forever_for_event(unsigned identifier);
void door_rtos_set_event(unsigned identifier);
bool door_rtos_get_event(unsigned identifier);
void door_rtos_clear_event(unsigned identifier);
void door_rtos_kill_current_task(void);
void door_rtos_task_delay(uint32_t ms);
void door_rtos_restart(void);
bool door_rtos_wait_ms_for_event(unsigned identifier, uint32_t ms);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_RTOS_H_
#ifndef DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_SNTP_H_
#define DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_SNTP_H_
void door_sntp_initialize(void);
#endif //DOOR_CONTROLLER_MAIN_INCLUDE_CONNECTORS_SNTP_H_
#ifndef DOOR__WIFI_H_
#define DOOR__WIFI_H_
#include <stdbool.h>
void door_wifi_initialize(void);
void door_wifi_destroy(void);
#endif //DOOR__WIFI_H_
/* WiFi station Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <app/event.h>
#include <app/update.h>
#include <esp_ota_ops.h>
#include "app/token.h"
#include "connectors/wifi.h"
#include "connectors/http.h"
#include "app/api.h"
#include "connectors/sntp.h"
#include "app/diagnostic.h"
#include "connectors/nvs.h"
#include "config.h"
static const char* TAG = "MAIN_MODULE";
static void check_code_task(void* pvParameters)
{
(void)pvParameters;
door_diagnostic_self_test();
door_rtos_wait_forever_for_event(WIFI_CONNECTED_EVENT);
ESP_LOGI(TAG, "Connected to AP, begin connecting to DoorCode API");
bool allowed = door_api_verify_code("123866");
if (allowed)
{
ESP_LOGI(TAG, "We are allowed in the door!");
}
else
{
ESP_LOGI(TAG, "REJECTED");
}
ESP_LOGI(TAG, "Making second request");
door_api_verify_code("123867");
ESP_LOGI(TAG, "Done with second request");
door_http_destroy();
door_wifi_destroy();
ESP_LOGI(TAG, "Finished Request");
door_rtos_kill_current_task();
}
void initialize(void)
{
// Setup non volatile storage
door_nvs_initialize();
// Setup API token (This uses NVS)
door_token_initialize(API_TOKEN);
// Setup the RTOS interfaces such as events
door_rtos_initialize();
// Start up WiFi (WiFi uses tokens and the RTOS events)
door_wifi_initialize();
// Start up the HTTP module (Uses WiFi)
if (door_http_initialize(door_token_get()) != DOOR_HTTP_SUCCESS) {
// Cannot start if we don't have http
esp_restart();
}
// Setup SNTP time syncing (Uses WiFi and the RTOS events)
door_sntp_initialize();
}
void start(void)
{
xTaskCreate(&check_code_task, "http_task", 8192, NULL, 5, NULL);
xTaskCreate(&update_task, "update_task", 1024 * 8, NULL, 8, NULL);
}
void app_main()
{
// Initialize all the modules
initialize();
// Do application rollback process
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
// run diagnostic function ...
bool diagnostic_is_ok = door_diagnostic_self_test();
if (diagnostic_is_ok) {
ESP_LOGI(TAG, "Diagnostics completed successfully! Continuing execution ...");
esp_ota_mark_app_valid_cancel_rollback();
} else {
ESP_LOGE(TAG, "Diagnostics failed! Start rollback to the previous version ...");
esp_ota_mark_app_invalid_rollback_and_reboot();
}
}
}
// Start all tasks
start();
}
#include <string.h>
#include "app/api.h"
#include "connectors/http.h"
#include "config.h"
#define ACCESS_ENDPOINT API_BASE_PATH "/access/"
#define MAX_CODE_COUNT 500
bool door_api_verify_code(const char* code)
{
// ignore null terminators, having 3 or so more bytes won't hurt anything
#define MAX_DOOR_CODE_PATH_SIZE (MAX_CODE_COUNT + sizeof(ACCESS_ENDPOINT))
if (strlen(code) > MAX_CODE_COUNT)
return false;
char endpoint[MAX_DOOR_CODE_PATH_SIZE] = {0};
strcat(endpoint, ACCESS_ENDPOINT);
strcat(endpoint, code);
return door_http_get(endpoint, NULL) == 200;
#undef MAX_DOOR_CODE_PATH_SIZE
}
#include "app/event.h"
#include <stddef.h>
#include "app/diagnostic.h"
#include "app/api.h"
#include "connectors/rtos.h"
#include "config.h"
// Wait maximum of 20 seconds to connect to the WiFi for the self diagnostic test
#define DOOR_DIAGNOSTIC_CONNECT_TIME_MS (1000 * 20)
#define DOOR_DIAGNOSTIC_TEST_URL "/ping"
bool door_diagnostic_self_test(void)
{
if (!door_rtos_wait_ms_for_event(WIFI_CONNECTED_EVENT, DOOR_DIAGNOSTIC_CONNECT_TIME_MS))
{
return false;
}
return door_http_get(API_BASE_PATH DOOR_DIAGNOSTIC_TEST_URL, NULL) == HTTP_SUCCESS;
}
#include <assert.h>
#include "app/random.h"
#include "connectors/random.h"
uint32_t door_random_get_random_between(uint32_t min, uint32_t max)
{
assert(max > min);
// Not perfectly uniform, but it's ok for what we're using this for.
return (door_random_get_random_number() % (max - min)) + min;
}
#include "app/state.h"
#include <stdbool.h>
static bool done;
void state_task(void* parameters)
{
(void)parameters;
while (!done) {
}
}
#include "app/event.h"
#include "time.h"
int door_time_get_current_hour(void)
{
time_t now;
struct tm time_info;
if (!door_rtos_get_event(TIME_SYNCED_EVENT))
{
return -1;
}
time(&now);
localtime_r(&now, &time_info);
return time_info.tm_hour;
}
#include <stdlib.h>
#include "app/token.h"
#include "connectors/nvs.h"
#define API_TOKEN_NVS_KEY "API_TOKEN"
static const char* door_token;
void door_token_initialize(const char* default_token)
{
int ret = door_nvs_get_string(API_TOKEN_NVS_KEY, &door_token);
if (ret != DOOR_NVS_SUCCESS)
{
door_token_set(default_token);
}
}
void door_token_set(const char* token)
{
// We always set it locally for the case that it is changed, but for some reason does not save to NVS.
// That way we can continue operation until next reboot (Hopefully due to OTA which might fix the bug).
free((void*)door_token);
door_token = token;
door_nvs_set_string(API_TOKEN_NVS_KEY, token);
}
const char* door_token_get(void)
{
return door_token;
}
#include "connectors/http.h"
#include "app/token.h"
#include "app/update.h"
#include "app/event.h"
#include "config.h"