diff --git a/.gitignore b/.gitignore index e85163c..8ffba48 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,11 @@ Module.symvers Mkfile.old dkms.conf +/build +/main/build + +/sdkconfig.old + # ---> Nix # Ignore build outputs from performing a nix-build or `nix build` command result diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5409901 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +idf_build_set_property(MINIMAL_BUILD ON) +project(simple) diff --git a/README.md b/README.md index 467b721..e6ae0b3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,20 @@ # hackens-devant +Déploiement: +``` +nix-shell + +# Choisir le SSID/Password à cette étape dans Example Connection Configuration. +# On peut aussi choisir les GPIO dans Example Configuration (TODO changer ce nom) +idf.py menuconfig + +idf.py build + +# Il faut avoir le ESP connecté à cette étape +idf.py flash +``` + +## Requêtes possibles + +- `GET /status` +- `POST /0` (resp `/1`) avec en donnée `1` ou `0` pour changer l'état du pin `0` (resp. `1`) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..656fb1e --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,9 @@ +set(requires esp-tls nvs_flash esp_netif esp_http_server esp_wifi esp_eth esp_driver_gpio) +idf_build_get_property(target IDF_TARGET) + +if(${target} STREQUAL "linux") + list(APPEND requires esp_stubs protocol_examples_common) +endif() +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "." + PRIV_REQUIRES ${requires}) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..6693d8b --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,20 @@ +menu "Example Configuration" + + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config GPIO_OUTPUT_0 + int "GPIO output pin 0" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 8 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C61 + default 18 + help + GPIO pin number to be used as GPIO_OUTPUT_IO_0. + + config GPIO_OUTPUT_1 + int "GPIO output pin 1" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 9 if IDF_TARGET_ESP32C2 || IDF_TARGET_ESP32H2 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C61 + default 19 + help + GPIO pin number to be used as GPIO_OUTPUT_IO_1. +endmenu diff --git a/main/idf_component.yml b/main/idf_component.yml new file mode 100644 index 0000000..c17c116 --- /dev/null +++ b/main/idf_component.yml @@ -0,0 +1,7 @@ +dependencies: + protocol_examples_common: + path: ${IDF_PATH}/examples/common_components/protocol_examples_common + esp_stubs: + path: ${IDF_PATH}/examples/protocols/linux_stubs/esp_stubs + rules: + - if: "target in [linux]" diff --git a/main/main.c b/main/main.c new file mode 100644 index 0000000..a309305 --- /dev/null +++ b/main/main.c @@ -0,0 +1,263 @@ +/* Simple HTTP Server 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 +#include +#include +#include +#include +#include +#include +#include "esp_netif.h" +#include "protocol_examples_common.h" +#include "protocol_examples_utils.h" +#include "esp_tls_crypto.h" +#include +#include "esp_event.h" +#include "esp_netif.h" +#include "esp_tls.h" +#include "esp_check.h" +#include "driver/gpio.h" + +#if !CONFIG_IDF_TARGET_LINUX +#include +#include +#include "nvs_flash.h" +#include "esp_eth.h" +#endif // !CONFIG_IDF_TARGET_LINUX + +#define EXAMPLE_HTTP_QUERY_KEY_MAX_LEN (64) + +// GPIO pin definition. Use menu config to choose GPIO +#define GPIO_OUTPUT_IO_0 CONFIG_GPIO_OUTPUT_0 +#define GPIO_OUTPUT_IO_1 CONFIG_GPIO_OUTPUT_1 +#define GPIO_OUTPUT_PIN_SEL ((1ULL<content_len; + + while (remaining > 0) { + /* Read the data for the request */ + if ((ret = httpd_req_recv(req, buf, + MIN(remaining, sizeof(buf)))) <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + /* Retry receiving if timeout occurred */ + continue; + } + return ESP_FAIL; + } + + bool set = false; + if (buf[0] == '1') { + status_one = true; + set = true; + } else if (buf[0] == '0') { + status_one = false; + set = true; + }; + if (set) { + ESP_LOGI(TAG, "RECEIVED DATA"); + gpio_set_level(GPIO_OUTPUT_IO_0, status_one); + httpd_resp_send(req, NULL, 0); // TODO check simplification + return ESP_OK; + }; + remaining -= ret; + } + + + // Return 400 if found no info for status + httpd_resp_set_status(req, HTTPD_400); + httpd_resp_send(req, NULL, 0); + return ESP_OK; +} + +static const httpd_uri_t post_one = { + .uri = "/0", + .method = HTTP_POST, + .handler = post_one_handler, + .user_ctx = NULL +}; + + +/* An HTTP POST handler */ +static esp_err_t post_two_handler(httpd_req_t *req) +{ + char buf[1]; + int ret, remaining = req->content_len; + + while (remaining > 0) { + /* Read the data for the request */ + if ((ret = httpd_req_recv(req, buf, + MIN(remaining, sizeof(buf)))) <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + /* Retry receiving if timeout occurred */ + continue; + } + return ESP_FAIL; + } + + bool set = false; + if (buf[0] == '1') { + status_two = true; + set = true; + } else if (buf[0] == '0') { + status_two = false; + set = true; + }; + if (set) { + ESP_LOGI(TAG, "RECEIVED DATA"); + gpio_set_level(GPIO_OUTPUT_IO_1, status_two); + httpd_resp_send(req, NULL, 0); // TODO check simplification + return ESP_OK; + }; + remaining -= ret; + } + + + // Return 400 if found no info for status + httpd_resp_set_status(req, HTTPD_400); + httpd_resp_send(req, NULL, 0); + return ESP_OK; +} + +static const httpd_uri_t post_two = { + .uri = "/1", + .method = HTTP_POST, + .handler = post_two_handler, + .user_ctx = NULL +}; + +static httpd_handle_t start_webserver(void) +{ + httpd_handle_t server = NULL; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.lru_purge_enable = true; + + // Start the httpd server + ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); + if (httpd_start(&server, &config) == ESP_OK) { + // Set URI handlers + ESP_LOGI(TAG, "Registering URI handlers"); + httpd_register_uri_handler(server, &status); + httpd_register_uri_handler(server, &post_one); + httpd_register_uri_handler(server, &post_two); + return server; + } + + ESP_LOGI(TAG, "Error starting server!"); + return NULL; +} + +static esp_err_t stop_webserver(httpd_handle_t server) +{ + // Stop the httpd server + return httpd_stop(server); +} + +static void disconnect_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + httpd_handle_t* server = (httpd_handle_t*) arg; + if (*server) { + ESP_LOGI(TAG, "Stopping webserver"); + if (stop_webserver(*server) == ESP_OK) { + *server = NULL; + } else { + ESP_LOGE(TAG, "Failed to stop http server"); + } + } +} + +static void connect_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) +{ + httpd_handle_t* server = (httpd_handle_t*) arg; + if (*server == NULL) { + ESP_LOGI(TAG, "Starting webserver"); + *server = start_webserver(); + } +} + +void app_main(void) +{ + static httpd_handle_t server = NULL; + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + //zero-initialize the config structure. + gpio_config_t io_conf = {}; + //disable interrupt + io_conf.intr_type = GPIO_INTR_DISABLE; + //set as output mode + io_conf.mode = GPIO_MODE_OUTPUT; + //bit mask of the pins that you want to set,e.g.GPIO18/19 + io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; + //disable pull-down mode + io_conf.pull_down_en = 0; + //disable pull-up mode + io_conf.pull_up_en = 0; + //configure GPIO with the given settings + gpio_config(&io_conf); + + gpio_set_level(GPIO_OUTPUT_IO_0, 0); + gpio_set_level(GPIO_OUTPUT_IO_1, 0); + + printf("Minimum free heap size: %"PRIu32" bytes\n", esp_get_minimum_free_heap_size()); + + /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig. + * Read "Establishing Wi-Fi or Ethernet Connection" section in + * examples/protocols/README.md for more information about this function. + */ + ESP_ERROR_CHECK(example_connect()); + + /* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected, + * and re-start it upon connection. + */ + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server)); + + /* Start the server for the first time */ + server = start_webserver(); + while (server) { + sleep(5); + } + +} diff --git a/npins/default.nix b/npins/default.nix new file mode 100644 index 0000000..5e7d086 --- /dev/null +++ b/npins/default.nix @@ -0,0 +1,80 @@ +# Generated by npins. Do not modify; will be overwritten regularly +let + data = builtins.fromJSON (builtins.readFile ./sources.json); + version = data.version; + + mkSource = + spec: + assert spec ? type; + let + path = + if spec.type == "Git" then + mkGitSource spec + else if spec.type == "GitRelease" then + mkGitSource spec + else if spec.type == "PyPi" then + mkPyPiSource spec + else if spec.type == "Channel" then + mkChannelSource spec + else + builtins.throw "Unknown source type ${spec.type}"; + in + spec // { outPath = path; }; + + mkGitSource = + { + repository, + revision, + url ? null, + hash, + branch ? null, + ... + }: + assert repository ? type; + # At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository + # In the latter case, there we will always be an url to the tarball + if url != null then + (builtins.fetchTarball { + inherit url; + sha256 = hash; # FIXME: check nix version & use SRI hashes + }) + else + assert repository.type == "Git"; + let + urlToName = + url: rev: + let + matched = builtins.match "^.*/([^/]*)(\\.git)?$" repository.url; + + short = builtins.substring 0 7 rev; + + appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else ""; + in + "${if matched == null then "source" else builtins.head matched}${appendShort}"; + name = urlToName repository.url revision; + in + builtins.fetchGit { + url = repository.url; + rev = revision; + inherit name; + # hash = hash; + }; + + mkPyPiSource = + { url, hash, ... }: + builtins.fetchurl { + inherit url; + sha256 = hash; + }; + + mkChannelSource = + { url, hash, ... }: + builtins.fetchTarball { + inherit url; + sha256 = hash; + }; +in +if version == 3 then + builtins.mapAttrs (_: mkSource) data.pins +else + throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`" diff --git a/npins/sources.json b/npins/sources.json new file mode 100644 index 0000000..711383c --- /dev/null +++ b/npins/sources.json @@ -0,0 +1,22 @@ +{ + "pins": { + "nixpkgs": { + "type": "Channel", + "name": "nixpkgs-unstable", + "url": "https://releases.nixos.org/nixpkgs/nixpkgs-25.05pre715208.2c15aa59df00/nixexprs.tar.xz", + "hash": "1grhv1ryj80261zf0k92jqpvlwbna2r2r2a48as4fgcihv8sn076" + }, + "nixpkgs-esp-dev": { + "type": "Git", + "repository": { + "type": "Git", + "url": "https://github.com/mirrexagon/nixpkgs-esp-dev.git" + }, + "branch": "master", + "revision": "31ee58005f43e93a6264e3667c9bf5c31b368733", + "url": null, + "hash": "1pzbv8ywa1k3w8kivz9sglbswv1mjj1l5jxnrnmk81i5wxc4vg2w" + } + }, + "version": 3 +} \ No newline at end of file diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..4dce308 --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_GPIO_OUTPUT_0=0 +CONFIG_GPIO_OUTPUT_1=1 diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..0a8bc58 --- /dev/null +++ b/shell.nix @@ -0,0 +1,13 @@ +{ + sources ? import ./npins, + overlay ? import (sources.nixpkgs-esp-dev + /overlay.nix), + pkgs ? import sources.nixpkgs { overlays = [ overlay ]; }, +}: +pkgs.mkShell { + name = "hackes-devant"; + + buildInputs = with pkgs; [ + esp-idf-esp32c3 + ]; +} +