Added built-in HTTP Server
This commit is contained in:
@@ -2,13 +2,11 @@
|
||||
CANbus to http bridge using esp32.
|
||||
It's purpose is to be a bridge between a CAN-Bus and a HTTP-Server.
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
You can visualize CAN-Frame using a visualization library such as Matplotlib.
|
||||
|
||||

|
||||
|
||||
I inspired from [here](https://github.com/c3re/can2mqtt).
|
||||

|
||||
|
||||
# Software requirement
|
||||
esp-idf v4.2-dev-2243 or later.
|
||||
@@ -100,7 +98,13 @@ idf.py flash
|
||||

|
||||
|
||||
## WiFi Setting
|
||||

|
||||

|
||||
|
||||
You can use static IP.
|
||||

|
||||
|
||||
Connect using mDNS.
|
||||

|
||||
|
||||
## HTTP Server Setting
|
||||

|
||||
@@ -111,8 +115,8 @@ 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-POST-Path.
|
||||
Each CAN-ID and each HTTP-POST-Path is allowed to appear only once in the whole file.
|
||||
In the last column you have to specify the HTTP-POST-Path of external HTTP server.
|
||||
Each CAN-ID is allowed to appear only once in the whole file.
|
||||
|
||||
```
|
||||
S,101,/post
|
||||
@@ -121,20 +125,39 @@ S,103,/post
|
||||
E,103,/post
|
||||
```
|
||||
|
||||
When a Standard CAN frame with ID 0x101 is received, POST with the "/post" PATH.
|
||||
When a Extended CAN frame with ID 0x101 is received, POST with the "/post" PATH.
|
||||
|
||||
# POST Parameter Example
|
||||
```
|
||||
{'canid':257, 'frame': 'standard', 'data': [1,2,3,4,5,6,7,8]}
|
||||
{'canid':257, 'frame': 'extended', 'data': [1,2,3,4,5,6,7,8]}
|
||||
{'canid':259, 'frame': 'standard', 'data': [1,2,3,4,5,6,7,8]}
|
||||
{'canid':259, 'frame': 'extended', 'data': [1,2,3,4,5,6,7,8]}
|
||||
```
|
||||
|
||||
When a CAN frame with ID 0x101 is received, POST with the 'canid':257.
|
||||
When a CAN frame with ID 0x103 is received, POST with the 'canid':259.
|
||||
|
||||
POST Parameter Example:
|
||||
```
|
||||
{"canid":257, "frame": "standard", "data": [16, 17, 18]}
|
||||
{"canid":257, "frame": "extended", "data": [16, 17, 18]}
|
||||
{"canid":259, "frame": "standard", "data": [16, 17, 18]}
|
||||
{"canid":259, "frame": "extended", "data": [16, 17, 18]}
|
||||
```
|
||||
|
||||
# Definition from HTTP to CANbus
|
||||
When HTTP POST is received, it is sent by CANBus according to csv/http2can.csv.
|
||||
Other than this, it is the same as csv/can2http.csv.
|
||||
In the last column you have to specify the HTTP-POST-Path of built-in HTTP server.
|
||||
|
||||
```
|
||||
S,201,/receive
|
||||
E,201,/receive
|
||||
S,203,/receive
|
||||
E,203,/receive
|
||||
```
|
||||
|
||||
When receiving the {"canid": 513, "frame": "standard", "data": [16, 17, 18]}, send the Standard CAN frame with ID 0x201.
|
||||
When receiving the {"canid": 515, "frame": "extended", "data": [16, 17, 18]}, send the Extended CAN frame with ID 0x201.
|
||||
|
||||
|
||||
# Send message using curl
|
||||
```
|
||||
$ curl -X POST -H "Content-Type: application/json" -d '{"canid": 513, "frame": "standard", "data": [16, 17, 18]}' http://esp32-server.local:8000/api/twai/send
|
||||
|
||||
$ curl -X POST -H "Content-Type: application/json" -d '{"canid": 513, "frame": "extended", "data": [16, 17, 18]}' http://esp32-server.local:8000/api/twai/send
|
||||
```
|
||||
|
||||
# HTTP Server Using Tornado
|
||||
```
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@
|
||||
#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.
|
||||
#In the last column you have to specify the PATH of external web server.
|
||||
#Each CAN-ID is allowed to appear only once in the whole file.
|
||||
|
||||
S,101,/post
|
||||
E,101,/post
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
#The file mqtt2can.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 PATH of built-in web server.
|
||||
#Each CAN-ID is allowed to appear only once in the whole file.
|
||||
|
||||
S,201,/receive
|
||||
E,201,/receive
|
||||
S,203,/receive
|
||||
E,203,/receive
|
||||
|
+1
-1
@@ -1,4 +1,4 @@
|
||||
set(COMPONENT_SRCS "main.c" "http_post.c" "twai.c")
|
||||
set(COMPONENT_SRCS "main.c" "http_post.c" "http_server.c" "twai.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
||||
|
||||
@@ -90,6 +90,39 @@ menu "Application Configuration"
|
||||
help
|
||||
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
|
||||
|
||||
config MDNS_HOSTNAME
|
||||
string "mDNS Hostname"
|
||||
default "esp32-server"
|
||||
help
|
||||
The mDNS host name used by the ESP32.
|
||||
|
||||
config STATIC_IP
|
||||
bool "Enable Static IP Address"
|
||||
default false
|
||||
help
|
||||
Enable Static IP Address.
|
||||
|
||||
config STATIC_IP_ADDRESS
|
||||
depends on STATIC_IP
|
||||
string "Static IP Address"
|
||||
default "192.168.10.100"
|
||||
help
|
||||
Static IP Address for Station.
|
||||
|
||||
config STATIC_GW_ADDRESS
|
||||
depends on STATIC_IP
|
||||
string "Static GW Address"
|
||||
default "192.168.10.1"
|
||||
help
|
||||
Static GW Address for Station.
|
||||
|
||||
config STATIC_NM_ADDRESS
|
||||
depends on STATIC_IP
|
||||
string "Static Netmask"
|
||||
default "255.255.255.0"
|
||||
help
|
||||
Static Netmask for Station.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "HTTP Server Setting"
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@
|
||||
|
||||
static const char *TAG = "HTTP";
|
||||
|
||||
extern QueueHandle_t xQueue_http;
|
||||
extern QueueHandle_t xQueue_http_client;
|
||||
|
||||
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
|
||||
{
|
||||
@@ -151,7 +151,7 @@ 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);
|
||||
xQueueReceive(xQueue_http_client, &frameBuf, portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "canid=%x ext=%d topic=[%s]", frameBuf.canid, frameBuf.ext, frameBuf.topic);
|
||||
for(int i=0;i<frameBuf.data_len;i++) {
|
||||
ESP_LOGI(TAG, "DATA=%x", frameBuf.data[i]);
|
||||
|
||||
@@ -0,0 +1,356 @@
|
||||
/* HTTP Server Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "esp_http_server.h"
|
||||
#include "cJSON.h"
|
||||
#include "driver/twai.h"
|
||||
|
||||
#include "twai.h"
|
||||
|
||||
static const char *TAG = "SERVER";
|
||||
//static SemaphoreHandle_t ctrl_task_sem;
|
||||
|
||||
extern QueueHandle_t xQueue_twai_tx;
|
||||
extern TOPIC_t *subscribe;
|
||||
extern int16_t nsubscribe;
|
||||
|
||||
#define SCRATCH_BUFSIZE (1024)
|
||||
|
||||
typedef struct rest_server_context {
|
||||
char base_path[ESP_VFS_PATH_MAX + 1]; // Not used in this project
|
||||
char scratch[SCRATCH_BUFSIZE];
|
||||
} rest_server_context_t;
|
||||
|
||||
|
||||
/* Handler for roor get handler */
|
||||
static esp_err_t root_get_handler(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGI(TAG, "root_get_handler req->uri=[%s]", req->uri);
|
||||
for(int index=0;index<nsubscribe;index++) {
|
||||
ESP_LOGI(TAG, "subscribe[%d] frame=%d canid=0x%x topic=[%s] topic_len=%d",
|
||||
index, subscribe[index].frame, subscribe[index].canid, subscribe[index].topic, subscribe[index].topic_len);
|
||||
}
|
||||
|
||||
/* Send HTML header */
|
||||
httpd_resp_sendstr_chunk(req, "<!DOCTYPE html><html><body>");
|
||||
|
||||
httpd_resp_sendstr_chunk(req, "<table border=\"1\">");
|
||||
httpd_resp_sendstr_chunk(req, "<thead><tr><th>Frame Type</th><th>Frame ID</th><th>HTTP Path</th></tr></thead>");
|
||||
|
||||
char chunk[64];
|
||||
for(int index=0;index<nsubscribe;index++) {
|
||||
char frameType[16];
|
||||
strcpy(frameType, "Standard");
|
||||
if (subscribe[index].frame == 1) strcpy(frameType, "Entended");
|
||||
httpd_resp_sendstr_chunk(req, "<tr>");
|
||||
sprintf(chunk, "<td align=\"center\">%16s</td>", frameType);
|
||||
httpd_resp_sendstr_chunk(req, chunk);
|
||||
sprintf(chunk, "<td align=\"center\">%x</td>", subscribe[index].canid);
|
||||
httpd_resp_sendstr_chunk(req, chunk);
|
||||
sprintf(chunk, "<td align=\"center\">%s</td>", subscribe[index].topic);
|
||||
httpd_resp_sendstr_chunk(req, chunk);
|
||||
httpd_resp_sendstr_chunk(req, "</tr>");
|
||||
}
|
||||
|
||||
/* Finish the table */
|
||||
httpd_resp_sendstr_chunk(req, "</tbody></table>");
|
||||
|
||||
/* Send remaining chunk of HTML file to complete it */
|
||||
httpd_resp_sendstr_chunk(req, "</body></html>");
|
||||
|
||||
/* Send empty chunk to signal HTTP response completion */
|
||||
httpd_resp_sendstr_chunk(req, NULL);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Handler for getting system information handler */
|
||||
// curl 'http://esp32-server.local:8000/api/system/info' | python -m json.tool
|
||||
static esp_err_t system_info_get_handler(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGI(TAG, "system_info_get_handler req->uri=[%s]", req->uri);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
esp_chip_info_t chip_info;
|
||||
esp_chip_info(&chip_info);
|
||||
cJSON_AddStringToObject(root, "version", IDF_VER);
|
||||
cJSON_AddNumberToObject(root, "cores", chip_info.cores);
|
||||
//const char *sys_info = cJSON_Print(root);
|
||||
char *sys_info = cJSON_Print(root);
|
||||
httpd_resp_sendstr(req, sys_info);
|
||||
// Buffers returned by cJSON_Print must be freed by the caller.
|
||||
// Please use the proper API (cJSON_free) rather than directly calling stdlib free.
|
||||
cJSON_free(sys_info);
|
||||
cJSON_Delete(root);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
// Create array
|
||||
cJSON *Create_array_of_anything(cJSON **objects,int array_num)
|
||||
{
|
||||
cJSON *prev = 0;
|
||||
cJSON *root;
|
||||
root = cJSON_CreateArray();
|
||||
for (int i=0;i<array_num;i++) {
|
||||
if (!i) {
|
||||
root->child=objects[i];
|
||||
} else {
|
||||
prev->next=objects[i];
|
||||
objects[i]->prev=prev;
|
||||
}
|
||||
prev=objects[i];
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
char *JSON_Types(int type) {
|
||||
if (type == cJSON_Invalid) return ("cJSON_Invalid");
|
||||
if (type == cJSON_False) return ("cJSON_False");
|
||||
if (type == cJSON_True) return ("cJSON_True");
|
||||
if (type == cJSON_NULL) return ("cJSON_NULL");
|
||||
if (type == cJSON_Number) return ("cJSON_Number");
|
||||
if (type == cJSON_String) return ("cJSON_String");
|
||||
if (type == cJSON_Array) return ("cJSON_Array");
|
||||
if (type == cJSON_Object) return ("cJSON_Object");
|
||||
if (type == cJSON_Raw) return ("cJSON_Raw");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Handler for twai send handler */
|
||||
// curl -X POST -H "Content-Type: application/json" -d '{"canid": 513, "frame": "standard", "data": [11, 12, 13, 14]}' http://esp32-server.local:8000/api/twai/send
|
||||
static esp_err_t twai_send_handler(httpd_req_t *req)
|
||||
{
|
||||
ESP_LOGI(TAG, "twai_send_handler req->uri=[%s]", req->uri);
|
||||
int total_len = req->content_len;
|
||||
int cur_len = 0;
|
||||
char *buf = ((rest_server_context_t *)(req->user_ctx))->scratch;
|
||||
int received = 0;
|
||||
if (total_len >= SCRATCH_BUFSIZE) {
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "content too long");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
while (cur_len < total_len) {
|
||||
received = httpd_req_recv(req, buf + cur_len, total_len);
|
||||
if (received <= 0) {
|
||||
/* Respond with 500 Internal Server Error */
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to post control value");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
cur_len += received;
|
||||
}
|
||||
buf[total_len] = '\0';
|
||||
ESP_LOGI(TAG, "buf=[%s]", buf);
|
||||
|
||||
|
||||
bool parse = true;
|
||||
cJSON *root = cJSON_Parse(buf);
|
||||
|
||||
// Search canid item
|
||||
int32_t canid = 0;
|
||||
cJSON* state = cJSON_GetObjectItem(root, "canid");
|
||||
if (state) {
|
||||
canid = cJSON_GetObjectItem(root, "canid")->valueint;
|
||||
ESP_LOGI(TAG, "canid=%x", canid);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "canid item not found");
|
||||
parse = false;
|
||||
}
|
||||
|
||||
// Search frame item
|
||||
char frameStr[12];
|
||||
uint16_t frame = 0;
|
||||
state = cJSON_GetObjectItem(root, "frame");
|
||||
if (state) {
|
||||
strcpy(frameStr, cJSON_GetObjectItem(root,"frame")->valuestring);
|
||||
ESP_LOGI(TAG, "frameStr=[%s]", frameStr);
|
||||
if (strcmp(frameStr, "standard") != 0 && strcmp(frameStr, "extended") != 0 ) {
|
||||
ESP_LOGE(TAG, "frame item not correct");
|
||||
parse = false;
|
||||
} else {
|
||||
if (strcmp(frameStr, "standard") == 0) frame = STANDARD_FRAME;
|
||||
if (strcmp(frameStr, "extended") == 0) frame = EXTENDED_FRAME;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "frame item not found");
|
||||
parse = false;
|
||||
}
|
||||
|
||||
// Search data item
|
||||
int16_t data_len;
|
||||
char data_value[8];
|
||||
state = cJSON_GetObjectItem(root, "data");
|
||||
if (state) {
|
||||
cJSON *data_array = cJSON_GetObjectItem(root,"data");
|
||||
ESP_LOGI(TAG, "data_array->type=%s", JSON_Types(data_array->type));
|
||||
if (data_array->type == cJSON_Array) {
|
||||
int data_array_size = cJSON_GetArraySize(data_array);
|
||||
ESP_LOGI(TAG, "data_array_size=%d", data_array_size);
|
||||
bool data_valid = true;
|
||||
data_len = data_array_size;
|
||||
for (int i=0;i<data_array_size;i++) {
|
||||
cJSON *array = cJSON_GetArrayItem(data_array,i);
|
||||
//ESP_LOGI(TAG, "array->type=%s", JSON_Types(array->type));
|
||||
uint16_t data_int = array->valueint;
|
||||
ESP_LOGI(TAG, "data_int[%d]=%x", i, data_int);
|
||||
if (data_int <= 0xff) {
|
||||
data_value[i] = data_int;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "data too large");
|
||||
data_valid = false;
|
||||
}
|
||||
} // end for
|
||||
if (data_valid == false) {
|
||||
parse = false;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "data item not array");
|
||||
parse = false;
|
||||
} // end if
|
||||
|
||||
|
||||
} else {
|
||||
ESP_LOGE(TAG, "data item not found");
|
||||
parse = false;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
|
||||
// JSON parse success. Send twai data.
|
||||
if (parse) {
|
||||
ESP_LOGI(TAG, "twai_send_handler frame=%d canid=%x data_len=%d", frame, canid, data_len);
|
||||
ESP_LOG_BUFFER_HEX(TAG, data_value, data_len);
|
||||
bool isMatch = false;
|
||||
for(int index=0;index<nsubscribe;index++) {
|
||||
ESP_LOGI(TAG, "subscribe[%d] frame=%d canid=%x topic=[%s] topic_len=%d",
|
||||
index, subscribe[index].frame, subscribe[index].canid, subscribe[index].topic, subscribe[index].topic_len);
|
||||
if (subscribe[index].frame == frame && subscribe[index].canid == canid) {
|
||||
isMatch = true;
|
||||
twai_message_t tx_msg;
|
||||
tx_msg.extd = frame;
|
||||
tx_msg.ss = 1;
|
||||
tx_msg.self = 0;
|
||||
tx_msg.dlc_non_comp = 0;
|
||||
tx_msg.identifier = canid;
|
||||
tx_msg.data_length_code = data_len;
|
||||
for (int i=0;i<data_len;i++) {
|
||||
tx_msg.data[i] = data_value[i];
|
||||
}
|
||||
if (xQueueSend(xQueue_twai_tx, &tx_msg, portMAX_DELAY) != pdPASS) {
|
||||
ESP_LOGE(TAG, "xQueueSend Fail");
|
||||
}
|
||||
httpd_resp_sendstr(req, "twai send successfully");
|
||||
break;
|
||||
} // end if
|
||||
} // end for
|
||||
if (isMatch == false) {
|
||||
ESP_LOGE(TAG, "Not found canid in csv");
|
||||
httpd_resp_sendstr(req, "Not found canid in csv");
|
||||
}
|
||||
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Request parameter not correct");
|
||||
httpd_resp_sendstr(req, "Request parameter not correct");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Function to start the file server */
|
||||
esp_err_t start_server(const char *base_path, int port)
|
||||
{
|
||||
rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t));
|
||||
if (rest_context == NULL) {
|
||||
ESP_LOGE(TAG, "No memory for rest context");
|
||||
while(1) { vTaskDelay(1); }
|
||||
}
|
||||
|
||||
httpd_handle_t server = NULL;
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
config.server_port = port;
|
||||
|
||||
/* Use the URI wildcard matching function in order to
|
||||
* allow the same handler to respond to multiple different
|
||||
* target URIs which match the wildcard scheme */
|
||||
config.uri_match_fn = httpd_uri_match_wildcard;
|
||||
|
||||
ESP_LOGD(TAG, "Starting HTTP Server on port: '%d'", config.server_port);
|
||||
if (httpd_start(&server, &config) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start file server!");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* URI handler for root */
|
||||
httpd_uri_t root = {
|
||||
.uri = "/", // Match all URIs of type /path/to/file
|
||||
.method = HTTP_GET,
|
||||
.handler = root_get_handler,
|
||||
//.user_ctx = server_data // Pass server data as context
|
||||
};
|
||||
httpd_register_uri_handler(server, &root);
|
||||
|
||||
/* URI handler for getting system info */
|
||||
httpd_uri_t system_info_get_uri = {
|
||||
.uri = "/api/system/info",
|
||||
.method = HTTP_GET,
|
||||
.handler = system_info_get_handler,
|
||||
.user_ctx = rest_context
|
||||
};
|
||||
httpd_register_uri_handler(server, &system_info_get_uri);
|
||||
|
||||
/* URI handler for send twai */
|
||||
httpd_uri_t twai_send_post_uri = {
|
||||
.uri = "/api/twai/send",
|
||||
.method = HTTP_POST,
|
||||
.handler = twai_send_handler,
|
||||
.user_ctx = rest_context
|
||||
};
|
||||
httpd_register_uri_handler(server, &twai_send_post_uri);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void http_server_task(void *pvParameters)
|
||||
{
|
||||
char *task_parameter = (char *)pvParameters;
|
||||
ESP_LOGI(TAG, "Start task_parameter=%s", task_parameter);
|
||||
char url[64];
|
||||
sprintf(url, "http://%s:%d", task_parameter, CONFIG_WEB_PORT);
|
||||
ESP_LOGI(TAG, "Starting server on %s", url);
|
||||
|
||||
#if 0
|
||||
// Create Semaphore
|
||||
// This Semaphore is used for locking
|
||||
ctrl_task_sem = xSemaphoreCreateBinary();
|
||||
configASSERT( ctrl_task_sem );
|
||||
xSemaphoreGive(ctrl_task_sem);
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(start_server("/spiffs", CONFIG_WEB_PORT));
|
||||
|
||||
while(1) {
|
||||
// Nothing to do
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
// Never reach here
|
||||
ESP_LOGI(TAG, "finish");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
+109
-40
@@ -22,6 +22,8 @@
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_spiffs.h"
|
||||
#include "mdns.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "driver/twai.h" // Update from V4.2
|
||||
|
||||
#include "twai.h"
|
||||
@@ -67,10 +69,13 @@ static EventGroupHandle_t s_wifi_event_group;
|
||||
|
||||
static int s_retry_num = 0;
|
||||
|
||||
QueueHandle_t xQueue_http;
|
||||
QueueHandle_t xQueue_http_client;
|
||||
QueueHandle_t xQueue_twai_tx;
|
||||
|
||||
TOPIC_t *publish;
|
||||
int16_t npublish;
|
||||
TOPIC_t *subscribe;
|
||||
int16_t nsubscribe;
|
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void* event_data)
|
||||
@@ -94,45 +99,83 @@ static void event_handler(void* arg, esp_event_base_t event_base,
|
||||
}
|
||||
}
|
||||
|
||||
bool wifi_init_sta(void)
|
||||
void wifi_init_sta()
|
||||
{
|
||||
bool ret = false;
|
||||
s_wifi_event_group = xEventGroupCreate();
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_LOGI(TAG,"ESP-IDF Ver%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR);
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 1
|
||||
ESP_LOGI(TAG,"ESP-IDF esp_netif");
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
esp_netif_create_default_wifi_sta();
|
||||
esp_netif_t *netif = esp_netif_create_default_wifi_sta();
|
||||
#else
|
||||
ESP_LOGI(TAG,"ESP-IDF tcpip_adapter");
|
||||
tcpip_adapter_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
#endif
|
||||
|
||||
#if CONFIG_STATIC_IP
|
||||
|
||||
ESP_LOGI(TAG, "CONFIG_STATIC_IP_ADDRESS=[%s]",CONFIG_STATIC_IP_ADDRESS);
|
||||
ESP_LOGI(TAG, "CONFIG_STATIC_GW_ADDRESS=[%s]",CONFIG_STATIC_GW_ADDRESS);
|
||||
ESP_LOGI(TAG, "CONFIG_STATIC_NM_ADDRESS=[%s]",CONFIG_STATIC_NM_ADDRESS);
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4 && ESP_IDF_VERSION_MINOR >= 1
|
||||
/* Stop DHCP client */
|
||||
ESP_ERROR_CHECK(esp_netif_dhcpc_stop(netif));
|
||||
ESP_LOGI(TAG, "Stop DHCP Services");
|
||||
|
||||
/* Set STATIC IP Address */
|
||||
esp_netif_ip_info_t ip_info;
|
||||
memset(&ip_info, 0 , sizeof(esp_netif_ip_info_t));
|
||||
ip_info.ip.addr = ipaddr_addr(CONFIG_STATIC_IP_ADDRESS);
|
||||
ip_info.netmask.addr = ipaddr_addr(CONFIG_STATIC_NM_ADDRESS);
|
||||
ip_info.gw.addr = ipaddr_addr(CONFIG_STATIC_GW_ADDRESS);;
|
||||
esp_netif_set_ip_info(netif, &ip_info);
|
||||
|
||||
#else
|
||||
/* Stop DHCP client */
|
||||
tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
|
||||
ESP_LOGI(TAG, "Stop DHCP Services");
|
||||
|
||||
/* Set STATIC IP Address */
|
||||
tcpip_adapter_ip_info_t ip_info;
|
||||
memset(&ip_info, 0 , sizeof(tcpip_adapter_ip_info_t));
|
||||
ip_info.ip.addr = ipaddr_addr(CONFIG_STATIC_IP_ADDRESS);
|
||||
ip_info.netmask.addr = ipaddr_addr(CONFIG_STATIC_NM_ADDRESS);
|
||||
ip_info.gw.addr = ipaddr_addr(CONFIG_STATIC_GW_ADDRESS);;
|
||||
tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info);
|
||||
#endif
|
||||
|
||||
/*
|
||||
I referred from here.
|
||||
https://www.esp32.com/viewtopic.php?t=5380
|
||||
if we should not be using DHCP (for example we are using static IP addresses),
|
||||
then we need to instruct the ESP32 of the locations of the DNS servers manually.
|
||||
Google publicly makes available two name servers with the addresses of 8.8.8.8 and 8.8.4.4.
|
||||
*/
|
||||
|
||||
ip_addr_t d;
|
||||
d.type = IPADDR_TYPE_V4;
|
||||
d.u_addr.ip4.addr = 0x08080808; //8.8.8.8 dns
|
||||
dns_setserver(0, &d);
|
||||
d.u_addr.ip4.addr = 0x08080404; //8.8.4.4 dns
|
||||
dns_setserver(1, &d);
|
||||
|
||||
#endif
|
||||
|
||||
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));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
|
||||
|
||||
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
|
||||
},
|
||||
.password = CONFIG_ESP_WIFI_PASSWORD
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
|
||||
@@ -154,19 +197,27 @@ bool wifi_init_sta(void)
|
||||
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;
|
||||
}
|
||||
|
||||
void initialise_mdns(void)
|
||||
{
|
||||
//initialize mDNS
|
||||
ESP_ERROR_CHECK( mdns_init() );
|
||||
//set mDNS hostname (required if you want to advertise services)
|
||||
ESP_ERROR_CHECK( mdns_hostname_set(CONFIG_MDNS_HOSTNAME) );
|
||||
ESP_LOGI(TAG, "mdns hostname set to: [%s]", CONFIG_MDNS_HOSTNAME);
|
||||
|
||||
#if 0
|
||||
//set default mDNS instance name
|
||||
ESP_ERROR_CHECK( mdns_instance_name_set("ESP32 with mDNS") );
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t mountSPIFFS(char * partition_label, char * base_path) {
|
||||
@@ -326,6 +377,7 @@ void dump_table(TOPIC_t *topics, int16_t ntopic)
|
||||
}
|
||||
|
||||
void http_client_task(void *pvParameters);
|
||||
void http_server_task(void *pvParameters);
|
||||
void twai_task(void *pvParameters);
|
||||
|
||||
void app_main()
|
||||
@@ -338,10 +390,9 @@ void app_main()
|
||||
}
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
// WiFi initialize
|
||||
if (wifi_init_sta() == false) {
|
||||
while(1) vTaskDelay(10);
|
||||
}
|
||||
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
|
||||
wifi_init_sta();
|
||||
initialise_mdns();
|
||||
|
||||
// Install and start TWAI driver
|
||||
ESP_LOGI(TAG, "%s",BITRATE);
|
||||
@@ -364,8 +415,10 @@ void app_main()
|
||||
}
|
||||
|
||||
// Create Queue
|
||||
xQueue_http = xQueueCreate( 10, sizeof(FRAME_t) );
|
||||
configASSERT( xQueue_http );
|
||||
xQueue_http_client = xQueueCreate( 10, sizeof(FRAME_t) );
|
||||
configASSERT( xQueue_http_client );
|
||||
xQueue_twai_tx = xQueueCreate( 10, sizeof(twai_message_t) );
|
||||
configASSERT( xQueue_twai_tx );
|
||||
|
||||
// build publish table
|
||||
ret = build_table(&publish, "/spiffs/can2http.csv", &npublish);
|
||||
@@ -375,6 +428,22 @@ void app_main()
|
||||
}
|
||||
dump_table(publish, npublish);
|
||||
|
||||
xTaskCreate(http_client_task, "http", 1024*6, NULL, 2, NULL);
|
||||
// build subscribe table
|
||||
ret = build_table(&subscribe, "/spiffs/http2can.csv", &nsubscribe);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "build subscribe table fail");
|
||||
while(1) { vTaskDelay(1); }
|
||||
}
|
||||
dump_table(subscribe, nsubscribe);
|
||||
|
||||
/* Get the local IP address */
|
||||
tcpip_adapter_ip_info_t ip_info;
|
||||
ESP_ERROR_CHECK(tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_STA, &ip_info));
|
||||
|
||||
char cparam0[64];
|
||||
sprintf(cparam0, "%s", ip4addr_ntoa(&ip_info.ip));
|
||||
xTaskCreate(http_server_task, "server", 1024*6, (void *)cparam0, 2, NULL);
|
||||
|
||||
xTaskCreate(http_client_task, "client", 1024*6, NULL, 2, NULL);
|
||||
xTaskCreate(twai_task, "twai_rx", 1024*6, NULL, 2, NULL);
|
||||
}
|
||||
|
||||
+27
-7
@@ -22,8 +22,8 @@
|
||||
|
||||
static const char *TAG = "TWAI";
|
||||
|
||||
extern QueueHandle_t xQueue_http;
|
||||
//extern QueueHandle_t xQueue_twai_tx;
|
||||
extern QueueHandle_t xQueue_http_client;
|
||||
extern QueueHandle_t xQueue_twai_tx;
|
||||
|
||||
extern TOPIC_t *publish;
|
||||
extern int16_t npublish;
|
||||
@@ -36,9 +36,9 @@ void twai_task(void *pvParameters)
|
||||
dump_table(publish, npublish);
|
||||
|
||||
twai_message_t rx_msg;
|
||||
twai_message_t tx_msg;
|
||||
FRAME_t frameBuf;
|
||||
while (1) {
|
||||
//esp_err_t ret = twai_receive(&rx_msg, pdMS_TO_TICKS(1));
|
||||
esp_err_t ret = twai_receive(&rx_msg, pdMS_TO_TICKS(10));
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGD(TAG,"twai_receive identifier=0x%x flags=0x%x data_length_code=%d",
|
||||
@@ -49,8 +49,8 @@ void twai_task(void *pvParameters)
|
||||
ESP_LOGD(TAG, "ext=%x rtr=%x", ext, rtr);
|
||||
|
||||
#if CONFIG_ENABLE_PRINT
|
||||
if (ext == 0) {
|
||||
printf("Standard ID: 0x%03x ", rx_msg.identifier);
|
||||
if (ext == STANDARD_FRAME) {
|
||||
printf("Standard ID: 0x%03x ", rx_msg.identifier);
|
||||
} else {
|
||||
printf("Extended ID: 0x%08x", rx_msg.identifier);
|
||||
}
|
||||
@@ -73,6 +73,7 @@ void twai_task(void *pvParameters)
|
||||
ESP_LOGI(TAG, "publish[%d] frame=%d canid=0x%x topic=[%s] topic_len=%d",
|
||||
index, publish[index].frame, publish[index].canid, publish[index].topic, publish[index].topic_len);
|
||||
strcpy(frameBuf.topic, publish[index].topic);
|
||||
frameBuf.command = CMD_RECEIVE;
|
||||
frameBuf.topic_len = publish[index].topic_len;
|
||||
frameBuf.canid = rx_msg.identifier;
|
||||
frameBuf.ext = ext;
|
||||
@@ -85,20 +86,39 @@ void twai_task(void *pvParameters)
|
||||
for(int i=0;i<frameBuf.data_len;i++) {
|
||||
frameBuf.data[i] = rx_msg.data[i];
|
||||
}
|
||||
if (xQueueSend(xQueue_http, &frameBuf, portMAX_DELAY) != pdPASS) {
|
||||
if (xQueueSend(xQueue_http_client, &frameBuf, portMAX_DELAY) != pdPASS) {
|
||||
ESP_LOGE(TAG, "xQueueSend Fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (ret == ESP_ERR_TIMEOUT) {
|
||||
if (xQueueReceive(xQueue_twai_tx, &tx_msg, 0) == pdTRUE) {
|
||||
ESP_LOGI(TAG, "tx_msg.identifier=[0x%x] tx_msg.extd=%d", tx_msg.identifier, tx_msg.extd);
|
||||
twai_status_info_t status_info;
|
||||
twai_get_status_info(&status_info);
|
||||
ESP_LOGD(TAG, "status_info.state=%d",status_info.state);
|
||||
if (status_info.state != TWAI_STATE_RUNNING) {
|
||||
ESP_LOGE(TAG, "TWAI driver not running %d", status_info.state);
|
||||
continue;
|
||||
}
|
||||
ESP_LOGD(TAG, "status_info.msgs_to_tx=%d",status_info.msgs_to_tx);
|
||||
ESP_LOGD(TAG, "status_info.msgs_to_rx=%d",status_info.msgs_to_rx);
|
||||
//esp_err_t ret = twai_transmit(&tx_msg, pdMS_TO_TICKS(10));
|
||||
esp_err_t ret = twai_transmit(&tx_msg, 0);
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "twai_transmit success");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "twai_transmit Fail %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ESP_LOGE(TAG, "twai_receive Fail %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
// never reach
|
||||
// Never reach here
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
|
||||
+13
-3
@@ -1,4 +1,14 @@
|
||||
#define STANDARD_FRAME 0
|
||||
#define EXTENDED_FRAME 1
|
||||
|
||||
#define DATA_FRAME 0
|
||||
#define REMOTE_FRAME 1
|
||||
|
||||
#define CMD_RECEIVE 100
|
||||
#define CMD_SEND 200
|
||||
|
||||
typedef struct {
|
||||
int16_t command;
|
||||
char topic[64];
|
||||
int16_t topic_len;
|
||||
int32_t canid;
|
||||
@@ -9,9 +19,9 @@ typedef struct {
|
||||
} FRAME_t;
|
||||
|
||||
typedef struct {
|
||||
uint16_t frame;
|
||||
uint32_t canid;
|
||||
char * topic;
|
||||
uint16_t frame;
|
||||
uint32_t canid;
|
||||
char * topic;
|
||||
int16_t topic_len;
|
||||
} TOPIC_t;
|
||||
|
||||
|
||||
@@ -15,3 +15,13 @@ CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
#
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
||||
#
|
||||
# mDNS
|
||||
#
|
||||
CONFIG_MDNS_STRICT_MODE=y
|
||||
|
||||
#
|
||||
# HTTP Server
|
||||
#
|
||||
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
|
||||
|
||||
Reference in New Issue
Block a user