Alex Mikhalev
6 years ago
10 changed files with 359 additions and 116 deletions
@ -0,0 +1,7 @@ |
|||||||
|
set(COMPONENT_SRCS |
||||||
|
"minmea/minmea.c") |
||||||
|
set(COMPONENT_ADD_INCLUDEDIRS "minmea") |
||||||
|
|
||||||
|
register_component() |
||||||
|
|
||||||
|
component_compile_options(PUBLIC -Dtimegm=mktime) |
@ -0,0 +1 @@ |
|||||||
|
Subproject commit e8ae137c96444ea313485ed1118c5e43b2099cf1 |
@ -0,0 +1,216 @@ |
|||||||
|
#include "ugv_io_gps.hh" |
||||||
|
|
||||||
|
#include <driver/uart.h> |
||||||
|
#include <esp_log.h> |
||||||
|
#include <cstring> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "minmea.h" |
||||||
|
|
||||||
|
namespace ugv { |
||||||
|
namespace io { |
||||||
|
|
||||||
|
static constexpr size_t GPS_BUF_SIZE = 1024; |
||||||
|
static constexpr uart_port_t GPS_UART = UART_NUM_1; |
||||||
|
static constexpr int GPS_UART_TX_PIN = 17; |
||||||
|
static constexpr int GPS_UART_RX_PIN = 23; |
||||||
|
static constexpr int GPS_UART_BAUD = 9600; |
||||||
|
static constexpr size_t GPS_UART_RX_BUF_SIZE = 1024; |
||||||
|
static constexpr size_t GPS_UART_TX_BUF_SIZE = 0; |
||||||
|
static constexpr size_t GPS_UART_QUEUE_SIZE = 8; |
||||||
|
|
||||||
|
const char NMEA_OUTPUT_RMCONLY[] = |
||||||
|
"$PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*29"; |
||||||
|
const char NMEA_OUTPUT_RMCGGA[] = |
||||||
|
"$PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"; |
||||||
|
const char NMEA_OUTPUT_ALL[] = |
||||||
|
"$PMTK314,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0*28"; |
||||||
|
const char NMEA_OUTPUT_OFF[] = |
||||||
|
"$PMTK314,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*28"; |
||||||
|
|
||||||
|
const char NMEA_UPDATE_1HZ[] = "$PMTK220,1000*1F"; |
||||||
|
const char NMEA_UPDATE_5HZ[] = "$PMTK220,200*2C"; |
||||||
|
const char NMEA_UPDATE_10HZ[] = "$PMTK220,100*2F"; |
||||||
|
|
||||||
|
const char NMEA_Q_RELEASE[] = "$PMTK605*31"; |
||||||
|
|
||||||
|
const char NMEA_END_CMD[] = "\r\n"; |
||||||
|
|
||||||
|
std::string st; |
||||||
|
|
||||||
|
static const char *TAG = "ugv_io"; |
||||||
|
|
||||||
|
UART_GPS::UART_GPS() {} |
||||||
|
|
||||||
|
UART_GPS::~UART_GPS() {} |
||||||
|
|
||||||
|
void UART_GPS::Init() { |
||||||
|
esp_err_t ret; |
||||||
|
|
||||||
|
uart_config_t gps_uart_config; |
||||||
|
gps_uart_config.baud_rate = GPS_UART_BAUD; |
||||||
|
gps_uart_config.data_bits = UART_DATA_8_BITS; |
||||||
|
gps_uart_config.parity = UART_PARITY_DISABLE; |
||||||
|
gps_uart_config.stop_bits = UART_STOP_BITS_1; |
||||||
|
gps_uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE; |
||||||
|
gps_uart_config.rx_flow_ctrl_thresh = 122; |
||||||
|
gps_uart_config.use_ref_tick = false; |
||||||
|
ret = uart_param_config(GPS_UART, &gps_uart_config); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
ESP_LOGE(TAG, "uart_param_config: %d", ret); |
||||||
|
return; |
||||||
|
} |
||||||
|
uart_set_pin(GPS_UART, GPS_UART_TX_PIN, GPS_UART_TX_PIN, UART_PIN_NO_CHANGE, |
||||||
|
UART_PIN_NO_CHANGE); |
||||||
|
ret = uart_driver_install(GPS_UART, GPS_UART_RX_BUF_SIZE, |
||||||
|
GPS_UART_TX_BUF_SIZE, GPS_UART_QUEUE_SIZE, NULL, 0); |
||||||
|
if (ret != ESP_OK) { |
||||||
|
ESP_LOGE(TAG, "uart_driver_install: %d", ret); |
||||||
|
return; |
||||||
|
} |
||||||
|
ESP_LOGI(TAG, "gps uart configured"); |
||||||
|
|
||||||
|
mutex_ = xSemaphoreCreateMutex(); |
||||||
|
info_ = GpsInfo{}; |
||||||
|
|
||||||
|
BaseType_t xRet = xTaskCreate(UART_GPS::GPS_Task, "ugv_io_gps", 2 * 1024, |
||||||
|
this, 1, &this->task_); |
||||||
|
if (xRet != pdTRUE) { |
||||||
|
ESP_LOGE(TAG, "error creating GPS task"); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UART_GPS::GetInfo(GpsInfo &info) { |
||||||
|
xSemaphoreTake(mutex_, pdMS_TO_TICKS(1000)); |
||||||
|
info = GpsInfo(info_); |
||||||
|
xSemaphoreGive(mutex_); |
||||||
|
} |
||||||
|
|
||||||
|
esp_err_t UART_GPS::WriteCommand(const char *cmd, size_t len) { |
||||||
|
esp_err_t ret; |
||||||
|
ret = uart_write_bytes(GPS_UART, cmd, len); |
||||||
|
if (ret != ESP_OK) goto err; |
||||||
|
ret = uart_write_bytes(GPS_UART, NMEA_END_CMD, sizeof(NMEA_END_CMD)); |
||||||
|
if (ret != ESP_OK) goto err; |
||||||
|
ret = uart_wait_tx_done(GPS_UART, 1000); |
||||||
|
if (ret != ESP_OK) goto err; |
||||||
|
|
||||||
|
return ESP_OK; |
||||||
|
|
||||||
|
err: |
||||||
|
const char *error_name = esp_err_to_name(ret); |
||||||
|
ESP_LOGE(TAG, "WriteCommand error: %s (%d)", error_name, ret); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
void UART_GPS::ProcessLine(const char *line, size_t len) { |
||||||
|
ESP_LOGI(TAG, "gps data: %.*s", len, line); |
||||||
|
enum minmea_sentence_id id = minmea_sentence_id(line, false); |
||||||
|
switch (id) { |
||||||
|
case MINMEA_INVALID: |
||||||
|
invalid: |
||||||
|
ESP_LOGE(TAG, "invalid nmea sentence: %s", line); |
||||||
|
return; |
||||||
|
case MINMEA_SENTENCE_RMC: { |
||||||
|
minmea_sentence_rmc rmc; |
||||||
|
bool parse_success; |
||||||
|
parse_success = minmea_parse_rmc(&rmc, line); |
||||||
|
if (!parse_success) { |
||||||
|
goto invalid; |
||||||
|
} |
||||||
|
xSemaphoreTake(mutex_, pdMS_TO_TICKS(1000)); |
||||||
|
info_.valid = rmc.valid; |
||||||
|
info_.latitude = minmea_tofloat(&rmc.latitude); |
||||||
|
info_.longitude = minmea_tofloat(&rmc.longitude); |
||||||
|
info_.speed = minmea_tofloat(&rmc.speed); |
||||||
|
info_.course = minmea_tofloat(&rmc.course); |
||||||
|
info_.last_update = xTaskGetTickCount(); |
||||||
|
xSemaphoreGive(mutex_); |
||||||
|
} |
||||||
|
case MINMEA_SENTENCE_GGA: { |
||||||
|
minmea_sentence_gga gga; |
||||||
|
bool parse_success; |
||||||
|
parse_success = minmea_parse_gga(&gga, line); |
||||||
|
if (!parse_success) { |
||||||
|
goto invalid; |
||||||
|
} |
||||||
|
xSemaphoreTake(mutex_, pdMS_TO_TICKS(1000)); |
||||||
|
info_.fix_quality = (GpsFixQual)gga.fix_quality; |
||||||
|
info_.num_satellites = gga.satellites_tracked; |
||||||
|
info_.latitude = minmea_tofloat(&gga.latitude); |
||||||
|
info_.longitude = minmea_tofloat(&gga.longitude); |
||||||
|
if (gga.altitude_units != 'M') { |
||||||
|
ESP_LOGW(TAG, "unknown altitude units: %c", gga.altitude_units); |
||||||
|
} |
||||||
|
info_.altitude = minmea_tofloat(&gga.altitude); |
||||||
|
info_.last_update = xTaskGetTickCount(); |
||||||
|
xSemaphoreGive(mutex_); |
||||||
|
} |
||||||
|
default: ESP_LOGW(TAG, "unsupported nmea sentence: %s", line); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void UART_GPS::GPS_Task(void *arg) { |
||||||
|
UART_GPS *gps = (UART_GPS *)arg; |
||||||
|
(void)gps; |
||||||
|
|
||||||
|
ESP_LOGI(TAG, "gps_task start"); |
||||||
|
char * buf1 = (char *)malloc(GPS_BUF_SIZE); |
||||||
|
char * buf2 = (char *)malloc(GPS_BUF_SIZE); |
||||||
|
char * buf_end = buf1; |
||||||
|
size_t read_bytes; |
||||||
|
|
||||||
|
esp_err_t ret; |
||||||
|
ret = gps->WriteCommand(NMEA_OUTPUT_RMCGGA, sizeof(NMEA_OUTPUT_RMCGGA)); |
||||||
|
ESP_LOGI(TAG, "sent output rmc and gga: %d", ret); |
||||||
|
ret = gps->WriteCommand(NMEA_UPDATE_1HZ, sizeof(NMEA_UPDATE_1HZ)); |
||||||
|
ESP_LOGI(TAG, "sent update 1hz: %d", ret); |
||||||
|
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100)); |
||||||
|
ret = gps->WriteCommand(NMEA_Q_RELEASE, sizeof(NMEA_Q_RELEASE)); |
||||||
|
ESP_LOGI(TAG, "sent q release: %d", ret); |
||||||
|
|
||||||
|
while (true) { |
||||||
|
size_t buf_remaining = GPS_BUF_SIZE - (buf_end - buf1); |
||||||
|
read_bytes = uart_read_bytes(GPS_UART, (uint8_t *)buf_end, buf_remaining, |
||||||
|
portMAX_DELAY); |
||||||
|
if (read_bytes <= 0) { |
||||||
|
ESP_LOGW(TAG, "GPS error: %d", read_bytes); |
||||||
|
continue; |
||||||
|
} |
||||||
|
ESP_LOGW(TAG, "GPS bytes received: %.*s", read_bytes, buf_end); |
||||||
|
buf_end += read_bytes; |
||||||
|
int lines_received = 0; |
||||||
|
for (char *c = buf1; c < buf_end; c++) { |
||||||
|
if (*c != '\n') { // wait for \n, if not found loop again
|
||||||
|
continue; |
||||||
|
} |
||||||
|
lines_received++; |
||||||
|
c++; // now c is one past the end of the whole string including \n
|
||||||
|
|
||||||
|
size_t remaining = buf_end - c; |
||||||
|
memcpy(buf2, c, remaining); // copy remaining text from buf1 to buf2
|
||||||
|
buf_end = buf2 + remaining; // update buf_end to point to the end of the
|
||||||
|
// remaining text in buf2
|
||||||
|
|
||||||
|
char * str_ptr = buf1; |
||||||
|
size_t str_len = c - buf1; |
||||||
|
str_ptr[str_len] = |
||||||
|
'\0'; // append the NULL byte (safe because old data was copied)
|
||||||
|
gps->ProcessLine(str_ptr, str_len); // process that line
|
||||||
|
|
||||||
|
std::swap(buf1, buf2); // swap buffers
|
||||||
|
c = buf1; // start over on text which was copied to buf2 (now buf1)
|
||||||
|
} |
||||||
|
if (lines_received == 0) { |
||||||
|
ESP_LOGW(TAG, "no lines received, waiting for more data"); |
||||||
|
} |
||||||
|
} |
||||||
|
free(buf1); |
||||||
|
free(buf2); |
||||||
|
vTaskDelete(NULL); |
||||||
|
} |
||||||
|
|
||||||
|
}; // namespace io
|
||||||
|
}; // namespace ugv
|
@ -0,0 +1,57 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <freertos/FreeRTOS.h> |
||||||
|
#include <freertos/semphr.h> |
||||||
|
#include <freertos/task.h> |
||||||
|
#include <stdint.h> |
||||||
|
|
||||||
|
namespace ugv { |
||||||
|
namespace io { |
||||||
|
|
||||||
|
enum GpsFixQual { |
||||||
|
GPS_FIX_INVALID = 0, // invalid gps fix
|
||||||
|
GPS_FIX_GPS = 1, // GPS fix
|
||||||
|
GPS_FIX_DGPS = 2, // differential GPS fix
|
||||||
|
GPS_FIX_PPS = 3, // PPS fix
|
||||||
|
GPS_FIX_RTK = 4, // Real Time Kinematic fix
|
||||||
|
GPS_FIX_FRTK = 5, // Float Real Time Kinematic fix
|
||||||
|
GPS_FIX_ESTIMATED = 6, // Estimated fix
|
||||||
|
GPS_FIX_MANUAL = 7, // Manual fix
|
||||||
|
GPS_FIX_SIMULATED = 8, // Simulated fix
|
||||||
|
}; |
||||||
|
|
||||||
|
struct GpsInfo { |
||||||
|
TickType_t last_update; |
||||||
|
bool valid; |
||||||
|
GpsFixQual fix_quality; |
||||||
|
uint8_t num_satellites; |
||||||
|
|
||||||
|
float latitude; // degrees +/-
|
||||||
|
float longitude; // degrees +/-
|
||||||
|
float altitude; // meters
|
||||||
|
float speed; // knots
|
||||||
|
float course; // degrees clockwise of north
|
||||||
|
}; |
||||||
|
|
||||||
|
class UART_GPS { |
||||||
|
public: |
||||||
|
explicit UART_GPS(); |
||||||
|
~UART_GPS(); |
||||||
|
|
||||||
|
void Init(); |
||||||
|
|
||||||
|
void GetInfo(GpsInfo& info); |
||||||
|
|
||||||
|
private: |
||||||
|
TaskHandle_t task_; |
||||||
|
SemaphoreHandle_t mutex_; |
||||||
|
GpsInfo info_; |
||||||
|
|
||||||
|
esp_err_t WriteCommand(const char* cmd, size_t len); |
||||||
|
void ProcessLine(const char* line, size_t len); |
||||||
|
|
||||||
|
static void GPS_Task(void* arg); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace io
|
||||||
|
} // namespace ugv
|
Loading…
Reference in new issue