diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..114213b
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,12 @@
+# The following lines of boilerplate have to be in your project's CMakeLists
+# in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.5)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(can2http)
+
+# Create a SPIFFS image from the contents of the 'font' directory
+# that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that
+# the generated image should be flashed when the entire project is flashed to
+# the target with 'idf.py -p PORT flash
+spiffs_create_partition_image(storage csv FLASH_IN_PROJECT)
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..38c808b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+#
+# This is a project Makefile. It is assumed the directory this Makefile resides in is a
+# project subdirectory.
+#
+
+PROJECT_NAME := can2mqtt
+
+include $(IDF_PATH)/make/project.mk
+
+# Create a SPIFFS image from the contents of the 'spiffs_image' directory
+# that fits the partition named 'storage'. FLASH_IN_PROJECT indicates that
+# the generated image should be flashed when the entire project is flashed to
+# the target with 'make flash'.
+SPIFFS_IMAGE_FLASH_IN_PROJECT := 1
+$(eval $(call spiffs_create_partition_image,storage,csv))
+
diff --git a/README.md b/README.md
index 67c1c7c..8a800f3 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,174 @@
# esp-idf-can2http
-CANbus to http bridge using esp32
+CANbus to http bridge using esp32.
+It's purpose is to be a bridge between a CAN-Bus and a HTTP-Server.
+
+I inspired from [here](https://github.com/c3re/can2mqtt).
+
+
+
+
+# Software requirement
+esp-idf v4.2-dev-2243 or later.
+Use twai(Two-Wire Automotive Interface) driver instead of can driver.
+
+# Hardware requirements
+1. SN65HVD23x CAN-BUS Transceiver
+
+|SN65HVD23x||ESP32|ESP32-S2||
+|:-:|:-:|:-:|:-:|:-:|
+|D(CTX)|--|GPIO21|GPIO20|(*1)|
+|GND|--|GND|GND||
+|Vcc|--|3.3V|3.3V||
+|R(CRX)|--|GPIO22|GPIO21|(*1)|
+|Vref|--|N/C|N/C||
+|CANL|--|||To CAN Bus|
+|CANH|--|||To CAN Bus|
+|RS|--|GND|GND|(*2)|
+
+(*1) You can change using menuconfig.
+
+(*2) N/C for SN65HVD232
+
+2. Termination resistance
+I used 150 ohms.
+
+
+# Test Circuit
+```
+ +-----------+ +-----------+ +-----------+
+ | Atmega328 | | Atmega328 | | ESP32 |
+ | | | | | |
+ | Transmit | | Receive | | 21 22 |
+ +-----------+ +-----------+ +-----------+
+ | | | | | |
+ +-----------+ +-----------+ | |
+ | | | | | |
+ | MCP2515 | | MCP2515 | | |
+ | | | | | |
+ +-----------+ +-----------+ | |
+ | | | | | |
+ +-----------+ +-----------+ +-----------+
+ | | | | | D R |
+ | MCP2551 | | MCP2551 | | VP230 |
+ | H L | | H L | | H L |
+ +-----------+ +-----------+ +-----------+
+ | | | | | |
+ +--^^^--+ | | +--^^^--+
+ | R1 | | | | R2 |
+ |---+-------|-----+-------|-----+-------|---| BackBorn H
+ | | |
+ | | |
+ | | |
+ |-----------+-------------+-------------+---| BackBorn L
+
+ +--^^^--+:Terminaror register
+ R1:120 ohms
+ R2:150 ohms(Not working at 120 ohms)
+```
+
+__NOTE__
+3V CAN Trasnceviers like VP230 are fully interoperable with 5V CAN trasnceviers like MCP2551.
+Check [here](http://www.ti.com/lit/an/slla337/slla337.pdf).
+
+
+# Installation for ESP32
+```
+git clone https://github.com/nopnop2002/esp-idf-can2http
+cd esp-idf-can2htp
+idf.py set-target esp32
+idf.py menuconfig
+idf.py flash
+```
+
+# Installation for ESP32-S2
+```
+git clone https://github.com/nopnop2002/esp-idf-can2http
+cd esp-idf-can2http
+idf.py set-target esp32s2
+idf.py menuconfig
+idf.py flash
+```
+
+# Configuration
+
+
+
+## CAN Setting
+
+
+## WiFi Setting
+
+
+## HTTP Server Setting
+
+
+# Definition from CANbus to HTTP
+When CANbus data is received, it is sent by HTTP according to csv/can2http.csv.
+The file can2http.csv has three columns.
+In the first column you need to specify the CAN Frame type.
+The CAN frame type is either S(Standard frame) or E(Extended frame).
+In the second column you have to specify the CAN-ID as a __hexdecimal number__.
+In the last column you have to specify the HTTP-Path.
+Each CAN-ID and each HTTP-Path is allowed to appear only once in the whole file.
+
+```
+S,101,/post
+E,101,/post
+S,103,/post
+E,103,/post
+```
+
+When a Standard CAN frame with ID 0x101 is received, it is POST by PATH of "/post".
+When a Extended CAN frame with ID 0x101 is received, it is POST by PATH of "/post".
+http://{HTTP-Server-IP}:{HTTP-Server-Port}/post/{CAN-Data}
+
+# HTTP Server Using Tornado
+```
+sudo apt install python3-pip python3-setuptools python3-magic
+python -m pip install -U pip
+python -m pip install -U wheel
+python -m pip install tornado
+cd tornado
+python can.py
+```
+
+
+# HTTP Server Using Flask
+```
+sudo apt install python3-pip python3-setuptools python3-magic
+python -m pip install -U pip
+python -m pip install -U wheel
+python -m pip install -U Werkzeug
+python -m pip install flask
+cd flask
+python can.py
+```
+
+
+
+
+# List data Using Brouser
+Open your browser and put the IP address in the address bar.
+
+
+
+# Troubleshooting
+There is a module of SN65HVD230 like this.
+
+
+There is a __120 ohms__ terminating resistor on the left side.
+
+
+I have removed the terminating resistor.
+And I used a external resistance of __150 ohms__.
+A transmission fail is fixed.
+
+
+If the transmission fails, these are the possible causes.
+- There is no receiving app on CanBus.
+- The speed does not match the receiver.
+- There is no terminating resistor on the CanBus.
+- There are three terminating resistors on the CanBus.
+- The resistance value of the terminating resistor is incorrect.
+- Stub length in CAN bus is too long. See [here](https://e2e.ti.com/support/interface-group/interface/f/interface-forum/378932/iso1050-can-bus-stub-length).
+
diff --git a/csv/can2http.csv b/csv/can2http.csv
new file mode 100644
index 0000000..df7422e
--- /dev/null
+++ b/csv/can2http.csv
@@ -0,0 +1,11 @@
+#The file can2http.csv has three columns.
+#In the first column you need to specify the CAN Frame type.
+#The CAN frame type is either S(Standard frame) or E(Extended frame).
+#In the second column you have to specify the CAN-ID as a __hexdecimal number__.
+#In the last column you have to specify the HTTP-Path.
+#Each CAN-ID and each HTTP-Path is allowed to appear only once in the whole file.
+
+S,101,/post
+E,101,/post
+S,103,/post
+E,103,/post
diff --git a/flask/can.py b/flask/can.py
new file mode 100644
index 0000000..5b4c652
--- /dev/null
+++ b/flask/can.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# Simple REST Server
+
+import sys
+import json
+import datetime
+from flask import Flask, request
+app = Flask(__name__)
+
+database = []
+
+@app.route("/")
+def root():
+ global database
+ res = ""
+ for data in database:
+ print("{}".format(data))
+ #print("{}".format(len(data[2])))
+ res = res + "{} {:x} {} {}".format(data[0],data[1],data[2],data[3]) + "
"
+ #return "Hello World!"
+ return res
+
+#curl -X POST -H "Content-Type: application/json" -d '{"canid":101, "frame":"standard" , "data": [1,2,3,4,5,6,7,8]}' http://192.168.10.43:8000/post
+@app.route("/post", methods=["POST"])
+def post():
+ function = sys._getframe().f_code.co_name
+ #print("{}: request={}".format(function, request))
+ #print("{}: request.data={}".format(function, request.data))
+ dict = json.loads(request.data)
+ print("{} dict={}".format(function, dict))
+
+ items = []
+ now = datetime.datetime.now()
+ items.append(now.strftime("%Y/%m/%d %H:%M:%S"))
+ if ("canid" in dict):
+ print("{} canid=0x{:x}".format(function, dict['canid']))
+ items.append(dict['canid'])
+
+ if ("frame" in dict):
+ print("{} frame={}".format(function, dict['frame']))
+ items.append(dict['frame'])
+
+ if ("data" in dict):
+ print("{} data length={}".format(function, len(dict['data'])))
+ print("{} data={}".format(function, dict['data']))
+ items.append(dict['data'])
+
+ global database
+ #print("{} {} {}".format(type(database), len(database), database))
+ if(len(database) >= 20):
+ database.pop(0)
+ database.append(items)
+
+ data = json.dumps(['result', 'ok'])
+ return data
+
+if __name__ == "__main__":
+ #app.run()
+ app.run(host='0.0.0.0', port=8000, debug=True)
+
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
new file mode 100644
index 0000000..2103cc0
--- /dev/null
+++ b/main/CMakeLists.txt
@@ -0,0 +1,4 @@
+set(COMPONENT_SRCS "main.c" "http_post.c" "twai.c")
+set(COMPONENT_ADD_INCLUDEDIRS ".")
+
+register_component()
diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild
new file mode 100644
index 0000000..83c068b
--- /dev/null
+++ b/main/Kconfig.projbuild
@@ -0,0 +1,110 @@
+menu "Application Configuration"
+
+ menu "CAN Setting"
+
+ choice CAN_BITRATE
+ prompt "CAN Bitrate"
+ default CAN_BITRATE_500
+ help
+ Select the CAN bitrate for the example.
+ config CAN_BITRATE_25
+ bool "BITRATE_25"
+ help
+ CAN bitrate is 25 Kbit/s.
+ config CAN_BITRATE_50
+ bool "BITRATE_50"
+ help
+ CAN bitrate is 50 Kbit/s.
+ config CAN_BITRATE_100
+ bool "BITRATE_100"
+ help
+ CAN bitrate is 100 Kbit/s.
+ config CAN_BITRATE_125
+ bool "BITRATE_125"
+ help
+ CAN bitrate is 125 Kbit/s.
+ config CAN_BITRATE_250
+ bool "BITRATE_250"
+ help
+ CAN bitrate is 250 Kbit/s.
+ config CAN_BITRATE_500
+ bool "BITRATE_500"
+ help
+ CAN bitrate is 500 Kbit/s.
+ config CAN_BITRATE_800
+ bool "BITRATE_800"
+ help
+ CAN bitrate is 800 Kbit/s.
+ config CAN_BITRATE_1000
+ bool "BITRATE_1000"
+ help
+ CAN bitrate is 1 Mbit/s.
+ endchoice
+
+ config CTX_GPIO
+ int "CTX GPIO number"
+ range 0 34
+ default 2 if IDF_TARGET_ESP32C3
+ default 20 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
+ default 21 if IDF_TARGET_ESP32
+ help
+ GPIO number (IOxx) to CTX.
+ Some GPIOs are used for other purposes (flash connections, etc.).
+ GPIOs 35-39 are input-only so cannot be used as outputs.
+
+ config CRX_GPIO
+ int "CRX GPIO number"
+ range 0 34
+ default 3 if IDF_TARGET_ESP32C3
+ default 21 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
+ default 22 if IDF_TARGET_ESP32
+ help
+ GPIO number (IOxx) to CRX.
+ Some GPIOs are used for other purposes (flash connections, etc.).
+ GPIOs 35-39 are input-only so cannot be used as outputs.
+
+ config ENABLE_PRINT
+ bool "Output the received CAN FRAME to STDOUT"
+ help
+ Output the received CAN FRAME to STDOUT.
+
+ endmenu
+
+ menu "WiFi Setting"
+
+ config ESP_WIFI_SSID
+ string "WiFi SSID"
+ default "myssid"
+ help
+ SSID (network name) to connect to.
+
+ config ESP_WIFI_PASSWORD
+ string "WiFi Password"
+ default "mypassword"
+ help
+ WiFi password (WPA or WPA2) to connect to.
+
+ config ESP_MAXIMUM_RETRY
+ int "Maximum retry"
+ default 5
+ help
+ Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
+
+ endmenu
+
+ menu "HTTP Server Setting"
+
+ config WEB_SERVER
+ string "HTTP Server"
+ default "myhttpserver"
+ help
+ The host name or IP address of the HTTP server to use.
+
+ config WEB_PORT
+ int "HTTP Port"
+ default 8000
+ help
+ HTTP server port to use.
+
+ endmenu
+endmenu
diff --git a/main/component.mk b/main/component.mk
new file mode 100644
index 0000000..b4fa727
--- /dev/null
+++ b/main/component.mk
@@ -0,0 +1,4 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/main/http_post.c b/main/http_post.c
new file mode 100644
index 0000000..a011e30
--- /dev/null
+++ b/main/http_post.c
@@ -0,0 +1,188 @@
+/* ESP HTTP Client Example
+
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_log.h"
+#include "esp_system.h"
+#include "esp_tls.h"
+#include "esp_http_client.h"
+
+#include "cJSON.h"
+
+#include "twai.h"
+
+static const char *TAG = "HTTP";
+
+extern QueueHandle_t xQueue_http;
+
+esp_err_t _http_event_handler(esp_http_client_event_t *evt)
+{
+ static char *output_buffer; // Buffer to store response of http request from event handler
+ static int output_len; // Stores number of bytes read
+ switch(evt->event_id) {
+ case HTTP_EVENT_ERROR:
+ ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
+ break;
+ case HTTP_EVENT_ON_CONNECTED:
+ ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
+ break;
+ case HTTP_EVENT_HEADER_SENT:
+ ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
+ break;
+ case HTTP_EVENT_ON_HEADER:
+ ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
+ break;
+ case HTTP_EVENT_ON_DATA:
+ ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
+ /*
+ * Check for chunked encoding is added as the URL for chunked encoding used in this example returns binary data.
+ * However, event handler can also be used in case chunked encoding is used.
+ */
+ if (!esp_http_client_is_chunked_response(evt->client)) {
+ // If user_data buffer is configured, copy the response into the buffer
+ if (evt->user_data) {
+ memcpy(evt->user_data + output_len, evt->data, evt->data_len);
+ } else {
+ if (output_buffer == NULL) {
+ output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client));
+ output_len = 0;
+ if (output_buffer == NULL) {
+ ESP_LOGE(TAG, "Failed to allocate memory for output buffer");
+ return ESP_FAIL;
+ }
+ }
+ memcpy(output_buffer + output_len, evt->data, evt->data_len);
+ }
+ output_len += evt->data_len;
+ }
+
+ break;
+ case HTTP_EVENT_ON_FINISH:
+ ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
+ if (output_buffer != NULL) {
+ // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response
+ // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);
+ free(output_buffer);
+ output_buffer = NULL;
+ }
+ output_len = 0;
+ break;
+ case HTTP_EVENT_DISCONNECTED:
+ ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
+ int mbedtls_err = 0;
+ esp_err_t err = esp_tls_get_and_clear_last_error(evt->data, &mbedtls_err, NULL);
+ if (err != 0) {
+ if (output_buffer != NULL) {
+ free(output_buffer);
+ output_buffer = NULL;
+ }
+ output_len = 0;
+ ESP_LOGI(TAG, "Last esp error code: 0x%x", err);
+ ESP_LOGI(TAG, "Last mbedtls failure: 0x%x", mbedtls_err);
+ }
+ break;
+ }
+ return ESP_OK;
+}
+
+#define MAX_HTTP_OUTPUT_BUFFER 2048
+
+static void http_rest_with_url(char * path, char * post_data)
+{
+ char local_response_buffer[MAX_HTTP_OUTPUT_BUFFER] = {0};
+ /**
+ * NOTE: All the configuration parameters for http_client must be spefied either in URL or as host and path parameters.
+ * If host and path parameters are not set, query parameter will be ignored. In such cases,
+ * query parameter should be specified in URL.
+ *
+ * If URL as well as host and path parameters are specified, values of host and path will be considered.
+ */
+ esp_http_client_config_t config = {
+ .host = CONFIG_WEB_SERVER,
+ .port = CONFIG_WEB_PORT,
+ .path = path,
+ .event_handler = _http_event_handler,
+ .user_data = local_response_buffer, // Pass address of local buffer to get response
+ .disable_auto_redirect = true,
+ };
+
+#if 0
+ // Same as above
+ esp_http_client_config_t config = {
+ .url = "http://192.168.10.43:8000/post",
+ .event_handler = _http_event_handler,
+ .user_data = local_response_buffer, // Pass address of local buffer to get response
+ .disable_auto_redirect = true,
+ };
+#endif
+ esp_http_client_handle_t client = esp_http_client_init(&config);
+
+ // POST
+ // no need to change url
+ //esp_http_client_set_url(client, "http://192.168.10.43:8000/post");
+ esp_http_client_set_method(client, HTTP_METHOD_POST);
+ esp_http_client_set_header(client, "Content-Type", "application/json");
+ esp_http_client_set_post_field(client, post_data, strlen(post_data));
+ esp_err_t err = esp_http_client_perform(client);
+ if (err == ESP_OK) {
+ ESP_LOGI(TAG, "HTTP POST Status = %d, content_length = %d",
+ esp_http_client_get_status_code(client),
+ esp_http_client_get_content_length(client));
+ ESP_LOGI(TAG, "local_response_buffer=[%s]", local_response_buffer);
+ } else {
+ ESP_LOGE(TAG, "HTTP POST request failed: %s", esp_err_to_name(err));
+ }
+
+ esp_http_client_cleanup(client);
+}
+
+
+void http_client_task(void *pvParameters)
+{
+ ESP_LOGI(TAG, "Start HTTP Client: http://%s:%d", CONFIG_WEB_SERVER, CONFIG_WEB_PORT);
+ FRAME_t frameBuf;
+ while (1) {
+ xQueueReceive(xQueue_http, &frameBuf, portMAX_DELAY);
+ ESP_LOGI(TAG, "canid=%x ext=%d topic=[%s]", frameBuf.canid, frameBuf.ext, frameBuf.topic);
+ for(int i=0;i
+#include
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/event_groups.h"
+#include "freertos/semphr.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event.h"
+#include "esp_vfs.h"
+#include "nvs_flash.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "esp_spiffs.h"
+#include "driver/twai.h" // Update from V4.2
+
+#include "twai.h"
+
+#define TAG "MAIN"
+
+static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
+
+#if CONFIG_CAN_BITRATE_25
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS();
+#define BITRATE "Bitrate is 25 Kbit/s"
+#elif CONFIG_CAN_BITRATE_50
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_50KBITS();
+#define BITRATE "Bitrate is 50 Kbit/s"
+#elif CONFIG_CAN_BITRATE_100
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_100KBITS();
+#define BITRATE "Bitrate is 100 Kbit/s"
+#elif CONFIG_CAN_BITRATE_125
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_125KBITS();
+#define BITRATE "Bitrate is 125 Kbit/s"
+#elif CONFIG_CAN_BITRATE_250
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
+#define BITRATE "Bitrate is 250 Kbit/s"
+#elif CONFIG_CAN_BITRATE_500
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
+#define BITRATE "Bitrate is 500 Kbit/s"
+#elif CONFIG_CAN_BITRATE_800
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_800KBITS();
+#define BITRATE "Bitrate is 800 Kbit/s"
+#elif CONFIG_CAN_BITRATE_1000
+static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_1MBITS();
+#define BITRATE "Bitrate is 1 Mbit/s"
+#endif
+
+/* FreeRTOS event group to signal when we are connected*/
+static EventGroupHandle_t s_wifi_event_group;
+
+/* The event group allows multiple bits for each event, but we only care about two events:
+ * - we are connected to the AP with an IP
+ * - we failed to connect after the maximum amount of retries */
+#define WIFI_CONNECTED_BIT BIT0
+#define WIFI_FAIL_BIT BIT1
+
+static int s_retry_num = 0;
+
+QueueHandle_t xQueue_http;
+
+TOPIC_t *publish;
+int16_t npublish;
+
+static void event_handler(void* arg, esp_event_base_t event_base,
+ int32_t event_id, void* event_data)
+{
+ if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
+ esp_wifi_connect();
+ } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
+ if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) {
+ esp_wifi_connect();
+ s_retry_num++;
+ ESP_LOGI(TAG, "retry to connect to the AP");
+ } else {
+ xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
+ }
+ ESP_LOGI(TAG,"connect to the AP fail");
+ } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
+ ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
+ ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
+ s_retry_num = 0;
+ xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
+ }
+}
+
+bool wifi_init_sta(void)
+{
+ bool ret = false;
+ s_wifi_event_group = xEventGroupCreate();
+
+ ESP_ERROR_CHECK(esp_netif_init());
+
+ ESP_ERROR_CHECK(esp_event_loop_create_default());
+ esp_netif_create_default_wifi_sta();
+
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+ esp_event_handler_instance_t instance_any_id;
+ esp_event_handler_instance_t instance_got_ip;
+ ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
+ ESP_EVENT_ANY_ID,
+ &event_handler,
+ NULL,
+ &instance_any_id));
+ ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
+ IP_EVENT_STA_GOT_IP,
+ &event_handler,
+ NULL,
+ &instance_got_ip));
+
+ wifi_config_t wifi_config = {
+ .sta = {
+ .ssid = CONFIG_ESP_WIFI_SSID,
+ .password = CONFIG_ESP_WIFI_PASSWORD,
+ /* Setting a password implies station will connect to all security modes including WEP/WPA.
+ * However these modes are deprecated and not advisable to be used. Incase your Access point
+ * doesn't support WPA2, these mode can be enabled by commenting below line */
+ .threshold.authmode = WIFI_AUTH_WPA2_PSK,
+
+ .pmf_cfg = {
+ .capable = true,
+ .required = false
+ },
+ },
+ };
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+ ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+ ESP_ERROR_CHECK(esp_wifi_start() );
+
+ ESP_LOGI(TAG, "wifi_init_sta finished.");
+
+ /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
+ * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
+ EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
+ WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
+ pdFALSE,
+ pdFALSE,
+ portMAX_DELAY);
+
+ /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
+ * happened. */
+ if (bits & WIFI_CONNECTED_BIT) {
+ ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
+ CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
+ ret = true;
+ } else if (bits & WIFI_FAIL_BIT) {
+ ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
+ CONFIG_ESP_WIFI_SSID, CONFIG_ESP_WIFI_PASSWORD);
+ } else {
+ ESP_LOGE(TAG, "UNEXPECTED EVENT");
+ }
+
+ /* The event will not be processed after unregister */
+ ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
+ ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
+ vEventGroupDelete(s_wifi_event_group);
+ return ret;
+}
+
+esp_err_t mountSPIFFS(char * partition_label, char * base_path) {
+ ESP_LOGI(TAG, "Initializing SPIFFS file system");
+
+ esp_vfs_spiffs_conf_t conf = {
+ .base_path = base_path,
+ .partition_label = partition_label,
+ .max_files = 5,
+ .format_if_mount_failed = true
+ };
+
+ // Use settings defined above to initialize and mount SPIFFS filesystem.
+ // Note: esp_vfs_spiffs_register is an all-in-one convenience function.
+ esp_err_t ret = esp_vfs_spiffs_register(&conf);
+
+ if (ret != ESP_OK) {
+ if (ret == ESP_FAIL) {
+ ESP_LOGE(TAG, "Failed to mount or format filesystem");
+ } else if (ret == ESP_ERR_NOT_FOUND) {
+ ESP_LOGE(TAG, "Failed to find SPIFFS partition");
+ } else {
+ ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
+ }
+ return ret;
+ }
+
+ size_t total = 0, used = 0;
+ ret = esp_spiffs_info(partition_label, &total, &used);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
+ } else {
+ ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
+ DIR* dir = opendir(base_path);
+ assert(dir != NULL);
+ while (true) {
+ struct dirent*pe = readdir(dir);
+ if (!pe) break;
+ ESP_LOGI(TAG, "d_name=%s d_ino=%d d_type=%x", pe->d_name,pe->d_ino, pe->d_type);
+ }
+ closedir(dir);
+ }
+ ESP_LOGI(TAG, "Mount SPIFFS filesystem");
+ return ret;
+}
+
+esp_err_t build_table(TOPIC_t **topics, char *file, int16_t *ntopic)
+{
+ ESP_LOGI(TAG, "build_table file=%s", file);
+ char line[128];
+ int _ntopic = 0;
+
+ FILE* f = fopen(file, "r");
+ if (f == NULL) {
+ ESP_LOGE(TAG, "Failed to open file for reading");
+ return ESP_FAIL;
+ }
+ while (1){
+ if ( fgets(line, sizeof(line) ,f) == 0 ) break;
+ // strip newline
+ char* pos = strchr(line, '\n');
+ if (pos) {
+ *pos = '\0';
+ }
+ ESP_LOGD(TAG, "line=[%s]", line);
+ if (strlen(line) == 0) continue;
+ if (line[0] == '#') continue;
+ _ntopic++;
+ }
+ fclose(f);
+ ESP_LOGI(TAG, "build_table _ntopic=%d", _ntopic);
+
+ *topics = calloc(_ntopic, sizeof(TOPIC_t));
+ if (*topics == NULL) {
+ ESP_LOGE(TAG, "Error allocating memory for topic");
+ return ESP_ERR_NO_MEM;
+ }
+
+ f = fopen(file, "r");
+ if (f == NULL) {
+ ESP_LOGE(TAG, "Failed to open file for reading");
+ return ESP_FAIL;
+ }
+
+ char *ptr;
+ int index = 0;
+ while (1){
+ if ( fgets(line, sizeof(line) ,f) == 0 ) break;
+ // strip newline
+ char* pos = strchr(line, '\n');
+ if (pos) {
+ *pos = '\0';
+ }
+ ESP_LOGD(TAG, "line=[%s]", line);
+ if (strlen(line) == 0) continue;
+ if (line[0] == '#') continue;
+
+ // Frame type
+ ptr = strtok(line, ",");
+ ESP_LOGD(TAG, "ptr=%s", ptr);
+ if (strcmp(ptr, "S") == 0) {
+ (*topics+index)->frame = 0;
+ } else if (strcmp(ptr, "E") == 0) {
+ (*topics+index)->frame = 1;
+ } else {
+ ESP_LOGE(TAG, "This line is invalid [%s]", line);
+ continue;
+ }
+
+ // CAN ID
+ uint32_t canid;
+ ptr = strtok(NULL, ",");
+ if(ptr == NULL) continue;
+ ESP_LOGD(TAG, "ptr=%s", ptr);
+ canid = strtol(ptr, NULL, 16);
+ if (canid == 0) {
+ ESP_LOGE(TAG, "This line is invalid [%s]", line);
+ continue;
+ }
+ (*topics+index)->canid = canid;
+
+ // mqtt topic
+ char *sp;
+ ptr = strtok(NULL, ",");
+ if(ptr == NULL) {
+ ESP_LOGE(TAG, "This line is invalid [%s]", line);
+ continue;
+ }
+ ESP_LOGD(TAG, "ptr=[%s] strlen=%d", ptr, strlen(ptr));
+ sp = strstr(ptr,"#");
+ if (sp != NULL) {
+ ESP_LOGE(TAG, "This line is invalid [%s]", line);
+ continue;
+ }
+ sp = strstr(ptr,"+");
+ if (sp != NULL) {
+ ESP_LOGE(TAG, "This line is invalid [%s]", line);
+ continue;
+ }
+ (*topics+index)->topic = (char *)malloc(strlen(ptr)+1);
+ strcpy((*topics+index)->topic, ptr);
+ (*topics+index)->topic_len = strlen(ptr);
+ index++;
+ }
+ fclose(f);
+ *ntopic = index;
+ return ESP_OK;
+}
+
+void dump_table(TOPIC_t *topics, int16_t ntopic)
+{
+ for(int i=0;iframe, (topics+i)->canid, (topics+i)->topic, (topics+i)->topic_len);
+ }
+
+}
+
+void http_client_task(void *pvParameters);
+void twai_task(void *pvParameters);
+
+void app_main()
+{
+ // Initialize NVS
+ esp_err_t ret = nvs_flash_init();
+ if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+ ESP_ERROR_CHECK(nvs_flash_erase());
+ ret = nvs_flash_init();
+ }
+ ESP_ERROR_CHECK(ret);
+
+ // WiFi initialize
+ if (wifi_init_sta() == false) {
+ while(1) vTaskDelay(10);
+ }
+
+ // Install and start TWAI driver
+ ESP_LOGI(TAG, "%s",BITRATE);
+ ESP_LOGI(TAG, "CTX_GPIO=%d",CONFIG_CTX_GPIO);
+ ESP_LOGI(TAG, "CRX_GPIO=%d",CONFIG_CRX_GPIO);
+
+ static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(CONFIG_CTX_GPIO, CONFIG_CRX_GPIO, TWAI_MODE_NORMAL);
+ ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
+ ESP_LOGI(TAG, "Driver installed");
+ ESP_ERROR_CHECK(twai_start());
+ ESP_LOGI(TAG, "Driver started");
+
+ // Mount SPIFFS
+ char *partition_label = "storage";
+ char *base_path = "/spiffs";
+ ret = mountSPIFFS(partition_label, base_path);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "mountSPIFFS fail");
+ while(1) { vTaskDelay(1); }
+ }
+
+ // Create Queue
+ xQueue_http = xQueueCreate( 10, sizeof(FRAME_t) );
+ configASSERT( xQueue_http );
+
+ // build publish table
+ ret = build_table(&publish, "/spiffs/can2http.csv", &npublish);
+ if (ret != ESP_OK) {
+ ESP_LOGE(TAG, "build publish table fail");
+ while(1) { vTaskDelay(1); }
+ }
+ dump_table(publish, npublish);
+
+ xTaskCreate(http_client_task, "http", 1024*6, NULL, 2, NULL);
+ xTaskCreate(twai_task, "twai_rx", 1024*6, NULL, 2, NULL);
+}
diff --git a/main/twai.c b/main/twai.c
new file mode 100644
index 0000000..b237cb3
--- /dev/null
+++ b/main/twai.c
@@ -0,0 +1,103 @@
+/* TWAI Network Example
+
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+#include
+#include
+#include
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "freertos/semphr.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "driver/twai.h" // Update from V4.2
+
+#include "twai.h"
+
+static const char *TAG = "TWAI";
+
+extern QueueHandle_t xQueue_http;
+//extern QueueHandle_t xQueue_twai_tx;
+
+extern TOPIC_t *publish;
+extern int16_t npublish;
+
+void dump_table(TOPIC_t *topics, int16_t ntopic);
+
+void twai_task(void *pvParameters)
+{
+ ESP_LOGI(TAG,"task start");
+ dump_table(publish, npublish);
+
+ twai_message_t rx_msg;
+ FRAME_t frameBuf;
+ while (1) {
+ esp_err_t ret = twai_receive(&rx_msg, pdMS_TO_TICKS(1));
+ if (ret == ESP_OK) {
+ ESP_LOGD(TAG,"twai_receive identifier=0x%x flags=0x%x data_length_code=%d",
+ rx_msg.identifier, rx_msg.flags, rx_msg.data_length_code);
+
+ int ext = rx_msg.flags & 0x01;
+ int rtr = rx_msg.flags & 0x02;
+ ESP_LOGD(TAG, "ext=%x rtr=%x", ext, rtr);
+
+#if CONFIG_ENABLE_PRINT
+ if (ext == 0) {
+ printf("Standard ID: 0x%03x ", rx_msg.identifier);
+ } else {
+ printf("Extended ID: 0x%08x", rx_msg.identifier);
+ }
+ printf(" DLC: %d Data: ", rx_msg.data_length_code);
+
+ if (rtr == 0) {
+ for (int i = 0; i < rx_msg.data_length_code; i++) {
+ printf("0x%02x ", rx_msg.data[i]);
+ }
+ } else {
+ printf("REMOTE REQUEST FRAME");
+
+ }
+ printf("\n");
+#endif
+
+ for(int index=0;index"
+ self.write(res)
+
+#curl -X POST -H "Content-Type: application/json" -d '{"canid":101, "frame":"standard" , "data": [1,2,3,4,5,6,7,8]}' http://192.168.10.43:8000/post
+class PostHandler(tornado.web.RequestHandler):
+ def post(self):
+ #print("{}".format(self.request.body))
+ function = sys._getframe().f_code.co_name
+ dict = tornado.escape.json_decode(self.request.body)
+ print("{} dict={}".format(function, dict))
+
+ items = []
+ now = datetime.datetime.now()
+ items.append(now.strftime("%Y/%m/%d %H:%M:%S"))
+ if ("canid" in dict):
+ print("{} canid=0x{:x}".format(function, dict['canid']))
+ items.append(dict['canid'])
+
+ if ("frame" in dict):
+ print("{} frame={}".format(function, dict['frame']))
+ items.append(dict['frame'])
+
+ if ("data" in dict):
+ print("{} data length={}".format(function, len(dict['data'])))
+ print("{} data={}".format(function, dict['data']))
+ items.append(dict['data'])
+
+ global database
+ #print("{} {} {}".format(type(database), len(database), database))
+ if(len(database) >= 20):
+ database.pop(0)
+ database.append(items)
+
+ self.write(json.dumps({"result":"ok"}))
+
+if __name__ == "__main__":
+ tornado.options.parse_command_line()
+ app = tornado.web.Application(
+ handlers=[
+ (r"/", IndexHandler),
+ (r"/post", PostHandler),
+ ],debug=True
+ )
+ http_server = tornado.httpserver.HTTPServer(app)
+ http_server.listen(options.port)
+ tornado.ioloop.IOLoop.current().start()