mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
example: add nmea0183 parser example
Add NMEA0183 Parser example to illustrate how to use uart event driver together with esp event library to get GPS information.
This commit is contained in:
6
examples/peripherals/uart/nmea0183_parser/CMakeLists.txt
Normal file
6
examples/peripherals/uart/nmea0183_parser/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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(nmea_parser)
|
9
examples/peripherals/uart/nmea0183_parser/Makefile
Normal file
9
examples/peripherals/uart/nmea0183_parser/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := nmea_parser
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
110
examples/peripherals/uart/nmea0183_parser/README.md
Normal file
110
examples/peripherals/uart/nmea0183_parser/README.md
Normal file
@@ -0,0 +1,110 @@
|
||||
# NMEA Parser Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
## Overview
|
||||
|
||||
This example will show how to parse NMEA-0183 data streams output from GPS/BDS/GLONASS modules based on ESP UART Event driver and ESP event loop library.
|
||||
For the convenience of the presentation, this example will only parse the following basic statements:
|
||||
* GGA
|
||||
* GSA
|
||||
* GSV
|
||||
* RMC
|
||||
* GLL
|
||||
* VTG
|
||||
|
||||
See [Limitation for multiple navigation system](#Limitation) for more information about this example.
|
||||
|
||||
Usually, modules will also output some vendor specific statements which common nmea library can not cover. In this example, the NMEA Parser will propagate all unknown statements to the user, where a custom handler can parse information from it.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
To run this example, you need an ESP32 dev board (e.g. ESP32-WROVER Kit) or ESP32 core board (e.g. ESP32-DevKitC). For test purpose, you also need a GPS module. Here we take the [ATGM332D-5N](http://www.icofchina.com/pro/mokuai/2016-08-01/5.html) as an example to show how to parse the NMEA statements and output common information such as UTC time, latitude, longitude, altitude, speed and so on.
|
||||
|
||||
#### Pin Assignment:
|
||||
|
||||
**Note:** The following pin assignments are used by default which can be changed in `nmea_parser_config_t` structure.
|
||||
|
||||
| ESP32 | GPS |
|
||||
| ---------------- | --------------- |
|
||||
| UART-TX (option) | GPS-RX (option) |
|
||||
| UART-RX | GPS-TX |
|
||||
| GND | GND |
|
||||
| 5V | VCC |
|
||||
|
||||
**Note:** UART TX pin in ESP32 is not necessary if you only use uart to receive data.
|
||||
|
||||
|
||||
### Configure the project
|
||||
|
||||
Enter `make menuconfig` if you are using GNU Make based build system or enter `idf.py menuconfig` if you are using CMake based build system. Then go into `Example Configuration` menu.
|
||||
|
||||
- Set the size of ring buffer used by uart driver in `NMEA Parser Ring Buffer Size` option.
|
||||
- Set the stack size of the NMEA Parser task in `NMEA Parser Task Stack Size` option.
|
||||
- Set the priority of the NMEA Parser task in `NMEA Parser Task Priority` option.
|
||||
- In the `NMEA Statement support` submenu, you can choose the type of statements that you want to parse. **Note:** you should choose at least one statement to parse.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Enter `make -j4 flash monitor` if you are using GNU Make based build system or enter `idf.py build flash monitor` if you are using CMake based build system.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```bash
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (317) uart: queue free spaces: 16
|
||||
I (317) nmea_parser: NMEA Parser init OK
|
||||
I (1067) gps_demo: 2018/12/4 13:59:34 =>
|
||||
latitude = 31.20177°N
|
||||
longtitude = 121.57933°E
|
||||
altitude = 17.30m
|
||||
speed = 0.370400m/s
|
||||
W (1177) gps_demo: Unknown statement:$GPTXT,01,01,01,ANTENNA OK*35
|
||||
I (2067) gps_demo: 2018/12/4 13:59:35 =>
|
||||
latitude = 31.20177°N
|
||||
longtitude = 121.57933°E
|
||||
altitude = 17.30m
|
||||
speed = 0.000000m/s
|
||||
W (2177) gps_demo: Unknown statement:$GPTXT,01,01,01,ANTENNA OK*35
|
||||
I (3067) gps_demo: 2018/12/4 13:59:36 =>
|
||||
latitude = 31.20178°N
|
||||
longtitude = 121.57933°E
|
||||
altitude = 17.30m
|
||||
speed = 0.000000m/s
|
||||
W (3177) gps_demo: Unknown statement:$GPTXT,01,01,01,ANTENNA OK*35
|
||||
I (4067) gps_demo: 2018/12/4 13:59:37 =>
|
||||
latitude = 31.20178°N
|
||||
longtitude = 121.57933°E
|
||||
altitude = 17.30m
|
||||
speed = 0.000000m/s
|
||||
W (4177) gps_demo: Unknown statement:$GPTXT,01,01,01,ANTENNA OK*35
|
||||
I (5067) gps_demo: 2018/12/4 13:59:38 =>
|
||||
latitude = 31.20178°N
|
||||
longtitude = 121.57933°E
|
||||
altitude = 17.30m
|
||||
speed = 0.685240m/s
|
||||
W (5177) gps_demo: Unknown statement:$GPTXT,01,01,01,ANTENNA OK*35
|
||||
```
|
||||
As shown above, ESP32 finally got the information after parsed the NMEA0183 format statements. But as we didn't add `GPTXT` type statement in the library (that means it is UNKNOWN to NMEA Parser library), so it was propagated to user without any process.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. I cannot receive any statements from GPS although I have checked all the pin connections.
|
||||
* Test your GPS via other terminal (e.g. minicom, putty) to check the right communication parameters (e.g. baudrate supported by GPS).
|
||||
|
||||
## Limitation
|
||||
If the GPS module supports multiple satellite navigation system (e.g. GPS, BDS), then the satellite ids and descriptions may be delivered in different statements (e.g. GPGSV, BDGSV, GPGSA, BDGSA), depend on the version of NMEA protocol used by the GPS module. This example currently can only record id and description of satellites from one navigation system.
|
||||
However, for other statements, this example can parse them correctly whatever the navigation system is.
|
||||
|
||||
### Steps to skip the limitation
|
||||
1. Uncheck the `GSA` and `GSV` statements in menuconfig
|
||||
2. In the `gps_event_handler` will get a signal called `GPS_UNKNOWN`, and the unknown statement itself (It's a deep copy of the original statement).
|
||||
3. Manually parse the unknown statements and get the satellites' descriptions.
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
@@ -0,0 +1,5 @@
|
||||
set(COMPONENT_SRCS "nmea_parser_example_main.c"
|
||||
"nmea_parser.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
@@ -0,0 +1,85 @@
|
||||
menu "Example Configuration"
|
||||
|
||||
config NMEA_PARSER_RING_BUFFER_SIZE
|
||||
int "NMEA Parser Ring Buffer Size"
|
||||
range 0 2048
|
||||
default 1024
|
||||
help
|
||||
Size of the ring buffer used for UART Rx channel.
|
||||
|
||||
config NMEA_PARSER_TASK_STACK_SIZE
|
||||
int "NMEA Parser Task Stack Size"
|
||||
range 0 4096
|
||||
default 2048
|
||||
help
|
||||
Stack size of NMEA Parser task.
|
||||
|
||||
config NMEA_PARSER_TASK_PRIORITY
|
||||
int "NMEA Parser Task Priority"
|
||||
range 0 24
|
||||
default 2
|
||||
help
|
||||
Priority of NMEA Parser task.
|
||||
|
||||
menu "NMEA Statement Support"
|
||||
comment "At least one statement must be selected"
|
||||
config NMEA_STATEMENT_GGA
|
||||
bool "GGA Statement"
|
||||
default y
|
||||
help
|
||||
Enabling this option will parse the following parameter from GGA statement:
|
||||
|
||||
- Latitude, Longitude, Altitude;
|
||||
- Number of satellites in use, fix status (no fix, GPS, DGPS), UTC time;
|
||||
|
||||
config NMEA_STATEMENT_GSA
|
||||
bool "GSA Statement"
|
||||
default y
|
||||
help
|
||||
Enabling this option will parse the following parameter from GSA statement:
|
||||
|
||||
- Position/Vertical/Horizontal dilution of precision;
|
||||
- Fix mode (no fix, 2D, 3D fix);
|
||||
- IDs of satellites in use;
|
||||
|
||||
config NMEA_STATEMENT_GSV
|
||||
bool "GSV Statement"
|
||||
default y
|
||||
help
|
||||
Enabling this option will parse the following parameter from GSV statement:
|
||||
|
||||
- Number of satellites in view;
|
||||
- Optional details of each satellite in view;
|
||||
|
||||
config NMEA_STATEMENT_RMC
|
||||
bool "RMC Statement"
|
||||
default y
|
||||
help
|
||||
Enabling this option will parse the following parameter from RMC statement:
|
||||
|
||||
- Validity of GPS signal;
|
||||
- Ground speed (knots) and course over ground (degrees);
|
||||
- Magnetic variation;
|
||||
- UTC date;
|
||||
|
||||
config NMEA_STATEMENT_GLL
|
||||
bool "GLL Statement"
|
||||
default y
|
||||
help
|
||||
Enabling this option will parse the following parameter from GLL statement:
|
||||
|
||||
- Latitude, Longitude;
|
||||
- UTC time;
|
||||
|
||||
config NMEA_STATEMENT_VTG
|
||||
bool "VTG Statement"
|
||||
default y
|
||||
help
|
||||
Enabling this option will parse the following parameter from VTG statement:
|
||||
|
||||
- Ground speed (knots, km/h) and course over ground (degrees);
|
||||
- Magnetic variation;
|
||||
|
||||
endmenu
|
||||
|
||||
endmenu
|
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
792
examples/peripherals/uart/nmea0183_parser/main/nmea_parser.c
Normal file
792
examples/peripherals/uart/nmea0183_parser/main/nmea_parser.c
Normal file
@@ -0,0 +1,792 @@
|
||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "nmea_parser.h"
|
||||
|
||||
/**
|
||||
* @brief NMEA Parser runtime buffer size
|
||||
*
|
||||
*/
|
||||
#define NMEA_PARSER_RUNTIME_BUFFER_SIZE (CONFIG_NMEA_PARSER_RING_BUFFER_SIZE / 2)
|
||||
#define NMEA_MAX_STATEMENT_ITEM_LENGTH (16)
|
||||
#define NMEA_EVENT_LOOP_QUEUE_SIZE (16)
|
||||
|
||||
/**
|
||||
* @brief Define of NMEA Parser Event base
|
||||
*
|
||||
*/
|
||||
ESP_EVENT_DEFINE_BASE(ESP_NMEA_EVENT)
|
||||
|
||||
static const char *GPS_TAG = "nmea_parser";
|
||||
|
||||
/**
|
||||
* @brief GPS parser library runtime structure
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t item_pos; /*!< Current position in item */
|
||||
uint8_t item_num; /*!< Current item number */
|
||||
uint8_t asterisk; /*!< Asterisk detected flag */
|
||||
uint8_t crc; /*!< Calculated CRC value */
|
||||
uint8_t parsed_statement; /*!< OR'd of statements that have been parsed */
|
||||
uint8_t sat_num; /*!< Satellite number */
|
||||
uint8_t sat_count; /*!< Satellite count */
|
||||
uint8_t cur_statement; /*!< Current statement ID */
|
||||
uint32_t all_statements; /*!< All statements mask */
|
||||
char item_str[NMEA_MAX_STATEMENT_ITEM_LENGTH]; /*!< Current item */
|
||||
gps_t parent; /*!< Parent class */
|
||||
uart_port_t uart_port; /*!< Uart port number */
|
||||
uint8_t *buffer; /*!< Runtime buffer */
|
||||
esp_event_loop_handle_t event_loop_hdl; /*!< Event loop handle */
|
||||
TaskHandle_t tsk_hdl; /*!< NMEA Parser task handle */
|
||||
QueueHandle_t event_queue; /*!< UART event queue handle */
|
||||
} esp_gps_t;
|
||||
|
||||
/**
|
||||
* @brief parse latitude or longitude
|
||||
* format of latitude in NMEA is ddmm.sss and longitude is dddmm.sss
|
||||
* @param esp_gps esp_gps_t type object
|
||||
* @return float Latitude or Longitude value (unit: degree)
|
||||
*/
|
||||
static float parse_lat_long(esp_gps_t *esp_gps)
|
||||
{
|
||||
float ll = strtof(esp_gps->item_str, NULL);
|
||||
int deg = ((int)ll) / 100;
|
||||
float min = ll - (deg * 100);
|
||||
ll = deg + min / 60.0f;
|
||||
return ll;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Converter two continuous numeric character into a uint8_t number
|
||||
*
|
||||
* @param digit_char numeric character
|
||||
* @return uint8_t result of converting
|
||||
*/
|
||||
static inline uint8_t convert_two_digit2number(const char *digit_char)
|
||||
{
|
||||
return 10 * (digit_char[0] - '0') + (digit_char[1] - '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse UTC time in GPS statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_utc_time(esp_gps_t *esp_gps)
|
||||
{
|
||||
esp_gps->parent.tim.hour = convert_two_digit2number(esp_gps->item_str + 0);
|
||||
esp_gps->parent.tim.minute = convert_two_digit2number(esp_gps->item_str + 2);
|
||||
esp_gps->parent.tim.second = convert_two_digit2number(esp_gps->item_str + 4);
|
||||
if (esp_gps->item_str[6] == '.') {
|
||||
uint16_t tmp = 0;
|
||||
uint8_t i = 7;
|
||||
while (esp_gps->item_str[i]) {
|
||||
tmp = 10 * tmp + esp_gps->item_str[i] - '0';
|
||||
i++;
|
||||
}
|
||||
esp_gps->parent.tim.thousand = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_NMEA_STATEMENT_GGA
|
||||
/**
|
||||
* @brief Parse GGA statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_gga(esp_gps_t *esp_gps)
|
||||
{
|
||||
/* Process GGA statement */
|
||||
switch (esp_gps->item_num) {
|
||||
case 1: /* Process UTC time */
|
||||
parse_utc_time(esp_gps);
|
||||
break;
|
||||
case 2: /* Latitude */
|
||||
esp_gps->parent.latitude = parse_lat_long(esp_gps);
|
||||
break;
|
||||
case 3: /* Latitude north(1)/south(-1) information */
|
||||
if (esp_gps->item_str[0] == 'S' || esp_gps->item_str[0] == 's') {
|
||||
esp_gps->parent.latitude *= -1;
|
||||
}
|
||||
break;
|
||||
case 4: /* Longitude */
|
||||
esp_gps->parent.longitude = parse_lat_long(esp_gps);
|
||||
break;
|
||||
case 5: /* Longitude east(1)/west(-1) information */
|
||||
if (esp_gps->item_str[0] == 'W' || esp_gps->item_str[0] == 'w') {
|
||||
esp_gps->parent.longitude *= -1;
|
||||
}
|
||||
break;
|
||||
case 6: /* Fix status */
|
||||
esp_gps->parent.fix = (gps_fix_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
break;
|
||||
case 7: /* Satellites in use */
|
||||
esp_gps->parent.sats_in_use = (uint8_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
break;
|
||||
case 8: /* HDOP */
|
||||
esp_gps->parent.dop_h = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 9: /* Altitude */
|
||||
esp_gps->parent.altitude = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 11: /* Altitude above ellipsoid */
|
||||
esp_gps->parent.altitude += strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_NMEA_STATEMENT_GSA
|
||||
/**
|
||||
* @brief Parse GSA statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_gsa(esp_gps_t *esp_gps)
|
||||
{
|
||||
/* Process GSA statement */
|
||||
switch (esp_gps->item_num) {
|
||||
case 2: /* Process fix mode */
|
||||
esp_gps->parent.fix_mode = (gps_fix_mode_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
break;
|
||||
case 15: /* Process PDOP */
|
||||
esp_gps->parent.dop_p = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 16: /* Process HDOP */
|
||||
esp_gps->parent.dop_h = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 17: /* Process VDOP */
|
||||
esp_gps->parent.dop_v = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
default:
|
||||
/* Parse satellite IDs */
|
||||
if (esp_gps->item_num >= 3 && esp_gps->item_num <= 14) {
|
||||
esp_gps->parent.sats_id_in_use[esp_gps->item_num - 3] = (uint8_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_NMEA_STATEMENT_GSV
|
||||
/**
|
||||
* @brief Parse GSV statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_gsv(esp_gps_t *esp_gps)
|
||||
{
|
||||
/* Process GSV statement */
|
||||
switch (esp_gps->item_num) {
|
||||
case 1: /* total GSV numbers */
|
||||
esp_gps->sat_count = (uint8_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
case 2: /* Current GSV statement number */
|
||||
esp_gps->sat_num = (uint8_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
break;
|
||||
case 3: /* Process satellites in view */
|
||||
esp_gps->parent.sats_in_view = (uint8_t)strtol(esp_gps->item_str, NULL, 10);
|
||||
break;
|
||||
default:
|
||||
if (esp_gps->item_num >= 4 && esp_gps->item_num <= 19) {
|
||||
uint8_t item_num = esp_gps->item_num - 4; /* Normalize item number from 4-19 to 0-15 */
|
||||
uint8_t index;
|
||||
uint32_t value;
|
||||
index = 4 * (esp_gps->sat_num - 1) + item_num / 4; /* Get array index */
|
||||
if (index < GPS_MAX_SATELLITES_IN_VIEW) {
|
||||
value = strtol(esp_gps->item_str, NULL, 10);
|
||||
switch (item_num % 4) {
|
||||
case 0:
|
||||
esp_gps->parent.sats_desc_in_view[index].num = (uint8_t)value;
|
||||
break;
|
||||
case 1:
|
||||
esp_gps->parent.sats_desc_in_view[index].elevation = (uint8_t)value;
|
||||
break;
|
||||
case 2:
|
||||
esp_gps->parent.sats_desc_in_view[index].azimuth = (uint16_t)value;
|
||||
break;
|
||||
case 3:
|
||||
esp_gps->parent.sats_desc_in_view[index].snr = (uint8_t)value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_NMEA_STATEMENT_RMC
|
||||
/**
|
||||
* @brief Parse RMC statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_rmc(esp_gps_t *esp_gps)
|
||||
{
|
||||
/* Process GPRMC statement */
|
||||
switch (esp_gps->item_num) {
|
||||
case 1:/* Process UTC time */
|
||||
parse_utc_time(esp_gps);
|
||||
break;
|
||||
case 2: /* Process valid status */
|
||||
esp_gps->parent.valid = (esp_gps->item_str[0] == 'A');
|
||||
break;
|
||||
case 3:/* Latitude */
|
||||
esp_gps->parent.latitude = parse_lat_long(esp_gps);
|
||||
break;
|
||||
case 4: /* Latitude north(1)/south(-1) information */
|
||||
if (esp_gps->item_str[0] == 'S' || esp_gps->item_str[0] == 's') {
|
||||
esp_gps->parent.latitude *= -1;
|
||||
}
|
||||
break;
|
||||
case 5: /* Longitude */
|
||||
esp_gps->parent.longitude = parse_lat_long(esp_gps);
|
||||
break;
|
||||
case 6: /* Longitude east(1)/west(-1) information */
|
||||
if (esp_gps->item_str[0] == 'W' || esp_gps->item_str[0] == 'w') {
|
||||
esp_gps->parent.longitude *= -1;
|
||||
}
|
||||
break;
|
||||
case 7: /* Process ground speed in unit m/s */
|
||||
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) * 1.852;
|
||||
break;
|
||||
case 8: /* Process true course over ground */
|
||||
esp_gps->parent.cog = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 9: /* Process date */
|
||||
esp_gps->parent.date.day = convert_two_digit2number(esp_gps->item_str + 0);
|
||||
esp_gps->parent.date.month = convert_two_digit2number(esp_gps->item_str + 2);
|
||||
esp_gps->parent.date.year = convert_two_digit2number(esp_gps->item_str + 4);
|
||||
break;
|
||||
case 10: /* Process magnetic variation */
|
||||
esp_gps->parent.variation = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_NMEA_STATEMENT_GLL
|
||||
/**
|
||||
* @brief Parse GLL statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_gll(esp_gps_t *esp_gps)
|
||||
{
|
||||
/* Process GPGLL statement */
|
||||
switch (esp_gps->item_num) {
|
||||
case 1:/* Latitude */
|
||||
esp_gps->parent.latitude = parse_lat_long(esp_gps);
|
||||
break;
|
||||
case 2: /* Latitude north(1)/south(-1) information */
|
||||
if (esp_gps->item_str[0] == 'S' || esp_gps->item_str[0] == 's') {
|
||||
esp_gps->parent.latitude *= -1;
|
||||
}
|
||||
break;
|
||||
case 3: /* Longitude */
|
||||
esp_gps->parent.longitude = parse_lat_long(esp_gps);
|
||||
break;
|
||||
case 4: /* Longitude east(1)/west(-1) information */
|
||||
if (esp_gps->item_str[0] == 'W' || esp_gps->item_str[0] == 'w') {
|
||||
esp_gps->parent.longitude *= -1;
|
||||
}
|
||||
break;
|
||||
case 5:/* Process UTC time */
|
||||
parse_utc_time(esp_gps);
|
||||
break;
|
||||
case 6: /* Process valid status */
|
||||
esp_gps->parent.valid = (esp_gps->item_str[0] == 'A');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_NMEA_STATEMENT_VTG
|
||||
/**
|
||||
* @brief Parse VTG statements
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void parse_vtg(esp_gps_t *esp_gps)
|
||||
{
|
||||
/* Process GPVGT statement */
|
||||
switch (esp_gps->item_num) {
|
||||
case 1: /* Process true course over ground */
|
||||
esp_gps->parent.cog = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 3:/* Process magnetic variation */
|
||||
esp_gps->parent.variation = strtof(esp_gps->item_str, NULL);
|
||||
break;
|
||||
case 5:/* Process ground speed in unit m/s */
|
||||
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) * 1.852;//knots to m/s
|
||||
break;
|
||||
case 7:/* Process ground speed in unit m/s */
|
||||
esp_gps->parent.speed = strtof(esp_gps->item_str, NULL) / 3.6;//km/h to m/s
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Parse received item
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on error
|
||||
*/
|
||||
static esp_err_t parse_item(esp_gps_t *esp_gps)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
/* start of a statement */
|
||||
if (esp_gps->item_num == 0 && esp_gps->item_str[0] == '$') {
|
||||
if (0) {
|
||||
}
|
||||
#if CONFIG_NMEA_STATEMENT_GGA
|
||||
else if (strstr(esp_gps->item_str, "GGA")) {
|
||||
esp_gps->cur_statement = STATEMENT_GGA;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSA
|
||||
else if (strstr(esp_gps->item_str, "GSA")) {
|
||||
esp_gps->cur_statement = STATEMENT_GSA;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_RMC
|
||||
else if (strstr(esp_gps->item_str, "RMC")) {
|
||||
esp_gps->cur_statement = STATEMENT_RMC;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSV
|
||||
else if (strstr(esp_gps->item_str, "GSV")) {
|
||||
esp_gps->cur_statement = STATEMENT_GSV;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GLL
|
||||
else if (strstr(esp_gps->item_str, "GLL")) {
|
||||
esp_gps->cur_statement = STATEMENT_GLL;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_VTG
|
||||
else if (strstr(esp_gps->item_str, "VTG")) {
|
||||
esp_gps->cur_statement = STATEMENT_VTG;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
esp_gps->cur_statement = STATEMENT_UNKNOWN;
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
/* Parse each item, depend on the type of the statement */
|
||||
if (esp_gps->cur_statement == STATEMENT_UNKNOWN) {
|
||||
goto out;
|
||||
}
|
||||
#if CONFIG_NMEA_STATEMENT_GGA
|
||||
else if (esp_gps->cur_statement == STATEMENT_GGA) {
|
||||
parse_gga(esp_gps);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSA
|
||||
else if (esp_gps->cur_statement == STATEMENT_GSA) {
|
||||
parse_gsa(esp_gps);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSV
|
||||
else if (esp_gps->cur_statement == STATEMENT_GSV) {
|
||||
parse_gsv(esp_gps);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_RMC
|
||||
else if (esp_gps->cur_statement == STATEMENT_RMC) {
|
||||
parse_rmc(esp_gps);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GLL
|
||||
else if (esp_gps->cur_statement == STATEMENT_GLL) {
|
||||
parse_gll(esp_gps);
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_VTG
|
||||
else if (esp_gps->cur_statement == STATEMENT_VTG) {
|
||||
parse_vtg(esp_gps);
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Parse NMEA statements from GPS receiver
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
* @param len number of bytes to decode
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on error
|
||||
*/
|
||||
static esp_err_t gps_decode(esp_gps_t *esp_gps, size_t len)
|
||||
{
|
||||
const uint8_t *d = esp_gps->buffer;
|
||||
while (*d) {
|
||||
/* Start of a statement */
|
||||
if (*d == '$') {
|
||||
/* Reset runtime information */
|
||||
esp_gps->asterisk = 0;
|
||||
esp_gps->item_num = 0;
|
||||
esp_gps->item_pos = 0;
|
||||
esp_gps->cur_statement = 0;
|
||||
esp_gps->crc = 0;
|
||||
esp_gps->sat_count = 0;
|
||||
esp_gps->sat_num = 0;
|
||||
/* Add character to item */
|
||||
esp_gps->item_str[esp_gps->item_pos++] = *d;
|
||||
esp_gps->item_str[esp_gps->item_pos] = '\0';
|
||||
}
|
||||
/* Detect item separator character */
|
||||
else if (*d == ',') {
|
||||
/* Parse current item */
|
||||
parse_item(esp_gps);
|
||||
/* Add character to CRC computation */
|
||||
esp_gps->crc ^= (uint8_t)(*d);
|
||||
/* Start with next item */
|
||||
esp_gps->item_pos = 0;
|
||||
esp_gps->item_str[0] = '\0';
|
||||
esp_gps->item_num++;
|
||||
}
|
||||
/* End of CRC computation */
|
||||
else if (*d == '*') {
|
||||
/* Parse current item */
|
||||
parse_item(esp_gps);
|
||||
/* Asterisk detected */
|
||||
esp_gps->asterisk = 1;
|
||||
/* Start with next item */
|
||||
esp_gps->item_pos = 0;
|
||||
esp_gps->item_str[0] = '\0';
|
||||
esp_gps->item_num++;
|
||||
}
|
||||
/* End of statement */
|
||||
else if (*d == '\r') {
|
||||
/* Convert received CRC from string (hex) to number */
|
||||
uint8_t crc = (uint8_t)strtol(esp_gps->item_str, NULL, 16);
|
||||
/* CRC passed */
|
||||
if (esp_gps->crc == crc) {
|
||||
switch (esp_gps->cur_statement) {
|
||||
#if CONFIG_NMEA_STATEMENT_GGA
|
||||
case STATEMENT_GGA:
|
||||
esp_gps->parsed_statement |= 1 << STATEMENT_GGA;
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSA
|
||||
case STATEMENT_GSA:
|
||||
esp_gps->parsed_statement |= 1 << STATEMENT_GSA;
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_RMC
|
||||
case STATEMENT_RMC:
|
||||
esp_gps->parsed_statement |= 1 << STATEMENT_RMC;
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSV
|
||||
case STATEMENT_GSV:
|
||||
if (esp_gps->sat_num == esp_gps->sat_count) {
|
||||
esp_gps->parsed_statement |= 1 << STATEMENT_GSV;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GLL
|
||||
case STATEMENT_GLL:
|
||||
esp_gps->parsed_statement |= 1 << STATEMENT_GLL;
|
||||
break;
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_VTG
|
||||
case STATEMENT_VTG:
|
||||
esp_gps->parsed_statement |= 1 << STATEMENT_VTG;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Check if all statements have been parsed */
|
||||
if (((esp_gps->parsed_statement) & esp_gps->all_statements) == esp_gps->all_statements) {
|
||||
esp_gps->parsed_statement = 0;
|
||||
/* Send signal to notify that GPS information has been updated */
|
||||
esp_event_post_to(esp_gps->event_loop_hdl, ESP_NMEA_EVENT, GPS_UPDATE,
|
||||
&(esp_gps->parent), sizeof(gps_t), 100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
} else {
|
||||
ESP_LOGD(GPS_TAG, "CRC Error for statement:%s", esp_gps->buffer);
|
||||
}
|
||||
if (esp_gps->cur_statement == STATEMENT_UNKNOWN) {
|
||||
/* Send signal to notify that one unknown statement has been met */
|
||||
esp_event_post_to(esp_gps->event_loop_hdl, ESP_NMEA_EVENT, GPS_UNKNOWN,
|
||||
esp_gps->buffer, len, 100 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
/* Other non-space character */
|
||||
else {
|
||||
if (!(esp_gps->asterisk)) {
|
||||
/* Add to CRC */
|
||||
esp_gps->crc ^= (uint8_t)(*d);
|
||||
}
|
||||
/* Add character to item */
|
||||
esp_gps->item_str[esp_gps->item_pos++] = *d;
|
||||
esp_gps->item_str[esp_gps->item_pos] = '\0';
|
||||
}
|
||||
/* Process next character */
|
||||
d++;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle when a pattern has been detected by uart
|
||||
*
|
||||
* @param esp_gps esp_gps_t type object
|
||||
*/
|
||||
static void esp_handle_uart_pattern(esp_gps_t *esp_gps)
|
||||
{
|
||||
int pos = uart_pattern_pop_pos(esp_gps->uart_port);
|
||||
if (pos != -1) {
|
||||
/* read one line(include '\n') */
|
||||
int read_len = uart_read_bytes(esp_gps->uart_port, esp_gps->buffer, pos + 1, 100 / portTICK_PERIOD_MS);
|
||||
/* make sure the line is a standard string */
|
||||
esp_gps->buffer[read_len] = '\0';
|
||||
/* Send new line to handle */
|
||||
if (gps_decode(esp_gps, read_len + 1) != ESP_OK) {
|
||||
ESP_LOGW(GPS_TAG, "GPS decode line failed");
|
||||
}
|
||||
} else {
|
||||
ESP_LOGW(GPS_TAG, "Pattern Queue Size too small");
|
||||
uart_flush_input(esp_gps->uart_port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief NMEA Parser Task Entry
|
||||
*
|
||||
* @param arg argument
|
||||
*/
|
||||
static void nmea_parser_task_entry(void *arg)
|
||||
{
|
||||
esp_gps_t *esp_gps = (esp_gps_t *)arg;
|
||||
uart_event_t event;
|
||||
while (1) {
|
||||
if (xQueueReceive(esp_gps->event_queue, &event, pdMS_TO_TICKS(200))) {
|
||||
switch (event.type) {
|
||||
case UART_DATA:
|
||||
break;
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGW(GPS_TAG, "HW FIFO Overflow");
|
||||
uart_flush(esp_gps->uart_port);
|
||||
xQueueReset(esp_gps->event_queue);
|
||||
break;
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGW(GPS_TAG, "Ring Buffer Full");
|
||||
uart_flush(esp_gps->uart_port);
|
||||
xQueueReset(esp_gps->event_queue);
|
||||
break;
|
||||
case UART_BREAK:
|
||||
ESP_LOGW(GPS_TAG, "Rx Break");
|
||||
break;
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGE(GPS_TAG, "Parity Error");
|
||||
break;
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGE(GPS_TAG, "Frame Error");
|
||||
break;
|
||||
case UART_PATTERN_DET:
|
||||
esp_handle_uart_pattern(esp_gps);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(GPS_TAG, "unknown uart event type: %d", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Drive the event loop */
|
||||
esp_event_loop_run(esp_gps->event_loop_hdl, pdMS_TO_TICKS(50));
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Init NMEA Parser
|
||||
*
|
||||
* @param config Configuration of NMEA Parser
|
||||
* @return nmea_parser_handle_t handle of nmea_parser
|
||||
*/
|
||||
nmea_parser_handle_t nmea_parser_init(const nmea_parser_config_t *config)
|
||||
{
|
||||
esp_gps_t *esp_gps = calloc(1, sizeof(esp_gps_t));
|
||||
if (!esp_gps) {
|
||||
ESP_LOGE(GPS_TAG, "calloc memory for esp_fps failed");
|
||||
goto err_gps;
|
||||
}
|
||||
esp_gps->buffer = calloc(1, NMEA_PARSER_RUNTIME_BUFFER_SIZE);
|
||||
if (!esp_gps->buffer) {
|
||||
ESP_LOGE(GPS_TAG, "calloc memory for runtime buffer failed");
|
||||
goto err_buffer;
|
||||
}
|
||||
#if CONFIG_NMEA_STATEMENT_GSA
|
||||
esp_gps->all_statements |= (1 << STATEMENT_GSA);
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GSV
|
||||
esp_gps->all_statements |= (1 << STATEMENT_GSV);
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GGA
|
||||
esp_gps->all_statements |= (1 << STATEMENT_GGA);
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_RMC
|
||||
esp_gps->all_statements |= (1 << STATEMENT_RMC);
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_GLL
|
||||
esp_gps->all_statements |= (1 << STATEMENT_GLL);
|
||||
#endif
|
||||
#if CONFIG_NMEA_STATEMENT_VTG
|
||||
esp_gps->all_statements |= (1 << STATEMENT_VTG);
|
||||
#endif
|
||||
/* Set attributes */
|
||||
esp_gps->uart_port = config->uart.uart_port;
|
||||
esp_gps->all_statements &= 0xFE;
|
||||
/* Install UART friver */
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = config->uart.baud_rate,
|
||||
.data_bits = config->uart.data_bits,
|
||||
.parity = config->uart.parity,
|
||||
.stop_bits = config->uart.stop_bits,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
|
||||
};
|
||||
if (uart_param_config(esp_gps->uart_port, &uart_config) != ESP_OK) {
|
||||
ESP_LOGE(GPS_TAG, "config uart parameter failed");
|
||||
goto err_uart_config;
|
||||
}
|
||||
if (uart_set_pin(esp_gps->uart_port, UART_PIN_NO_CHANGE, config->uart.rx_pin,
|
||||
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != ESP_OK) {
|
||||
ESP_LOGE(GPS_TAG, "config uart gpio failed");
|
||||
goto err_uart_config;
|
||||
}
|
||||
if (uart_driver_install(esp_gps->uart_port, CONFIG_NMEA_PARSER_RING_BUFFER_SIZE, 0,
|
||||
config->uart.event_queue_size, &esp_gps->event_queue, 0) != ESP_OK) {
|
||||
ESP_LOGE(GPS_TAG, "install uart driver failed");
|
||||
goto err_uart_install;
|
||||
}
|
||||
/* Set pattern interrupt, used to detect the end of a line */
|
||||
uart_enable_pattern_det_intr(esp_gps->uart_port, '\n', 1, 10000, 10, 10);
|
||||
/* Set pattern queue size */
|
||||
uart_pattern_queue_reset(esp_gps->uart_port, config->uart.event_queue_size);
|
||||
uart_flush(esp_gps->uart_port);
|
||||
/* Create Event loop */
|
||||
esp_event_loop_args_t loop_args = {
|
||||
.queue_size = NMEA_EVENT_LOOP_QUEUE_SIZE,
|
||||
.task_name = NULL
|
||||
};
|
||||
if (esp_event_loop_create(&loop_args, &esp_gps->event_loop_hdl) != ESP_OK) {
|
||||
ESP_LOGE(GPS_TAG, "create event loop faild");
|
||||
goto err_eloop;
|
||||
}
|
||||
/* Create NMEA Parser task */
|
||||
BaseType_t err = xTaskCreate(
|
||||
nmea_parser_task_entry,
|
||||
"nmea_parser",
|
||||
CONFIG_NMEA_PARSER_TASK_STACK_SIZE,
|
||||
esp_gps,
|
||||
CONFIG_NMEA_PARSER_TASK_PRIORITY,
|
||||
&esp_gps->tsk_hdl);
|
||||
if (err != pdTRUE) {
|
||||
ESP_LOGE(GPS_TAG, "create NMEA Parser task failed");
|
||||
goto err_task_create;
|
||||
}
|
||||
ESP_LOGI(GPS_TAG, "NMEA Parser init OK");
|
||||
return esp_gps;
|
||||
/*Error Handling*/
|
||||
err_task_create:
|
||||
esp_event_loop_delete(esp_gps->event_loop_hdl);
|
||||
err_eloop:
|
||||
err_uart_install:
|
||||
uart_driver_delete(esp_gps->uart_port);
|
||||
err_uart_config:
|
||||
err_buffer:
|
||||
free(esp_gps->buffer);
|
||||
err_gps:
|
||||
free(esp_gps);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deinit NMEA Parser
|
||||
*
|
||||
* @param nmea_hdl handle of NMEA parser
|
||||
* @return esp_err_t ESP_OK on success,ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t nmea_parser_deinit(nmea_parser_handle_t nmea_hdl)
|
||||
{
|
||||
esp_gps_t *esp_gps = (esp_gps_t *)nmea_hdl;
|
||||
vTaskDelete(esp_gps->tsk_hdl);
|
||||
esp_event_loop_delete(esp_gps->event_loop_hdl);
|
||||
esp_err_t err = uart_driver_delete(esp_gps->uart_port);
|
||||
free(esp_gps->buffer);
|
||||
free(esp_gps);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add user defined handler for NMEA parser
|
||||
*
|
||||
* @param nmea_hdl handle of NMEA parser
|
||||
* @param event_handler user defined event handler
|
||||
* @param handler_args handler specific arguments
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_NO_MEM: Cannot allocate memory for the handler
|
||||
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t nmea_parser_add_handler(nmea_parser_handle_t nmea_hdl, esp_event_handler_t event_handler, void *handler_args)
|
||||
{
|
||||
esp_gps_t *esp_gps = (esp_gps_t *)nmea_hdl;
|
||||
return esp_event_handler_register_with(esp_gps->event_loop_hdl, ESP_NMEA_EVENT, ESP_EVENT_ANY_ID,
|
||||
event_handler, handler_args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove user defined handler for NMEA parser
|
||||
*
|
||||
* @param nmea_hdl handle of NMEA parser
|
||||
* @param event_handler user defined event handler
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t nmea_parser_remove_handler(nmea_parser_handle_t nmea_hdl, esp_event_handler_t event_handler)
|
||||
{
|
||||
esp_gps_t *esp_gps = (esp_gps_t *)nmea_hdl;
|
||||
return esp_event_handler_unregister_with(esp_gps->event_loop_hdl, ESP_NMEA_EVENT, ESP_EVENT_ANY_ID, event_handler);
|
||||
}
|
218
examples/peripherals/uart/nmea0183_parser/main/nmea_parser.h
Normal file
218
examples/peripherals/uart/nmea0183_parser/main/nmea_parser.h
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_types.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_err.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
#define GPS_MAX_SATELLITES_IN_USE (12)
|
||||
#define GPS_MAX_SATELLITES_IN_VIEW (16)
|
||||
|
||||
/**
|
||||
* @brief Declare of NMEA Parser Event base
|
||||
*
|
||||
*/
|
||||
ESP_EVENT_DECLARE_BASE(ESP_NMEA_EVENT)
|
||||
|
||||
/**
|
||||
* @brief GPS fix type
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
GPS_FIX_INVALID, /*!< Not fixed */
|
||||
GPS_FIX_GPS, /*!< GPS */
|
||||
GPS_FIX_DGPS, /*!< Differential GPS */
|
||||
} gps_fix_t;
|
||||
|
||||
/**
|
||||
* @brief GPS fix mode
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
GPS_MODE_INVALID = 1, /*!< Not fixed */
|
||||
GPS_MODE_2D, /*!< 2D GPS */
|
||||
GPS_MODE_3D /*!< 3D GPS */
|
||||
} gps_fix_mode_t;
|
||||
|
||||
/**
|
||||
* @brief GPS satellite information
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t num; /*!< Satellite number */
|
||||
uint8_t elevation; /*!< Satellite elevation */
|
||||
uint16_t azimuth; /*!< Satellite azimuth */
|
||||
uint8_t snr; /*!< Satellite signal noise ratio */
|
||||
} gps_satellite_t;
|
||||
|
||||
/**
|
||||
* @brief GPS time
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t hour; /*!< Hour */
|
||||
uint8_t minute; /*!< Minute */
|
||||
uint8_t second; /*!< Second */
|
||||
uint16_t thousand; /*!< Thousand */
|
||||
} gps_time_t;
|
||||
|
||||
/**
|
||||
* @brief GPS date
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t day; /*!< Day (start from 1) */
|
||||
uint8_t month; /*!< Month (start from 1) */
|
||||
uint16_t year; /*!< Year (start from 2000) */
|
||||
} gps_date_t;
|
||||
|
||||
/**
|
||||
* @brief NMEA Statement
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
STATEMENT_UNKNOWN = 0, /*!< Unknown statement */
|
||||
STATEMENT_GGA, /*!< GGA */
|
||||
STATEMENT_GSA, /*!< GSA */
|
||||
STATEMENT_RMC, /*!< RMC */
|
||||
STATEMENT_GSV, /*!< GSV */
|
||||
STATEMENT_GLL, /*!< GLL */
|
||||
STATEMENT_VTG /*!< VTG */
|
||||
} nmea_statement_t;
|
||||
|
||||
/**
|
||||
* @brief GPS object
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
float latitude; /*!< Latitude (degrees) */
|
||||
float longitude; /*!< Longitude (degrees) */
|
||||
float altitude; /*!< Altitude (meters) */
|
||||
gps_fix_t fix; /*!< Fix status */
|
||||
uint8_t sats_in_use; /*!< Number of satellites in use */
|
||||
gps_time_t tim; /*!< time in UTC */
|
||||
gps_fix_mode_t fix_mode; /*!< Fix mode */
|
||||
uint8_t sats_id_in_use[GPS_MAX_SATELLITES_IN_USE]; /*!< ID list of satellite in use */
|
||||
float dop_h; /*!< Horizontal dilution of precision */
|
||||
float dop_p; /*!< Position dilution of precision */
|
||||
float dop_v; /*!< Vertical dilution of precision */
|
||||
uint8_t sats_in_view; /*!< Number of satellites in view */
|
||||
gps_satellite_t sats_desc_in_view[GPS_MAX_SATELLITES_IN_VIEW]; /*!< Information of satellites in view */
|
||||
gps_date_t date; /*!< Fix date */
|
||||
bool valid; /*!< GPS validity */
|
||||
float speed; /*!< Ground speed, unit: m/s */
|
||||
float cog; /*!< Course over ground */
|
||||
float variation; /*!< Magnetic variation */
|
||||
} gps_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration of NMEA Parser
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
uart_port_t uart_port; /*!< UART port number */
|
||||
uint32_t rx_pin; /*!< UART Rx Pin number */
|
||||
uint32_t baud_rate; /*!< UART baud rate */
|
||||
uart_word_length_t data_bits; /*!< UART data bits length */
|
||||
uart_parity_t parity; /*!< UART parity */
|
||||
uart_stop_bits_t stop_bits; /*!< UART stop bits length */
|
||||
uint32_t event_queue_size; /*!< UART event queue size */
|
||||
} uart; /*!< UART specific configuration */
|
||||
} nmea_parser_config_t;
|
||||
|
||||
/**
|
||||
* @brief NMEA Parser Handle
|
||||
*
|
||||
*/
|
||||
typedef void *nmea_parser_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Default configuration for NMEA Parser
|
||||
*
|
||||
*/
|
||||
#define NMEA_PARSER_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.uart = { \
|
||||
.uart_port = UART_NUM_1, \
|
||||
.rx_pin = 2, \
|
||||
.baud_rate = 9600, \
|
||||
.data_bits = UART_DATA_8_BITS, \
|
||||
.parity = UART_PARITY_DISABLE, \
|
||||
.stop_bits = UART_STOP_BITS_1, \
|
||||
.event_queue_size = 16 \
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief NMEA Parser Event ID
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
GPS_UPDATE, /*!< GPS information has been updated */
|
||||
GPS_UNKNOWN /*!< Unknown statements detected */
|
||||
} nmea_event_id_t;
|
||||
|
||||
/**
|
||||
* @brief Init NMEA Parser
|
||||
*
|
||||
* @param config Configuration of NMEA Parser
|
||||
* @return nmea_parser_handle_t handle of NMEA parser
|
||||
*/
|
||||
nmea_parser_handle_t nmea_parser_init(const nmea_parser_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinit NMEA Parser
|
||||
*
|
||||
* @param nmea_hdl handle of NMEA parser
|
||||
* @return esp_err_t ESP_OK on success, ESP_FAIL on error
|
||||
*/
|
||||
esp_err_t nmea_parser_deinit(nmea_parser_handle_t nmea_hdl);
|
||||
|
||||
/**
|
||||
* @brief Add user defined handler for NMEA parser
|
||||
*
|
||||
* @param nmea_hdl handle of NMEA parser
|
||||
* @param event_handler user defined event handler
|
||||
* @param handler_args handler specific arguments
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_NO_MEM: Cannot allocate memory for the handler
|
||||
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t nmea_parser_add_handler(nmea_parser_handle_t nmea_hdl, esp_event_handler_t event_handler, void *handler_args);
|
||||
|
||||
/**
|
||||
* @brief Remove user defined handler for NMEA parser
|
||||
*
|
||||
* @param nmea_hdl handle of NMEA parser
|
||||
* @param event_handler user defined event handler
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALIG_ARG: Invalid combination of event base and event id
|
||||
* - Others: Fail
|
||||
*/
|
||||
esp_err_t nmea_parser_remove_handler(nmea_parser_handle_t nmea_hdl, esp_event_handler_t event_handler);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,70 @@
|
||||
/* NMEA Parser example, that decode data stream from GPS receiver
|
||||
|
||||
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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "nmea_parser.h"
|
||||
|
||||
static const char *TAG = "gps_demo";
|
||||
|
||||
#define TIME_ZONE (+8) //Beijing Time
|
||||
#define YEAR_BASE (2000) //date in GPS starts from 2000
|
||||
|
||||
/**
|
||||
* @brief GPS Event Handler
|
||||
*
|
||||
* @param event_handler_arg handler specific arguments
|
||||
* @param event_base event base, here is fixed to ESP_NMEA_EVENT
|
||||
* @param event_id event id
|
||||
* @param event_data event specific arguments
|
||||
*/
|
||||
static void gps_event_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data)
|
||||
{
|
||||
gps_t *gps = NULL;
|
||||
switch (event_id) {
|
||||
case GPS_UPDATE:
|
||||
gps = (gps_t *)event_data;
|
||||
/* print information parsed from GPS statements */
|
||||
ESP_LOGI(TAG, "%d/%d/%d %d:%d:%d => \r\n"
|
||||
"\t\t\t\t\t\tlatitude = %.05f°N\r\n"
|
||||
"\t\t\t\t\t\tlongtitude = %.05f°E\r\n"
|
||||
"\t\t\t\t\t\taltitude = %.02fm\r\n"
|
||||
"\t\t\t\t\t\tspeed = %fm/s",
|
||||
gps->date.year + YEAR_BASE, gps->date.month, gps->date.day,
|
||||
gps->tim.hour + TIME_ZONE, gps->tim.minute, gps->tim.second,
|
||||
gps->latitude, gps->longitude, gps->altitude, gps->speed);
|
||||
break;
|
||||
case GPS_UNKNOWN:
|
||||
/* print unknown statements */
|
||||
ESP_LOGW(TAG, "Unknown statement:%s", (char *)event_data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
/* NMEA parser configuration */
|
||||
nmea_parser_config_t config = NMEA_PARSER_CONFIG_DEFAULT();
|
||||
/* init NMEA parser library */
|
||||
nmea_parser_handle_t nmea_hdl = nmea_parser_init(&config);
|
||||
/* register event handler for NMEA parser library */
|
||||
nmea_parser_add_handler(nmea_hdl, gps_event_handler, NULL);
|
||||
|
||||
vTaskDelay(10000 / portTICK_PERIOD_MS);
|
||||
|
||||
/* unregister event handler */
|
||||
nmea_parser_remove_handler(nmea_hdl, gps_event_handler);
|
||||
/* deinit NMEA parser library */
|
||||
nmea_parser_deinit(nmea_hdl);
|
||||
}
|
@@ -0,0 +1,6 @@
|
||||
# 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(uart_async_rxtxtasks)
|
8
examples/peripherals/uart/uart_async_rxtxtasks/Makefile
Normal file
8
examples/peripherals/uart/uart_async_rxtxtasks/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := uart_async_rxtxtasks
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
65
examples/peripherals/uart/uart_async_rxtxtasks/README.md
Normal file
65
examples/peripherals/uart/uart_async_rxtxtasks/README.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# UART Asynchronous Example with Separate Receive and Transfer Tasks
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how two asynchronous tasks can use the same UART interface for communication. One can use
|
||||
this example to develop more complex applications for serial communication.
|
||||
|
||||
The example starts two FreeRTOS tasks:
|
||||
1. The first task periodically transmits `Hello world` via the UART.
|
||||
2. The second task task listens, receives and prints data from the UART.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
The example can be run on any commonly available ESP32 development board. You will need a USB cable to connect the
|
||||
development board to a computer, and a simple one-wire cable for shorting two pins of the board.
|
||||
|
||||
### Setup the Hardware
|
||||
|
||||
The `RXD_PIN` and `TXD_PIN` which are configurable in the code (by default `GPIO4` and `GPIO5`) need to be shorted in
|
||||
order to receive back the same data which were sent out.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
make menuconfig
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set serial port under Serial Flasher Options.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
make -j4 flash monitor
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
You will receive the following repeating output from the monitoring console:
|
||||
```
|
||||
...
|
||||
I (3261) TX_TASK: Wrote 11 bytes
|
||||
I (4261) RX_TASK: Read 11 bytes: 'Hello world'
|
||||
I (4261) RX_TASK: 0x3ffb821c 48 65 6c 6c 6f 20 77 6f 72 6c 64 |Hello world|
|
||||
...
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you do not see any output from `RX_TASK` then check if you have the `RXD_PIN` and `TXD_PIN` pins shorted on the board.
|
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "uart_async_rxtxtasks_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
@@ -0,0 +1,75 @@
|
||||
/* UART asynchronous example, that uses separate RX and TX tasks
|
||||
|
||||
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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/uart.h"
|
||||
#include "soc/uart_struct.h"
|
||||
#include "string.h"
|
||||
|
||||
static const int RX_BUF_SIZE = 1024;
|
||||
|
||||
#define TXD_PIN (GPIO_NUM_4)
|
||||
#define RXD_PIN (GPIO_NUM_5)
|
||||
|
||||
void init() {
|
||||
const uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
|
||||
};
|
||||
uart_param_config(UART_NUM_1, &uart_config);
|
||||
uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
// We won't use a buffer for sending data.
|
||||
uart_driver_install(UART_NUM_1, RX_BUF_SIZE * 2, 0, 0, NULL, 0);
|
||||
}
|
||||
|
||||
int sendData(const char* logName, const char* data)
|
||||
{
|
||||
const int len = strlen(data);
|
||||
const int txBytes = uart_write_bytes(UART_NUM_1, data, len);
|
||||
ESP_LOGI(logName, "Wrote %d bytes", txBytes);
|
||||
return txBytes;
|
||||
}
|
||||
|
||||
static void tx_task()
|
||||
{
|
||||
static const char *TX_TASK_TAG = "TX_TASK";
|
||||
esp_log_level_set(TX_TASK_TAG, ESP_LOG_INFO);
|
||||
while (1) {
|
||||
sendData(TX_TASK_TAG, "Hello world");
|
||||
vTaskDelay(2000 / portTICK_PERIOD_MS);
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_task()
|
||||
{
|
||||
static const char *RX_TASK_TAG = "RX_TASK";
|
||||
esp_log_level_set(RX_TASK_TAG, ESP_LOG_INFO);
|
||||
uint8_t* data = (uint8_t*) malloc(RX_BUF_SIZE+1);
|
||||
while (1) {
|
||||
const int rxBytes = uart_read_bytes(UART_NUM_1, data, RX_BUF_SIZE, 1000 / portTICK_RATE_MS);
|
||||
if (rxBytes > 0) {
|
||||
data[rxBytes] = 0;
|
||||
ESP_LOGI(RX_TASK_TAG, "Read %d bytes: '%s'", rxBytes, data);
|
||||
ESP_LOG_BUFFER_HEXDUMP(RX_TASK_TAG, data, rxBytes, ESP_LOG_INFO);
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
init();
|
||||
xTaskCreate(rx_task, "uart_rx_task", 1024*2, NULL, configMAX_PRIORITIES, NULL);
|
||||
xTaskCreate(tx_task, "uart_tx_task", 1024*2, NULL, configMAX_PRIORITIES-1, NULL);
|
||||
}
|
6
examples/peripherals/uart/uart_echo/CMakeLists.txt
Normal file
6
examples/peripherals/uart/uart_echo/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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(uart_echo)
|
9
examples/peripherals/uart/uart_echo/Makefile
Normal file
9
examples/peripherals/uart/uart_echo/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := uart_echo
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
73
examples/peripherals/uart/uart_echo/README.md
Normal file
73
examples/peripherals/uart/uart_echo/README.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# UART Echo Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example demonstrates how to utilize UART interfaces of ESP32 by echoing back to the sender any data received on
|
||||
UART1.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
The example can be run on any ESP32 development board connected to a PC with a single USB cable for flashing and
|
||||
monitoring. The external interface should have 3.3V outputs. You may use e.g. 3.3V compatible USB-to-Serial dongle.
|
||||
|
||||
### Setup the Hardware
|
||||
|
||||
Connect the external serial interface to the ESP32 board as follows.
|
||||
|
||||
| ESP32 Interface | #define | ESP32 Pin | External UART Pin |
|
||||
| --- | --- | --- | --- |
|
||||
| Transmit Data (TxD) | ECHO_TEST_TXD | GPIO4 | RxD |
|
||||
| Receive Data (RxD) | ECHO_TEST_RXD | GPIO5 | TxD |
|
||||
| Ground | n/a | GND | GND |
|
||||
|
||||
Optionally, you can set-up and use a serial interface that has RTS and CTS signals in order to verify that the
|
||||
hardware control flow works. Connect the extra signals according to the following table, configure both extra pins in
|
||||
the example code by replacing existing `UART_PIN_NO_CHANGE` macros with the appropriate pin numbers and configure
|
||||
UART1 driver to use the hardware flow control by setting `.flow_ctrl = UART_HW_FLOWCTRL_CTS_RTS` and adding
|
||||
`.rx_flow_ctrl_thresh = 122`.
|
||||
|
||||
| ESP32 Interface | #define | ESP32 Pin | External UART Pin |
|
||||
| --- | --- | --- | --- |
|
||||
| Request to Send (RTS) | ECHO_TEST_RTS | GPIO18 | CTS |
|
||||
| Clear to Send (CTS) | ECHO_TEST_CTS | GPIO19 | RTS |
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
make menuconfig
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set serial port under Serial Flasher Options.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
make -j4 flash monitor
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Type some characters in the terminal connected to the external serial interface. As result you should see echo in the
|
||||
terminal which is used for flashing and monitoring. You can verify if the echo indeed comes from ESP32 by
|
||||
disconnecting either `TxD` or `RxD` pin: no characters will appear when typing.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
You are not supposed to see the echo in the terminal which is used for flashing and monitoring, but in the other one
|
||||
which is connected to UART1.
|
4
examples/peripherals/uart/uart_echo/main/CMakeLists.txt
Normal file
4
examples/peripherals/uart/uart_echo/main/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "uart_echo_example_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
3
examples/peripherals/uart/uart_echo/main/component.mk
Normal file
3
examples/peripherals/uart/uart_echo/main/component.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
@@ -0,0 +1,62 @@
|
||||
/* UART Echo 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 "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
/**
|
||||
* This is an example which echos any data it receives on UART1 back to the sender,
|
||||
* with hardware flow control turned off. It does not use UART driver event queue.
|
||||
*
|
||||
* - Port: UART1
|
||||
* - Receive (Rx) buffer: on
|
||||
* - Transmit (Tx) buffer: off
|
||||
* - Flow control: off
|
||||
* - Event queue: off
|
||||
* - Pin assignment: see defines below
|
||||
*/
|
||||
|
||||
#define ECHO_TEST_TXD (GPIO_NUM_4)
|
||||
#define ECHO_TEST_RXD (GPIO_NUM_5)
|
||||
#define ECHO_TEST_RTS (UART_PIN_NO_CHANGE)
|
||||
#define ECHO_TEST_CTS (UART_PIN_NO_CHANGE)
|
||||
|
||||
#define BUF_SIZE (1024)
|
||||
|
||||
static void echo_task()
|
||||
{
|
||||
/* Configure parameters of an UART driver,
|
||||
* communication pins and install the driver */
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
|
||||
};
|
||||
uart_param_config(UART_NUM_1, &uart_config);
|
||||
uart_set_pin(UART_NUM_1, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);
|
||||
uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0);
|
||||
|
||||
// Configure a temporary buffer for the incoming data
|
||||
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
|
||||
|
||||
while (1) {
|
||||
// Read data from the UART
|
||||
int len = uart_read_bytes(UART_NUM_1, data, BUF_SIZE, 20 / portTICK_RATE_MS);
|
||||
// Write data back to the UART
|
||||
uart_write_bytes(UART_NUM_1, (const char *) data, len);
|
||||
}
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreate(echo_task, "uart_echo_task", 1024, NULL, 10, NULL);
|
||||
}
|
6
examples/peripherals/uart/uart_echo_rs485/CMakeLists.txt
Normal file
6
examples/peripherals/uart/uart_echo_rs485/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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(echo_rs485)
|
9
examples/peripherals/uart/uart_echo_rs485/Makefile
Normal file
9
examples/peripherals/uart/uart_echo_rs485/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := echo_rs485
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
88
examples/peripherals/uart/uart_echo_rs485/README.md
Normal file
88
examples/peripherals/uart/uart_echo_rs485/README.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# UART RS485 Echo Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This is an example which echoes any data it receives on UART2 back to the sender in the RS485 network.
|
||||
It uses ESP-IDF UART software driver in RS485 half duplex transmission mode and requires external connection of bus drivers.
|
||||
The approach demonstrated in this example can be used in user application to transmit/receive data in RS485 networks.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
PC + USB Serial adapter connected to USB port + RS485 line drivers + ESP32-WROVER-KIT board.
|
||||
The MAX485 line driver is used for example below but other similar chips can be used as well.
|
||||
|
||||
#### RS485 example connection circuit schematic:
|
||||
```
|
||||
VCC ---------------+ +--------------- VCC
|
||||
| |
|
||||
+-------x-------+ +-------x-------+
|
||||
RXD <------| RO | | RO|-----> RXD
|
||||
| B|---------------|B |
|
||||
TXD ------>| DI MAX485 | \ / | MAX485 DI|<----- TXD
|
||||
ESP32-WROVER-KIT | | RS-485 side | | SERIAL ADAPTER SIDE
|
||||
RTS --+--->| DE | / \ | DE|---+
|
||||
| | A|---------------|A | |
|
||||
+----| /RE | | /RE|---+-- RTS
|
||||
+-------x-------+ +-------x-------+
|
||||
| |
|
||||
--- ---
|
||||
```
|
||||
|
||||
#### Connect an external RS485 serial interface to an ESP32 board
|
||||
```
|
||||
----------------------------------------------------------------------
|
||||
| ESP32 Interface | #define | ESP32 Pin | External RS485 |
|
||||
| | | | Driver Pin |
|
||||
| ----------------------|---------------|-----------|----------------|
|
||||
| Transmit Data (TxD) | ECHO_TEST_TXD | GPIO23 | DI |
|
||||
| Receive Data (RxD) | ECHO_TEST_RXD | GPIO22 | RO |
|
||||
| Request To Send (RTS) | ECHO_TEST_RTS | GPIO18 | ~RE/DE |
|
||||
| Ground | n/a | GND | GND |
|
||||
----------------------------------------------------------------------
|
||||
```
|
||||
Connect USB to RS485 adapter to computer and connect its D+, D- output lines with the D+, D- lines of RS485 line driver connected to ESP32 (See picture above).
|
||||
|
||||
### Configure the project
|
||||
```
|
||||
make menuconfig
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
* Set serial port under Serial Flasher Options to the serial port of ESP32-WROVER-KIT board.
|
||||
|
||||
### Build and Flash
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
```
|
||||
make -j4 flash monitor
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
#### Setup external terminal software
|
||||
Refer to the example and set up a serial terminal program to the same settings as of UART in ESP32-WROVER-KIT board.
|
||||
Open the external serial interface in the terminal. By default if no any symbols are received, the application sends character `.` to check transmission side.
|
||||
When typing message and push send button in the terminal you should see the message `RS485 Received: [ your message ]`, where "your message" is the message you sent from terminal.
|
||||
Verify if echo indeed comes from ESP32 by disconnecting either `TxD` or `RxD` pin. Once done there should be no any `.` displayed.
|
||||
|
||||
## Example Output
|
||||
Example output of the application:
|
||||
```
|
||||
I (655020) RS485_ECHO_APP: Received 12 bytes:
|
||||
[ 0x79 0x6F 0x75 0x72 0x20 0x6D 0x65 0x73 0x73 0x61 0x67 0x65 ]
|
||||
```
|
||||
The received message is showed in hexadecimal form in the brackets.
|
||||
|
||||
## Troubleshooting
|
||||
When example software does not show the `.` symbol, the issue is most likely related to connection errors of the external RS485 interface.
|
||||
Check the RS485 interface connection with the environment according to schematic above and restart the application.
|
||||
Then start terminal software and open the appropriate serial port.
|
||||
|
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "rs485_example.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
126
examples/peripherals/uart/uart_echo_rs485/main/rs485_example.c
Normal file
126
examples/peripherals/uart/uart_echo_rs485/main/rs485_example.c
Normal file
@@ -0,0 +1,126 @@
|
||||
/* Uart Events Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_system.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "driver/uart.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/uart_struct.h"
|
||||
|
||||
/**
|
||||
* This is a example example which echos any data it receives on UART back to the sender.
|
||||
*
|
||||
* - port: UART2
|
||||
* - rx buffer: on
|
||||
* - tx buffer: off
|
||||
* - flow control: off
|
||||
*
|
||||
* This example has been tested on a 3 node RS485 Serial Bus
|
||||
*
|
||||
*/
|
||||
|
||||
// Note: UART2 default pins IO16, IO17 do not work on ESP32-WROVER module
|
||||
// because these pins connected to PSRAM
|
||||
#define ECHO_TEST_TXD (23)
|
||||
#define ECHO_TEST_RXD (22)
|
||||
|
||||
// RTS for RS485 Half-Duplex Mode manages DE/~RE
|
||||
#define ECHO_TEST_RTS (18)
|
||||
|
||||
// CTS is not used in RS485 Half-Duplex Mode
|
||||
#define ECHO_TEST_CTS UART_PIN_NO_CHANGE
|
||||
|
||||
#define BUF_SIZE (127)
|
||||
#define BAUD_RATE (115200)
|
||||
|
||||
// Read packet timeout
|
||||
#define PACKET_READ_TICS (100 / portTICK_RATE_MS)
|
||||
#define ECHO_TASK_STACK_SIZE (2048)
|
||||
#define ECHO_TASK_PRIO (10)
|
||||
#define ECHO_UART_PORT (UART_NUM_2)
|
||||
|
||||
static const char *TAG = "RS485_ECHO_APP";
|
||||
|
||||
// An example of echo test with hardware flow control on UART
|
||||
static void echo_task()
|
||||
{
|
||||
const int uart_num = ECHO_UART_PORT;
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = BAUD_RATE,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
|
||||
.rx_flow_ctrl_thresh = 122,
|
||||
};
|
||||
|
||||
// Set UART log level
|
||||
esp_log_level_set(TAG, ESP_LOG_INFO);
|
||||
|
||||
ESP_LOGI(TAG, "Start RS485 application test and configure UART.");
|
||||
|
||||
// Configure UART parameters
|
||||
uart_param_config(uart_num, &uart_config);
|
||||
|
||||
ESP_LOGI(TAG, "UART set pins, mode and install driver.");
|
||||
// Set UART1 pins(TX: IO23, RX: I022, RTS: IO18, CTS: IO19)
|
||||
uart_set_pin(uart_num, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);
|
||||
|
||||
// Install UART driver (we don't need an event queue here)
|
||||
// In this example we don't even use a buffer for sending data.
|
||||
uart_driver_install(uart_num, BUF_SIZE * 2, 0, 0, NULL, 0);
|
||||
|
||||
// Set RS485 half duplex mode
|
||||
uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX);
|
||||
|
||||
// Allocate buffers for UART
|
||||
uint8_t* data = (uint8_t*) malloc(BUF_SIZE);
|
||||
|
||||
ESP_LOGI(TAG, "UART start recieve loop.\r\n");
|
||||
uart_write_bytes(uart_num, "Start RS485 UART test.\r\n", 24);
|
||||
|
||||
while(1) {
|
||||
//Read data from UART
|
||||
int len = uart_read_bytes(uart_num, data, BUF_SIZE, PACKET_READ_TICS);
|
||||
|
||||
//Write data back to UART
|
||||
if (len > 0) {
|
||||
uart_write_bytes(uart_num, "\r\n", 2);
|
||||
char prefix[] = "RS485 Received: [";
|
||||
uart_write_bytes(uart_num, prefix, (sizeof(prefix) - 1));
|
||||
|
||||
ESP_LOGI(TAG, "Received %u bytes:", len);
|
||||
printf("[ ");
|
||||
for (int i = 0; i < len; i++) {
|
||||
printf("0x%.2X ", (uint8_t)data[i]);
|
||||
uart_write_bytes(uart_num, (const char*)&data[i], 1);
|
||||
// Add a Newline character if you get a return charater from paste (Paste tests multibyte receipt/buffer)
|
||||
if (data[i] == '\r') {
|
||||
uart_write_bytes(uart_num, "\n", 1);
|
||||
}
|
||||
}
|
||||
printf("] \n");
|
||||
uart_write_bytes(uart_num, "]\r\n", 3);
|
||||
} else {
|
||||
// Echo a "." to show we are alive while we wait for input
|
||||
uart_write_bytes(uart_num, ".", 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
//A uart read/write example without event queue;
|
||||
xTaskCreate(echo_task, "uart_echo_task", ECHO_TASK_STACK_SIZE, NULL, ECHO_TASK_PRIO, NULL);
|
||||
}
|
6
examples/peripherals/uart/uart_events/CMakeLists.txt
Normal file
6
examples/peripherals/uart/uart_events/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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(uart_events)
|
9
examples/peripherals/uart/uart_events/Makefile
Normal file
9
examples/peripherals/uart/uart_events/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := uart_events
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
56
examples/peripherals/uart/uart_events/README.md
Normal file
56
examples/peripherals/uart/uart_events/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# UART Events Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
This example shows how to use the UART driver to handle special UART events. It also reads data from `UART0` directly,
|
||||
and echoes it back to the monitoring console.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
The example can be used with any ESP32 development board connected to a computer with a USB cable.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
make menuconfig
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set serial port under Serial Flasher Options.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
make -j4 flash monitor
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
Pushing `a` followed by a `b` on the keyboard will generate the following output:
|
||||
```
|
||||
...
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (299) uart: queue free spaces: 20
|
||||
I (2249) uart_events: uart[0] event:
|
||||
I (2249) uart_events: [UART DATA]: 1
|
||||
I (2249) uart_events: [DATA EVT]:
|
||||
aI (12089) uart_events: uart[0] event:
|
||||
I (12089) uart_events: [UART DATA]: 1
|
||||
I (12089) uart_events: [DATA EVT]:
|
||||
b
|
||||
```
|
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "uart_events_example_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
3
examples/peripherals/uart/uart_events/main/component.mk
Normal file
3
examples/peripherals/uart/uart_events/main/component.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
@@ -0,0 +1,149 @@
|
||||
/* UART Events Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "uart_events";
|
||||
|
||||
/**
|
||||
* This example shows how to use the UART driver to handle special UART events.
|
||||
*
|
||||
* It also reads data from UART0 directly, and echoes it to console.
|
||||
*
|
||||
* - Port: UART0
|
||||
* - Receive (Rx) buffer: on
|
||||
* - Transmit (Tx) buffer: off
|
||||
* - Flow control: off
|
||||
* - Event queue: on
|
||||
* - Pin assignment: TxD (default), RxD (default)
|
||||
*/
|
||||
|
||||
#define EX_UART_NUM UART_NUM_0
|
||||
#define PATTERN_CHR_NUM (3) /*!< Set the number of consecutive and identical characters received by receiver which defines a UART pattern*/
|
||||
|
||||
#define BUF_SIZE (1024)
|
||||
#define RD_BUF_SIZE (BUF_SIZE)
|
||||
static QueueHandle_t uart0_queue;
|
||||
|
||||
static void uart_event_task(void *pvParameters)
|
||||
{
|
||||
uart_event_t event;
|
||||
size_t buffered_size;
|
||||
uint8_t* dtmp = (uint8_t*) malloc(RD_BUF_SIZE);
|
||||
for(;;) {
|
||||
//Waiting for UART event.
|
||||
if(xQueueReceive(uart0_queue, (void * )&event, (portTickType)portMAX_DELAY)) {
|
||||
bzero(dtmp, RD_BUF_SIZE);
|
||||
ESP_LOGI(TAG, "uart[%d] event:", EX_UART_NUM);
|
||||
switch(event.type) {
|
||||
//Event of UART receving data
|
||||
/*We'd better handler data event fast, there would be much more data events than
|
||||
other types of events. If we take too much time on data event, the queue might
|
||||
be full.*/
|
||||
case UART_DATA:
|
||||
ESP_LOGI(TAG, "[UART DATA]: %d", event.size);
|
||||
uart_read_bytes(EX_UART_NUM, dtmp, event.size, portMAX_DELAY);
|
||||
ESP_LOGI(TAG, "[DATA EVT]:");
|
||||
uart_write_bytes(EX_UART_NUM, (const char*) dtmp, event.size);
|
||||
break;
|
||||
//Event of HW FIFO overflow detected
|
||||
case UART_FIFO_OVF:
|
||||
ESP_LOGI(TAG, "hw fifo overflow");
|
||||
// If fifo overflow happened, you should consider adding flow control for your application.
|
||||
// The ISR has already reset the rx FIFO,
|
||||
// As an example, we directly flush the rx buffer here in order to read more data.
|
||||
uart_flush_input(EX_UART_NUM);
|
||||
xQueueReset(uart0_queue);
|
||||
break;
|
||||
//Event of UART ring buffer full
|
||||
case UART_BUFFER_FULL:
|
||||
ESP_LOGI(TAG, "ring buffer full");
|
||||
// If buffer full happened, you should consider encreasing your buffer size
|
||||
// As an example, we directly flush the rx buffer here in order to read more data.
|
||||
uart_flush_input(EX_UART_NUM);
|
||||
xQueueReset(uart0_queue);
|
||||
break;
|
||||
//Event of UART RX break detected
|
||||
case UART_BREAK:
|
||||
ESP_LOGI(TAG, "uart rx break");
|
||||
break;
|
||||
//Event of UART parity check error
|
||||
case UART_PARITY_ERR:
|
||||
ESP_LOGI(TAG, "uart parity error");
|
||||
break;
|
||||
//Event of UART frame error
|
||||
case UART_FRAME_ERR:
|
||||
ESP_LOGI(TAG, "uart frame error");
|
||||
break;
|
||||
//UART_PATTERN_DET
|
||||
case UART_PATTERN_DET:
|
||||
uart_get_buffered_data_len(EX_UART_NUM, &buffered_size);
|
||||
int pos = uart_pattern_pop_pos(EX_UART_NUM);
|
||||
ESP_LOGI(TAG, "[UART PATTERN DETECTED] pos: %d, buffered size: %d", pos, buffered_size);
|
||||
if (pos == -1) {
|
||||
// There used to be a UART_PATTERN_DET event, but the pattern position queue is full so that it can not
|
||||
// record the position. We should set a larger queue size.
|
||||
// As an example, we directly flush the rx buffer here.
|
||||
uart_flush_input(EX_UART_NUM);
|
||||
} else {
|
||||
uart_read_bytes(EX_UART_NUM, dtmp, pos, 100 / portTICK_PERIOD_MS);
|
||||
uint8_t pat[PATTERN_CHR_NUM + 1];
|
||||
memset(pat, 0, sizeof(pat));
|
||||
uart_read_bytes(EX_UART_NUM, pat, PATTERN_CHR_NUM, 100 / portTICK_PERIOD_MS);
|
||||
ESP_LOGI(TAG, "read data: %s", dtmp);
|
||||
ESP_LOGI(TAG, "read pat : %s", pat);
|
||||
}
|
||||
break;
|
||||
//Others
|
||||
default:
|
||||
ESP_LOGI(TAG, "uart event type: %d", event.type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(dtmp);
|
||||
dtmp = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
esp_log_level_set(TAG, ESP_LOG_INFO);
|
||||
|
||||
/* Configure parameters of an UART driver,
|
||||
* communication pins and install the driver */
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
|
||||
};
|
||||
uart_param_config(EX_UART_NUM, &uart_config);
|
||||
|
||||
//Set UART log level
|
||||
esp_log_level_set(TAG, ESP_LOG_INFO);
|
||||
//Set UART pins (using UART0 default pins ie no changes.)
|
||||
uart_set_pin(EX_UART_NUM, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
||||
//Install UART driver, and get the queue.
|
||||
uart_driver_install(EX_UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 20, &uart0_queue, 0);
|
||||
|
||||
//Set uart pattern detect function.
|
||||
uart_enable_pattern_det_intr(EX_UART_NUM, '+', PATTERN_CHR_NUM, 10000, 10, 10);
|
||||
//Reset the pattern queue length to record at most 20 pattern positions.
|
||||
uart_pattern_queue_reset(EX_UART_NUM, 20);
|
||||
|
||||
//Create a task to handler UART event from ISR
|
||||
xTaskCreate(uart_event_task, "uart_event_task", 2048, NULL, 12, NULL);
|
||||
}
|
6
examples/peripherals/uart/uart_select/CMakeLists.txt
Normal file
6
examples/peripherals/uart/uart_select/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# 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(uart_select)
|
8
examples/peripherals/uart/uart_select/Makefile
Normal file
8
examples/peripherals/uart/uart_select/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := uart_select
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
78
examples/peripherals/uart/uart_select/README.md
Normal file
78
examples/peripherals/uart/uart_select/README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# UART Select Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The UART select example is for demonstrating the use of `select()` for
|
||||
synchronous I/O multiplexing on the UART interface. The example waits for a
|
||||
character from UART using `select()` until a blocking read without delay or a
|
||||
successful non-blocking read is possible.
|
||||
|
||||
Please note that the same result can be achieved by using `uart_read_bytes()`
|
||||
but the use of `select()` allows to use it together with other virtual
|
||||
file system (VFS) drivers, e.g. LWIP sockets.
|
||||
|
||||
This example can be used to develop applications for non-blocking read and write from/to various sources (UART,
|
||||
sockets, ...) where a ready resource can be served without being blocked by a busy resource.
|
||||
|
||||
For a more comprehensive example please refer to `system/select`.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
The example can be run on any ESP32 development board connected to a PC with a single USB cable for communication
|
||||
through UART.
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
make menuconfig
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
* Set serial port under Serial Flasher Options.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
make -j4 flash monitor
|
||||
```
|
||||
or
|
||||
```
|
||||
idf.py flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
You can see a similar output after flashing and monitoring the application:
|
||||
|
||||
```
|
||||
...
|
||||
I (276) cpu_start: Pro cpu start user code
|
||||
I (294) cpu_start: Starting scheduler on PRO CPU.
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (10295) uart_select_example: Timeout has been reached and nothing has been received
|
||||
I (15295) uart_select_example: Timeout has been reached and nothing has been received
|
||||
I (20295) uart_select_example: Timeout has been reached and nothing has been received
|
||||
```
|
||||
|
||||
You can push any key on your keyboard to see the result of a successful detection by `select()` and subsequent
|
||||
blocking read (without delay). The following output shows the result of pushing `a` on the keyboard:
|
||||
|
||||
```
|
||||
...
|
||||
I (15295) uart_select_example: Timeout has been reached and nothing has been received
|
||||
I (20295) uart_select_example: Timeout has been reached and nothing has been received
|
||||
I (20945) uart_select_example: Received: a
|
||||
I (25955) uart_select_example: Timeout has been reached and nothing has been received
|
||||
...
|
||||
```
|
@@ -0,0 +1,4 @@
|
||||
set(COMPONENT_SRCS "uart_select_example_main.c")
|
||||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
|
||||
register_component()
|
3
examples/peripherals/uart/uart_select/main/component.mk
Normal file
3
examples/peripherals/uart/uart_select/main/component.mk
Normal file
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
@@ -0,0 +1,93 @@
|
||||
/* UART Select 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 <sys/fcntl.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_dev.h"
|
||||
#include "driver/uart.h"
|
||||
|
||||
static const char* TAG = "uart_select_example";
|
||||
|
||||
static void uart_select_task()
|
||||
{
|
||||
uart_config_t uart_config = {
|
||||
.baud_rate = 115200,
|
||||
.data_bits = UART_DATA_8_BITS,
|
||||
.parity = UART_PARITY_DISABLE,
|
||||
.stop_bits = UART_STOP_BITS_1,
|
||||
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE
|
||||
};
|
||||
uart_param_config(UART_NUM_0, &uart_config);
|
||||
uart_driver_install(UART_NUM_0, 2*1024, 0, 0, NULL, 0);
|
||||
|
||||
while (1) {
|
||||
int fd;
|
||||
|
||||
if ((fd = open("/dev/uart/0", O_RDWR)) == -1) {
|
||||
ESP_LOGE(TAG, "Cannot open UART");
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a driver now installed so set up the read/write functions to use driver also.
|
||||
esp_vfs_dev_uart_use_driver(0);
|
||||
|
||||
while (1) {
|
||||
int s;
|
||||
fd_set rfds;
|
||||
struct timeval tv = {
|
||||
.tv_sec = 5,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
|
||||
s = select(fd + 1, &rfds, NULL, NULL, &tv);
|
||||
|
||||
if (s < 0) {
|
||||
ESP_LOGE(TAG, "Select failed: errno %d", errno);
|
||||
break;
|
||||
} else if (s == 0) {
|
||||
ESP_LOGI(TAG, "Timeout has been reached and nothing has been received");
|
||||
} else {
|
||||
if (FD_ISSET(fd, &rfds)) {
|
||||
char buf;
|
||||
if (read(fd, &buf, 1) > 0) {
|
||||
ESP_LOGI(TAG, "Received: %c", buf);
|
||||
// Note: Only one character was read even the buffer contains more. The other characters will
|
||||
// be read one-by-one by subsequent calls to select() which will then return immediately
|
||||
// without timeout.
|
||||
} else {
|
||||
ESP_LOGE(TAG, "UART read error");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ESP_LOGE(TAG, "No FD has been set in select()");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
xTaskCreate(uart_select_task, "uart_select_task", 4*1024, NULL, 5, NULL);
|
||||
}
|
Reference in New Issue
Block a user