├── ChartJs ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ └── root.html └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c ├── EpochJs ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ └── root.html └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c ├── HorizontalLinearGauge ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ └── root.html └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c ├── LICENSE ├── PlotlyChart ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ └── root.html └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c ├── PlotlyGauge ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ └── root.html └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c ├── README.md ├── RadialGauge ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ └── root.html └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c ├── SevenSegment ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html │ ├── error.html │ ├── favicon.ico │ ├── main.css │ ├── main.js │ ├── root.html │ └── seven_segment_display.js └── main │ ├── CMakeLists.txt │ ├── Kconfig.projbuild │ ├── component.mk │ ├── idf_component.yml │ ├── main.c │ ├── web_client.c │ └── web_server.c └── VerticalLinearGauge ├── CMakeLists.txt ├── LICENSE ├── README.md ├── html ├── error.html ├── favicon.ico ├── main.css ├── main.js └── root.html └── main ├── CMakeLists.txt ├── Kconfig.projbuild ├── component.mk ├── idf_component.yml ├── main.c ├── web_client.c └── web_server.c /ChartJs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(chartjs-plugin-streaming) 7 | -------------------------------------------------------------------------------- /ChartJs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ChartJs/README.md: -------------------------------------------------------------------------------- 1 | # Chart display using chart.js 2 | ![Image](https://github.com/user-attachments/assets/bf142ab9-bc34-4118-9643-452733157c5c) 3 | 4 | I used [this](https://nagix.github.io/chartjs-plugin-streaming/1.9.0/) for chart display. 5 | You can easily change the chart design. 6 | 7 | Document is [here](https://www.chartjs.org/docs/latest/). 8 | 9 | -------------------------------------------------------------------------------- /ChartJs/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /ChartJs/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/ChartJs/html/favicon.ico -------------------------------------------------------------------------------- /ChartJs/html/main.css: -------------------------------------------------------------------------------- 1 | .button{ 2 | width: 100px; 3 | padding: 10px; 4 | box-sizing: border-box; 5 | border: 1px solid #68779a; 6 | background: #cbe8fa; 7 | cursor: pointer; 8 | } 9 | -------------------------------------------------------------------------------- /ChartJs/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | 8 | function pauseDatasetPush(name) { 9 | console.log('pauseDatasetPush'); 10 | sendText(name); 11 | 12 | var button = document.getElementById("pauseDataset"); 13 | button.style.backgroundColor = "lightblue"; 14 | } 15 | 16 | function fixedDatasetPush(name) { 17 | console.log('fixedDatasetPush'); 18 | sendText(name); 19 | 20 | var button = document.getElementById("fixedDataset"); 21 | button.style.backgroundColor = "lightblue"; 22 | } 23 | 24 | 25 | function resumeDatasetPush(name) { 26 | console.log('resumeDatasetPush'); 27 | sendText(name); 28 | 29 | var button = document.getElementById("pauseDataset"); 30 | button.style.backgroundColor = ""; 31 | var button = document.getElementById("fixedDataset"); 32 | button.style.backgroundColor = ""; 33 | } 34 | 35 | function sendText(name) { 36 | console.log('sendText'); 37 | var data = {}; 38 | data["id"] = name; 39 | console.log('data=', data); 40 | json_data = JSON.stringify(data); 41 | console.log('json_data=' + json_data); 42 | websocket.send(json_data); 43 | } 44 | 45 | websocket.onopen = function(evt) { 46 | console.log('WebSocket connection opened'); 47 | var data = {}; 48 | data["id"] = "init"; 49 | console.log('data=', data); 50 | json_data = JSON.stringify(data); 51 | console.log('json_data=' + json_data); 52 | websocket.send(json_data); 53 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 54 | } 55 | 56 | websocket.onmessage = function(evt) { 57 | var msg = evt.data; 58 | console.log("msg=" + msg); 59 | var values = msg.split('\4'); // \4 is EOT 60 | console.log("values=" + values); 61 | switch(values[0]) { 62 | case 'HEAD': 63 | console.log("HEAD values[1]=" + values[1]); 64 | var h1 = document.getElementById( 'header' ); 65 | h1.textContent = values[1]; 66 | break; 67 | 68 | case 'METER': 69 | console.log("METER values[1]=" + values[1]); 70 | console.log("METER values[2]=" + values[2]); 71 | console.log("METER values[3]=" + values[3]); 72 | console.log("config=" + Object.keys(config)); 73 | console.log("config.data.datasets=" + Object.keys(config.data.datasets)); 74 | console.log("config.data.datasets.length=", config.data.datasets.length); 75 | console.log("config.data.datasets[0]=" + Object.keys(config.data.datasets[0])); 76 | console.log("config.data.datasets[0].label=", config.data.datasets[0].label); 77 | console.log("config.data.datasets[0].backgroundColor=", config.data.datasets[0].backgroundColor); 78 | console.log("config.data.datasets[0].borderColor=", config.data.datasets[0].borderColor); 79 | if (values[1] != "") { 80 | config.data.datasets[0].label=values[1]; 81 | config.data.datasets[0].backgroundColor = color(chartColors.red).alpha(0.5).rgbString(); 82 | config.data.datasets[0].borderColor = chartColors.red; 83 | meter1 = 1; 84 | } 85 | if (values[2] != "") { 86 | config.data.datasets[1].label=values[2]; 87 | config.data.datasets[1].backgroundColor = color(chartColors.green).alpha(0.5).rgbString(); 88 | config.data.datasets[1].borderColor = chartColors.green; 89 | meter2 = 1; 90 | } 91 | if (values[3] != "") { 92 | config.data.datasets[2].label=values[3]; 93 | config.data.datasets[2].backgroundColor = color(chartColors.blue).alpha(0.5).rgbString(); 94 | config.data.datasets[2].borderColor = chartColors.blue; 95 | meter3 = 1; 96 | } 97 | window.myChart.update(); 98 | 99 | case 'DATA': 100 | console.log("DATA values[1]=" + values[1]); 101 | var voltage1 = parseInt(values[1], 10); 102 | var now = Date.now(); 103 | window.myChart.data.datasets[0].data.push({ 104 | x: now, 105 | y: voltage1 106 | }); 107 | if (meter2) { 108 | console.log("DATA values[2]=" + values[2]); 109 | var voltage2 = parseInt(values[2], 10); 110 | var now = Date.now(); 111 | window.myChart.data.datasets[1].data.push({ 112 | x: now, 113 | y: voltage2 114 | }); 115 | } 116 | if (meter3) { 117 | console.log("DATA values[3]=" + values[3]); 118 | var voltage3 = parseInt(values[3], 10); 119 | var now = Date.now(); 120 | window.myChart.data.datasets[2].data.push({ 121 | x: now, 122 | y: voltage3 123 | }); 124 | } 125 | /* 126 | var counter = 1; 127 | window.myChart.data.datasets.forEach(function(dataset) { 128 | var val = parseInt(values[counter], 10); 129 | console.log("counter=%d val=%d", counter, val); 130 | dataset.data.push({ 131 | x: now, 132 | //y: randomScalingFactor() 133 | y: val 134 | }); 135 | counter++; 136 | }); 137 | */ 138 | 139 | default: 140 | break; 141 | } 142 | } 143 | 144 | websocket.onclose = function(evt) { 145 | console.log('Websocket connection closed'); 146 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 147 | } 148 | 149 | websocket.onerror = function(evt) { 150 | console.log('Websocket error: ' + evt); 151 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 152 | } 153 | -------------------------------------------------------------------------------- /ChartJs/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | ChartJs Chart 9 | 10 | 11 |

