From d9196fda4fa05188b393e13bd65edb7f1f7059fc Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Fri, 28 Mar 2025 13:43:10 +0000 Subject: [PATCH] feat: Add native esp-idf template Tested with generating a Vue3 app for esp32, using esp-idf v5.3.2 This is perfectly functional, but there's always room for improvement. Published in the hope it may be useful for others. Note: no examples for esp-idf. Not sure where best to put them. mini example ``` #include #include "your_generated_file.h" void start_http_server(void) { httpd_handle_t httpd; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.max_uri_handlers = SVELTEESP32_COUNT + xxx; LOGI("Starting server on port: '%d'", config.server_port); ESP_ERROR_CHECK(httpd_start(&httpd, &config)); initSvelteStaticFiles(httpd); // any other handlers you need.... } ``` Tested with and without etag, and with and without gzip. Signed-off-by: Karl Palsson --- README.md | 3 + src/commandLine.ts | 3 +- src/cppCode.ts | 21 ++++- src/template_espidf.ts | 187 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 src/template_espidf.ts diff --git a/README.md b/README.md index a913464..b12a90d 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ npx svelteesp32 -e psychic2 -s ../svelteapp/dist -o ../esp32project/svelteesp32. // for ESPAsyncWebServer npx svelteesp32 -e async -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag=true + +// for native esp-idf +npx svelteesp32 -e espidf -s ../svelteapp/dist -o ../esp32project/svelteesp32.h --etag=true ``` During the **translation process**, the processed file details are visible, and at the end, the result shows the ESP's memory allocation (gzip size) diff --git a/src/commandLine.ts b/src/commandLine.ts index c5a33be..ab1d8b5 100644 --- a/src/commandLine.ts +++ b/src/commandLine.ts @@ -3,7 +3,7 @@ import { existsSync, statSync } from 'node:fs'; import { parse } from 'ts-command-line-args'; interface ICopyFilesArguments { - engine: 'psychic' | 'psychic2' | 'async'; + engine: 'psychic' | 'psychic2' | 'async' | 'espidf'; sourcepath: string; outputfile: string; espmethod: string; @@ -23,6 +23,7 @@ export const cmdLine = parse( if (value === 'psychic') return 'psychic'; if (value === 'psychic2') return 'psychic2'; if (value === 'async') return 'async'; + if (value === 'espidf') return 'espidf'; throw new Error(`Invalid engine: ${value}`); }, alias: 'e', diff --git a/src/cppCode.ts b/src/cppCode.ts index 6a7d33e..6752f3d 100644 --- a/src/cppCode.ts +++ b/src/cppCode.ts @@ -2,6 +2,8 @@ import { compile as handlebarsCompile, HelperOptions } from 'handlebars'; import { cmdLine } from './commandLine'; +import { espidfTemplate } from './template_espidf'; + export type CppCodeSource = { filename: string; dataname: string; @@ -577,11 +579,24 @@ void {{methodName}}(AsyncWebServer * server) { {{/each}} }`; + +const getTemplate = (engine: string): string => { + switch (engine) { + case 'psychic': + return psychicTemplate; + case 'psychic2': + return psychic2Template; + case 'espidf': + return espidfTemplate; + default: + return asyncTemplate; + } +} + + let switchValue: string; export const getCppCode = (sources: CppCodeSources, filesByExtension: ExtensionGroups): string => - handlebarsCompile( - cmdLine.engine === 'psychic' ? psychicTemplate : cmdLine.engine === 'psychic2' ? psychic2Template : asyncTemplate - )( + handlebarsCompile(getTemplate(cmdLine.engine))( { commandLine: process.argv.slice(2).join(' '), now: `${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}`, diff --git a/src/template_espidf.ts b/src/template_espidf.ts new file mode 100644 index 0000000..8950246 --- /dev/null +++ b/src/template_espidf.ts @@ -0,0 +1,187 @@ +export const espidfTemplate = ` +//engine: espidf +//cmdline: {{{commandLine}}} +{{#if created }} +//created: {{now}} +{{/if}} +// + +{{#switch etag}} +{{#case "true"}} +#ifdef {{definePrefix}}_ENABLE_ETAG +#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched ON +#endif +{{/case}} +{{#case "false"}} +#ifdef {{definePrefix}}_ENABLE_ETAG +#warning {{definePrefix}}_ENABLE_ETAG has no effect because it is permanently switched OFF +#endif +{{/case}} +{{/switch}} + +{{#switch gzip}} +{{#case "true"}} +#ifdef {{definePrefix}}_ENABLE_GZIP +#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched ON +#endif +{{/case}} +{{#case "false"}} +#ifdef {{definePrefix}}_ENABLE_GZIP +#warning {{definePrefix}}_ENABLE_GZIP has no effect because it is permanently switched OFF +#endif +{{/case}} +{{/switch}} + +// +{{#if version }} +#define {{definePrefix}}_VERSION "{{version}}" +{{/if}} +#define {{definePrefix}}_COUNT {{fileCount}} +#define {{definePrefix}}_SIZE {{fileSize}} +#define {{definePrefix}}_SIZE_GZIP {{fileGzipSize}} + +// +{{#each sources}} +#define {{../definePrefix}}_FILE_{{this.datanameUpperCase}} +{{/each}} + +// +{{#each filesByExtension}} +#define {{../definePrefix}}_{{this.extension}}_FILES {{this.count}} +{{/each}} + +#include +#include +#include + +// +{{#switch gzip}} +{{#case "true"}} + {{#each sources}} +const char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} }; + {{/each}} +{{/case}} +{{#case "false"}} + {{#each sources}} +const char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} }; + {{/each}} +{{/case}} +{{#case "compiler"}} +#ifdef {{definePrefix}}_ENABLE_GZIP + {{#each sources}} +const char datagzip_{{this.dataname}}[{{this.lengthGzip}}] = { {{this.bytesGzip}} }; + {{/each}} +#else + {{#each sources}} +const char data_{{this.dataname}}[{{this.length}}] = { {{this.bytes}} }; + {{/each}} +#endif +{{/case}} +{{/switch}} + +// +{{#switch etag}} +{{#case "true"}} + {{#each sources}} +const char * etag_{{this.dataname}} = "{{this.md5}}"; + {{/each}} +{{/case}} +{{#case "false"}} +{{/case}} +{{#case "compiler"}} +#ifdef {{definePrefix}}_ENABLE_ETAG + {{#each sources}} +const char * etag_{{this.dataname}} = "{{this.md5}}"; + {{/each}} +#endif +{{/case}} +{{/switch}} + +{{#each sources}} + +static esp_err_t file_handler_{{this.datanameUpperCase}} (httpd_req_t *req) +{ + httpd_resp_set_type(req, "{{this.mime}}"); +{{#switch ../gzip}} +{{#case "true"}} +{{#if this.isGzip}} + httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); +{{/if}} +{{/case}} +{{#case "compiler"}} + {{#if this.isGzip}} + #ifdef {{../definePrefix}}_ENABLE_GZIP + httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); + #endif + {{/if}} +{{/case}} +{{/switch}} + +{{#switch ../etag}} +{{#case "true"}} +{{#../cacheTime}} + httpd_resp_set_hdr(req, "Cache-Control", "max-age={{value}}"); +{{/../cacheTime}} +{{^../cacheTime}} + httpd_resp_set_hdr(req, "Cache-Control", "no-cache"); +{{/../cacheTime}} + httpd_resp_set_hdr(req, "ETag", etag_{{this.dataname}}); +{{/case}} +{{#case "compiler"}} + #ifdef {{../definePrefix}}_ENABLE_ETAG +{{#../cacheTime}} + httpd_resp_set_hdr(req, "Cache-Control", "max-age={{value}}"); +{{/../cacheTime}} +{{^../cacheTime}} + httpd_resp_set_hdr(req, "Cache-Control", "no-cache"); +{{/../cacheTime}} + httpd_resp_set_hdr(req, "ETag", etag_{{this.dataname}}); + #endif +{{/case}} +{{/switch}} + +{{#switch ../gzip}} +{{#case "true"}} + httpd_resp_send(req, datagzip_{{this.dataname}}, {{this.lengthGzip}}); +{{/case}} +{{#case "false"}} + httpd_resp_send(req, data_{{this.dataname}}, {{this.length}}); +{{/case}} +{{#case "compiler"}} + #ifdef {{../definePrefix}}_ENABLE_GZIP + httpd_resp_send(req, datagzip_{{this.dataname}}, {{this.lengthGzip}}); + #else + httpd_resp_send(req, data_{{this.dataname}}, {{this.length}}); + #endif +{{/case}} +{{/switch}} + return ESP_OK; +} + +{{#if this.isDefault}} +static const httpd_uri_t route_def_{{this.datanameUpperCase}} = { + .uri = "/", + .method = HTTP_GET, + .handler = file_handler_{{this.datanameUpperCase}}, +}; +{{/if}} + +static const httpd_uri_t route_{{this.datanameUpperCase}} = { + .uri = "/{{this.filename}}", + .method = HTTP_GET, + .handler = file_handler_{{this.datanameUpperCase}}, +}; + +{{/each}} + + + +static inline void {{methodName}}(httpd_handle_t server) { +{{#each sources}} +{{#if this.isDefault}} + httpd_register_uri_handler(server, &route_def_{{this.datanameUpperCase}}); +{{/if}} + httpd_register_uri_handler(server, &route_{{this.datanameUpperCase}}); +{{/each}} + +}`;