migration

This commit is contained in:
2026-05-26 21:39:05 -06:00
parent 30691e90ce
commit d45a05dce3
25 changed files with 2115 additions and 2115 deletions
+11 -11
View File
@@ -1,11 +1,11 @@
# 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 'csv' 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)
# 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 'csv' 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)
+21 -21
View File
@@ -1,21 +1,21 @@
MIT License
Copyright (c) 2024 PrincessPi3 and nopnop2002
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
MIT License
Copyright (c) 2024 PrincessPi3 and nopnop2002
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+289 -289
View File
@@ -1,289 +1,289 @@
# Sillyfilly-CAN
A fork of [esp-idf-can2http](https://github.com/nopnop2002/esp-idf-can2http)
A CAN bus tool built for the esp32. It has a builtin web interface and works with nearly any CAN transceiver like the [N65HVD230](https://www.amazon.com/SN65HVD230-CAN-Board-Communication-Development/dp/B00KM6XMXO/) or the [TJA1050](https://www.amazon.com/Comimark-Transceiver-TJA1050-Controller-Schnittstelle/dp/B07W4VZ2F2/)
## Hardware needed
* esp32(-X) (My favorite esp32)[https://www.amazon.com/Espressif-ESP32-S3-DevKitC-1-N32R8V-Development-Board/dp/B09R4GSDJM]
* CAN transceiver like the [N65HVD230](https://www.amazon.com/SN65HVD230-CAN-Board-Communication-Development/dp/B00KM6XMXO/) or the [TJA1050](https://www.amazon.com/Comimark-Transceiver-TJA1050-Controller-Schnittstelle/dp/B07W4VZ2F2/)
some jumper wires
* Jumper wires
## Optional
* [ODB2 pigtail](https://www.amazon.com/iKKEGOL-Connector-Diagnostic-Extension-Pigtail/dp/B0828YHWFG/)
## Installation
1) install [esp-idf](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html#installation) if not installed on your computer already
2) open esp-idf terminal
3) plug in your esp32 to computer via USB cable
4) Run these commands:
```
git clone https://github.com/PrincessPi3/Sillyfilly-CAN.git
cd Sillyfilly-CAN
idf.py set-target esp32s3 # example idf.py set-target <your_esp32_ type>
idf.py menuconfig
```
5) Under "Sillyfilly-CAN Configuration" set your settings like wifi and CRX and CTX pins.
6) Wire transceiver to the pins: CRX->RX, CTX-TX, GND->GND
7) run this command
```idf.py flash monitor # ctrl+] to exit monitor```
<your_esp32_type> can be one of 'esp32', 'esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'
# Original README.md
# esp-idf-can2http
CANbus to http bridge using esp32.
It's purpose is to be a bridge between a CAN-Bus and a HTTP-Server.
![slide0001](https://user-images.githubusercontent.com/6020549/125031346-7ae6ec80-e0c7-11eb-8d8a-f419154d277c.jpg)
![slide0002](https://user-images.githubusercontent.com/6020549/125031353-7d494680-e0c7-11eb-8382-df74fab44737.jpg)
![slide0003](https://user-images.githubusercontent.com/6020549/125031358-7e7a7380-e0c7-11eb-9880-07c42a1a7d64.jpg)
# Software requirement
ESP-IDF V4.4/V5.x.
ESP-IDF V5.1 is required when using ESP32C6.
# Hardware requirements
- SN65HVD23x CAN-BUS Transceiver
SN65HVD23x series has 230/231/232.
They differ in standby/sleep mode functionality.
Other features are the same.
- Termination resistance
I used 150 ohms.
# Wireing
|SN65HVD23x||ESP32|ESP32-S2/S3|ESP32-C3/C6||
|:-:|:-:|:-:|:-:|:-:|:-:|
|D(CTX)|--|GPIO21|GPIO17|GPIO0|(*1)|
|GND|--|GND|GND|GND||
|Vcc|--|3.3V|3.3V|3.3V||
|R(CRX)|--|GPIO22|GPIO18|GPIO1|(*1)|
|Vref|--|N/C|N/C|N/C||
|CANL|--||||To CAN Bus|
|CANH|--||||To CAN Bus|
|RS|--|GND|GND|GND|(*2)|
(*1) You can change using menuconfig. But it may not work with other GPIOs.
(*2) N/C for SN65HVD232
# 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
```
git clone https://github.com/nopnop2002/esp-idf-can2http
cd esp-idf-can2http
idf.py set-target {esp32/esp32s2/esp32s3/esp32c3/esp32c6}
idf.py menuconfig
idf.py flash
```
# Configuration
![config-main](https://user-images.githubusercontent.com/6020549/123870635-92a6ce00-d96d-11eb-9b67-7b6a26e95fbd.jpg)
![config-app](https://user-images.githubusercontent.com/6020549/123870638-94709180-d96d-11eb-94da-b4860148be6a.jpg)
## CAN Setting
![config-can](https://user-images.githubusercontent.com/6020549/160263252-f2a10f2b-a970-44bf-ab9c-22ae7baa4ab5.jpg)
## WiFi Setting
![config-wifi-1](https://user-images.githubusercontent.com/6020549/125028704-73254900-e0c3-11eb-9a44-25482d7a08be.jpg)
You can use static IP.
![config-wifi-2](https://user-images.githubusercontent.com/6020549/125028738-80423800-e0c3-11eb-984f-9dc4f892bdb6.jpg)
You can connect using mDNS.
![config-wifi-3](https://user-images.githubusercontent.com/6020549/125028763-89cba000-e0c3-11eb-921d-9baca5ec58d4.jpg)
## External HTTP Server Setting
The External HTTP Server receives CAN Bus received data.
![config-http](https://user-images.githubusercontent.com/6020549/160263251-1b1a4771-a5f0-43f2-b75e-572fa6904ac9.jpg)
__Note__
The Built-in HTTP Server receives CAN Bus transmittion data.
# Definition from CANbus to HTTP
When CANbus data is received, it is sent by HTTP POST 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-POST-Path of external HTTP server.
Each CAN-ID is allowed to appear only once in the whole file.
```
S,101,/post
E,101,/post
S,103,/post
E,103,/post
```
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:
CAN-ID is a decimal number, not a hexadecimal number.
```
{"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]}
```
__CAN messages not registered in csv/can2http.csv are discarded and not POSTed to HTTP.__
# Definition from HTTP to CANbus
Not exist.
You can send any CAN-ID.
# Send CANBus frame using curl
CAN-ID is specified in __decimal number__.
```
$ 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
twai send successfully
$ 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
twai send successfully
```
CANBus reception using UNO.
![send_standard](https://user-images.githubusercontent.com/6020549/125029834-2c385300-e0c5-11eb-8f25-f3af82e90038.jpg)
![send_extended](https://user-images.githubusercontent.com/6020549/125029930-5558e380-e0c5-11eb-951b-baee58e66d18.jpg)
---
# HTTP Server Using Tornado
```
cd $HOME
sudo apt install python3-pip python3-setuptools
python3 -m pip install -U pip
python3 -m pip install -U wheel
python3 -m pip install tornado
git clone https://github.com/nopnop2002/esp-idf-can2http
cd esp-idf-can2http
cd tornado
python3 can.py
```
You can specify the number of lines to display and the port number.
The default port number is 8000 and the default number of display lines is 20.
Redisplay every 5 seconds.
![can2http-tornado](https://user-images.githubusercontent.com/6020549/202159913-74fab7ef-d95c-4774-8432-ceceac24af4e.jpg)
# HTTP Server Using Flask
```
cd $HOME
sudo apt install python3-pip python3-setuptools
python3 -m pip install -U pip
python3 -m pip install -U wheel
python3 -m pip install -U Werkzeug
python3 -m pip install flask
git clone https://github.com/nopnop2002/esp-idf-can2http
cd esp-idf-can2http
cd flask
python3 can.py
```
You can specify the number of lines to display and the port number.
The default port number is 8000 and the default number of display lines is 20.
Redisplay every 5 seconds.
![can2http-flask](https://user-images.githubusercontent.com/6020549/202165156-dea58ea4-cbb8-4e35-bdc8-a19e5be575e7.jpg)
# Brows received data Using Tornado/Flask
Open your browser and put the Server's IP in the address bar.
![can2http-browser](https://user-images.githubusercontent.com/6020549/202160043-0ca6e869-f6bc-4319-b8db-48a8fe9b1c5f.jpg)
# Visualize CAN-Frame
## Using python
There is a lot of information on the internet about the Python + visualization library.
- [matplotlib](https://matplotlib.org/)
- [seaborn](https://seaborn.pydata.org/index.html)
- [bokeh](https://bokeh.org/)
- [plotly](https://plotly.com/python/)
## Using node.js
There is a lot of information on the internet about the node.js + __real time__ visualization library.
- [epoch](https://epochjs.github.io/epoch/real-time/)
- [plotly](https://plotly.com/javascript/streaming/)
- [chartjs-plugin-streaming](https://nagix.github.io/chartjs-plugin-streaming/1.9.0/)
# Troubleshooting
There is a module of SN65HVD230 like this.
![SN65HVD230-1](https://user-images.githubusercontent.com/6020549/80897499-4d204e00-8d34-11ea-80c9-3dc41b1addab.JPG)
There is a __120 ohms__ terminating resistor on the left side.
![SN65HVD230-22](https://user-images.githubusercontent.com/6020549/89281044-74185400-d684-11ea-9f55-830e0e9e6424.JPG)
I have removed the terminating resistor.
And I used a external resistance of __150 ohms__.
A transmission fail is fixed.
![SN65HVD230-33](https://user-images.githubusercontent.com/6020549/89280710-f7857580-d683-11ea-9b36-12e36910e7d9.JPG)
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).
# Reference
https://github.com/nopnop2002/esp-idf-candump
https://github.com/nopnop2002/esp-idf-can2mqtt
https://github.com/nopnop2002/esp-idf-can2usb
https://github.com/nopnop2002/esp-idf-can2websocket
https://github.com/nopnop2002/esp-idf-can2socket
https://github.com/nopnop2002/esp-idf-CANBus-Monitor
# Sillyfilly-CAN
A fork of [esp-idf-can2http](https://github.com/nopnop2002/esp-idf-can2http)
A CAN bus tool built for the esp32. It has a builtin web interface and works with nearly any CAN transceiver like the [N65HVD230](https://www.amazon.com/SN65HVD230-CAN-Board-Communication-Development/dp/B00KM6XMXO/) or the [TJA1050](https://www.amazon.com/Comimark-Transceiver-TJA1050-Controller-Schnittstelle/dp/B07W4VZ2F2/)
## Hardware needed
* esp32(-X) (My favorite esp32)[https://www.amazon.com/Espressif-ESP32-S3-DevKitC-1-N32R8V-Development-Board/dp/B09R4GSDJM]
* CAN transceiver like the [N65HVD230](https://www.amazon.com/SN65HVD230-CAN-Board-Communication-Development/dp/B00KM6XMXO/) or the [TJA1050](https://www.amazon.com/Comimark-Transceiver-TJA1050-Controller-Schnittstelle/dp/B07W4VZ2F2/)
some jumper wires
* Jumper wires
## Optional
* [ODB2 pigtail](https://www.amazon.com/iKKEGOL-Connector-Diagnostic-Extension-Pigtail/dp/B0828YHWFG/)
## Installation
1) install [esp-idf](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/index.html#installation) if not installed on your computer already
2) open esp-idf terminal
3) plug in your esp32 to computer via USB cable
4) Run these commands:
```
git clone https://github.com/PrincessPi3/Sillyfilly-CAN.git
cd Sillyfilly-CAN
idf.py set-target esp32s3 # example idf.py set-target <your_esp32_ type>
idf.py menuconfig
```
5) Under "Sillyfilly-CAN Configuration" set your settings like wifi and CRX and CTX pins.
6) Wire transceiver to the pins: CRX->RX, CTX-TX, GND->GND
7) run this command
```idf.py flash monitor # ctrl+] to exit monitor```
<your_esp32_type> can be one of 'esp32', 'esp32s2', 'esp32c3', 'esp32s3', 'esp32c2', 'esp32c6', 'esp32h2', 'esp32p4', 'esp32c5', 'esp32c61'
# Original README.md
# esp-idf-can2http
CANbus to http bridge using esp32.
It's purpose is to be a bridge between a CAN-Bus and a HTTP-Server.
![slide0001](https://user-images.githubusercontent.com/6020549/125031346-7ae6ec80-e0c7-11eb-8d8a-f419154d277c.jpg)
![slide0002](https://user-images.githubusercontent.com/6020549/125031353-7d494680-e0c7-11eb-8382-df74fab44737.jpg)
![slide0003](https://user-images.githubusercontent.com/6020549/125031358-7e7a7380-e0c7-11eb-9880-07c42a1a7d64.jpg)
# Software requirement
ESP-IDF V4.4/V5.x.
ESP-IDF V5.1 is required when using ESP32C6.
# Hardware requirements
- SN65HVD23x CAN-BUS Transceiver
SN65HVD23x series has 230/231/232.
They differ in standby/sleep mode functionality.
Other features are the same.
- Termination resistance
I used 150 ohms.
# Wireing
|SN65HVD23x||ESP32|ESP32-S2/S3|ESP32-C3/C6||
|:-:|:-:|:-:|:-:|:-:|:-:|
|D(CTX)|--|GPIO21|GPIO17|GPIO0|(*1)|
|GND|--|GND|GND|GND||
|Vcc|--|3.3V|3.3V|3.3V||
|R(CRX)|--|GPIO22|GPIO18|GPIO1|(*1)|
|Vref|--|N/C|N/C|N/C||
|CANL|--||||To CAN Bus|
|CANH|--||||To CAN Bus|
|RS|--|GND|GND|GND|(*2)|
(*1) You can change using menuconfig. But it may not work with other GPIOs.
(*2) N/C for SN65HVD232
# 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
```
git clone https://github.com/nopnop2002/esp-idf-can2http
cd esp-idf-can2http
idf.py set-target {esp32/esp32s2/esp32s3/esp32c3/esp32c6}
idf.py menuconfig
idf.py flash
```
# Configuration
![config-main](https://user-images.githubusercontent.com/6020549/123870635-92a6ce00-d96d-11eb-9b67-7b6a26e95fbd.jpg)
![config-app](https://user-images.githubusercontent.com/6020549/123870638-94709180-d96d-11eb-94da-b4860148be6a.jpg)
## CAN Setting
![config-can](https://user-images.githubusercontent.com/6020549/160263252-f2a10f2b-a970-44bf-ab9c-22ae7baa4ab5.jpg)
## WiFi Setting
![config-wifi-1](https://user-images.githubusercontent.com/6020549/125028704-73254900-e0c3-11eb-9a44-25482d7a08be.jpg)
You can use static IP.
![config-wifi-2](https://user-images.githubusercontent.com/6020549/125028738-80423800-e0c3-11eb-984f-9dc4f892bdb6.jpg)
You can connect using mDNS.
![config-wifi-3](https://user-images.githubusercontent.com/6020549/125028763-89cba000-e0c3-11eb-921d-9baca5ec58d4.jpg)
## External HTTP Server Setting
The External HTTP Server receives CAN Bus received data.
![config-http](https://user-images.githubusercontent.com/6020549/160263251-1b1a4771-a5f0-43f2-b75e-572fa6904ac9.jpg)
__Note__
The Built-in HTTP Server receives CAN Bus transmittion data.
# Definition from CANbus to HTTP
When CANbus data is received, it is sent by HTTP POST 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-POST-Path of external HTTP server.
Each CAN-ID is allowed to appear only once in the whole file.
```
S,101,/post
E,101,/post
S,103,/post
E,103,/post
```
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:
CAN-ID is a decimal number, not a hexadecimal number.
```
{"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]}
```
__CAN messages not registered in csv/can2http.csv are discarded and not POSTed to HTTP.__
# Definition from HTTP to CANbus
Not exist.
You can send any CAN-ID.
# Send CANBus frame using curl
CAN-ID is specified in __decimal number__.
```
$ 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
twai send successfully
$ 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
twai send successfully
```
CANBus reception using UNO.
![send_standard](https://user-images.githubusercontent.com/6020549/125029834-2c385300-e0c5-11eb-8f25-f3af82e90038.jpg)
![send_extended](https://user-images.githubusercontent.com/6020549/125029930-5558e380-e0c5-11eb-951b-baee58e66d18.jpg)
---
# HTTP Server Using Tornado
```
cd $HOME
sudo apt install python3-pip python3-setuptools
python3 -m pip install -U pip
python3 -m pip install -U wheel
python3 -m pip install tornado
git clone https://github.com/nopnop2002/esp-idf-can2http
cd esp-idf-can2http
cd tornado
python3 can.py
```
You can specify the number of lines to display and the port number.
The default port number is 8000 and the default number of display lines is 20.
Redisplay every 5 seconds.
![can2http-tornado](https://user-images.githubusercontent.com/6020549/202159913-74fab7ef-d95c-4774-8432-ceceac24af4e.jpg)
# HTTP Server Using Flask
```
cd $HOME
sudo apt install python3-pip python3-setuptools
python3 -m pip install -U pip
python3 -m pip install -U wheel
python3 -m pip install -U Werkzeug
python3 -m pip install flask
git clone https://github.com/nopnop2002/esp-idf-can2http
cd esp-idf-can2http
cd flask
python3 can.py
```
You can specify the number of lines to display and the port number.
The default port number is 8000 and the default number of display lines is 20.
Redisplay every 5 seconds.
![can2http-flask](https://user-images.githubusercontent.com/6020549/202165156-dea58ea4-cbb8-4e35-bdc8-a19e5be575e7.jpg)
# Brows received data Using Tornado/Flask
Open your browser and put the Server's IP in the address bar.
![can2http-browser](https://user-images.githubusercontent.com/6020549/202160043-0ca6e869-f6bc-4319-b8db-48a8fe9b1c5f.jpg)
# Visualize CAN-Frame
## Using python
There is a lot of information on the internet about the Python + visualization library.
- [matplotlib](https://matplotlib.org/)
- [seaborn](https://seaborn.pydata.org/index.html)
- [bokeh](https://bokeh.org/)
- [plotly](https://plotly.com/python/)
## Using node.js
There is a lot of information on the internet about the node.js + __real time__ visualization library.
- [epoch](https://epochjs.github.io/epoch/real-time/)
- [plotly](https://plotly.com/javascript/streaming/)
- [chartjs-plugin-streaming](https://nagix.github.io/chartjs-plugin-streaming/1.9.0/)
# Troubleshooting
There is a module of SN65HVD230 like this.
![SN65HVD230-1](https://user-images.githubusercontent.com/6020549/80897499-4d204e00-8d34-11ea-80c9-3dc41b1addab.JPG)
There is a __120 ohms__ terminating resistor on the left side.
![SN65HVD230-22](https://user-images.githubusercontent.com/6020549/89281044-74185400-d684-11ea-9f55-830e0e9e6424.JPG)
I have removed the terminating resistor.
And I used a external resistance of __150 ohms__.
A transmission fail is fixed.
![SN65HVD230-33](https://user-images.githubusercontent.com/6020549/89280710-f7857580-d683-11ea-9b36-12e36910e7d9.JPG)
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).
# Reference
https://github.com/nopnop2002/esp-idf-candump
https://github.com/nopnop2002/esp-idf-can2mqtt
https://github.com/nopnop2002/esp-idf-can2usb
https://github.com/nopnop2002/esp-idf-can2websocket
https://github.com/nopnop2002/esp-idf-can2socket
https://github.com/nopnop2002/esp-idf-CANBus-Monitor
+11 -11
View File
@@ -1,11 +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 PATH of external web server.
#Each CAN-ID is allowed to appear only once in the whole file.
S,101,/post
E,101,/post
S,103,/post
E,103,/post
#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 PATH of external web server.
#Each CAN-ID is allowed to appear only once in the whole file.
S,101,/post
E,101,/post
S,103,/post
E,103,/post
1 #The file can2http.csv has three columns.
2 #In the first column you need to specify the CAN Frame type.
3 #The CAN frame type is either S(Standard frame) or E(Extended frame).
4 #In the second column you have to specify the CAN-ID as a __hexdecimal number__.
5 #In the last column you have to specify the PATH of external web server.
6 #Each CAN-ID is allowed to appear only once in the whole file.
7 S,101,/post
8 E,101,/post
9 S,103,/post
10 E,103,/post
11
+78 -78
View File
@@ -1,79 +1,79 @@
<h1>Sillyfilly CAN Interface</h1>
<form id="txform">
<div class="form-group">
<label for="canidi">CAN ID (decimal)</label>
<input type="text" name="canidi" class="form-control" value="257" />
</div>
<div class="form-group">
<label>Frame Type</label><br />
<label for="standard">Standard</label>
<input type="radio" id="standard" name="frame" value="standard" checked /><br />
<label for="extended">Extended</label>
<input type="radio" name="frame" id="extended" value="extended" />
</div>
<div class="form-group">
<label for="datai">Standard (decimal seperated by commas)</label>
<input type="text" id="datai" name="datai" value="16,17,18" />
</div>
<input type="button" id="send" value="Send >>" onclick="send_tx()" />
<h3>TX</h3>
<div id="output">none yet</div>
<h3>RX</h3>
<div id="frames">none yet</div>
</form>
<script>
var clearedtx = 0;
var clearedrx = 0;
function mapFn(element, index) {
return parseInt(element);
}
function send_tx() {
var form = document.getElementById('txform');
var datai = document.getElementById('datai').value;
var datai_arr = Array.from(datai.split(","), mapFn);
var xhr = new XMLHttpRequest();
var formData = new FormData(form);
//var txHTML = document.getElementById('output');
//open the request
xhr.open('POST','/api/twai/send');
xhr.setRequestHeader("Content-Type", "application/json");
//prepare da dataz
var obj = Object.fromEntries(formData);
//obj["data"] = datai.split(",");
obj["data"] = datai_arr;
obj["canid"] = parseInt(obj["canidi"]);
delete obj.datai;
delete obj.canidi;
console.log(obj);
var JSONstring = JSON.stringify(obj);
xhr.send(JSONstring);
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
if(clearedtx === 0) { document.getElementById('output').innerHTML=""; clearedtx = 1; }
clearedtx = 1;
document.getElementById('output').innerHTML += xhr.responseText+": "+JSONstring+"<br>";
}
}
return true;
}
function update_rx() {
var xhr = new XMLHttpRequest();
//var rxHTML = document.getElementById('frames');
xhr.open('GET','/api/twai/read');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
if(clearedrx === 0) { document.getElementById('frames').innerHTML=""; clearedrx = 1; }
document.getElementById('frames').innerHTML += xhr.responseText;
}
}
}
setInterval(update_rx,1500);
<h1>Sillyfilly CAN Interface</h1>
<form id="txform">
<div class="form-group">
<label for="canidi">CAN ID (decimal)</label>
<input type="text" name="canidi" class="form-control" value="257" />
</div>
<div class="form-group">
<label>Frame Type</label><br />
<label for="standard">Standard</label>
<input type="radio" id="standard" name="frame" value="standard" checked /><br />
<label for="extended">Extended</label>
<input type="radio" name="frame" id="extended" value="extended" />
</div>
<div class="form-group">
<label for="datai">Standard (decimal seperated by commas)</label>
<input type="text" id="datai" name="datai" value="16,17,18" />
</div>
<input type="button" id="send" value="Send >>" onclick="send_tx()" />
<h3>TX</h3>
<div id="output">none yet</div>
<h3>RX</h3>
<div id="frames">none yet</div>
</form>
<script>
var clearedtx = 0;
var clearedrx = 0;
function mapFn(element, index) {
return parseInt(element);
}
function send_tx() {
var form = document.getElementById('txform');
var datai = document.getElementById('datai').value;
var datai_arr = Array.from(datai.split(","), mapFn);
var xhr = new XMLHttpRequest();
var formData = new FormData(form);
//var txHTML = document.getElementById('output');
//open the request
xhr.open('POST','/api/twai/send');
xhr.setRequestHeader("Content-Type", "application/json");
//prepare da dataz
var obj = Object.fromEntries(formData);
//obj["data"] = datai.split(",");
obj["data"] = datai_arr;
obj["canid"] = parseInt(obj["canidi"]);
delete obj.datai;
delete obj.canidi;
console.log(obj);
var JSONstring = JSON.stringify(obj);
xhr.send(JSONstring);
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
if(clearedtx === 0) { document.getElementById('output').innerHTML=""; clearedtx = 1; }
clearedtx = 1;
document.getElementById('output').innerHTML += xhr.responseText+": "+JSONstring+"<br>";
}
}
return true;
}
function update_rx() {
var xhr = new XMLHttpRequest();
//var rxHTML = document.getElementById('frames');
xhr.open('GET','/api/twai/read');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
if(clearedrx === 0) { document.getElementById('frames').innerHTML=""; clearedrx = 1; }
document.getElementById('frames').innerHTML += xhr.responseText;
}
}
}
setInterval(update_rx,1500);
</script>
+80 -80
View File
@@ -1,80 +1,80 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Simple REST Server
import os
import sys
import json
import datetime
import argparse
from flask import Flask, request, render_template
app = Flask(__name__)
database = []
@app.route("/")
def root():
global database
items = []
for data in database:
#print("{}".format(data))
datetime = data[0].split()
#print("data={} datetime={}".format(data[0], datetime))
items.append({
"date": datetime[0],
"time": datetime[1],
"id": data[1],
"frame": data[2],
"value": data[3]
})
#print("items={}".format(items))
return render_template("index.html", lines=app.config['lines'], items=items)
#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))
print("lines={}".format(app.config['lines']))
#if(len(database) >= 20):
if(len(database) >= app.config['lines']):
database.pop(0)
database.append(items)
data = json.dumps(['result', 'ok'])
return data
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-port', type=int, default=8000)
parser.add_argument('-lines', type=int, default=20)
args = parser.parse_args()
print("port={}".format(args.port))
print("lines={}".format(args.lines))
app.config['lines'] = args.lines
#app.run()
app.run(host='127.0.0.1', port=args.port, debug=True)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Simple REST Server
import os
import sys
import json
import datetime
import argparse
from flask import Flask, request, render_template
app = Flask(__name__)
database = []
@app.route("/")
def root():
global database
items = []
for data in database:
#print("{}".format(data))
datetime = data[0].split()
#print("data={} datetime={}".format(data[0], datetime))
items.append({
"date": datetime[0],
"time": datetime[1],
"id": data[1],
"frame": data[2],
"value": data[3]
})
#print("items={}".format(items))
return render_template("index.html", lines=app.config['lines'], items=items)
#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))
print("lines={}".format(app.config['lines']))
#if(len(database) >= 20):
if(len(database) >= app.config['lines']):
database.pop(0)
database.append(items)
data = json.dumps(['result', 'ok'])
return data
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-port', type=int, default=8000)
parser.add_argument('-lines', type=int, default=20)
args = parser.parse_args()
print("port={}".format(args.port))
print("lines={}".format(args.lines))
app.config['lines'] = args.lines
#app.run()
app.run(host='127.0.0.1', port=args.port, debug=True)
+35 -35
View File
@@ -1,35 +1,35 @@
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="5;url="/">
<title>CAN DATA</title>
</head>
<body>
Display the latest {{ lines }} records.<br>
If you want to see more, specify the number of lines at startup.<br>
<table border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr style="color:#ffffff;" bgcolor="#800000">
<th valign="top">Date</th>
<th valign="top">Time</th>
<th valign="top">ID</th>
<th valign="top">Frame</th>
<th valign="top">Value</th>
</tr>
{% for i in items %}
<tr>
<td>{{ i.date }}</td>
<td>{{ i.time }}</td>
<td>{{ i.id }}</td>
<td>{{ i.frame }}</td>
<td>{{ i.value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="5;url="/">
<title>CAN DATA</title>
</head>
<body>
Display the latest {{ lines }} records.<br>
If you want to see more, specify the number of lines at startup.<br>
<table border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr style="color:#ffffff;" bgcolor="#800000">
<th valign="top">Date</th>
<th valign="top">Time</th>
<th valign="top">ID</th>
<th valign="top">Frame</th>
<th valign="top">Value</th>
</tr>
{% for i in items %}
<tr>
<td>{{ i.date }}</td>
<td>{{ i.time }}</td>
<td>{{ i.id }}</td>
<td>{{ i.frame }}</td>
<td>{{ i.value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</body>
</html>
+4 -4
View File
@@ -1,4 +1,4 @@
set(COMPONENT_SRCS "main.c" "http_post.c" "http_server.c" "twai.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()
set(COMPONENT_SRCS "main.c" "http_post.c" "http_server.c" "twai.c")
set(COMPONENT_ADD_INCLUDEDIRS ".")
register_component()
+152 -152
View File
@@ -1,152 +1,152 @@
menu "Sillyfilly-CAN Configuration"
config GPIO_RANGE_MAX
int
default 33 if IDF_TARGET_ESP32
default 46 if IDF_TARGET_ESP32S2
default 48 if IDF_TARGET_ESP32S3
default 19 if IDF_TARGET_ESP32C3
default 30 if IDF_TARGET_ESP32C6
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 GPIO_RANGE_MAX
default 21 if IDF_TARGET_ESP32
default 17 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 0 # C3 and others
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 GPIO_RANGE_MAX
default 22 if IDF_TARGET_ESP32
default 18 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 1 # C3 and others
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"
default true
help
Output the received CAN FRAME to STDOUT.
endmenu
menu "WiFi Setting"
config ESP_WIFI_SSID
string "WiFi SSID"
default "wifi-name"
help
SSID (network name) to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "wifi-password"
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.
config MDNS_HOSTNAME
string "mDNS Hostname"
default "esp32-can-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"
config WEB_SERVER
string "HTTP Server IP or mDNS"
default "esp32-can-server"
help
The host name or IP address of the HTTP server to use.
config WEB_PORT
int "HTTP Server Port"
default 8000
help
HTTP server port to use.
endmenu
endmenu
menu "Sillyfilly-CAN Configuration"
config GPIO_RANGE_MAX
int
default 33 if IDF_TARGET_ESP32
default 46 if IDF_TARGET_ESP32S2
default 48 if IDF_TARGET_ESP32S3
default 19 if IDF_TARGET_ESP32C3
default 30 if IDF_TARGET_ESP32C6
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 GPIO_RANGE_MAX
default 21 if IDF_TARGET_ESP32
default 17 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 0 # C3 and others
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 GPIO_RANGE_MAX
default 22 if IDF_TARGET_ESP32
default 18 if IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
default 1 # C3 and others
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"
default true
help
Output the received CAN FRAME to STDOUT.
endmenu
menu "WiFi Setting"
config ESP_WIFI_SSID
string "WiFi SSID"
default "wifi-name"
help
SSID (network name) to connect to.
config ESP_WIFI_PASSWORD
string "WiFi Password"
default "wifi-password"
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.
config MDNS_HOSTNAME
string "mDNS Hostname"
default "esp32-can-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"
config WEB_SERVER
string "HTTP Server IP or mDNS"
default "esp32-can-server"
help
The host name or IP address of the HTTP server to use.
config WEB_PORT
int "HTTP Server Port"
default 8000
help
HTTP server port to use.
endmenu
endmenu
+4 -4
View File
@@ -1,4 +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.)
#
# 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.)
+199 -199
View File
@@ -1,199 +1,199 @@
/* 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 <stdio.h>
#include <inttypes.h>
#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_client;
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;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
case HTTP_EVENT_REDIRECT:
ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
esp_http_client_set_header(evt->client, "From", "user@example.com");
esp_http_client_set_header(evt->client, "Accept", "text/html");
break;
#endif
}
return ESP_OK;
}
#define MAX_HTTP_OUTPUT_BUFFER 2048
static void http_rest_with_url(char * path, char * post_data)
{
ESP_LOGI(TAG, "path=[%s]", path);
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.
*/
#if 1
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,
};
#else
// Same as above
esp_http_client_config_t config = {
.url = "http://http-server.local: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 = %lld",
esp_http_client_get_status_code(client),
(int64_t)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: connect to http://%s:%d", CONFIG_WEB_SERVER, CONFIG_WEB_PORT);
FRAME_t frameBuf;
while (1) {
xQueueReceive(xQueue_http_client, &frameBuf, portMAX_DELAY);
ESP_LOGI(TAG, "canid=%"PRIx32" 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);
cJSON_free(json_string);
} // end while
// Never reach here
vTaskDelete(NULL);
}
/* 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 <stdio.h>
#include <inttypes.h>
#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_client;
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;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
case HTTP_EVENT_REDIRECT:
ESP_LOGD(TAG, "HTTP_EVENT_REDIRECT");
esp_http_client_set_header(evt->client, "From", "user@example.com");
esp_http_client_set_header(evt->client, "Accept", "text/html");
break;
#endif
}
return ESP_OK;
}
#define MAX_HTTP_OUTPUT_BUFFER 2048
static void http_rest_with_url(char * path, char * post_data)
{
ESP_LOGI(TAG, "path=[%s]", path);
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.
*/
#if 1
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,
};
#else
// Same as above
esp_http_client_config_t config = {
.url = "http://http-server.local: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 = %lld",
esp_http_client_get_status_code(client),
(int64_t)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: connect to http://%s:%d", CONFIG_WEB_SERVER, CONFIG_WEB_PORT);
FRAME_t frameBuf;
while (1) {
xQueueReceive(xQueue_http_client, &frameBuf, portMAX_DELAY);
ESP_LOGI(TAG, "canid=%"PRIx32" 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);
cJSON_free(json_string);
} // end while
// Never reach here
vTaskDelete(NULL);
}
+369 -369
View File
@@ -1,369 +1,369 @@
/* 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 <stdlib.h>
#include <inttypes.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 "esp_chip_info.h"
#include "cJSON.h"
#include "driver/twai.h"
#include "twai.h"
#define SCRATCH_BUFSIZE (1024)
#define TAG "HTTP_SERVER"
int pointer = 0;
static char *base_path = "/spiffs";
extern QueueHandle_t xQueue_twai_tx;
extern char* twai_string_buf;
extern int twai_allocation_size;
extern char* file_read_buf;
extern int allocation_size;
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;
esp_err_t readFile(char *ifile) {
int c;
ESP_LOGI(TAG, "File read llocated memory confirmed!: %d bytes", allocation_size);
char *spiffspath = "/spiffs";
int buflen = strlen(spiffspath)+strlen(ifile)+1;
char buf[buflen];
snprintf(buf, sizeof(buf), "%s%s", spiffspath, ifile);
ESP_LOGI(TAG, "buflen: %d, spiffspath: %s, ifile: %s, buf: %s, base_path: %s",buflen,spiffspath,ifile,buf,base_path);
FILE* f = fopen(buf, "r");
ESP_LOGI(TAG, "Begin reading file");
int i = 0;
while ((c = fgetc(f)) != EOF) {
file_read_buf[i] = c;
i++;
};
file_read_buf[i] = '\0';
fclose(f);
ESP_LOGI(TAG, "Read File - Iterations: %d", i);
return ESP_OK;
}
/* Handler for roor get handler */
esp_err_t root_get_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "root_get_handler req->uri=[%s]", req->uri);
readFile("/index.html");
esp_err_t sendcheck = httpd_resp_send(req, file_read_buf, HTTPD_RESP_USE_STRLEN);
//httpd_resp_sendstr_chunk(req, NULL);
if(sendcheck == ESP_OK) {
//free(file_read_buf);
memset(twai_string_buf, '\0', allocation_size);
ESP_LOGI(TAG, "malloc freed");
return ESP_OK;
} else {
return ESP_ERR_HTTPD_RESP_SEND;
}
return ESP_OK;
}
static esp_err_t twai_read_handler(httpd_req_t *req)
{
httpd_resp_sendstr(req, twai_string_buf);
// Buffers returned by cJSON_Print must be freed by the caller.
// Please use the proper API (cJSON_free) rather than directly
//twai_string_buf = {0};
memset(twai_string_buf, '\0', twai_allocation_size);
return ESP_OK;
}
/* Handler for getting system information handler */
// curl 'http://esp32-can-server: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=%"PRIx32, 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;
if (data_array_size > 8) {
ESP_LOGW(TAG, "Too many data arrays : %d", data_array_size);
data_len = 8;
}
for (int i=0;i<data_len;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, "Too large data value : %x", data_int);
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=%"PRIx32" data_len=%d", frame, canid, data_len);
ESP_LOG_BUFFER_HEX(TAG, data_value, data_len);
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, "CAN tx sent successfully");
} 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);
/* URI handler for send twai */
httpd_uri_t twai_read = {
.uri = "/api/twai/read",
.method = HTTP_GET,
.handler = twai_read_handler,
//.user_ctx = rest_context
};
httpd_register_uri_handler(server, &twai_read);
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);
}
/* 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 <stdlib.h>
#include <inttypes.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 "esp_chip_info.h"
#include "cJSON.h"
#include "driver/twai.h"
#include "twai.h"
#define SCRATCH_BUFSIZE (1024)
#define TAG "HTTP_SERVER"
int pointer = 0;
static char *base_path = "/spiffs";
extern QueueHandle_t xQueue_twai_tx;
extern char* twai_string_buf;
extern int twai_allocation_size;
extern char* file_read_buf;
extern int allocation_size;
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;
esp_err_t readFile(char *ifile) {
int c;
ESP_LOGI(TAG, "File read llocated memory confirmed!: %d bytes", allocation_size);
char *spiffspath = "/spiffs";
int buflen = strlen(spiffspath)+strlen(ifile)+1;
char buf[buflen];
snprintf(buf, sizeof(buf), "%s%s", spiffspath, ifile);
ESP_LOGI(TAG, "buflen: %d, spiffspath: %s, ifile: %s, buf: %s, base_path: %s",buflen,spiffspath,ifile,buf,base_path);
FILE* f = fopen(buf, "r");
ESP_LOGI(TAG, "Begin reading file");
int i = 0;
while ((c = fgetc(f)) != EOF) {
file_read_buf[i] = c;
i++;
};
file_read_buf[i] = '\0';
fclose(f);
ESP_LOGI(TAG, "Read File - Iterations: %d", i);
return ESP_OK;
}
/* Handler for roor get handler */
esp_err_t root_get_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "root_get_handler req->uri=[%s]", req->uri);
readFile("/index.html");
esp_err_t sendcheck = httpd_resp_send(req, file_read_buf, HTTPD_RESP_USE_STRLEN);
//httpd_resp_sendstr_chunk(req, NULL);
if(sendcheck == ESP_OK) {
//free(file_read_buf);
memset(twai_string_buf, '\0', allocation_size);
ESP_LOGI(TAG, "malloc freed");
return ESP_OK;
} else {
return ESP_ERR_HTTPD_RESP_SEND;
}
return ESP_OK;
}
static esp_err_t twai_read_handler(httpd_req_t *req)
{
httpd_resp_sendstr(req, twai_string_buf);
// Buffers returned by cJSON_Print must be freed by the caller.
// Please use the proper API (cJSON_free) rather than directly
//twai_string_buf = {0};
memset(twai_string_buf, '\0', twai_allocation_size);
return ESP_OK;
}
/* Handler for getting system information handler */
// curl 'http://esp32-can-server: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=%"PRIx32, 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;
if (data_array_size > 8) {
ESP_LOGW(TAG, "Too many data arrays : %d", data_array_size);
data_len = 8;
}
for (int i=0;i<data_len;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, "Too large data value : %x", data_int);
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=%"PRIx32" data_len=%d", frame, canid, data_len);
ESP_LOG_BUFFER_HEX(TAG, data_value, data_len);
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, "CAN tx sent successfully");
} 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);
/* URI handler for send twai */
httpd_uri_t twai_read = {
.uri = "/api/twai/read",
.method = HTTP_GET,
.handler = twai_read_handler,
//.user_ctx = rest_context
};
httpd_register_uri_handler(server, &twai_read);
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);
}
+6 -6
View File
@@ -1,6 +1,6 @@
## IDF Component Manager Manifest File
dependencies:
espressif/mdns:
version: "^1.0.3"
rules:
- if: "idf_version >=5.0"
## IDF Component Manager Manifest File
dependencies:
espressif/mdns:
version: "^1.0.3"
rules:
- if: "idf_version >=5.0"
+7 -7
View File
@@ -1,8 +1,8 @@
#define MAX_FILE_SIZE_KB 10
#define MAX_TWAI_SIZE_KB 5
#define MALLOCHI_TAG "INIT_MALLOCHI"
char* file_read_buf;
char* twai_string_buf;
int allocation_size;
#define MAX_FILE_SIZE_KB 10
#define MAX_TWAI_SIZE_KB 5
#define MALLOCHI_TAG "INIT_MALLOCHI"
char* file_read_buf;
char* twai_string_buf;
int allocation_size;
int twai_allocation_size;
+456 -456
View File
@@ -1,456 +1,456 @@
/*
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 <inttypes.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 "mdns.h"
#include "lwip/dns.h"
#include "driver/twai.h" // Update from V4.2
#include "twai.h"
#include "init_malloci.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;
static char *base_path = "/spiffs";
QueueHandle_t xQueue_http_client;
QueueHandle_t xQueue_twai_tx;
TOPIC_t *publish;
int16_t npublish;
//extern char* twai_string_buf;
//extern esp_err_t init_twai_read_malloc();
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);
}
}
void wifi_init_sta()
{
s_wifi_event_group = xEventGroupCreate();
ESP_LOGI(TAG,"ESP-IDF Ver%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR);
ESP_LOGI(TAG,"ESP_IDF_VERSION %d", ESP_IDF_VERSION);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *netif = esp_netif_create_default_wifi_sta();
assert(netif);
#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);
/* 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);
/*
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 // CONFIG_STATIC_IP
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
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
},
};
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);
} 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");
}
vEventGroupDelete(s_wifi_event_group);
}
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) {
ESP_LOGI(TAG, "Initializing SPIFFS file system");
esp_vfs_spiffs_conf_t conf = {
.base_path = base_path,
.partition_label = partition_label,
.max_files = 4,
.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(pcTaskGetName(0), "topics=[%d] frame=%d canid=0x%"PRIx32" topic=[%s] topic_len=%d",
i, (topics+i)->frame, (topics+i)->canid, (topics+i)->topic, (topics+i)->topic_len);
}
}
esp_err_t init_file_malloc() {
allocation_size = (MAX_FILE_SIZE_KB*1024*sizeof(char));
file_read_buf = (char*)malloc(allocation_size);
if (file_read_buf == NULL) {
ESP_LOGE(MALLOCHI_TAG, "File read memory not allocated.");
return ESP_FAIL;
} else {
ESP_LOGI(MALLOCHI_TAG, "File read malloc succeeded! %d bytes allocated", allocation_size);
return ESP_OK;
}
return ESP_OK;
}
esp_err_t init_twai_read_malloc() {
twai_allocation_size = (sizeof(char)*1024*MAX_TWAI_SIZE_KB);
twai_string_buf = (char*)malloc(twai_allocation_size);
if (twai_string_buf == NULL) {
ESP_LOGE(MALLOCHI_TAG, "TWAI memory not allocated.");
return ESP_FAIL;
} else {
ESP_LOGI(MALLOCHI_TAG, "TWAI malloc succeeded! %d bytes allocated", twai_allocation_size);
return ESP_OK;
}
return ESP_OK;
}
void http_client_task(void *pvParameters);
void http_server_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);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
initialise_mdns();
init_twai_read_malloc();
init_file_malloc();
// 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_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);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "build publish table fail");
while(1) { vTaskDelay(1); }
}
dump_table(publish, npublish);
/* 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));
esp_netif_ip_info_t ip_info;
ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info));
char cparam0[64];
//sprintf(cparam0, "%s", ip4addr_ntoa(&ip_info.ip));
sprintf(cparam0, IPSTR, IP2STR(&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);
}
/*
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 <inttypes.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 "mdns.h"
#include "lwip/dns.h"
#include "driver/twai.h" // Update from V4.2
#include "twai.h"
#include "init_malloci.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;
static char *base_path = "/spiffs";
QueueHandle_t xQueue_http_client;
QueueHandle_t xQueue_twai_tx;
TOPIC_t *publish;
int16_t npublish;
//extern char* twai_string_buf;
//extern esp_err_t init_twai_read_malloc();
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);
}
}
void wifi_init_sta()
{
s_wifi_event_group = xEventGroupCreate();
ESP_LOGI(TAG,"ESP-IDF Ver%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR);
ESP_LOGI(TAG,"ESP_IDF_VERSION %d", ESP_IDF_VERSION);
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_t *netif = esp_netif_create_default_wifi_sta();
assert(netif);
#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);
/* 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);
/*
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 // CONFIG_STATIC_IP
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
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
},
};
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);
} 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");
}
vEventGroupDelete(s_wifi_event_group);
}
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) {
ESP_LOGI(TAG, "Initializing SPIFFS file system");
esp_vfs_spiffs_conf_t conf = {
.base_path = base_path,
.partition_label = partition_label,
.max_files = 4,
.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(pcTaskGetName(0), "topics=[%d] frame=%d canid=0x%"PRIx32" topic=[%s] topic_len=%d",
i, (topics+i)->frame, (topics+i)->canid, (topics+i)->topic, (topics+i)->topic_len);
}
}
esp_err_t init_file_malloc() {
allocation_size = (MAX_FILE_SIZE_KB*1024*sizeof(char));
file_read_buf = (char*)malloc(allocation_size);
if (file_read_buf == NULL) {
ESP_LOGE(MALLOCHI_TAG, "File read memory not allocated.");
return ESP_FAIL;
} else {
ESP_LOGI(MALLOCHI_TAG, "File read malloc succeeded! %d bytes allocated", allocation_size);
return ESP_OK;
}
return ESP_OK;
}
esp_err_t init_twai_read_malloc() {
twai_allocation_size = (sizeof(char)*1024*MAX_TWAI_SIZE_KB);
twai_string_buf = (char*)malloc(twai_allocation_size);
if (twai_string_buf == NULL) {
ESP_LOGE(MALLOCHI_TAG, "TWAI memory not allocated.");
return ESP_FAIL;
} else {
ESP_LOGI(MALLOCHI_TAG, "TWAI malloc succeeded! %d bytes allocated", twai_allocation_size);
return ESP_OK;
}
return ESP_OK;
}
void http_client_task(void *pvParameters);
void http_server_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);
ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
wifi_init_sta();
initialise_mdns();
init_twai_read_malloc();
init_file_malloc();
// 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_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);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "build publish table fail");
while(1) { vTaskDelay(1); }
}
dump_table(publish, npublish);
/* 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));
esp_netif_ip_info_t ip_info;
ESP_ERROR_CHECK(esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info));
char cparam0[64];
//sprintf(cparam0, "%s", ip4addr_ntoa(&ip_info.ip));
sprintf(cparam0, IPSTR, IP2STR(&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);
}
+167 -167
View File
@@ -1,167 +1,167 @@
/* 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 <inttypes.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"
#include "cJSON.h"
//#include "init_malloci.h"
static const char *TAG = "TWAI";
extern QueueHandle_t xQueue_http_client;
extern QueueHandle_t xQueue_twai_tx;
extern TOPIC_t *publish;
extern int16_t npublish;
int str_iterator = 0;
extern char* twai_string_buf;
//extern esp_err_t init_twai_read_malloc();
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;
twai_message_t tx_msg;
FRAME_t frameBuf;
char* ftype;
//char* fdata = (char*)malloc(32*sizeof(char));
//int iterator = 0;
char flags[4];
char identifier[4];
while (1) {
esp_err_t ret = twai_receive(&rx_msg, pdMS_TO_TICKS(10));
if (ret == ESP_OK) {
ESP_LOGD(TAG,"twai_receive identifier=0x%"PRIx32" flags=0x%"PRIx32" 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 == STANDARD_FRAME) {
printf("Standard ID: 0x%03"PRIx32, rx_msg.identifier);
} else {
printf("Extended ID: 0x%08"PRIx32, rx_msg.identifier);
}
printf(" DLC: %d Data: ", rx_msg.data_length_code);
printf("\n");
#endif
// JSON fun fun fun output for da api
if (ext == STANDARD_FRAME) {
ftype = "STANDARD_FRAME";
} else {
ftype = "EXTENDED_FRAME";
}
cJSON *twai_root = cJSON_CreateObject();
int charslen_each = 6;
int data_len_chars = rx_msg.data_length_code*charslen_each;
char tmp_buf[charslen_each+2];
char fdata[data_len_chars];
if (rx_msg.data_length_code != 0) {
for (int i = 0; i < rx_msg.data_length_code; i++) {
snprintf(tmp_buf, charslen_each, "0x%02x ",rx_msg.data[i]);
strcat(fdata, tmp_buf);
}
fdata[strlen(fdata)-1] = '\0';
cJSON_AddStringToObject(twai_root, "data", fdata);
}
snprintf(flags, sizeof(rx_msg.flags), "0x%"PRIx32, rx_msg.flags);
snprintf(identifier, sizeof(rx_msg.identifier), "0x%"PRIx32, rx_msg.identifier);
cJSON_AddStringToObject(twai_root, "type", ftype);
cJSON_AddStringToObject(twai_root, "id", identifier);
cJSON_AddStringToObject(twai_root, "flags", flags);
cJSON_AddNumberToObject(twai_root, "ext", ext);
cJSON_AddNumberToObject(twai_root, "rtr", rtr);
cJSON_AddNumberToObject(twai_root, "dlc",rx_msg.data_length_code);
char *string = cJSON_Print(twai_root);
char br[] = "<br>\n";
strcat(twai_string_buf,string);
strcat(twai_string_buf,br);
cJSON_free(string);
cJSON_Delete(twai_root);
// end json buffer funssss
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%"PRIx32" 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;
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_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%"PRIx32"] 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=%"PRIu32,status_info.msgs_to_tx);
ESP_LOGD(TAG, "status_info.msgs_to_rx=%"PRIu32,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 here
vTaskDelete(NULL);
}
/* 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 <inttypes.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"
#include "cJSON.h"
//#include "init_malloci.h"
static const char *TAG = "TWAI";
extern QueueHandle_t xQueue_http_client;
extern QueueHandle_t xQueue_twai_tx;
extern TOPIC_t *publish;
extern int16_t npublish;
int str_iterator = 0;
extern char* twai_string_buf;
//extern esp_err_t init_twai_read_malloc();
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;
twai_message_t tx_msg;
FRAME_t frameBuf;
char* ftype;
//char* fdata = (char*)malloc(32*sizeof(char));
//int iterator = 0;
char flags[4];
char identifier[4];
while (1) {
esp_err_t ret = twai_receive(&rx_msg, pdMS_TO_TICKS(10));
if (ret == ESP_OK) {
ESP_LOGD(TAG,"twai_receive identifier=0x%"PRIx32" flags=0x%"PRIx32" 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 == STANDARD_FRAME) {
printf("Standard ID: 0x%03"PRIx32, rx_msg.identifier);
} else {
printf("Extended ID: 0x%08"PRIx32, rx_msg.identifier);
}
printf(" DLC: %d Data: ", rx_msg.data_length_code);
printf("\n");
#endif
// JSON fun fun fun output for da api
if (ext == STANDARD_FRAME) {
ftype = "STANDARD_FRAME";
} else {
ftype = "EXTENDED_FRAME";
}
cJSON *twai_root = cJSON_CreateObject();
int charslen_each = 6;
int data_len_chars = rx_msg.data_length_code*charslen_each;
char tmp_buf[charslen_each+2];
char fdata[data_len_chars];
if (rx_msg.data_length_code != 0) {
for (int i = 0; i < rx_msg.data_length_code; i++) {
snprintf(tmp_buf, charslen_each, "0x%02x ",rx_msg.data[i]);
strcat(fdata, tmp_buf);
}
fdata[strlen(fdata)-1] = '\0';
cJSON_AddStringToObject(twai_root, "data", fdata);
}
snprintf(flags, sizeof(rx_msg.flags), "0x%"PRIx32, rx_msg.flags);
snprintf(identifier, sizeof(rx_msg.identifier), "0x%"PRIx32, rx_msg.identifier);
cJSON_AddStringToObject(twai_root, "type", ftype);
cJSON_AddStringToObject(twai_root, "id", identifier);
cJSON_AddStringToObject(twai_root, "flags", flags);
cJSON_AddNumberToObject(twai_root, "ext", ext);
cJSON_AddNumberToObject(twai_root, "rtr", rtr);
cJSON_AddNumberToObject(twai_root, "dlc",rx_msg.data_length_code);
char *string = cJSON_Print(twai_root);
char br[] = "<br>\n";
strcat(twai_string_buf,string);
strcat(twai_string_buf,br);
cJSON_free(string);
cJSON_Delete(twai_root);
// end json buffer funssss
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%"PRIx32" 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;
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_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%"PRIx32"] 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=%"PRIu32,status_info.msgs_to_tx);
ESP_LOGD(TAG, "status_info.msgs_to_rx=%"PRIu32,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 here
vTaskDelete(NULL);
}
+27 -27
View File
@@ -1,27 +1,27 @@
#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;
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;
#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;
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;
+6 -6
View File
@@ -1,6 +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, 2M,
storage, data, spiffs, , 4M,
# 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, 2M,
storage, data, spiffs, , 4M,
1 # Name, Type, SubType, Offset, Size, Flags
2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
3 nvs, data, nvs, 0x9000, 0x6000,
4 phy_init, data, phy, 0xf000, 0x1000,
5 factory, app, factory, 0x10000, 2M,
6 storage, data, spiffs, , 4M,
+1 -1
View File
@@ -1 +1 @@
$Env:ESPPORT = "COM22"
$Env:ESPPORT = "COM22"
+3 -3
View File
@@ -1,3 +1,3 @@
idf.py set-target esp32s3
idf.py menuconfig
idf.py flash monitor
idf.py set-target esp32s3
idf.py menuconfig
idf.py flash monitor
+20 -20
View File
@@ -1,20 +1,20 @@
function Test-Delete {
param (
$file
)
if (Test-Path $file) {
rm -r -fo $file
Write-Output "$file DELETED"
} else {
Write-Output "$file NOT present"
}
}
idf.py erase-flash fullclean python-clean
Test-Delete -file .\managed_components
Test-Delete -file .\build
Test-Delete -file .\sdkconfig.old
Test-Delete -file .\dependencies.lock
Test-Delete -file .\sdkconfig.old
function Test-Delete {
param (
$file
)
if (Test-Path $file) {
rm -r -fo $file
Write-Output "$file DELETED"
} else {
Write-Output "$file NOT present"
}
}
idf.py erase-flash fullclean python-clean
Test-Delete -file .\managed_components
Test-Delete -file .\build
Test-Delete -file .\sdkconfig.old
Test-Delete -file .\dependencies.lock
Test-Delete -file .\sdkconfig.old
+23 -23
View File
@@ -1,23 +1,23 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32s3"
CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC=y
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ENABLE_PRINT=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_PURGE_BUF_LEN=100
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32s3"
CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC=y
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ENABLE_PRINT=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_PURGE_BUF_LEN=100
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
+23 -23
View File
@@ -1,23 +1,23 @@
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32s3"
CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC=y
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ENABLE_PRINT=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_PURGE_BUF_LEN=100
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
# This file was generated using idf.py save-defconfig. It can be edited manually.
# Espressif IoT Development Framework (ESP-IDF) 5.3.0 Project Minimal Configuration
#
CONFIG_IDF_TARGET="esp32s3"
CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC=y
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_ENABLE_PRINT=y
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
CONFIG_COMPILER_DUMP_RTL_FILES=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_PURGE_BUF_LEN=100
CONFIG_SPIRAM=y
CONFIG_SPIRAM_MODE_OCT=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_IGNORE_NOTFOUND=y
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT=y
+88 -88
View File
@@ -1,88 +1,88 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Simple REST Server
import os
import sys
import json
import datetime
import werkzeug
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)
define("lines", default=20, help="number of lines displayed", type=int)
database = []
class IndexHandler(tornado.web.RequestHandler):
def get(self):
global database
items = []
for data in database:
#print("{}".format(data))
datetime = data[0].split()
#print("data={} datetime={}".format(data[0], datetime))
items.append({
"date": datetime[0],
"time": datetime[1],
"id": data[1],
"frame": data[2],
"value": data[3]
})
#print("items={}".format(items))
self.render("index.html", lines=options.lines, items=items)
#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))
print("lines={}".format(options.lines))
#if(len(database) >= 20):
if(len(database) >= options.lines):
database.pop(0)
database.append(items)
self.write(json.dumps({"result":"ok"}))
if __name__ == "__main__":
tornado.options.parse_command_line()
print("port={}".format(options.port))
print("lines={}".format(options.lines))
app = tornado.web.Application(
handlers=[
(r"/", IndexHandler),
(r"/post", PostHandler),
],
template_path=os.path.join(os.getcwd(), "templates"),
debug=True
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Simple REST Server
import os
import sys
import json
import datetime
import werkzeug
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)
define("lines", default=20, help="number of lines displayed", type=int)
database = []
class IndexHandler(tornado.web.RequestHandler):
def get(self):
global database
items = []
for data in database:
#print("{}".format(data))
datetime = data[0].split()
#print("data={} datetime={}".format(data[0], datetime))
items.append({
"date": datetime[0],
"time": datetime[1],
"id": data[1],
"frame": data[2],
"value": data[3]
})
#print("items={}".format(items))
self.render("index.html", lines=options.lines, items=items)
#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))
print("lines={}".format(options.lines))
#if(len(database) >= 20):
if(len(database) >= options.lines):
database.pop(0)
database.append(items)
self.write(json.dumps({"result":"ok"}))
if __name__ == "__main__":
tornado.options.parse_command_line()
print("port={}".format(options.port))
print("lines={}".format(options.lines))
app = tornado.web.Application(
handlers=[
(r"/", IndexHandler),
(r"/post", PostHandler),
],
template_path=os.path.join(os.getcwd(), "templates"),
debug=True
)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(options.port)
tornado.ioloop.IOLoop.current().start()
+35 -35
View File
@@ -1,35 +1,35 @@
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="5;url="/">
<title>CAN DATA</title>
</head>
<body>
Display the latest {{ lines }} records.<br>
If you want to see more, specify the number of lines at startup.<br>
<table border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr style="color:#ffffff;" bgcolor="#800000">
<th valign="top">Date</th>
<th valign="top">Time</th>
<th valign="top">ID</th>
<th valign="top">Frame</th>
<th valign="top">Value</th>
</tr>
{% for i in items %}
<tr>
<td>{{ i['date'] }}</td>
<td>{{ i['time'] }}</td>
<td>{{ i['id'] }}</td>
<td>{{ i['frame'] }}</td>
<td>{{ i['value'] }}</td>
</tr>
{% end %}
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="refresh" content="5;url="/">
<title>CAN DATA</title>
</head>
<body>
Display the latest {{ lines }} records.<br>
If you want to see more, specify the number of lines at startup.<br>
<table border="1" cellpadding="2" cellspacing="2">
<tbody>
<tr style="color:#ffffff;" bgcolor="#800000">
<th valign="top">Date</th>
<th valign="top">Time</th>
<th valign="top">ID</th>
<th valign="top">Frame</th>
<th valign="top">Value</th>
</tr>
{% for i in items %}
<tr>
<td>{{ i['date'] }}</td>
<td>{{ i['time'] }}</td>
<td>{{ i['id'] }}</td>
<td>{{ i['frame'] }}</td>
<td>{{ i['value'] }}</td>
</tr>
{% end %}
</tbody>
</table>
</body>
</html>