12 | 13 |
14 | 15 | 22 |
23 | 24 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /ChartJs/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /ChartJs/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /ChartJs/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /ChartJs/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /ChartJs/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /ChartJs/main/web_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/message_buffer.h" 16 | #include "esp_log.h" 17 | 18 | #include "websocket_server.h" 19 | 20 | static QueueHandle_t client_queue; 21 | extern MessageBufferHandle_t xMessageBufferToClient; 22 | 23 | const static int client_queue_size = 10; 24 | 25 | // handles websocket events 26 | void websocket_callback(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) { 27 | const static char* TAG = "websocket_callback"; 28 | //int value; 29 | 30 | switch(type) { 31 | case WEBSOCKET_CONNECT: 32 | ESP_LOGI(TAG,"client %i connected!",num); 33 | break; 34 | case WEBSOCKET_DISCONNECT_EXTERNAL: 35 | ESP_LOGI(TAG,"client %i sent a disconnect message",num); 36 | break; 37 | case WEBSOCKET_DISCONNECT_INTERNAL: 38 | ESP_LOGI(TAG,"client %i was disconnected",num); 39 | break; 40 | case WEBSOCKET_DISCONNECT_ERROR: 41 | ESP_LOGI(TAG,"client %i was disconnected due to an error",num); 42 | break; 43 | case WEBSOCKET_TEXT: 44 | if(len) { // if the message length was greater than zero 45 | ESP_LOGI(TAG, "got message length %i: %s", (int)len, msg); 46 | size_t xBytesSent = xMessageBufferSendFromISR(xMessageBufferToClient, msg, len, NULL); 47 | if (xBytesSent != len) { 48 | ESP_LOGE(TAG, "xMessageBufferSend fail"); 49 | } 50 | } 51 | break; 52 | case WEBSOCKET_BIN: 53 | ESP_LOGI(TAG,"client %i sent binary message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 54 | break; 55 | case WEBSOCKET_PING: 56 | ESP_LOGI(TAG,"client %i pinged us with message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 57 | break; 58 | case WEBSOCKET_PONG: 59 | ESP_LOGI(TAG,"client %i responded to the ping",num); 60 | break; 61 | } 62 | } 63 | 64 | // serves any clients 65 | static void http_server(struct netconn *conn) { 66 | const static char* TAG = "http_server"; 67 | const static char HTML_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n"; 68 | const static char ERROR_HEADER[] = "HTTP/1.1 404 Not Found\nContent-type: text/html\n\n"; 69 | const static char JS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n"; 70 | const static char CSS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/css\n\n"; 71 | //const static char PNG_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/png\n\n"; 72 | const static char ICO_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/x-icon\n\n"; 73 | //const static char PDF_HEADER[] = "HTTP/1.1 200 OK\nContent-type: application/pdf\n\n"; 74 | //const static char EVENT_HEADER[] = "HTTP/1.1 200 OK\nContent-Type: text/event-stream\nCache-Control: no-cache\nretry: 3000\n\n"; 75 | struct netbuf* inbuf; 76 | static char* buf; 77 | static uint16_t buflen; 78 | static err_t err; 79 | 80 | // default page 81 | extern const uint8_t root_html_start[] asm("_binary_root_html_start"); 82 | extern const uint8_t root_html_end[] asm("_binary_root_html_end"); 83 | const uint32_t root_html_len = root_html_end - root_html_start; 84 | 85 | // main.js 86 | extern const uint8_t main_js_start[] asm("_binary_main_js_start"); 87 | extern const uint8_t main_js_end[] asm("_binary_main_js_end"); 88 | const uint32_t main_js_len = main_js_end - main_js_start; 89 | 90 | // main.css 91 | extern const uint8_t main_css_start[] asm("_binary_main_css_start"); 92 | extern const uint8_t main_css_end[] asm("_binary_main_css_end"); 93 | const uint32_t main_css_len = main_css_end - main_css_start; 94 | 95 | // favicon.ico 96 | extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start"); 97 | extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end"); 98 | const uint32_t favicon_ico_len = favicon_ico_end - favicon_ico_start; 99 | 100 | // error page 101 | extern const uint8_t error_html_start[] asm("_binary_error_html_start"); 102 | extern const uint8_t error_html_end[] asm("_binary_error_html_end"); 103 | const uint32_t error_html_len = error_html_end - error_html_start; 104 | 105 | netconn_set_recvtimeout(conn,1000); // allow a connection timeout of 1 second 106 | ESP_LOGI(TAG,"reading from client..."); 107 | err = netconn_recv(conn, &inbuf); 108 | ESP_LOGI(TAG,"read from client"); 109 | if(err==ERR_OK) { 110 | netbuf_data(inbuf, (void**)&buf, &buflen); 111 | if(buf) { 112 | 113 | ESP_LOGD(TAG, "buf=[%s]", buf); 114 | // default page 115 | if (strstr(buf,"GET / ") 116 | && !strstr(buf,"Upgrade: websocket")) { 117 | ESP_LOGI(TAG,"Sending /"); 118 | netconn_write(conn, HTML_HEADER, sizeof(HTML_HEADER)-1,NETCONN_NOCOPY); 119 | netconn_write(conn, root_html_start,root_html_len,NETCONN_NOCOPY); 120 | netconn_close(conn); 121 | netconn_delete(conn); 122 | netbuf_delete(inbuf); 123 | } 124 | 125 | // default page websocket 126 | else if(strstr(buf,"GET / ") 127 | && strstr(buf,"Upgrade: websocket")) { 128 | ESP_LOGI(TAG,"Requesting websocket on /"); 129 | ws_server_add_client(conn,buf,buflen,"/",websocket_callback); 130 | netbuf_delete(inbuf); 131 | } 132 | 133 | else if(strstr(buf,"GET /main.js ")) { 134 | ESP_LOGI(TAG,"Sending /main.js"); 135 | netconn_write(conn, JS_HEADER, sizeof(JS_HEADER)-1,NETCONN_NOCOPY); 136 | netconn_write(conn, main_js_start, main_js_len,NETCONN_NOCOPY); 137 | netconn_close(conn); 138 | netconn_delete(conn); 139 | netbuf_delete(inbuf); 140 | } 141 | 142 | else if(strstr(buf,"GET /main.css ")) { 143 | ESP_LOGI(TAG,"Sending /main.css"); 144 | netconn_write(conn, CSS_HEADER, sizeof(CSS_HEADER)-1,NETCONN_NOCOPY); 145 | netconn_write(conn, main_css_start, main_css_len,NETCONN_NOCOPY); 146 | netconn_close(conn); 147 | netconn_delete(conn); 148 | netbuf_delete(inbuf); 149 | } 150 | 151 | else if(strstr(buf,"GET /favicon.ico ")) { 152 | ESP_LOGI(TAG,"Sending favicon.ico"); 153 | netconn_write(conn,ICO_HEADER,sizeof(ICO_HEADER)-1,NETCONN_NOCOPY); 154 | netconn_write(conn,favicon_ico_start,favicon_ico_len,NETCONN_NOCOPY); 155 | netconn_close(conn); 156 | netconn_delete(conn); 157 | netbuf_delete(inbuf); 158 | } 159 | 160 | else if(strstr(buf,"GET /")) { 161 | ESP_LOGE(TAG,"Unknown request, sending error page: %s",buf); 162 | netconn_write(conn, ERROR_HEADER, sizeof(ERROR_HEADER)-1,NETCONN_NOCOPY); 163 | netconn_write(conn, error_html_start, error_html_len,NETCONN_NOCOPY); 164 | netconn_close(conn); 165 | netconn_delete(conn); 166 | netbuf_delete(inbuf); 167 | } 168 | 169 | else { 170 | ESP_LOGE(TAG,"Unknown request"); 171 | netconn_close(conn); 172 | netconn_delete(conn); 173 | netbuf_delete(inbuf); 174 | } 175 | } 176 | else { 177 | ESP_LOGI(TAG,"Unknown request (empty?...)"); 178 | netconn_close(conn); 179 | netconn_delete(conn); 180 | netbuf_delete(inbuf); 181 | } 182 | } 183 | else { // if err==ERR_OK 184 | ESP_LOGI(TAG,"error on read, closing connection"); 185 | netconn_close(conn); 186 | netconn_delete(conn); 187 | netbuf_delete(inbuf); 188 | } 189 | } 190 | 191 | // receives clients from queue, handles them 192 | void server_handle_task(void* pvParameters) { 193 | const static char* TAG = "server_handle_task"; 194 | struct netconn* conn; 195 | ESP_LOGI(TAG,"task starting"); 196 | for(;;) { 197 | xQueueReceive(client_queue,&conn,portMAX_DELAY); 198 | if(!conn) continue; 199 | http_server(conn); 200 | } 201 | vTaskDelete(NULL); 202 | } 203 | 204 | // handles clients when they first connect. passes to a queue 205 | void server_task(void* pvParameters) { 206 | const static char* TAG = "server_task"; 207 | char *task_parameter = (char *)pvParameters; 208 | ESP_LOGI(TAG, "Start task_parameter=%s", task_parameter); 209 | char url[64]; 210 | sprintf(url, "http://%s", task_parameter); 211 | ESP_LOGI(TAG, "Starting server on %s", url); 212 | 213 | struct netconn *conn, *newconn; 214 | static err_t err; 215 | client_queue = xQueueCreate(client_queue_size,sizeof(struct netconn*)); 216 | configASSERT( client_queue ); 217 | 218 | 219 | UBaseType_t PriorityGet = uxTaskPriorityGet(NULL); 220 | ESP_LOGI(TAG, "PriorityGet=%d", PriorityGet); 221 | xTaskCreate(&server_handle_task, "server_handle_task", 1024*3, NULL, PriorityGet, NULL); 222 | 223 | 224 | conn = netconn_new(NETCONN_TCP); 225 | netconn_bind(conn,NULL,80); 226 | netconn_listen(conn); 227 | ESP_LOGI(TAG,"server listening"); 228 | do { 229 | err = netconn_accept(conn, &newconn); 230 | ESP_LOGI(TAG,"new client"); 231 | if(err == ERR_OK) { 232 | xQueueSendToBack(client_queue,&newconn,portMAX_DELAY); 233 | //http_server(newconn); 234 | } 235 | } while(err == ERR_OK); 236 | netconn_close(conn); 237 | netconn_delete(conn); 238 | ESP_LOGE(TAG,"task ending, rebooting board"); 239 | esp_restart(); 240 | } 241 | -------------------------------------------------------------------------------- /EpochJs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /EpochJs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /EpochJs/README.md: -------------------------------------------------------------------------------- 1 | # Chart display using epoch.js 2 | ![Image](https://github.com/user-attachments/assets/a2376a04-1fe2-49b9-bd0d-b98ecf623513) 3 | 4 | I used [this](https://epochjs.github.io/epoch/real-time/) for chart display. 5 | You can easily change the chart design. 6 | 7 | 8 | -------------------------------------------------------------------------------- /EpochJs/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /EpochJs/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/EpochJs/html/favicon.ico -------------------------------------------------------------------------------- /EpochJs/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /EpochJs/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | 8 | 9 | /* 10 | function sendText(name) { 11 | console.log('sendText'); 12 | var data = {}; 13 | data["id"] = name; 14 | console.log('data=', data); 15 | json_data = JSON.stringify(data); 16 | console.log('json_data=' + json_data); 17 | websocket.send(json_data); 18 | } 19 | */ 20 | 21 | websocket.onopen = function(evt) { 22 | console.log('WebSocket connection opened'); 23 | var data = {}; 24 | data["id"] = "init"; 25 | console.log('data=', data); 26 | json_data = JSON.stringify(data); 27 | console.log('json_data=' + json_data); 28 | websocket.send(json_data); 29 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 30 | } 31 | 32 | websocket.onmessage = function(evt) { 33 | var msg = evt.data; 34 | console.log("msg=" + msg); 35 | var values = msg.split('\4'); // \4 is EOT 36 | //console.log("values=" + values); 37 | switch(values[0]) { 38 | case 'HEAD': 39 | console.log("HEAD values[1]=" + values[1]); 40 | var h1 = document.getElementById( 'header' ); 41 | h1.textContent = values[1]; 42 | break; 43 | 44 | case 'METER': 45 | //console.log("gauge1=" + Object.keys(gauge1.options)); 46 | //console.log("gauge1.options.units=" + gauge1.options.units); 47 | console.log("METER values[1]=" + values[1]); 48 | console.log("METER values[2]=" + values[2]); 49 | console.log("METER values[3]=" + values[3]); 50 | if (values[1] != "") { 51 | document.getElementById("label1").innerText = values[1] + " [mV]"; 52 | meter1 = 1; 53 | } 54 | if (values[2] != "") { 55 | document.getElementById("label2").innerText = values[2] + " [mV]"; 56 | meter2 = 1; 57 | } else { 58 | document.getElementById("label2").style.display ="none"; 59 | document.getElementById("chart2").style.display ="none"; 60 | } 61 | if (values[3] != "") { 62 | document.getElementById("label3").innerText = values[3] + " [mV]"; 63 | meter3 = 1; 64 | } else { 65 | document.getElementById("label3").style.display ="none"; 66 | document.getElementById("chart3").style.display ="none"; 67 | } 68 | break; 69 | 70 | case 'DATA': 71 | console.log("DATA values[1]=" + values[1]); 72 | var timeVal = getChartTime(); 73 | var voltage1 = parseInt(values[1], 10); 74 | // Set line color 75 | var catname = "category1"; 76 | chart1.getVisibleLayers()[0].className = "layer " + catname; 77 | chart1.push([ {time: timeVal, y: voltage1} ]); 78 | if (meter2) { 79 | console.log("DATA values[2]=" + values[2]); 80 | var voltage2 = parseInt(values[2], 10); 81 | // Set line color 82 | var catname = "category2"; 83 | chart2.getVisibleLayers()[0].className = "layer " + catname; 84 | chart2.push([ {time: timeVal, y: voltage2} ]); 85 | } 86 | if (meter3) { 87 | console.log("DATA values[3]=" + values[3]); 88 | var voltage3 = parseInt(values[3], 10); 89 | // Set line color 90 | var catname = "category3"; 91 | chart3.getVisibleLayers()[0].className = "layer " + catname; 92 | chart3.push([ {time: timeVal, y: voltage3} ]); 93 | } 94 | break; 95 | 96 | default: 97 | break; 98 | } 99 | } 100 | 101 | websocket.onclose = function(evt) { 102 | console.log('Websocket connection closed'); 103 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 104 | } 105 | 106 | websocket.onerror = function(evt) { 107 | console.log('Websocket error: ' + evt); 108 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 109 | } 110 | -------------------------------------------------------------------------------- /EpochJs/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Epoch Chart 11 | 12 | 13 | 14 | 15 | 29 | 30 |

31 | 32 | 33 |
34 | 35 |
36 | 37 |
38 | 39 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /EpochJs/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /EpochJs/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /EpochJs/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /EpochJs/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /EpochJs/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /EpochJs/main/web_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/message_buffer.h" 16 | #include "esp_log.h" 17 | 18 | #include "websocket_server.h" 19 | 20 | static QueueHandle_t client_queue; 21 | extern MessageBufferHandle_t xMessageBufferToClient; 22 | 23 | const static int client_queue_size = 10; 24 | 25 | // handles websocket events 26 | void websocket_callback(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) { 27 | const static char* TAG = "websocket_callback"; 28 | //int value; 29 | 30 | switch(type) { 31 | case WEBSOCKET_CONNECT: 32 | ESP_LOGI(TAG,"client %i connected!",num); 33 | break; 34 | case WEBSOCKET_DISCONNECT_EXTERNAL: 35 | ESP_LOGI(TAG,"client %i sent a disconnect message",num); 36 | break; 37 | case WEBSOCKET_DISCONNECT_INTERNAL: 38 | ESP_LOGI(TAG,"client %i was disconnected",num); 39 | break; 40 | case WEBSOCKET_DISCONNECT_ERROR: 41 | ESP_LOGI(TAG,"client %i was disconnected due to an error",num); 42 | break; 43 | case WEBSOCKET_TEXT: 44 | if(len) { // if the message length was greater than zero 45 | ESP_LOGI(TAG, "got message length %i: %s", (int)len, msg); 46 | size_t xBytesSent = xMessageBufferSendFromISR(xMessageBufferToClient, msg, len, NULL); 47 | if (xBytesSent != len) { 48 | ESP_LOGE(TAG, "xMessageBufferSend fail"); 49 | } 50 | } 51 | break; 52 | case WEBSOCKET_BIN: 53 | ESP_LOGI(TAG,"client %i sent binary message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 54 | break; 55 | case WEBSOCKET_PING: 56 | ESP_LOGI(TAG,"client %i pinged us with message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 57 | break; 58 | case WEBSOCKET_PONG: 59 | ESP_LOGI(TAG,"client %i responded to the ping",num); 60 | break; 61 | } 62 | } 63 | 64 | // serves any clients 65 | static void http_server(struct netconn *conn) { 66 | const static char* TAG = "http_server"; 67 | const static char HTML_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n"; 68 | const static char ERROR_HEADER[] = "HTTP/1.1 404 Not Found\nContent-type: text/html\n\n"; 69 | const static char JS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n"; 70 | const static char CSS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/css\n\n"; 71 | //const static char PNG_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/png\n\n"; 72 | const static char ICO_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/x-icon\n\n"; 73 | //const static char PDF_HEADER[] = "HTTP/1.1 200 OK\nContent-type: application/pdf\n\n"; 74 | //const static char EVENT_HEADER[] = "HTTP/1.1 200 OK\nContent-Type: text/event-stream\nCache-Control: no-cache\nretry: 3000\n\n"; 75 | struct netbuf* inbuf; 76 | static char* buf; 77 | static uint16_t buflen; 78 | static err_t err; 79 | 80 | // default page 81 | extern const uint8_t root_html_start[] asm("_binary_root_html_start"); 82 | extern const uint8_t root_html_end[] asm("_binary_root_html_end"); 83 | const uint32_t root_html_len = root_html_end - root_html_start; 84 | 85 | // main.js 86 | extern const uint8_t main_js_start[] asm("_binary_main_js_start"); 87 | extern const uint8_t main_js_end[] asm("_binary_main_js_end"); 88 | const uint32_t main_js_len = main_js_end - main_js_start; 89 | 90 | // main.css 91 | extern const uint8_t main_css_start[] asm("_binary_main_css_start"); 92 | extern const uint8_t main_css_end[] asm("_binary_main_css_end"); 93 | const uint32_t main_css_len = main_css_end - main_css_start; 94 | 95 | // favicon.ico 96 | extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start"); 97 | extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end"); 98 | const uint32_t favicon_ico_len = favicon_ico_end - favicon_ico_start; 99 | 100 | // error page 101 | extern const uint8_t error_html_start[] asm("_binary_error_html_start"); 102 | extern const uint8_t error_html_end[] asm("_binary_error_html_end"); 103 | const uint32_t error_html_len = error_html_end - error_html_start; 104 | 105 | netconn_set_recvtimeout(conn,1000); // allow a connection timeout of 1 second 106 | ESP_LOGI(TAG,"reading from client..."); 107 | err = netconn_recv(conn, &inbuf); 108 | ESP_LOGI(TAG,"read from client"); 109 | if(err==ERR_OK) { 110 | netbuf_data(inbuf, (void**)&buf, &buflen); 111 | if(buf) { 112 | 113 | ESP_LOGD(TAG, "buf=[%s]", buf); 114 | // default page 115 | if (strstr(buf,"GET / ") 116 | && !strstr(buf,"Upgrade: websocket")) { 117 | ESP_LOGI(TAG,"Sending /"); 118 | netconn_write(conn, HTML_HEADER, sizeof(HTML_HEADER)-1,NETCONN_NOCOPY); 119 | netconn_write(conn, root_html_start,root_html_len,NETCONN_NOCOPY); 120 | netconn_close(conn); 121 | netconn_delete(conn); 122 | netbuf_delete(inbuf); 123 | } 124 | 125 | // default page websocket 126 | else if(strstr(buf,"GET / ") 127 | && strstr(buf,"Upgrade: websocket")) { 128 | ESP_LOGI(TAG,"Requesting websocket on /"); 129 | ws_server_add_client(conn,buf,buflen,"/",websocket_callback); 130 | netbuf_delete(inbuf); 131 | } 132 | 133 | else if(strstr(buf,"GET /main.js ")) { 134 | ESP_LOGI(TAG,"Sending /main.js"); 135 | netconn_write(conn, JS_HEADER, sizeof(JS_HEADER)-1,NETCONN_NOCOPY); 136 | netconn_write(conn, main_js_start, main_js_len,NETCONN_NOCOPY); 137 | netconn_close(conn); 138 | netconn_delete(conn); 139 | netbuf_delete(inbuf); 140 | } 141 | 142 | else if(strstr(buf,"GET /main.css ")) { 143 | ESP_LOGI(TAG,"Sending /main.css"); 144 | netconn_write(conn, CSS_HEADER, sizeof(CSS_HEADER)-1,NETCONN_NOCOPY); 145 | netconn_write(conn, main_css_start, main_css_len,NETCONN_NOCOPY); 146 | netconn_close(conn); 147 | netconn_delete(conn); 148 | netbuf_delete(inbuf); 149 | } 150 | 151 | else if(strstr(buf,"GET /favicon.ico ")) { 152 | ESP_LOGI(TAG,"Sending favicon.ico"); 153 | netconn_write(conn,ICO_HEADER,sizeof(ICO_HEADER)-1,NETCONN_NOCOPY); 154 | netconn_write(conn,favicon_ico_start,favicon_ico_len,NETCONN_NOCOPY); 155 | netconn_close(conn); 156 | netconn_delete(conn); 157 | netbuf_delete(inbuf); 158 | } 159 | 160 | else if(strstr(buf,"GET /")) { 161 | ESP_LOGE(TAG,"Unknown request, sending error page: %s",buf); 162 | netconn_write(conn, ERROR_HEADER, sizeof(ERROR_HEADER)-1,NETCONN_NOCOPY); 163 | netconn_write(conn, error_html_start, error_html_len,NETCONN_NOCOPY); 164 | netconn_close(conn); 165 | netconn_delete(conn); 166 | netbuf_delete(inbuf); 167 | } 168 | 169 | else { 170 | ESP_LOGE(TAG,"Unknown request"); 171 | netconn_close(conn); 172 | netconn_delete(conn); 173 | netbuf_delete(inbuf); 174 | } 175 | } 176 | else { 177 | ESP_LOGI(TAG,"Unknown request (empty?...)"); 178 | netconn_close(conn); 179 | netconn_delete(conn); 180 | netbuf_delete(inbuf); 181 | } 182 | } 183 | else { // if err==ERR_OK 184 | ESP_LOGI(TAG,"error on read, closing connection"); 185 | netconn_close(conn); 186 | netconn_delete(conn); 187 | netbuf_delete(inbuf); 188 | } 189 | } 190 | 191 | // receives clients from queue, handles them 192 | void server_handle_task(void* pvParameters) { 193 | const static char* TAG = "server_handle_task"; 194 | struct netconn* conn; 195 | ESP_LOGI(TAG,"task starting"); 196 | for(;;) { 197 | xQueueReceive(client_queue,&conn,portMAX_DELAY); 198 | if(!conn) continue; 199 | http_server(conn); 200 | } 201 | vTaskDelete(NULL); 202 | } 203 | 204 | // handles clients when they first connect. passes to a queue 205 | void server_task(void* pvParameters) { 206 | const static char* TAG = "server_task"; 207 | char *task_parameter = (char *)pvParameters; 208 | ESP_LOGI(TAG, "Start task_parameter=%s", task_parameter); 209 | char url[64]; 210 | sprintf(url, "http://%s", task_parameter); 211 | ESP_LOGI(TAG, "Starting server on %s", url); 212 | 213 | struct netconn *conn, *newconn; 214 | static err_t err; 215 | client_queue = xQueueCreate(client_queue_size,sizeof(struct netconn*)); 216 | configASSERT( client_queue ); 217 | 218 | 219 | UBaseType_t PriorityGet = uxTaskPriorityGet(NULL); 220 | ESP_LOGI(TAG, "PriorityGet=%d", PriorityGet); 221 | xTaskCreate(&server_handle_task, "server_handle_task", 1024*3, NULL, PriorityGet, NULL); 222 | 223 | 224 | conn = netconn_new(NETCONN_TCP); 225 | netconn_bind(conn,NULL,80); 226 | netconn_listen(conn); 227 | ESP_LOGI(TAG,"server listening"); 228 | do { 229 | err = netconn_accept(conn, &newconn); 230 | ESP_LOGI(TAG,"new client"); 231 | if(err == ERR_OK) { 232 | xQueueSendToBack(client_queue,&newconn,portMAX_DELAY); 233 | //http_server(newconn); 234 | } 235 | } while(err == ERR_OK); 236 | netconn_close(conn); 237 | netconn_delete(conn); 238 | ESP_LOGE(TAG,"task ending, rebooting board"); 239 | esp_restart(); 240 | } 241 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/README.md: -------------------------------------------------------------------------------- 1 | # Horizontal Linear Gauge display using Canvas Gauge 2 | ![Image](https://github.com/user-attachments/assets/58989cbc-219c-45a0-a017-f096c7748688) 3 | 4 | I used [this](https://canvas-gauges.com/) for gauge display. 5 | You can easily change the gauge design. 6 | 7 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/HorizontalLinearGauge/html/favicon.ico -------------------------------------------------------------------------------- /HorizontalLinearGauge/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | 8 | 9 | function sendText(name) { 10 | console.log('sendText'); 11 | var data = {}; 12 | data["id"] = name; 13 | console.log('data=', data); 14 | json_data = JSON.stringify(data); 15 | console.log('json_data=' + json_data); 16 | websocket.send(json_data); 17 | } 18 | 19 | websocket.onopen = function(evt) { 20 | console.log('WebSocket connection opened'); 21 | var data = {}; 22 | data["id"] = "init"; 23 | console.log('data=', data); 24 | json_data = JSON.stringify(data); 25 | console.log('json_data=' + json_data); 26 | websocket.send(json_data); 27 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 28 | } 29 | 30 | websocket.onmessage = function(evt) { 31 | var msg = evt.data; 32 | console.log("msg=" + msg); 33 | var values = msg.split('\4'); // \4 is EOT 34 | //console.log("values=" + values); 35 | switch(values[0]) { 36 | case 'HEAD': 37 | console.log("HEAD values[1]=" + values[1]); 38 | var h1 = document.getElementById( 'header' ); 39 | h1.textContent = values[1]; 40 | break; 41 | 42 | case 'METER': 43 | //console.log("gauge1=" + Object.keys(gauge1.options)); 44 | //console.log("gauge1.options.units=" + gauge1.options.units); 45 | console.log("METER values[1]=" + values[1]); 46 | console.log("METER values[2]=" + values[2]); 47 | console.log("METER values[3]=" + values[3]); 48 | if (values[1] != "") { 49 | gauge1.options.units = values[1]; 50 | document.getElementById("canvas1").style.display = "block"; 51 | meter1 = 1; 52 | } 53 | if (values[2] != "") { 54 | gauge2.options.units = values[2]; 55 | document.getElementById("canvas2").style.display = "block"; 56 | meter2 = 1; 57 | } 58 | if (values[3] != "") { 59 | gauge3.options.units = values[3]; 60 | document.getElementById("canvas3").style.display = "block"; 61 | meter3 = 1; 62 | } 63 | break; 64 | 65 | case 'DATA': 66 | console.log("DATA values[1]=" + values[1]); 67 | var voltage1 = parseInt(values[1], 10); 68 | gauge1.value = voltage1; 69 | gauge1.update({ valueText: values[1] }); 70 | if (meter2) { 71 | console.log("DATA values[2]=" + values[2]); 72 | var voltage2 = parseInt(values[2], 10); 73 | gauge2.value = voltage2; 74 | gauge2.update({ valueText: values[2] }); 75 | } 76 | if (meter3) { 77 | console.log("DATA values[3]=" + values[3]); 78 | var voltage3 = parseInt(values[3], 10); 79 | gauge3.value = voltage3; 80 | gauge3.update({ valueText: values[3] }); 81 | } 82 | break; 83 | 84 | default: 85 | break; 86 | } 87 | } 88 | 89 | websocket.onclose = function(evt) { 90 | console.log('Websocket connection closed'); 91 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 92 | } 93 | 94 | websocket.onerror = function(evt) { 95 | console.log('Websocket error: ' + evt); 96 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 97 | } 98 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Canvas Gauge 8 | 9 | 10 | 11 |

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /HorizontalLinearGauge/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PlotlyChart/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /PlotlyChart/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PlotlyChart/README.md: -------------------------------------------------------------------------------- 1 | # Chart display using plotly 2 | ![Image](https://github.com/user-attachments/assets/cd3c45d6-61e9-49f0-82fb-6690663633c2) 3 | 4 | I used [this](https://plotly.com/javascript/) for chart display. 5 | You can easily change the chart design. 6 | 7 | -------------------------------------------------------------------------------- /PlotlyChart/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /PlotlyChart/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/PlotlyChart/html/favicon.ico -------------------------------------------------------------------------------- /PlotlyChart/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /PlotlyChart/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | 8 | 9 | function sendText(name) { 10 | console.log('sendText'); 11 | var data = {}; 12 | data["id"] = name; 13 | console.log('data=', data); 14 | json_data = JSON.stringify(data); 15 | console.log('json_data=' + json_data); 16 | websocket.send(json_data); 17 | } 18 | 19 | websocket.onopen = function(evt) { 20 | console.log('WebSocket connection opened'); 21 | var data = {}; 22 | data["id"] = "init"; 23 | console.log('data=', data); 24 | json_data = JSON.stringify(data); 25 | console.log('json_data=' + json_data); 26 | websocket.send(json_data); 27 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 28 | } 29 | 30 | websocket.onmessage = function(evt) { 31 | var msg = evt.data; 32 | console.log("msg=" + msg); 33 | var values = msg.split('\4'); // \4 is EOT 34 | //console.log("values=" + values); 35 | switch(values[0]) { 36 | case 'HEAD': 37 | console.log("HEAD values[1]=" + values[1]); 38 | var h1 = document.getElementById( 'header' ); 39 | h1.textContent = values[1]; 40 | break; 41 | 42 | case 'METER': 43 | //console.log("gauge1=" + Object.keys(gauge1.options)); 44 | //console.log("gauge1.options.units=" + gauge1.options.units); 45 | console.log("METER values[1]=" + values[1]); 46 | console.log("METER values[2]=" + values[2]); 47 | console.log("METER values[3]=" + values[3]); 48 | if (values[1] != "") { 49 | Plotly.restyle('graph', 'visible', true, [0]); 50 | Plotly.restyle('graph', 'name', values[1], [0]) 51 | meter1 = 1; 52 | } 53 | if (values[2] != "") { 54 | Plotly.restyle('graph', 'visible', true, [1]); 55 | Plotly.restyle('graph', 'name', values[2], [1]) 56 | meter2 = 1; 57 | } 58 | if (values[3] != "") { 59 | Plotly.restyle('graph', 'visible', true, [2]); 60 | Plotly.restyle('graph', 'name', values[3], [2]) 61 | meter3 = 1; 62 | } 63 | break; 64 | 65 | case 'DATA': 66 | console.log("DATA values[1]=" + values[1]); 67 | var voltage1 = parseInt(values[1], 10); 68 | var voltage2 = 0; 69 | var voltage3 = 0; 70 | if (meter2) { 71 | console.log("DATA values[2]=" + values[2]); 72 | voltage2 = parseInt(values[2], 10); 73 | } 74 | if (meter3) { 75 | console.log("DATA values[3]=" + values[3]); 76 | voltage3 = parseInt(values[3], 10); 77 | } 78 | var time = new Date(); 79 | var update = { 80 | x: [[time], [time], [time]], 81 | y: [[voltage1], [voltage2], [voltage3]] 82 | } 83 | 84 | var olderTime = time.setMinutes(time.getMinutes() - 1); 85 | var futureTime = time.setMinutes(time.getMinutes() + 1); 86 | 87 | var minuteView = { 88 | xaxis: { 89 | type: 'date', 90 | range: [olderTime,futureTime] 91 | } 92 | }; 93 | 94 | Plotly.relayout('graph', minuteView); 95 | 96 | Plotly.extendTraces('graph', update, [0, 1, 2]) 97 | break; 98 | 99 | default: 100 | break; 101 | } 102 | } 103 | 104 | websocket.onclose = function(evt) { 105 | console.log('Websocket connection closed'); 106 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 107 | } 108 | 109 | websocket.onerror = function(evt) { 110 | console.log('Websocket error: ' + evt); 111 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 112 | } 113 | -------------------------------------------------------------------------------- /PlotlyChart/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Plotly Chart 8 | 9 | 10 | 11 |

12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 |
20 | 21 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /PlotlyChart/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /PlotlyChart/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /PlotlyChart/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /PlotlyChart/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /PlotlyChart/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /PlotlyChart/main/web_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/message_buffer.h" 16 | #include "esp_log.h" 17 | 18 | #include "websocket_server.h" 19 | 20 | static QueueHandle_t client_queue; 21 | extern MessageBufferHandle_t xMessageBufferToClient; 22 | 23 | const static int client_queue_size = 10; 24 | 25 | // handles websocket events 26 | void websocket_callback(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) { 27 | const static char* TAG = "websocket_callback"; 28 | //int value; 29 | 30 | switch(type) { 31 | case WEBSOCKET_CONNECT: 32 | ESP_LOGI(TAG,"client %i connected!",num); 33 | break; 34 | case WEBSOCKET_DISCONNECT_EXTERNAL: 35 | ESP_LOGI(TAG,"client %i sent a disconnect message",num); 36 | break; 37 | case WEBSOCKET_DISCONNECT_INTERNAL: 38 | ESP_LOGI(TAG,"client %i was disconnected",num); 39 | break; 40 | case WEBSOCKET_DISCONNECT_ERROR: 41 | ESP_LOGI(TAG,"client %i was disconnected due to an error",num); 42 | break; 43 | case WEBSOCKET_TEXT: 44 | if(len) { // if the message length was greater than zero 45 | ESP_LOGI(TAG, "got message length %i: %s", (int)len, msg); 46 | size_t xBytesSent = xMessageBufferSendFromISR(xMessageBufferToClient, msg, len, NULL); 47 | if (xBytesSent != len) { 48 | ESP_LOGE(TAG, "xMessageBufferSend fail"); 49 | } 50 | } 51 | break; 52 | case WEBSOCKET_BIN: 53 | ESP_LOGI(TAG,"client %i sent binary message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 54 | break; 55 | case WEBSOCKET_PING: 56 | ESP_LOGI(TAG,"client %i pinged us with message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 57 | break; 58 | case WEBSOCKET_PONG: 59 | ESP_LOGI(TAG,"client %i responded to the ping",num); 60 | break; 61 | } 62 | } 63 | 64 | // serves any clients 65 | static void http_server(struct netconn *conn) { 66 | const static char* TAG = "http_server"; 67 | const static char HTML_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n"; 68 | const static char ERROR_HEADER[] = "HTTP/1.1 404 Not Found\nContent-type: text/html\n\n"; 69 | const static char JS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n"; 70 | const static char CSS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/css\n\n"; 71 | //const static char PNG_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/png\n\n"; 72 | const static char ICO_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/x-icon\n\n"; 73 | //const static char PDF_HEADER[] = "HTTP/1.1 200 OK\nContent-type: application/pdf\n\n"; 74 | //const static char EVENT_HEADER[] = "HTTP/1.1 200 OK\nContent-Type: text/event-stream\nCache-Control: no-cache\nretry: 3000\n\n"; 75 | struct netbuf* inbuf; 76 | static char* buf; 77 | static uint16_t buflen; 78 | static err_t err; 79 | 80 | // default page 81 | extern const uint8_t root_html_start[] asm("_binary_root_html_start"); 82 | extern const uint8_t root_html_end[] asm("_binary_root_html_end"); 83 | const uint32_t root_html_len = root_html_end - root_html_start; 84 | 85 | // main.js 86 | extern const uint8_t main_js_start[] asm("_binary_main_js_start"); 87 | extern const uint8_t main_js_end[] asm("_binary_main_js_end"); 88 | const uint32_t main_js_len = main_js_end - main_js_start; 89 | 90 | // main.css 91 | extern const uint8_t main_css_start[] asm("_binary_main_css_start"); 92 | extern const uint8_t main_css_end[] asm("_binary_main_css_end"); 93 | const uint32_t main_css_len = main_css_end - main_css_start; 94 | 95 | // favicon.ico 96 | extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start"); 97 | extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end"); 98 | const uint32_t favicon_ico_len = favicon_ico_end - favicon_ico_start; 99 | 100 | // error page 101 | extern const uint8_t error_html_start[] asm("_binary_error_html_start"); 102 | extern const uint8_t error_html_end[] asm("_binary_error_html_end"); 103 | const uint32_t error_html_len = error_html_end - error_html_start; 104 | 105 | netconn_set_recvtimeout(conn,1000); // allow a connection timeout of 1 second 106 | ESP_LOGI(TAG,"reading from client..."); 107 | err = netconn_recv(conn, &inbuf); 108 | ESP_LOGI(TAG,"read from client"); 109 | if(err==ERR_OK) { 110 | netbuf_data(inbuf, (void**)&buf, &buflen); 111 | if(buf) { 112 | 113 | ESP_LOGD(TAG, "buf=[%s]", buf); 114 | // default page 115 | if (strstr(buf,"GET / ") 116 | && !strstr(buf,"Upgrade: websocket")) { 117 | ESP_LOGI(TAG,"Sending /"); 118 | netconn_write(conn, HTML_HEADER, sizeof(HTML_HEADER)-1,NETCONN_NOCOPY); 119 | netconn_write(conn, root_html_start,root_html_len,NETCONN_NOCOPY); 120 | netconn_close(conn); 121 | netconn_delete(conn); 122 | netbuf_delete(inbuf); 123 | } 124 | 125 | // default page websocket 126 | else if(strstr(buf,"GET / ") 127 | && strstr(buf,"Upgrade: websocket")) { 128 | ESP_LOGI(TAG,"Requesting websocket on /"); 129 | ws_server_add_client(conn,buf,buflen,"/",websocket_callback); 130 | netbuf_delete(inbuf); 131 | } 132 | 133 | else if(strstr(buf,"GET /main.js ")) { 134 | ESP_LOGI(TAG,"Sending /main.js"); 135 | netconn_write(conn, JS_HEADER, sizeof(JS_HEADER)-1,NETCONN_NOCOPY); 136 | netconn_write(conn, main_js_start, main_js_len,NETCONN_NOCOPY); 137 | netconn_close(conn); 138 | netconn_delete(conn); 139 | netbuf_delete(inbuf); 140 | } 141 | 142 | else if(strstr(buf,"GET /main.css ")) { 143 | ESP_LOGI(TAG,"Sending /main.css"); 144 | netconn_write(conn, CSS_HEADER, sizeof(CSS_HEADER)-1,NETCONN_NOCOPY); 145 | netconn_write(conn, main_css_start, main_css_len,NETCONN_NOCOPY); 146 | netconn_close(conn); 147 | netconn_delete(conn); 148 | netbuf_delete(inbuf); 149 | } 150 | 151 | else if(strstr(buf,"GET /favicon.ico ")) { 152 | ESP_LOGI(TAG,"Sending favicon.ico"); 153 | netconn_write(conn,ICO_HEADER,sizeof(ICO_HEADER)-1,NETCONN_NOCOPY); 154 | netconn_write(conn,favicon_ico_start,favicon_ico_len,NETCONN_NOCOPY); 155 | netconn_close(conn); 156 | netconn_delete(conn); 157 | netbuf_delete(inbuf); 158 | } 159 | 160 | else if(strstr(buf,"GET /")) { 161 | ESP_LOGE(TAG,"Unknown request, sending error page: %s",buf); 162 | netconn_write(conn, ERROR_HEADER, sizeof(ERROR_HEADER)-1,NETCONN_NOCOPY); 163 | netconn_write(conn, error_html_start, error_html_len,NETCONN_NOCOPY); 164 | netconn_close(conn); 165 | netconn_delete(conn); 166 | netbuf_delete(inbuf); 167 | } 168 | 169 | else { 170 | ESP_LOGE(TAG,"Unknown request"); 171 | netconn_close(conn); 172 | netconn_delete(conn); 173 | netbuf_delete(inbuf); 174 | } 175 | } 176 | else { 177 | ESP_LOGI(TAG,"Unknown request (empty?...)"); 178 | netconn_close(conn); 179 | netconn_delete(conn); 180 | netbuf_delete(inbuf); 181 | } 182 | } 183 | else { // if err==ERR_OK 184 | ESP_LOGI(TAG,"error on read, closing connection"); 185 | netconn_close(conn); 186 | netconn_delete(conn); 187 | netbuf_delete(inbuf); 188 | } 189 | } 190 | 191 | // receives clients from queue, handles them 192 | void server_handle_task(void* pvParameters) { 193 | const static char* TAG = "server_handle_task"; 194 | struct netconn* conn; 195 | ESP_LOGI(TAG,"task starting"); 196 | for(;;) { 197 | xQueueReceive(client_queue,&conn,portMAX_DELAY); 198 | if(!conn) continue; 199 | http_server(conn); 200 | } 201 | vTaskDelete(NULL); 202 | } 203 | 204 | // handles clients when they first connect. passes to a queue 205 | void server_task(void* pvParameters) { 206 | const static char* TAG = "server_task"; 207 | char *task_parameter = (char *)pvParameters; 208 | ESP_LOGI(TAG, "Start task_parameter=%s", task_parameter); 209 | char url[64]; 210 | sprintf(url, "http://%s", task_parameter); 211 | ESP_LOGI(TAG, "Starting server on %s", url); 212 | 213 | struct netconn *conn, *newconn; 214 | static err_t err; 215 | client_queue = xQueueCreate(client_queue_size,sizeof(struct netconn*)); 216 | configASSERT( client_queue ); 217 | 218 | 219 | UBaseType_t PriorityGet = uxTaskPriorityGet(NULL); 220 | ESP_LOGI(TAG, "PriorityGet=%d", PriorityGet); 221 | xTaskCreate(&server_handle_task, "server_handle_task", 1024*3, NULL, PriorityGet, NULL); 222 | 223 | 224 | conn = netconn_new(NETCONN_TCP); 225 | netconn_bind(conn,NULL,80); 226 | netconn_listen(conn); 227 | ESP_LOGI(TAG,"server listening"); 228 | do { 229 | err = netconn_accept(conn, &newconn); 230 | ESP_LOGI(TAG,"new client"); 231 | if(err == ERR_OK) { 232 | xQueueSendToBack(client_queue,&newconn,portMAX_DELAY); 233 | //http_server(newconn); 234 | } 235 | } while(err == ERR_OK); 236 | netconn_close(conn); 237 | netconn_delete(conn); 238 | ESP_LOGE(TAG,"task ending, rebooting board"); 239 | esp_restart(); 240 | } 241 | -------------------------------------------------------------------------------- /PlotlyGauge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /PlotlyGauge/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PlotlyGauge/README.md: -------------------------------------------------------------------------------- 1 | # Gauge display using plotly 2 | ![Image](https://github.com/user-attachments/assets/e0823929-d190-495c-858a-cd0efb875fd7) 3 | 4 | I used [this](https://plotly.com/javascript/) for gauge display. 5 | You can easily change the chart design. 6 | 7 | -------------------------------------------------------------------------------- /PlotlyGauge/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /PlotlyGauge/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/PlotlyGauge/html/favicon.ico -------------------------------------------------------------------------------- /PlotlyGauge/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /PlotlyGauge/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | 5 | var data1 = [ 6 | { 7 | domain: { x: [0, 1], y: [0, 1] }, 8 | title: { text: "" }, 9 | value: 0, 10 | type: "indicator", 11 | mode: "gauge+number", 12 | gauge: { 13 | axis: { range: [null, 3000] }, 14 | steps: [ 15 | { range: [0, 2000], color: "cyan" }, 16 | { range: [2000, 3000], color: "royalblue" } 17 | ], 18 | } 19 | } 20 | ]; 21 | 22 | var data2 = data1; 23 | var data3 = data1; 24 | 25 | var layout = { width: 400, height: 300, margin: { t: 0, b: 0 } }; 26 | 27 | Plotly.newPlot('gauge1', data1, layout); 28 | Plotly.newPlot('gauge2', data2, layout); 29 | Plotly.newPlot('gauge3', data3, layout); 30 | 31 | var meter1 = 0; 32 | var meter2 = 0; 33 | var meter3 = 0; 34 | var gpio1 = ""; 35 | var gpio2 = ""; 36 | var gpio3 = ""; 37 | 38 | 39 | function sendText(name) { 40 | console.log('sendText'); 41 | var data = {}; 42 | data["id"] = name; 43 | console.log('data=', data); 44 | json_data = JSON.stringify(data); 45 | console.log('json_data=' + json_data); 46 | websocket.send(json_data); 47 | } 48 | 49 | websocket.onopen = function(evt) { 50 | console.log('WebSocket connection opened'); 51 | var data = {}; 52 | data["id"] = "init"; 53 | console.log('data=', data); 54 | json_data = JSON.stringify(data); 55 | console.log('json_data=' + json_data); 56 | websocket.send(json_data); 57 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 58 | } 59 | 60 | websocket.onmessage = function(evt) { 61 | var msg = evt.data; 62 | console.log("msg=" + msg); 63 | var values = msg.split('\4'); // \4 is EOT 64 | //console.log("values=" + values); 65 | switch(values[0]) { 66 | case 'HEAD': 67 | console.log("HEAD values[1]=" + values[1]); 68 | var h1 = document.getElementById( 'header' ); 69 | h1.textContent = values[1]; 70 | break; 71 | 72 | case 'METER': 73 | //console.log("gauge1=" + Object.keys(gauge1.options)); 74 | //console.log("gauge1.options.units=" + gauge1.options.units); 75 | console.log("METER values[1]=" + values[1]); 76 | console.log("METER values[2]=" + values[2]); 77 | console.log("METER values[3]=" + values[3]); 78 | if (values[1] != "") { 79 | meter1 = 1; 80 | gpio1 = values[1]; 81 | document.getElementById("gauge1").style.display = "inline-block"; 82 | } 83 | if (values[2] != "") { 84 | meter2 = 1; 85 | gpio2 = values[2]; 86 | document.getElementById("gauge2").style.display = "inline-block"; 87 | } 88 | if (values[3] != "") { 89 | meter3 = 1; 90 | gpio3 = values[3]; 91 | document.getElementById("gauge3").style.display = "inline-block"; 92 | } 93 | break; 94 | 95 | case 'DATA': 96 | console.log("DATA values[1]=" + values[1]); 97 | var voltage1 = parseInt(values[1], 10); 98 | var data1_update = { 99 | title: { text: gpio1 }, 100 | value: voltage1, 101 | }; 102 | Plotly.update('gauge1', data1_update, layout) 103 | 104 | var voltage2 = 0; 105 | var voltage3 = 0; 106 | if (meter2) { 107 | console.log("DATA values[2]=" + values[2]); 108 | voltage2 = parseInt(values[2], 10); 109 | var data2_update = { 110 | title: { text: gpio2 }, 111 | value: voltage2, 112 | }; 113 | Plotly.update('gauge2', data2_update, layout) 114 | } 115 | if (meter3) { 116 | console.log("DATA values[3]=" + values[3]); 117 | voltage3 = parseInt(values[3], 10); 118 | var data3_update = { 119 | title: { text: gpio3 }, 120 | value: voltage3, 121 | }; 122 | Plotly.update('gauge3', data3_update, layout) 123 | } 124 | break; 125 | 126 | default: 127 | break; 128 | } 129 | } 130 | 131 | websocket.onclose = function(evt) { 132 | console.log('Websocket connection closed'); 133 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 134 | } 135 | 136 | websocket.onerror = function(evt) { 137 | console.log('Websocket error: ' + evt); 138 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 139 | } 140 | -------------------------------------------------------------------------------- /PlotlyGauge/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | Plotly Gauge 13 | 14 | 15 | 16 |

17 | 18 |
19 |
20 |
21 |
22 |
23 | 24 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /PlotlyGauge/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /PlotlyGauge/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /PlotlyGauge/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /PlotlyGauge/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /PlotlyGauge/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /PlotlyGauge/main/web_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/message_buffer.h" 16 | #include "esp_log.h" 17 | 18 | #include "websocket_server.h" 19 | 20 | static QueueHandle_t client_queue; 21 | extern MessageBufferHandle_t xMessageBufferToClient; 22 | 23 | const static int client_queue_size = 10; 24 | 25 | // handles websocket events 26 | void websocket_callback(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) { 27 | const static char* TAG = "websocket_callback"; 28 | //int value; 29 | 30 | switch(type) { 31 | case WEBSOCKET_CONNECT: 32 | ESP_LOGI(TAG,"client %i connected!",num); 33 | break; 34 | case WEBSOCKET_DISCONNECT_EXTERNAL: 35 | ESP_LOGI(TAG,"client %i sent a disconnect message",num); 36 | break; 37 | case WEBSOCKET_DISCONNECT_INTERNAL: 38 | ESP_LOGI(TAG,"client %i was disconnected",num); 39 | break; 40 | case WEBSOCKET_DISCONNECT_ERROR: 41 | ESP_LOGI(TAG,"client %i was disconnected due to an error",num); 42 | break; 43 | case WEBSOCKET_TEXT: 44 | if(len) { // if the message length was greater than zero 45 | ESP_LOGI(TAG, "got message length %i: %s", (int)len, msg); 46 | size_t xBytesSent = xMessageBufferSendFromISR(xMessageBufferToClient, msg, len, NULL); 47 | if (xBytesSent != len) { 48 | ESP_LOGE(TAG, "xMessageBufferSend fail"); 49 | } 50 | } 51 | break; 52 | case WEBSOCKET_BIN: 53 | ESP_LOGI(TAG,"client %i sent binary message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 54 | break; 55 | case WEBSOCKET_PING: 56 | ESP_LOGI(TAG,"client %i pinged us with message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 57 | break; 58 | case WEBSOCKET_PONG: 59 | ESP_LOGI(TAG,"client %i responded to the ping",num); 60 | break; 61 | } 62 | } 63 | 64 | // serves any clients 65 | static void http_server(struct netconn *conn) { 66 | const static char* TAG = "http_server"; 67 | const static char HTML_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n"; 68 | const static char ERROR_HEADER[] = "HTTP/1.1 404 Not Found\nContent-type: text/html\n\n"; 69 | const static char JS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n"; 70 | const static char CSS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/css\n\n"; 71 | //const static char PNG_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/png\n\n"; 72 | const static char ICO_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/x-icon\n\n"; 73 | //const static char PDF_HEADER[] = "HTTP/1.1 200 OK\nContent-type: application/pdf\n\n"; 74 | //const static char EVENT_HEADER[] = "HTTP/1.1 200 OK\nContent-Type: text/event-stream\nCache-Control: no-cache\nretry: 3000\n\n"; 75 | struct netbuf* inbuf; 76 | static char* buf; 77 | static uint16_t buflen; 78 | static err_t err; 79 | 80 | // default page 81 | extern const uint8_t root_html_start[] asm("_binary_root_html_start"); 82 | extern const uint8_t root_html_end[] asm("_binary_root_html_end"); 83 | const uint32_t root_html_len = root_html_end - root_html_start; 84 | 85 | // main.js 86 | extern const uint8_t main_js_start[] asm("_binary_main_js_start"); 87 | extern const uint8_t main_js_end[] asm("_binary_main_js_end"); 88 | const uint32_t main_js_len = main_js_end - main_js_start; 89 | 90 | // main.css 91 | extern const uint8_t main_css_start[] asm("_binary_main_css_start"); 92 | extern const uint8_t main_css_end[] asm("_binary_main_css_end"); 93 | const uint32_t main_css_len = main_css_end - main_css_start; 94 | 95 | // favicon.ico 96 | extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start"); 97 | extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end"); 98 | const uint32_t favicon_ico_len = favicon_ico_end - favicon_ico_start; 99 | 100 | // error page 101 | extern const uint8_t error_html_start[] asm("_binary_error_html_start"); 102 | extern const uint8_t error_html_end[] asm("_binary_error_html_end"); 103 | const uint32_t error_html_len = error_html_end - error_html_start; 104 | 105 | netconn_set_recvtimeout(conn,1000); // allow a connection timeout of 1 second 106 | ESP_LOGI(TAG,"reading from client..."); 107 | err = netconn_recv(conn, &inbuf); 108 | ESP_LOGI(TAG,"read from client"); 109 | if(err==ERR_OK) { 110 | netbuf_data(inbuf, (void**)&buf, &buflen); 111 | if(buf) { 112 | 113 | ESP_LOGD(TAG, "buf=[%s]", buf); 114 | // default page 115 | if (strstr(buf,"GET / ") 116 | && !strstr(buf,"Upgrade: websocket")) { 117 | ESP_LOGI(TAG,"Sending /"); 118 | netconn_write(conn, HTML_HEADER, sizeof(HTML_HEADER)-1,NETCONN_NOCOPY); 119 | netconn_write(conn, root_html_start,root_html_len,NETCONN_NOCOPY); 120 | netconn_close(conn); 121 | netconn_delete(conn); 122 | netbuf_delete(inbuf); 123 | } 124 | 125 | // default page websocket 126 | else if(strstr(buf,"GET / ") 127 | && strstr(buf,"Upgrade: websocket")) { 128 | ESP_LOGI(TAG,"Requesting websocket on /"); 129 | ws_server_add_client(conn,buf,buflen,"/",websocket_callback); 130 | netbuf_delete(inbuf); 131 | } 132 | 133 | else if(strstr(buf,"GET /main.js ")) { 134 | ESP_LOGI(TAG,"Sending /main.js"); 135 | netconn_write(conn, JS_HEADER, sizeof(JS_HEADER)-1,NETCONN_NOCOPY); 136 | netconn_write(conn, main_js_start, main_js_len,NETCONN_NOCOPY); 137 | netconn_close(conn); 138 | netconn_delete(conn); 139 | netbuf_delete(inbuf); 140 | } 141 | 142 | else if(strstr(buf,"GET /main.css ")) { 143 | ESP_LOGI(TAG,"Sending /main.css"); 144 | netconn_write(conn, CSS_HEADER, sizeof(CSS_HEADER)-1,NETCONN_NOCOPY); 145 | netconn_write(conn, main_css_start, main_css_len,NETCONN_NOCOPY); 146 | netconn_close(conn); 147 | netconn_delete(conn); 148 | netbuf_delete(inbuf); 149 | } 150 | 151 | else if(strstr(buf,"GET /favicon.ico ")) { 152 | ESP_LOGI(TAG,"Sending favicon.ico"); 153 | netconn_write(conn,ICO_HEADER,sizeof(ICO_HEADER)-1,NETCONN_NOCOPY); 154 | netconn_write(conn,favicon_ico_start,favicon_ico_len,NETCONN_NOCOPY); 155 | netconn_close(conn); 156 | netconn_delete(conn); 157 | netbuf_delete(inbuf); 158 | } 159 | 160 | else if(strstr(buf,"GET /")) { 161 | ESP_LOGE(TAG,"Unknown request, sending error page: %s",buf); 162 | netconn_write(conn, ERROR_HEADER, sizeof(ERROR_HEADER)-1,NETCONN_NOCOPY); 163 | netconn_write(conn, error_html_start, error_html_len,NETCONN_NOCOPY); 164 | netconn_close(conn); 165 | netconn_delete(conn); 166 | netbuf_delete(inbuf); 167 | } 168 | 169 | else { 170 | ESP_LOGE(TAG,"Unknown request"); 171 | netconn_close(conn); 172 | netconn_delete(conn); 173 | netbuf_delete(inbuf); 174 | } 175 | } 176 | else { 177 | ESP_LOGI(TAG,"Unknown request (empty?...)"); 178 | netconn_close(conn); 179 | netconn_delete(conn); 180 | netbuf_delete(inbuf); 181 | } 182 | } 183 | else { // if err==ERR_OK 184 | ESP_LOGI(TAG,"error on read, closing connection"); 185 | netconn_close(conn); 186 | netconn_delete(conn); 187 | netbuf_delete(inbuf); 188 | } 189 | } 190 | 191 | // receives clients from queue, handles them 192 | void server_handle_task(void* pvParameters) { 193 | const static char* TAG = "server_handle_task"; 194 | struct netconn* conn; 195 | ESP_LOGI(TAG,"task starting"); 196 | for(;;) { 197 | xQueueReceive(client_queue,&conn,portMAX_DELAY); 198 | if(!conn) continue; 199 | http_server(conn); 200 | } 201 | vTaskDelete(NULL); 202 | } 203 | 204 | // handles clients when they first connect. passes to a queue 205 | void server_task(void* pvParameters) { 206 | const static char* TAG = "server_task"; 207 | char *task_parameter = (char *)pvParameters; 208 | ESP_LOGI(TAG, "Start task_parameter=%s", task_parameter); 209 | char url[64]; 210 | sprintf(url, "http://%s", task_parameter); 211 | ESP_LOGI(TAG, "Starting server on %s", url); 212 | 213 | struct netconn *conn, *newconn; 214 | static err_t err; 215 | client_queue = xQueueCreate(client_queue_size,sizeof(struct netconn*)); 216 | configASSERT( client_queue ); 217 | 218 | 219 | UBaseType_t PriorityGet = uxTaskPriorityGet(NULL); 220 | ESP_LOGI(TAG, "PriorityGet=%d", PriorityGet); 221 | xTaskCreate(&server_handle_task, "server_handle_task", 1024*3, NULL, PriorityGet, NULL); 222 | 223 | 224 | conn = netconn_new(NETCONN_TCP); 225 | netconn_bind(conn,NULL,80); 226 | netconn_listen(conn); 227 | ESP_LOGI(TAG,"server listening"); 228 | do { 229 | err = netconn_accept(conn, &newconn); 230 | ESP_LOGI(TAG,"new client"); 231 | if(err == ERR_OK) { 232 | xQueueSendToBack(client_queue,&newconn,portMAX_DELAY); 233 | //http_server(newconn); 234 | } 235 | } while(err == ERR_OK); 236 | netconn_close(conn); 237 | netconn_delete(conn); 238 | ESP_LOGE(TAG,"task ending, rebooting board"); 239 | esp_restart(); 240 | } 241 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp-idf-web-chart 2 | This is a demonstration of __real-time data visualization__ using a web browser. 3 | The purpose of this project is to demonstrate how to use components that enable real-time data visualization. 4 | Although it use as data to display ADC converted values, it can also be applied to data from sensors such as thermometers and hygrometers. 5 | 6 | The ESP32 has two ADCs: ADC1 and ADC2. 7 | You can use these to convert analog values to digital values. 8 | The analog values change dynamically and are suitable for this demonstration. 9 | This project uses ADC1. 10 | 11 | - Using Canvas Gauge 12 | ![Image](https://github.com/user-attachments/assets/bab9cbba-28a4-4663-a18c-c5328fe60527) 13 | ![Image](https://github.com/user-attachments/assets/58989cbc-219c-45a0-a017-f096c7748688) 14 | ![Image](https://github.com/user-attachments/assets/ab306473-b771-4c63-a8e2-9f6081f401dd) 15 | 16 | - Using Chartjs-Plugin-Streaming 17 | ![Image](https://github.com/user-attachments/assets/bf142ab9-bc34-4118-9643-452733157c5c) 18 | 19 | - Using Plotly 20 | ![Image](https://github.com/user-attachments/assets/cd3c45d6-61e9-49f0-82fb-6690663633c2) 21 | ![Image](https://github.com/user-attachments/assets/e0823929-d190-495c-858a-cd0efb875fd7) 22 | 23 | - Using Epoch 24 | ![Image](https://github.com/user-attachments/assets/a2376a04-1fe2-49b9-bd0d-b98ecf623513) 25 | 26 | - Using Seven Segment Display 27 | ![Image](https://github.com/user-attachments/assets/f2f0d663-6868-4c41-bacc-9d495e1556c8) 28 | 29 | # Software requirements 30 | ESP-IDF V5.0 or later. 31 | ESP-IDF V4.4 release branch reached EOL in July 2024. 32 | ESP-IDF V5.1 is required when using ESP32-C6. 33 | I used [this](https://github.com/Molorius/esp32-websocket) component. 34 | This component can communicate directly with the browser. 35 | 36 | # Installation 37 | ``` 38 | git clone https://github.com/nopnop2002/esp-idf-web-chart 39 | cd esp-idf-web-chart/RadialGauge/ 40 | idf.py set-target {esp32/esp32s2/esp32s3/esp32c2/esp32c3/esp32c6} 41 | idf.py menuconfig 42 | idf.py flash monitor 43 | ``` 44 | 45 | # Configuration 46 | ![config-top](https://user-images.githubusercontent.com/6020549/164379960-58350b2d-17d4-48b5-84d1-615ff037242a.jpg) 47 | ![config-app](https://user-images.githubusercontent.com/6020549/164379982-149e4044-7889-4755-813e-0185fd082c9b.jpg) 48 | 49 | 50 | ## WiFi Setting 51 | Set the information of your access point. 52 | ![config-wifi-1](https://user-images.githubusercontent.com/6020549/164383151-ea783d1c-406b-42d5-9767-2e6911be9b2f.jpg) 53 | 54 | You can connect using the mDNS hostname instead of the IP address. 55 | ![config-wifi-2](https://user-images.githubusercontent.com/6020549/164380164-8be36ca2-a5c4-402e-b83d-d21513e66c55.jpg) 56 | 57 | ## ADC Setting 58 | Set the information of gpio for analog input. 59 | ![config-adc-1](https://user-images.githubusercontent.com/6020549/164380386-c6dffeb8-9bdd-46bf-8e55-9c4ecef16090.jpg) 60 | 61 | It is possible to monitor 3 channels at the same time. 62 | ![config-adc-2](https://user-images.githubusercontent.com/6020549/164380399-fe125c4f-006d-48cb-9e4e-f104c389b8b5.jpg) 63 | 64 | Analog input gpio for ESP32 is GPIO32 ~ GPIO39. 12Bits width. 65 | Analog input gpio for ESP32S2 is GPIO01 ~ GPIO10. 13Bits width. 66 | Analog input gpio for ESP32S3 is GPIO01 ~ GPIO10. 12Bits width. 67 | Analog input gpio for ESP32C2 is GPIO00 ~ GPIO04. 12Bits width. 68 | Analog input gpio for ESP32C3 is GPIO00 ~ GPIO04. 12Bits width. 69 | Analog input gpio for ESP32C6 is GPIO00 ~ GPIO06. 12Bits width. 70 | 71 | # ADC Attenuation 72 | This project uses ADC_ATTEN_DB_12(12dB) for attenuation. 73 | 12dB attenuation (ADC_ATTEN_DB_12) gives full-scale voltage 3.9V. 74 | But the range that can be measured accurately is as follows: 75 | - Measurable input voltage range for ESP32 is 150 mV ~ 2450 mV. 76 | - Measurable input voltage range for ESP32S2 is 0 mV ~ 2500 mV. 77 | - Measurable input voltage range for ESP32S3 is 0 mV ~ 3100 mV. 78 | - Measurable input voltage range for ESP32C3 is 0 mV ~ 2500 mV. 79 | - Measurable input voltage range for ESP32C2 is 0 mV ~ 2800 mV. 80 | 81 | 82 | # Analog source 83 | Connect ESP32 and Analog source using wire cable. 84 | I used a variable resistor for testing. 85 | ``` 86 | +---------------------------+ 87 | | variable resistor | 88 | ESP32 3.3V -----------------------------+ Ra of variable resistor | 89 | | | 90 | | | 91 | ESP32 GPIO32 -------------------------+---+ Vout of variable resistor | 92 | | | | 93 | R1 R2 R3 | | | 94 | ESP32 GND ----^^^--+--^^^--+--^^^--+ | | 95 | | | | | 96 | | | | | 97 | ESP32 GPIO33 ---------+ | | | 98 | | | | 99 | | | | 100 | ESP32 GPIO34 -----------------+ | | 101 | | | 102 | | | 103 | ESP32 GND -----------------------------+ Rb of variable resistor | 104 | | | 105 | +---------------------------+ 106 | ``` 107 | 108 | # Launch a web browser 109 | Enter the following in the address bar of your web browser. 110 | 111 | ``` 112 | http:://{IP of ESP32}/ 113 | or 114 | http://esp32-server.local/ 115 | ``` 116 | 117 | # How to display your sensor data 118 | Modify [this](https://github.com/nopnop2002/esp-idf-web-chart/blob/main/RadialGauge/main/web_client.c#L231) block to read data from your sensor. 119 | The ```timer-request``` is notified by the timer every second. 120 | ``` 121 | if ( strcmp (id, "timer-request") == 0) { 122 | // read from sensor data to voltage1, voltage2, voltage3 123 | sprintf(outBuffer,"DATA%c%d%c%d%c%d", DEL, voltage1, DEL, voltage2, DEL, voltage3); 124 | ws_server_send_text_all(outBuffer,strlen(outBuffer)); 125 | } // end if 126 | ``` 127 | 128 | # WEB Pages 129 | WEB pages are stored in the html folder. 130 | You can change it as you like. 131 | -------------------------------------------------------------------------------- /RadialGauge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /RadialGauge/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RadialGauge/README.md: -------------------------------------------------------------------------------- 1 | # Radial Gauge display using Canvas Gauge 2 | ![Image](https://github.com/user-attachments/assets/bab9cbba-28a4-4663-a18c-c5328fe60527) 3 | 4 | I used [this](https://canvas-gauges.com/) for gauge display. 5 | You can easily change the gauge design. 6 | 7 | -------------------------------------------------------------------------------- /RadialGauge/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /RadialGauge/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/RadialGauge/html/favicon.ico -------------------------------------------------------------------------------- /RadialGauge/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /RadialGauge/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | 8 | 9 | function sendText(name) { 10 | console.log('sendText'); 11 | var data = {}; 12 | data["id"] = name; 13 | console.log('data=', data); 14 | json_data = JSON.stringify(data); 15 | console.log('json_data=' + json_data); 16 | websocket.send(json_data); 17 | } 18 | 19 | websocket.onopen = function(evt) { 20 | console.log('WebSocket connection opened'); 21 | var data = {}; 22 | data["id"] = "init"; 23 | console.log('data=', data); 24 | json_data = JSON.stringify(data); 25 | console.log('json_data=' + json_data); 26 | websocket.send(json_data); 27 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 28 | } 29 | 30 | websocket.onmessage = function(evt) { 31 | var msg = evt.data; 32 | console.log("msg=" + msg); 33 | var values = msg.split('\4'); // \4 is EOT 34 | //console.log("values=" + values); 35 | switch(values[0]) { 36 | case 'HEAD': 37 | console.log("HEAD values[1]=" + values[1]); 38 | var h1 = document.getElementById( 'header' ); 39 | h1.textContent = values[1]; 40 | break; 41 | 42 | case 'METER': 43 | //console.log("gauge1=" + Object.keys(gauge1.options)); 44 | //console.log("gauge1.options.units=" + gauge1.options.units); 45 | console.log("METER values[1]=" + values[1]); 46 | console.log("METER values[2]=" + values[2]); 47 | console.log("METER values[3]=" + values[3]); 48 | if (values[1] != "") { 49 | gauge1.options.units = values[1]; 50 | document.getElementById("canvas1").style.display = "inline-block"; 51 | meter1 = 1; 52 | } 53 | if (values[2] != "") { 54 | gauge2.options.units = values[2]; 55 | document.getElementById("canvas2").style.display = "inline-block"; 56 | meter2 = 1; 57 | } 58 | if (values[3] != "") { 59 | gauge3.options.units = values[3]; 60 | document.getElementById("canvas3").style.display = "inline-block"; 61 | meter3 = 1; 62 | } 63 | break; 64 | 65 | case 'DATA': 66 | console.log("DATA values[1]=" + values[1]); 67 | var voltage1 = parseInt(values[1], 10); 68 | gauge1.value = voltage1; 69 | gauge1.update({ valueText: values[1] }); 70 | if (meter2) { 71 | console.log("DATA values[2]=" + values[2]); 72 | var voltage2 = parseInt(values[2], 10); 73 | gauge2.value = voltage2; 74 | gauge2.update({ valueText: values[2] }); 75 | } 76 | if (meter3) { 77 | console.log("DATA values[3]=" + values[3]); 78 | var voltage3 = parseInt(values[3], 10); 79 | gauge3.value = voltage3; 80 | gauge3.update({ valueText: values[3] }); 81 | } 82 | break; 83 | 84 | default: 85 | break; 86 | } 87 | } 88 | 89 | websocket.onclose = function(evt) { 90 | console.log('Websocket connection closed'); 91 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 92 | } 93 | 94 | websocket.onerror = function(evt) { 95 | console.log('Websocket error: ' + evt); 96 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 97 | } 98 | -------------------------------------------------------------------------------- /RadialGauge/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Canvas Gauge 8 | 9 | 10 | 11 |

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /RadialGauge/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /RadialGauge/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /RadialGauge/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /RadialGauge/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /RadialGauge/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /RadialGauge/main/web_server.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/message_buffer.h" 16 | #include "esp_log.h" 17 | 18 | #include "websocket_server.h" 19 | 20 | static QueueHandle_t client_queue; 21 | extern MessageBufferHandle_t xMessageBufferToClient; 22 | 23 | const static int client_queue_size = 10; 24 | 25 | // handles websocket events 26 | void websocket_callback(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) { 27 | const static char* TAG = "websocket_callback"; 28 | //int value; 29 | 30 | switch(type) { 31 | case WEBSOCKET_CONNECT: 32 | ESP_LOGI(TAG,"client %i connected!",num); 33 | break; 34 | case WEBSOCKET_DISCONNECT_EXTERNAL: 35 | ESP_LOGI(TAG,"client %i sent a disconnect message",num); 36 | break; 37 | case WEBSOCKET_DISCONNECT_INTERNAL: 38 | ESP_LOGI(TAG,"client %i was disconnected",num); 39 | break; 40 | case WEBSOCKET_DISCONNECT_ERROR: 41 | ESP_LOGI(TAG,"client %i was disconnected due to an error",num); 42 | break; 43 | case WEBSOCKET_TEXT: 44 | if(len) { // if the message length was greater than zero 45 | ESP_LOGI(TAG, "got message length %i: %s", (int)len, msg); 46 | size_t xBytesSent = xMessageBufferSendFromISR(xMessageBufferToClient, msg, len, NULL); 47 | if (xBytesSent != len) { 48 | ESP_LOGE(TAG, "xMessageBufferSend fail"); 49 | } 50 | } 51 | break; 52 | case WEBSOCKET_BIN: 53 | ESP_LOGI(TAG,"client %i sent binary message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 54 | break; 55 | case WEBSOCKET_PING: 56 | ESP_LOGI(TAG,"client %i pinged us with message of size %"PRIu32":\n%s",num,(uint32_t)len,msg); 57 | break; 58 | case WEBSOCKET_PONG: 59 | ESP_LOGI(TAG,"client %i responded to the ping",num); 60 | break; 61 | } 62 | } 63 | 64 | // serves any clients 65 | static void http_server(struct netconn *conn) { 66 | const static char* TAG = "http_server"; 67 | const static char HTML_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/html\n\n"; 68 | const static char ERROR_HEADER[] = "HTTP/1.1 404 Not Found\nContent-type: text/html\n\n"; 69 | const static char JS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/javascript\n\n"; 70 | const static char CSS_HEADER[] = "HTTP/1.1 200 OK\nContent-type: text/css\n\n"; 71 | //const static char PNG_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/png\n\n"; 72 | const static char ICO_HEADER[] = "HTTP/1.1 200 OK\nContent-type: image/x-icon\n\n"; 73 | //const static char PDF_HEADER[] = "HTTP/1.1 200 OK\nContent-type: application/pdf\n\n"; 74 | //const static char EVENT_HEADER[] = "HTTP/1.1 200 OK\nContent-Type: text/event-stream\nCache-Control: no-cache\nretry: 3000\n\n"; 75 | struct netbuf* inbuf; 76 | static char* buf; 77 | static uint16_t buflen; 78 | static err_t err; 79 | 80 | // default page 81 | extern const uint8_t root_html_start[] asm("_binary_root_html_start"); 82 | extern const uint8_t root_html_end[] asm("_binary_root_html_end"); 83 | const uint32_t root_html_len = root_html_end - root_html_start; 84 | 85 | // main.js 86 | extern const uint8_t main_js_start[] asm("_binary_main_js_start"); 87 | extern const uint8_t main_js_end[] asm("_binary_main_js_end"); 88 | const uint32_t main_js_len = main_js_end - main_js_start; 89 | 90 | // main.css 91 | extern const uint8_t main_css_start[] asm("_binary_main_css_start"); 92 | extern const uint8_t main_css_end[] asm("_binary_main_css_end"); 93 | const uint32_t main_css_len = main_css_end - main_css_start; 94 | 95 | // favicon.ico 96 | extern const uint8_t favicon_ico_start[] asm("_binary_favicon_ico_start"); 97 | extern const uint8_t favicon_ico_end[] asm("_binary_favicon_ico_end"); 98 | const uint32_t favicon_ico_len = favicon_ico_end - favicon_ico_start; 99 | 100 | // error page 101 | extern const uint8_t error_html_start[] asm("_binary_error_html_start"); 102 | extern const uint8_t error_html_end[] asm("_binary_error_html_end"); 103 | const uint32_t error_html_len = error_html_end - error_html_start; 104 | 105 | netconn_set_recvtimeout(conn,1000); // allow a connection timeout of 1 second 106 | ESP_LOGI(TAG,"reading from client..."); 107 | err = netconn_recv(conn, &inbuf); 108 | ESP_LOGI(TAG,"read from client"); 109 | if(err==ERR_OK) { 110 | netbuf_data(inbuf, (void**)&buf, &buflen); 111 | if(buf) { 112 | 113 | ESP_LOGD(TAG, "buf=[%s]", buf); 114 | // default page 115 | if (strstr(buf,"GET / ") 116 | && !strstr(buf,"Upgrade: websocket")) { 117 | ESP_LOGI(TAG,"Sending /"); 118 | netconn_write(conn, HTML_HEADER, sizeof(HTML_HEADER)-1,NETCONN_NOCOPY); 119 | netconn_write(conn, root_html_start,root_html_len,NETCONN_NOCOPY); 120 | netconn_close(conn); 121 | netconn_delete(conn); 122 | netbuf_delete(inbuf); 123 | } 124 | 125 | // default page websocket 126 | else if(strstr(buf,"GET / ") 127 | && strstr(buf,"Upgrade: websocket")) { 128 | ESP_LOGI(TAG,"Requesting websocket on /"); 129 | ws_server_add_client(conn,buf,buflen,"/",websocket_callback); 130 | netbuf_delete(inbuf); 131 | } 132 | 133 | else if(strstr(buf,"GET /main.js ")) { 134 | ESP_LOGI(TAG,"Sending /main.js"); 135 | netconn_write(conn, JS_HEADER, sizeof(JS_HEADER)-1,NETCONN_NOCOPY); 136 | netconn_write(conn, main_js_start, main_js_len,NETCONN_NOCOPY); 137 | netconn_close(conn); 138 | netconn_delete(conn); 139 | netbuf_delete(inbuf); 140 | } 141 | 142 | else if(strstr(buf,"GET /main.css ")) { 143 | ESP_LOGI(TAG,"Sending /main.css"); 144 | netconn_write(conn, CSS_HEADER, sizeof(CSS_HEADER)-1,NETCONN_NOCOPY); 145 | netconn_write(conn, main_css_start, main_css_len,NETCONN_NOCOPY); 146 | netconn_close(conn); 147 | netconn_delete(conn); 148 | netbuf_delete(inbuf); 149 | } 150 | 151 | else if(strstr(buf,"GET /favicon.ico ")) { 152 | ESP_LOGI(TAG,"Sending favicon.ico"); 153 | netconn_write(conn,ICO_HEADER,sizeof(ICO_HEADER)-1,NETCONN_NOCOPY); 154 | netconn_write(conn,favicon_ico_start,favicon_ico_len,NETCONN_NOCOPY); 155 | netconn_close(conn); 156 | netconn_delete(conn); 157 | netbuf_delete(inbuf); 158 | } 159 | 160 | else if(strstr(buf,"GET /")) { 161 | ESP_LOGE(TAG,"Unknown request, sending error page: %s",buf); 162 | netconn_write(conn, ERROR_HEADER, sizeof(ERROR_HEADER)-1,NETCONN_NOCOPY); 163 | netconn_write(conn, error_html_start, error_html_len,NETCONN_NOCOPY); 164 | netconn_close(conn); 165 | netconn_delete(conn); 166 | netbuf_delete(inbuf); 167 | } 168 | 169 | else { 170 | ESP_LOGE(TAG,"Unknown request"); 171 | netconn_close(conn); 172 | netconn_delete(conn); 173 | netbuf_delete(inbuf); 174 | } 175 | } 176 | else { 177 | ESP_LOGI(TAG,"Unknown request (empty?...)"); 178 | netconn_close(conn); 179 | netconn_delete(conn); 180 | netbuf_delete(inbuf); 181 | } 182 | } 183 | else { // if err==ERR_OK 184 | ESP_LOGI(TAG,"error on read, closing connection"); 185 | netconn_close(conn); 186 | netconn_delete(conn); 187 | netbuf_delete(inbuf); 188 | } 189 | } 190 | 191 | // receives clients from queue, handles them 192 | void server_handle_task(void* pvParameters) { 193 | const static char* TAG = "server_handle_task"; 194 | struct netconn* conn; 195 | ESP_LOGI(TAG,"task starting"); 196 | for(;;) { 197 | xQueueReceive(client_queue,&conn,portMAX_DELAY); 198 | if(!conn) continue; 199 | http_server(conn); 200 | } 201 | vTaskDelete(NULL); 202 | } 203 | 204 | // handles clients when they first connect. passes to a queue 205 | void server_task(void* pvParameters) { 206 | const static char* TAG = "server_task"; 207 | char *task_parameter = (char *)pvParameters; 208 | ESP_LOGI(TAG, "Start task_parameter=%s", task_parameter); 209 | char url[64]; 210 | sprintf(url, "http://%s", task_parameter); 211 | ESP_LOGI(TAG, "Starting server on %s", url); 212 | 213 | struct netconn *conn, *newconn; 214 | static err_t err; 215 | client_queue = xQueueCreate(client_queue_size,sizeof(struct netconn*)); 216 | configASSERT( client_queue ); 217 | 218 | 219 | UBaseType_t PriorityGet = uxTaskPriorityGet(NULL); 220 | ESP_LOGI(TAG, "PriorityGet=%d", PriorityGet); 221 | xTaskCreate(&server_handle_task, "server_handle_task", 1024*3, NULL, PriorityGet, NULL); 222 | 223 | 224 | conn = netconn_new(NETCONN_TCP); 225 | netconn_bind(conn,NULL,80); 226 | netconn_listen(conn); 227 | ESP_LOGI(TAG,"server listening"); 228 | do { 229 | err = netconn_accept(conn, &newconn); 230 | ESP_LOGI(TAG,"new client"); 231 | if(err == ERR_OK) { 232 | xQueueSendToBack(client_queue,&newconn,portMAX_DELAY); 233 | //http_server(newconn); 234 | } 235 | } while(err == ERR_OK); 236 | netconn_close(conn); 237 | netconn_delete(conn); 238 | ESP_LOGE(TAG,"task ending, rebooting board"); 239 | esp_restart(); 240 | } 241 | -------------------------------------------------------------------------------- /SevenSegment/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /SevenSegment/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SevenSegment/README.md: -------------------------------------------------------------------------------- 1 | # SevenSegment view 2 | ![Image](https://github.com/user-attachments/assets/f2f0d663-6868-4c41-bacc-9d495e1556c8) 3 | 4 | I used [this](https://github.com/CodeDrome/seven-segment-display-javascript) for segment display. 5 | You can easily change the segment color and number of digits. 6 | ![Image](https://github.com/user-attachments/assets/df79972b-5e53-43f6-b672-07d766ad793c) 7 | ![Image](https://github.com/user-attachments/assets/c1325027-0794-4157-a3cd-9cb1f6a4ab7c) 8 | ![Image](https://github.com/user-attachments/assets/c8e8a5b2-4e4d-41a7-95e9-8a02bcf447e1) 9 | ![Image](https://github.com/user-attachments/assets/cfb7398f-c3fe-47e4-bc46-67aba6504399) 10 | ![Image](https://github.com/user-attachments/assets/ba5f217d-b564-47da-bc9b-252a50213a4e) 11 | ![Image](https://github.com/user-attachments/assets/501b2c0e-3f95-416d-b517-2aff480a0b4d) 12 | 13 | The display unit can be changed from mV to V. 14 | ![Image](https://github.com/user-attachments/assets/0e935f0e-541c-4528-94b6-b1782e22a7da) 15 | ![Image](https://github.com/user-attachments/assets/55782e9c-6bc2-41ab-9f9c-0211ba63b247) 16 | -------------------------------------------------------------------------------- /SevenSegment/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /SevenSegment/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/SevenSegment/html/favicon.ico -------------------------------------------------------------------------------- /SevenSegment/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /SevenSegment/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | var segment1 = new SevenSegmentDisplay("canvas1"); 8 | var segment2 = new SevenSegmentDisplay("canvas2"); 9 | var segment3 = new SevenSegmentDisplay("canvas3"); 10 | var gpio1 = ""; 11 | var gpio2 = ""; 12 | var gpio3 = ""; 13 | var uint = 1; 14 | 15 | function sendText(name) { 16 | console.log('sendText'); 17 | var data = {}; 18 | data["id"] = name; 19 | console.log('data=', data); 20 | json_data = JSON.stringify(data); 21 | console.log('json_data=' + json_data); 22 | websocket.send(json_data); 23 | } 24 | 25 | window.onload = function() { 26 | console.log("onload"); 27 | // ColorScheme is 1 to 6 28 | segment1.ColorScheme = 2; 29 | segment1.DecimalPointType = 2; 30 | segment1.NumberOfDecimalPlaces = 0; 31 | segment1.NumberOfDigits = 4; 32 | 33 | segment2.ColorScheme = 3; 34 | segment2.DecimalPointType = 2; 35 | segment2.NumberOfDecimalPlaces = 0; 36 | segment2.NumberOfDigits = 4; 37 | 38 | segment3.ColorScheme = 4; 39 | segment3.DecimalPointType = 2; 40 | segment3.NumberOfDecimalPlaces = 0; 41 | segment3.NumberOfDigits = 4; 42 | }; 43 | 44 | websocket.onopen = function(evt) { 45 | console.log('WebSocket connection opened'); 46 | var data = {}; 47 | data["id"] = "init"; 48 | console.log('data=', data); 49 | json_data = JSON.stringify(data); 50 | console.log('json_data=' + json_data); 51 | websocket.send(json_data); 52 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 53 | } 54 | 55 | websocket.onmessage = function(evt) { 56 | var msg = evt.data; 57 | console.log("msg=" + msg); 58 | var values = msg.split('\4'); // \4 is EOT 59 | //console.log("values=" + values); 60 | switch(values[0]) { 61 | case 'HEAD': 62 | console.log("HEAD values[1]=" + values[1]); 63 | var h1 = document.getElementById( 'header' ); 64 | h1.textContent = values[1]; 65 | break; 66 | 67 | case 'METER': 68 | console.log("METER values[1]=" + values[1]); 69 | console.log("METER values[2]=" + values[2]); 70 | console.log("METER values[3]=" + values[3]); 71 | if (values[1] == "") { 72 | document.getElementById("label1").style.display = "none"; 73 | document.getElementById("canvas1").style.display = "none"; 74 | } else { 75 | meter1 = 1; 76 | gpio1 = values[1]; 77 | document.getElementById("label1").style.display = "inline-block"; 78 | document.getElementById("label1").innerText = gpio1 + " [mV]"; 79 | document.getElementById("canvas1").style.display = "inline-block"; 80 | } 81 | if (values[2] == "") { 82 | document.getElementById("label2").style.display = "none"; 83 | document.getElementById("canvas2").style.display = "none"; 84 | } else { 85 | meter2 = 1; 86 | gpio2 = values[2]; 87 | document.getElementById("label2").style.display = "inline-block"; 88 | document.getElementById("label2").innerText = gpio2 + " [mV]"; 89 | document.getElementById("canvas2").style.display = "inline-block"; 90 | } 91 | if (values[3] == "") { 92 | document.getElementById("label3").style.display = "none"; 93 | document.getElementById("canvas3").style.display = "none"; 94 | } else { 95 | meter3 = 1; 96 | gpio3 = values[3]; 97 | document.getElementById("label3").style.display = "inline-block"; 98 | document.getElementById("label3").innerText = gpio3 + " [mV]"; 99 | document.getElementById("canvas3").style.display = "inline-block"; 100 | } 101 | break; 102 | 103 | case 'UNIT': 104 | console.log("UNIT"); 105 | uint = 1000; 106 | segment1.NumberOfDecimalPlaces = 3; 107 | segment2.NumberOfDecimalPlaces = 3; 108 | segment3.NumberOfDecimalPlaces = 3; 109 | document.getElementById("label1").innerText = gpio1 + " [V]"; 110 | document.getElementById("label2").innerText = gpio2 + " [V]"; 111 | document.getElementById("label3").innerText = gpio3 + " [V]"; 112 | break; 113 | 114 | case 'DATA': 115 | console.log("DATA values[1]=" + values[1]); 116 | var voltage1 = parseInt(values[1], 10); 117 | segment1.Value = voltage1 / uint; 118 | if (meter2) { 119 | console.log("DATA values[2]=" + values[2]); 120 | var voltage2 = parseInt(values[2], 10); 121 | segment2.Value = voltage2 / uint; 122 | } 123 | if (meter3) { 124 | console.log("DATA values[3]=" + values[3]); 125 | var voltage3 = parseInt(values[3], 10); 126 | segment3.Value = voltage3 / uint; 127 | } 128 | break; 129 | 130 | default: 131 | break; 132 | } 133 | } 134 | 135 | websocket.onclose = function(evt) { 136 | console.log('Websocket connection closed'); 137 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 138 | } 139 | 140 | websocket.onerror = function(evt) { 141 | console.log('Websocket error: ' + evt); 142 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 143 | } 144 | -------------------------------------------------------------------------------- /SevenSegment/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | Seven Segments 9 | 10 | 11 | 12 |

13 | 14 | 15 |
16 | 19 | 20 |
21 | 22 | 23 |
24 | 27 | 28 |
29 | 30 | 31 |
32 | 35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /SevenSegment/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/seven_segment_display.js" 6 | "../html/main.js" 7 | "../html/root.html" 8 | "../html/main.css") 9 | -------------------------------------------------------------------------------- /SevenSegment/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | choice UNIT 126 | prompt "Unit to display" 127 | default UNIT_MV 128 | help 129 | Select Unit to display. 130 | config UNIT_MV 131 | bool "mV unit" 132 | help 133 | mV unit. 134 | config UNIT_V 135 | bool "V unit" 136 | endchoice 137 | 138 | endmenu 139 | 140 | endmenu 141 | 142 | -------------------------------------------------------------------------------- /SevenSegment/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /SevenSegment/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /SevenSegment/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | -------------------------------------------------------------------------------- /VerticalLinearGauge/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following five lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(web-analog) 7 | -------------------------------------------------------------------------------- /VerticalLinearGauge/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 nopnop2002 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /VerticalLinearGauge/README.md: -------------------------------------------------------------------------------- 1 | # Vertical Linear Gauge display using Canvas Gauge 2 | ![Image](https://github.com/user-attachments/assets/ab306473-b771-4c63-a8e2-9f6081f401dd) 3 | 4 | I used [this](https://canvas-gauges.com/) for gauge display. 5 | You can easily change the gauge design. 6 | 7 | -------------------------------------------------------------------------------- /VerticalLinearGauge/html/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ESP32 Error 404 5 | 6 | 7 | 8 | 9 | 10 | 11 |

Error 404

12 |

Unknown page. Return home.

13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /VerticalLinearGauge/html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nopnop2002/esp-idf-web-chart/ce073d46522b28e6a06e84bd34ece938afb5857a/VerticalLinearGauge/html/favicon.ico -------------------------------------------------------------------------------- /VerticalLinearGauge/html/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | Your favorite style is defined here. 3 | */ 4 | -------------------------------------------------------------------------------- /VerticalLinearGauge/html/main.js: -------------------------------------------------------------------------------- 1 | //document.getElementById("datetime").innerHTML = "WebSocket is not connected"; 2 | 3 | var websocket = new WebSocket('ws://'+location.hostname+'/'); 4 | var meter1 = 0; 5 | var meter2 = 0; 6 | var meter3 = 0; 7 | 8 | 9 | function sendText(name) { 10 | console.log('sendText'); 11 | var data = {}; 12 | data["id"] = name; 13 | console.log('data=', data); 14 | json_data = JSON.stringify(data); 15 | console.log('json_data=' + json_data); 16 | websocket.send(json_data); 17 | } 18 | 19 | websocket.onopen = function(evt) { 20 | console.log('WebSocket connection opened'); 21 | var data = {}; 22 | data["id"] = "init"; 23 | console.log('data=', data); 24 | json_data = JSON.stringify(data); 25 | console.log('json_data=' + json_data); 26 | websocket.send(json_data); 27 | //document.getElementById("datetime").innerHTML = "WebSocket is connected!"; 28 | } 29 | 30 | websocket.onmessage = function(evt) { 31 | var msg = evt.data; 32 | console.log("msg=" + msg); 33 | var values = msg.split('\4'); // \4 is EOT 34 | //console.log("values=" + values); 35 | switch(values[0]) { 36 | case 'HEAD': 37 | console.log("HEAD values[1]=" + values[1]); 38 | var h1 = document.getElementById( 'header' ); 39 | h1.textContent = values[1]; 40 | break; 41 | 42 | case 'METER': 43 | //console.log("gauge1=" + Object.keys(gauge1.options)); 44 | //console.log("gauge1.options.units=" + gauge1.options.units); 45 | console.log("METER values[1]=" + values[1]); 46 | console.log("METER values[2]=" + values[2]); 47 | console.log("METER values[3]=" + values[3]); 48 | if (values[1] != "") { 49 | gauge1.options.units = values[1]; 50 | document.getElementById("canvas1").style.display = "inline-block"; 51 | meter1 = 1; 52 | } 53 | if (values[2] != "") { 54 | gauge2.options.units = values[2]; 55 | document.getElementById("canvas2").style.display = "inline-block"; 56 | meter2 = 1; 57 | } 58 | if (values[3] != "") { 59 | gauge3.options.units = values[3]; 60 | document.getElementById("canvas3").style.display = "inline-block"; 61 | meter3 = 1; 62 | } 63 | break; 64 | 65 | case 'DATA': 66 | console.log("DATA values[1]=" + values[1]); 67 | var voltage1 = parseInt(values[1], 10); 68 | gauge1.value = voltage1; 69 | gauge1.update({ valueText: values[1] }); 70 | if (meter2) { 71 | console.log("DATA values[2]=" + values[2]); 72 | var voltage2 = parseInt(values[2], 10); 73 | gauge2.value = voltage2; 74 | gauge2.update({ valueText: values[2] }); 75 | } 76 | if (meter3) { 77 | console.log("DATA values[3]=" + values[3]); 78 | var voltage3 = parseInt(values[3], 10); 79 | gauge3.value = voltage3; 80 | gauge3.update({ valueText: values[3] }); 81 | } 82 | break; 83 | 84 | default: 85 | break; 86 | } 87 | } 88 | 89 | websocket.onclose = function(evt) { 90 | console.log('Websocket connection closed'); 91 | //document.getElementById("datetime").innerHTML = "WebSocket closed"; 92 | } 93 | 94 | websocket.onerror = function(evt) { 95 | console.log('Websocket error: ' + evt); 96 | //document.getElementById("datetime").innerHTML = "WebSocket error????!!!1!!"; 97 | } 98 | -------------------------------------------------------------------------------- /VerticalLinearGauge/html/root.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Canvas Gauge 8 | 9 | 10 | 11 |

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /VerticalLinearGauge/main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | idf_component_register(SRCS "main.c" "web_server.c" "web_client.c" 2 | INCLUDE_DIRS "." 3 | EMBED_FILES "../html/error.html" 4 | "../html/favicon.ico" 5 | "../html/main.js" 6 | "../html/root.html" 7 | "../html/main.css") 8 | -------------------------------------------------------------------------------- /VerticalLinearGauge/main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "Application configuration" 2 | 3 | config GPIO_RANGE_MIN 4 | int 5 | default 32 if IDF_TARGET_ESP32 6 | default 1 if IDF_TARGET_ESP32S2 7 | default 1 if IDF_TARGET_ESP32S3 8 | default 0 if IDF_TARGET_ESP32C2 9 | default 0 if IDF_TARGET_ESP32C3 10 | default 0 if IDF_TARGET_ESP32C6 11 | 12 | config GPIO_RANGE_MAX 13 | int 14 | default 39 if IDF_TARGET_ESP32 15 | default 10 if IDF_TARGET_ESP32S2 16 | default 10 if IDF_TARGET_ESP32S3 17 | default 4 if IDF_TARGET_ESP32C2 18 | default 4 if IDF_TARGET_ESP32C3 19 | default 6 if IDF_TARGET_ESP32C6 20 | 21 | menu "WiFi Setting" 22 | 23 | config ESP_WIFI_SSID 24 | string "WiFi SSID" 25 | default "myssid" 26 | help 27 | SSID (network name) to connect to. 28 | 29 | config ESP_WIFI_PASSWORD 30 | string "WiFi Password" 31 | default "mypassword" 32 | help 33 | WiFi password (WPA or WPA2) to connect to. 34 | 35 | config ESP_MAXIMUM_RETRY 36 | int "Maximum retry" 37 | default 5 38 | help 39 | Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent. 40 | 41 | config MDNS_HOSTNAME 42 | string "mDNS Hostname" 43 | default "esp32-server" 44 | help 45 | The mDNS host name used by the ESP32. 46 | 47 | endmenu 48 | 49 | menu "ADC Setting" 50 | 51 | config METER1_GPIO 52 | int "GPIO for ADC1" 53 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 54 | default 32 if IDF_TARGET_ESP32 55 | default 1 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 56 | default 0 # C3 and others 57 | help 58 | ADC1_CHANNEL number. 59 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 60 | On the ESP32, 8 channels: GPIO32 - GPIO39. 61 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 62 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 63 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 64 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 65 | 66 | config ENABLE_METER2 67 | bool "Enable METER2" 68 | default n 69 | help 70 | Enable Meter2. 71 | 72 | config METER2_GPIO 73 | depends on ENABLE_METER2 74 | int "GPIO for METER2" 75 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 76 | default 33 if IDF_TARGET_ESP32 77 | default 2 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 78 | default 1 # C3 and others 79 | help 80 | ADC1_CHANNEL number. 81 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 82 | On the ESP32, 8 channels: GPIO32 - GPIO39. 83 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 84 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 85 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 86 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 87 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 88 | 89 | config ENABLE_METER3 90 | bool "Enable METER3" 91 | default n 92 | help 93 | Enable Meter3. 94 | 95 | config METER3_GPIO 96 | depends on ENABLE_METER3 97 | int "GPIO for METER3" 98 | range GPIO_RANGE_MIN GPIO_RANGE_MAX 99 | default 34 if IDF_TARGET_ESP32 100 | default 3 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 101 | default 2 # C3 and others 102 | help 103 | ADC1_CHANNEL number. 104 | Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to ADC. 105 | On the ESP32, 8 channels: GPIO32 - GPIO39. 106 | On the ESP32-S2, 10 channels: GPIO1 - GPIO10. 107 | On the ESP32-S3, 10 channels: GPIO1 - GPIO10. 108 | On the ESP32-C2, 5 channels: GPIO0 - GPIO4. 109 | On the ESP32-C3, 5 channels: GPIO0 - GPIO4. 110 | On the ESP32-C6, 7 channels: GPIO0 - GPIO6. 111 | 112 | config ADC_CYCLE 113 | int "ADC measurement cycle tick" 114 | range 100 1000 115 | default 100 116 | help 117 | ADC measurement cycle tick. 118 | 119 | config ENABLE_STDOUT 120 | bool "Enable STDOUT" 121 | default n 122 | help 123 | Enable STDOUT. 124 | 125 | endmenu 126 | 127 | endmenu 128 | 129 | -------------------------------------------------------------------------------- /VerticalLinearGauge/main/component.mk: -------------------------------------------------------------------------------- 1 | COMPONENT_EMBED_FILES := ../html/error.html 2 | COMPONENT_EMBED_FILES += ../html/favicon.ico 3 | COMPONENT_EMBED_FILES += ../html/main.js 4 | COMPONENT_EMBED_FILES += ../html/root.html 5 | COMPONENT_EMBED_FILES += ../html/bulma.css 6 | COMPONENT_EMBED_FILES += ../html/main.css 7 | -------------------------------------------------------------------------------- /VerticalLinearGauge/main/idf_component.yml: -------------------------------------------------------------------------------- 1 | ## IDF Component Manager Manifest File 2 | dependencies: 3 | Molorius/esp32-websocket: 4 | git: https://github.com/Molorius/esp32-websocket 5 | espressif/mdns: 6 | version: "^1.0.3" 7 | rules: 8 | - if: "idf_version >=5.0" 9 | -------------------------------------------------------------------------------- /VerticalLinearGauge/main/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | Example using WEB Socket. 3 | This example code is in the Public Domain (or CC0 licensed, at your option.) 4 | Unless required by applicable law or agreed to in writing, this 5 | software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 6 | CONDITIONS OF ANY KIND, either express or implied. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "freertos/FreeRTOS.h" 13 | #include "freertos/task.h" 14 | #include "freertos/queue.h" 15 | #include "freertos/event_groups.h" 16 | #include "freertos/message_buffer.h" 17 | 18 | #include "esp_wifi.h" 19 | #include "esp_log.h" 20 | #include "nvs_flash.h" 21 | #include "mdns.h" 22 | 23 | #include "websocket_server.h" 24 | 25 | MessageBufferHandle_t xMessageBufferToClient; 26 | 27 | /* FreeRTOS event group to signal when we are connected*/ 28 | static EventGroupHandle_t s_wifi_event_group; 29 | 30 | /* The event group allows multiple bits for each event, but we only care about two events: 31 | * - we are connected to the AP with an IP 32 | * - we failed to connect after the maximum amount of retries */ 33 | #define WIFI_CONNECTED_BIT BIT0 34 | #define WIFI_FAIL_BIT BIT1 35 | 36 | static const char *TAG = "main"; 37 | 38 | static int s_retry_num = 0; 39 | 40 | static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) 41 | { 42 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 43 | esp_wifi_connect(); 44 | } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 45 | if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { 46 | esp_wifi_connect(); 47 | s_retry_num++; 48 | ESP_LOGI(TAG, "retry to connect to the AP"); 49 | } else { 50 | xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT); 51 | } 52 | ESP_LOGI(TAG,"connect to the AP fail"); 53 | } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 54 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 55 | ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 56 | s_retry_num = 0; 57 | xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 58 | } 59 | } 60 | 61 | void wifi_init_sta(void) 62 | { 63 | s_wifi_event_group = xEventGroupCreate(); 64 | 65 | ESP_ERROR_CHECK(esp_netif_init()); 66 | 67 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 68 | esp_netif_create_default_wifi_sta(); 69 | 70 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 71 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 72 | 73 | esp_event_handler_instance_t instance_any_id; 74 | esp_event_handler_instance_t instance_got_ip; 75 | ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, 76 | ESP_EVENT_ANY_ID, 77 | &event_handler, 78 | NULL, 79 | &instance_any_id)); 80 | ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, 81 | IP_EVENT_STA_GOT_IP, 82 | &event_handler, 83 | NULL, 84 | &instance_got_ip)); 85 | 86 | wifi_config_t wifi_config = { 87 | .sta = { 88 | .ssid = CONFIG_ESP_WIFI_SSID, 89 | .password = CONFIG_ESP_WIFI_PASSWORD, 90 | /* Setting a password implies station will connect to all security modes including WEP/WPA. 91 | * However these modes are deprecated and not advisable to be used. Incase your Access point 92 | * doesn't support WPA2, these mode can be enabled by commenting below line */ 93 | .threshold.authmode = WIFI_AUTH_WPA2_PSK, 94 | 95 | .pmf_cfg = { 96 | .capable = true, 97 | .required = false 98 | }, 99 | }, 100 | }; 101 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 102 | ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); 103 | ESP_ERROR_CHECK(esp_wifi_start() ); 104 | 105 | ESP_LOGI(TAG, "wifi_init_sta finished."); 106 | 107 | /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum 108 | * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ 109 | EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 110 | WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 111 | pdFALSE, 112 | pdFALSE, 113 | portMAX_DELAY); 114 | 115 | /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually 116 | * happened. */ 117 | if (bits & WIFI_CONNECTED_BIT) { 118 | ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 119 | } else if (bits & WIFI_FAIL_BIT) { 120 | ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD); 121 | } else { 122 | ESP_LOGE(TAG, "UNEXPECTED EVENT"); 123 | } 124 | 125 | /* The event will not be processed after unregister */ 126 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip)); 127 | ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id)); 128 | vEventGroupDelete(s_wifi_event_group); 129 | } 130 | 131 | void initialise_mdns(void) 132 | { 133 | //initialize mDNS 134 | ESP_ERROR_CHECK( mdns_init() ); 135 | //set mDNS hostname (required if you want to advertise services) 136 | ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) ); 137 | ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME); 138 | 139 | //initialize service 140 | ESP_ERROR_CHECK( mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0) ); 141 | 142 | #if 0 143 | //set default mDNS instance name 144 | ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") ); 145 | #endif 146 | } 147 | 148 | void client_task(void* pvParameters); 149 | void server_task(void* pvParameters); 150 | 151 | void app_main() { 152 | // Initialize NVS 153 | esp_err_t ret = nvs_flash_init(); 154 | if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { 155 | ESP_ERROR_CHECK(nvs_flash_erase()); 156 | ret = nvs_flash_init(); 157 | } 158 | ESP_ERROR_CHECK(ret); 159 | 160 | // Initialize WiFi 161 | wifi_init_sta(); 162 | 163 | // Initialize mDNS 164 | initialise_mdns(); 165 | 166 | // Create Message Buffer 167 | xMessageBufferToClient = xMessageBufferCreate(1024); 168 | configASSERT( xMessageBufferToClient ); 169 | 170 | // Get the local IP address 171 | esp_netif_ip_info_t ip_info; 172 | ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info)); 173 | char cparam0[64]; 174 | sprintf(cparam0, IPSTR, IP2STR(&ip_info.ip)); 175 | 176 | // Start web socket server 177 | ws_server_start(); 178 | 179 | // Start web server 180 | xTaskCreate(&server_task, "server_task", 1024*4, (void *)cparam0, 5, NULL); 181 | 182 | // Start web client 183 | xTaskCreate(&client_task, "client_task", 1024*4, NULL, 5, NULL); 184 | 185 | vTaskDelay(100); 186 | } 187 | --------------------------------------------------------------------------------