First Release
This commit is contained in:
@@ -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)
|
||||||
@@ -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))
|
||||||
|
|
||||||
@@ -1,2 +1,174 @@
|
|||||||
# esp-idf-can2http
|
# 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).
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
@@ -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]) + "<br>"
|
||||||
|
#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)
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
set(COMPONENT_SRCS "main.c" "http_post.c" "twai.c")
|
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||||
|
|
||||||
|
register_component()
|
||||||
@@ -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
|
||||||
@@ -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.)
|
||||||
@@ -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 <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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<frameBuf.data_len;i++) {
|
||||||
|
ESP_LOGI(TAG, "DATA=%x", frameBuf.data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// build JSON string
|
||||||
|
cJSON *root;
|
||||||
|
root = cJSON_CreateObject();
|
||||||
|
cJSON_AddNumberToObject(root, "canid", frameBuf.canid);
|
||||||
|
if (frameBuf.ext == 0) {
|
||||||
|
cJSON_AddStringToObject(root, "frame", "standard");
|
||||||
|
} else {
|
||||||
|
cJSON_AddStringToObject(root, "frame", "extended");
|
||||||
|
}
|
||||||
|
cJSON *dataArray;
|
||||||
|
dataArray = cJSON_CreateArray();
|
||||||
|
cJSON_AddItemToObject(root, "data", dataArray);
|
||||||
|
for(int i=0;i<frameBuf.data_len;i++) {
|
||||||
|
cJSON *dataItem = NULL;
|
||||||
|
dataItem = cJSON_CreateNumber(frameBuf.data[i]);
|
||||||
|
cJSON_AddItemToArray(dataArray, dataItem);
|
||||||
|
}
|
||||||
|
char *json_string = cJSON_Print(root);
|
||||||
|
ESP_LOGI(TAG, "json_string\n%s",json_string);
|
||||||
|
cJSON_Delete(root);
|
||||||
|
|
||||||
|
//char *post_data = "{\"canid\":257}";
|
||||||
|
//http_rest_with_url(frameBuf.topic, post_data);
|
||||||
|
http_rest_with_url(frameBuf.topic, json_string);
|
||||||
|
} // end while
|
||||||
|
|
||||||
|
// Never reach here
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
+380
@@ -0,0 +1,380 @@
|
|||||||
|
/*
|
||||||
|
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 <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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;i<ntopic;i++) {
|
||||||
|
ESP_LOGI(pcTaskGetTaskName(0), "topics[%d] frame=%d canid=0x%x topic=[%s] topic_len=%d",
|
||||||
|
i, (topics+i)->frame, (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);
|
||||||
|
}
|
||||||
+103
@@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#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<npublish;index++) {
|
||||||
|
if (publish[index].frame != ext) continue;
|
||||||
|
if (publish[index].canid == rx_msg.identifier) {
|
||||||
|
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.topic_len = publish[index].topic_len;
|
||||||
|
frameBuf.canid = rx_msg.identifier;
|
||||||
|
frameBuf.ext = ext;
|
||||||
|
frameBuf.rtr = rtr;
|
||||||
|
if (rtr == 0) {
|
||||||
|
frameBuf.data_len = rx_msg.data_length_code;
|
||||||
|
} else {
|
||||||
|
frameBuf.data_len = 0;
|
||||||
|
}
|
||||||
|
for(int i=0;i<frameBuf.data_len;i++) {
|
||||||
|
frameBuf.data[i] = rx_msg.data[i];
|
||||||
|
}
|
||||||
|
if (xQueueSend(xQueue_http, &frameBuf, portMAX_DELAY) != pdPASS) {
|
||||||
|
ESP_LOGE(TAG, "xQueueSend Fail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (ret == ESP_ERR_TIMEOUT) {
|
||||||
|
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(TAG, "twai_receive Fail %s", esp_err_to_name(ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// never reach
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
typedef struct {
|
||||||
|
char topic[64];
|
||||||
|
int16_t topic_len;
|
||||||
|
int32_t canid;
|
||||||
|
int16_t ext;
|
||||||
|
int16_t rtr;
|
||||||
|
int16_t data_len;
|
||||||
|
char data[8];
|
||||||
|
} FRAME_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t frame;
|
||||||
|
uint32_t canid;
|
||||||
|
char * topic;
|
||||||
|
int16_t topic_len;
|
||||||
|
} TOPIC_t;
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 1M,
|
||||||
|
storage, data, spiffs, , 0xF0000,
|
||||||
|
@@ -0,0 +1,17 @@
|
|||||||
|
#
|
||||||
|
# Serial flasher config
|
||||||
|
#
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
|
||||||
|
#
|
||||||
|
# Partition Table
|
||||||
|
#
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||||
|
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||||
|
|
||||||
|
#
|
||||||
|
# ESP32-specific
|
||||||
|
#
|
||||||
|
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||||
|
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Simple REST Server
|
||||||
|
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import datetime
|
||||||
|
import tornado.httpserver
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.options
|
||||||
|
import tornado.web
|
||||||
|
|
||||||
|
from tornado.options import define, options
|
||||||
|
define("port", default=8000, help="run on the given port", type=int)
|
||||||
|
|
||||||
|
database = []
|
||||||
|
|
||||||
|
class IndexHandler(tornado.web.RequestHandler):
|
||||||
|
def get(self):
|
||||||
|
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]) + "<br>"
|
||||||
|
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()
|
||||||
Reference in New Issue
Block a user