diff --git a/firmware/esp32/CMakeLists.txt b/firmware/esp32/CMakeLists.txt new file mode 100644 index 0000000..7fb2309 --- /dev/null +++ b/firmware/esp32/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(plant-manager) diff --git a/firmware/esp32/Dockerfile b/firmware/esp32/Dockerfile new file mode 100644 index 0000000..afaa728 --- /dev/null +++ b/firmware/esp32/Dockerfile @@ -0,0 +1,48 @@ +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + wget \ + flex \ + bison \ + gperf \ + python3 \ + python3-pip \ + python3-venv \ + cmake \ + ninja-build \ + ccache \ + libffi-dev \ + libssl-dev \ + dfu-util \ + libusb-1.0-0 \ + && apt-get autoremove + +WORKDIR /usr/src/ + +ENV IDF_PATH=/usr/src/esp/esp-idf +ENV IDF_PATH_FORCE=1 + +RUN mkdir -p /usr/src/esp/firmware +RUN git clone --recursive https://github.com/espressif/esp-idf.git $IDF_PATH + +RUN cd $IDF_PATH && ./install.sh esp32c3 + +COPY . /usr/src/esp/firmware/ + +WORKDIR /usr/src/esp/firmware/ + +# Create sdkconfig.defaults with correct flash settings +RUN echo "CONFIG_ESPTOOLPY_FLASHMODE=dio" > sdkconfig.defaults && \ + echo "CONFIG_ESPTOOLPY_FLASHFREQ=40m" >> sdkconfig.defaults && \ + echo "CONFIG_ESPTOOLPY_FLASHSIZE=4MB" >> sdkconfig.defaults + +RUN rm -rf build && /bin/bash -c "source $IDF_PATH/export.sh && idf.py set-target esp32c3 && idf.py build" + +RUN mkdir -p /usr/src/build && \ + cp build/bootloader/bootloader.bin /usr/src/build/ && \ + cp build/partition_table/partition-table.bin /usr/src/build/ && \ + cp build/plant-manager.bin /usr/src/build/ diff --git a/firmware/esp32/build-esp32.sh b/firmware/esp32/build-esp32.sh new file mode 100755 index 0000000..86f46a0 --- /dev/null +++ b/firmware/esp32/build-esp32.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e + +IMAGE_NAME="esp32-builder" +SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +DOCKERFILE="$SCRIPT_DIR/Dockerfile" +BUILD_CTX="$SCRIPT_DIR" +DEST_DIR="$SCRIPT_DIR/build" + +docker build -t "$IMAGE_NAME" -f "$DOCKERFILE" "$BUILD_CTX" + +mkdir -p "$DEST_DIR" + +docker run --rm -v "$DEST_DIR":/output "$IMAGE_NAME" cp -r /usr/src/build/. /output/ + +#echo "Erasing flash..." +#esptool --chip esp32c3 --port /dev/tty.usbmodem101 erase-flash + +esptool --chip esp32c3 --port /dev/tty.usbmodem101 --baud 115200 write-flash \ + --flash-mode dio \ + --flash-freq 40m \ + --flash-size 4MB \ + 0x0000 "$DEST_DIR/bootloader.bin" \ + 0x8000 "$DEST_DIR/partition-table.bin" \ + 0x10000 "$DEST_DIR/plant-manager.bin" + +screen -fn /dev/tty.usbmodem101 115200 diff --git a/firmware/esp32/include/main.hpp b/firmware/esp32/include/main.hpp new file mode 100644 index 0000000..1433a50 --- /dev/null +++ b/firmware/esp32/include/main.hpp @@ -0,0 +1,9 @@ + #ifdef __cplusplus + extern "C" { + #endif + + extern volatile bool criticalErrorFlag; + + #ifdef __cplusplus + } + #endif diff --git a/firmware/esp32/include/wifi-credentials.h b/firmware/esp32/include/wifi-credentials.h new file mode 100644 index 0000000..271369b --- /dev/null +++ b/firmware/esp32/include/wifi-credentials.h @@ -0,0 +1,8 @@ +#ifndef WIFI_CREDENTIALS_H +#define WIFI_CREDENTIALS_H + +#define SSID "your_wifi_ssid" +#define PASSWORD "your_wifi_password" +#define AUTH_MODE WIFI_AUTH_WPA2_PSK + +#endif diff --git a/firmware/esp32/include/wifi.hpp b/firmware/esp32/include/wifi.hpp new file mode 100644 index 0000000..782fac0 --- /dev/null +++ b/firmware/esp32/include/wifi.hpp @@ -0,0 +1,6 @@ +#ifndef WIFI_HPP +#define WIFI_HPP + +void wifiTask(void *pvParameters); + +#endif diff --git a/firmware/esp32/main/CMakeLists.txt b/firmware/esp32/main/CMakeLists.txt new file mode 100644 index 0000000..6ea9e3e --- /dev/null +++ b/firmware/esp32/main/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_component_register(SRCS "./main.cpp" + "./wifi.cpp" + INCLUDE_DIRS "../include" + REQUIRES esp_wifi + nvs_flash + esp_pm + esp_hw_support + esp_system + freertos + log) diff --git a/firmware/esp32/main/main.cpp b/firmware/esp32/main/main.cpp new file mode 100644 index 0000000..deaad6f --- /dev/null +++ b/firmware/esp32/main/main.cpp @@ -0,0 +1,51 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_system.h" +#include "esp_log.h" +#include "esp_pm.h" +#include "esp_task_wdt.h" +#include "wifi.hpp" + +static const char* TAG = "MAIN"; + +volatile bool criticalErrorFlag = false; + +static void watchdogTask(void *pvParameters) { + esp_task_wdt_config_t twdt_config = { + .timeout_ms = 12000, + .idle_core_mask = 0, + .trigger_panic = true, + }; + ESP_ERROR_CHECK(esp_task_wdt_deinit()); + ESP_ERROR_CHECK(esp_task_wdt_init(&twdt_config)); + + ESP_ERROR_CHECK(esp_task_wdt_add(NULL)); + + const TickType_t keepAlivePeriod = pdMS_TO_TICKS(8000); + + for(;;) { + if (criticalErrorFlag) { + ESP_LOGE(TAG, "Critical error flag raised. Rebooting."); + esp_restart(); + } + + ESP_ERROR_CHECK(esp_task_wdt_reset()); + ESP_LOGI(TAG, "WDT reset"); + + vTaskDelay(keepAlivePeriod); + } +} + +extern "C" void app_main(void) +{ + static const esp_pm_config_t pm_cfg = { + .max_freq_mhz = 160, + .min_freq_mhz = 10, + .light_sleep_enable = true + }; + esp_pm_configure(&pm_cfg); + + xTaskCreate(watchdogTask, "watchdogs", 2048, NULL, configMAX_PRIORITIES-1, NULL); + xTaskCreate(wifiTask, "wifi", 4096, NULL, configMAX_PRIORITIES-4, NULL); +} diff --git a/firmware/esp32/main/wifi.cpp b/firmware/esp32/main/wifi.cpp new file mode 100644 index 0000000..0dc72c8 --- /dev/null +++ b/firmware/esp32/main/wifi.cpp @@ -0,0 +1,92 @@ +#include "freertos/FreeRTOS.h" +#include "freertos/event_groups.h" +#include "esp_task_wdt.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "main.hpp" +#include "wifi-credentials.h" + +#define WIFI_TIMEOUT_TICKS pdMS_TO_TICKS(8000) + +static const char* TAG = "WIFI"; +const int WIFI_CONNECTED_BIT = BIT0; + +EventGroupHandle_t wifi_event_group; + +static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { + if (event_base == WIFI_EVENT) { + switch (event_id) { + case WIFI_EVENT_STA_START: + ESP_LOGI(TAG, "Wi-Fi process started, connecting..."); + + esp_wifi_connect(); + break; + case WIFI_EVENT_STA_DISCONNECTED: { + wifi_event_sta_disconnected_t* disconn = (wifi_event_sta_disconnected_t*) event_data; + ESP_LOGW(TAG, "Disconnected, reason: %d", disconn->reason); + + xEventGroupClearBits(wifi_event_group, WIFI_CONNECTED_BIT); + esp_wifi_connect(); + break; + } + default: + break; + } + } + else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip)); + xEventGroupSetBits(wifi_event_group, WIFI_CONNECTED_BIT); + } +} + + +static void wifi_init_sta(void) { + esp_err_t nvs_err = nvs_flash_init(); + + if (nvs_err == ESP_ERR_NVS_NO_FREE_PAGES || nvs_err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ESP_ERROR_CHECK(nvs_flash_init()); + } + + wifi_event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); + + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + esp_netif_create_default_wifi_sta(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL)); + ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL)); + + wifi_config_t wifi_config{}; + strncpy((char*)wifi_config.sta.ssid, SSID, sizeof(wifi_config.sta.ssid)); + strncpy((char*)wifi_config.sta.password, PASSWORD, sizeof(wifi_config.sta.password)); + wifi_config.sta.threshold.authmode = AUTH_MODE; + wifi_config.sta.pmf_cfg.capable = true; + wifi_config.sta.pmf_cfg.required = false; + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + +void wifiTask(void *pvParameters) { + ESP_LOGI(TAG, "WiFi task started"); + + wifi_init_sta(); + ESP_ERROR_CHECK(esp_task_wdt_add(NULL)); + + for(;;) { + EventBits_t bits = xEventGroupWaitBits(wifi_event_group, WIFI_CONNECTED_BIT, pdFALSE, pdTRUE, WIFI_TIMEOUT_TICKS); + + ESP_ERROR_CHECK(esp_task_wdt_reset()); + ESP_LOGI(TAG, "Wi-Fi task reset"); + vTaskDelay(pdMS_TO_TICKS(4000)); + } +} diff --git a/firmware/esp32/source/main.cpp b/firmware/esp32/source/main.cpp deleted file mode 100644 index 3300397..0000000 --- a/firmware/esp32/source/main.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" - -extern "C" void app_main(void) -{ - -}