用哪個ttyUSB開啟,就會直接輸出,加入下面條件,應該可以開機時,順利啟動NMEA
環境: NV Orin R36.3
avoid blocking ttyUSB2 by modemmanager
$ cat /etc/udev/rules.d/99-le910Cx.rules
KERNEL=="ttyUSB2", SUBSYSTEM=="tty", ENV{ID_VENDOR_ID}=="1bc7", ENV{ID_MODEL_ID}=="1031", ENV{ID_MM_DEVICE_IGNORE}="1"
。Setting ENV{ID_MM_DEVICE_IGNORE}="1" on a USB device or interface tells ModemManager to ignore all related ports.
。ENV{ID_MM_DEVICE_IGNORE}="1" tells ModemManager to ignore this port only.
enable_gnss.c (delay 5 secs)
#include <errno.h>
#include <fcntl.h>
#include <libudev.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#define TIMEOUT_MS 3000 // Timeout in milliseconds
#define MAX_BUFFER_SIZE 256
#define MAX_AT_RESPONSE 16
#define MAX_DEVICE_PATH 64 // Define a constant for device path size
// Structure to hold AT command and its response time
typedef struct {
char command[32];
double response_time;
} ATCommand;
// Structure to hold the AT command responses
typedef struct {
char device_path[MAX_DEVICE_PATH]; // Use the defined constant
char response[MAX_BUFFER_SIZE];
} DeviceResponse;
DeviceResponse device_responses[MAX_AT_RESPONSE];
pthread_mutex_t response_mutex = PTHREAD_MUTEX_INITIALIZER;
int response_count = 0;
// Function prototypes (for clarity)
int open_serial_port(const char *port_name);
int send_at_command(int fd, const char *command, char *response,
size_t response_size, double response_time);
void close_serial_port(int fd);
void *process_device(void *arg);
int find_tty_acm(int argc, char *argv[], int *get_count,
char dev_path[][MAX_DEVICE_PATH]); // Use the defined constant
void GetResponseData(char *input, char *output, size_t array_size,
int line_sopt);
ATCommand ATCS[] = {
{"at$GPSP=1", 0.5}, // Example response time of 300ms (converted to seconds)
{"AT$GPSNMUN=1,1,1,1,1,1,1", 0.4}, // Example response time of 400ms
};
int open_serial_port(const char *port_name) {
int fd = open(port_name, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror("open_serial_port: open failed");
return -1;
}
struct termios tty;
memset(&tty, 0, sizeof(tty));
if (tcgetattr(fd, &tty) != 0) {
perror("open_serial_port: tcgetattr failed");
close(fd);
return -1;
}
cfsetospeed(&tty, B115200); // Set baud rate (adjust as needed)
cfsetispeed(&tty, B115200);
tty.c_cflag &= ~PARENB; // No parity
tty.c_cflag &= ~CSTOPB; // 1 stop bit
tty.c_cflag &= ~CSIZE;
tty.c_cflag |= CS8; // 8 data bits
tty.c_cflag &= ~CRTSCTS; // No hardware flow control
tty.c_cflag |= CREAD | CLOCAL; // Enable read and ignore control lines
tty.c_lflag &= ~ICANON; // Disable canonical mode
tty.c_lflag &= ~ECHO; // Disable echo
tty.c_lflag &= ~ECHOE; // Disable erasure
tty.c_lflag &= ~ECHONL; // Disable new-line echo
tty.c_lflag &= ~ISIG; // Disable interpretation of interrupt, quit, and
// suspend characters
tty.c_iflag &= ~(IXON | IXOFF | IXANY); // Disable software flow control
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR |
ICRNL); // Disable special handling of received bytes
tty.c_oflag &= ~OPOST; // Prevent special interpretation of output bytes
tty.c_oflag &=
~ONLCR; // Prevent conversion of newline to carriage return/line feed
tty.c_cc[VTIME] = TIMEOUT_MS / 100; // Timeout in deciseconds (correct)
tty.c_cc[VMIN] = 0; // Minimum number of characters to receive
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
perror("open_serial_port: tcsetattr failed");
close(fd);
return -1;
}
return fd;
}
int send_at_command(int fd, const char *command, char *response,
size_t response_size, double response_time) {
tcflush(fd, TCIFLUSH); // Flush the input buffer
ssize_t bytes_written = write(fd, command, strlen(command));
if (bytes_written < 0) {
perror("send_at_command: write failed");
return -1;
}
memset(response, 0, response_size);
size_t total_bytes_read = 0;
struct timeval start_time, current_time;
gettimeofday(&start_time, NULL); // Use gettimeofday for better precision
while (total_bytes_read < response_size - 1) {
ssize_t bytes_read = read(fd, response + total_bytes_read,
response_size - 1 - total_bytes_read);
if (bytes_read < 0) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
gettimeofday(¤t_time, NULL);
double elapsed_time =
(current_time.tv_sec - start_time.tv_sec) +
(current_time.tv_usec - start_time.tv_usec) / 1000000.0;
if (elapsed_time > response_time) {
tcflush(fd, TCIFLUSH); // clear the buffer
fprintf(stderr, "send_at_command: Timeout occurred\n");
return -1; // Timeout occurred
}
usleep(10000); // small delay before retry (10ms)
continue; // retry read
} else {
perror("send_at_command: read failed");
return -1;
}
}
if (bytes_read == 0) {
// End of file or no more data
break;
}
total_bytes_read += bytes_read;
if (strstr(response, "OK\r\n") != NULL) {
break; // Stop reading when OK is received.
}
}
response[total_bytes_read] = '\0'; // Null-terminate the string
return 0;
}
void close_serial_port(int fd) { close(fd); }
void *process_device(void *arg) {
int serial_fd;
char *dev_path = (char *)arg;
char response[MAX_BUFFER_SIZE];
int at_command_count =
sizeof(ATCS) / sizeof(ATCS[0]); // Calculate number of AT commands
printf("Thread processing device: %s\n", dev_path);
serial_fd = open_serial_port(dev_path);
if (serial_fd == -1) {
fprintf(stderr, "Error opening serial port: %s\n", strerror(errno));
return NULL;
}
// Send AT commands
for (int i = 0; i < at_command_count; i++) {
char command[128]; // Ensure enough space for the command
snprintf(command, sizeof(command) - 1, "%s\r",
ATCS[i].command); // Append \r
if (send_at_command(serial_fd, command, response, sizeof(response),
ATCS[i].response_time) == 0) {
printf("Response to '%s': %s\n", ATCS[i].command, response);
pthread_mutex_lock(&response_mutex);
if (response_count < MAX_AT_RESPONSE) {
strncpy(device_responses[response_count].device_path, dev_path,
MAX_DEVICE_PATH - 1);
device_responses[response_count].device_path[MAX_DEVICE_PATH - 1] =
'\0'; // Ensure null termination
strncpy(device_responses[response_count].response, response,
MAX_BUFFER_SIZE - 1);
device_responses[response_count].response[MAX_BUFFER_SIZE - 1] =
'\0'; // Ensure null termination
response_count++; // Increment response count
} else {
fprintf(stderr, "Response buffer is full!\n");
}
pthread_mutex_unlock(&response_mutex);
} else {
fprintf(stderr, "Error sending command '%s'\n", ATCS[i].command);
}
}
close_serial_port(serial_fd);
pthread_exit(NULL);
}
int find_tty_acm(int argc, char *argv[], int *get_count,
char dev_path[][MAX_DEVICE_PATH]) {
if (argc != 4) {
fprintf(stderr, "Usage: %s \n", argv[0]); // Corrected Usage message
return 1;
}
const char *target_vid = argv[1];
const char *target_pid = argv[2];
const char *target_bInterfaceNumber = argv[3];
struct udev *udev = udev_new();
if (!udev) {
fprintf(stderr, "Failed to create udev object\n");
return 1;
}
struct udev_enumerate *enumerate = udev_enumerate_new(udev);
udev_enumerate_add_match_subsystem(enumerate, "tty");
udev_enumerate_add_match_property(enumerate, "ID_VENDOR_ID", target_vid);
udev_enumerate_add_match_property(enumerate, "ID_MODEL_ID", target_pid);
udev_enumerate_scan_devices(enumerate);
struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate);
struct udev_list_entry *entry;
int i = 0;
udev_list_entry_foreach(entry, devices) {
const char *path = udev_list_entry_get_name(entry);
struct udev_device *dev = udev_device_new_from_syspath(udev, path);
if (!dev) {
fprintf(stderr, "Error creating udev_device from syspath: %s\n", path);
continue; // Skip to the next device
}
const char *devnode = udev_device_get_devnode(dev);
const char *bInterface =
udev_device_get_property_value(dev, "ID_USB_INTERFACE_NUM");
if (devnode && bInterface &&
strcmp(bInterface, target_bInterfaceNumber) == 0) {
if (i < MAX_AT_RESPONSE) { // Check for overflow
strncpy(dev_path[i], devnode, MAX_DEVICE_PATH - 1); // Use strncpy
dev_path[i][MAX_DEVICE_PATH - 1] = '\0'; // Null-terminate
printf("Found device: %s\n", devnode);
i++;
(*get_count)++;
} else {
fprintf(stderr, "Too many devices found. Increase MAX_AT_RESPONSE.\n");
break;
}
}
udev_device_unref(dev);
}
udev_enumerate_unref(enumerate); // Free enumerate object
udev_unref(udev);
return 0;
}
void GetResponseData(char *input, char *output, size_t array_size,
int line_sopt) {
char *line, *saveptr;
int stop = 0;
output[0] = '\0'; // Initialize output string
line = strtok_r(input, "\n", &saveptr);
while (line != NULL) {
if (stop == line_sopt) {
strncpy(output, line, array_size - 1); // Use strncpy for safety
output[array_size - 1] = '\0'; // Ensure null termination
return;
}
line = strtok_r(NULL, "\n", &saveptr);
stop++;
}
output[0] = '\0'; // If line_sopt is out of range, return an empty string
}
int main(int argc, char *argv[]) {
int device_count = 0;
char dev_path[MAX_AT_RESPONSE]
[MAX_DEVICE_PATH]; // Array to store device paths
pthread_t threads[MAX_AT_RESPONSE]; // Array to store thread IDs
int thread_creation_result;
sleep(5); //waiting for systemctl...le910cx.service and Modemanager
if (find_tty_acm(argc, argv, &device_count, dev_path) != 0) {
fprintf(stderr, "find_tty_acm failed\n");
exit(EXIT_FAILURE);
}
printf("Loop count: %d\n", device_count);
if (device_count == 0) {
printf("No devices found. Exiting.\n");
exit(EXIT_SUCCESS);
}
// Create threads for each device found
for (int i = 0; i < device_count; i++) {
printf("Creating thread for device: %s\n", dev_path[i]);
thread_creation_result =
pthread_create(&threads[i], NULL, process_device, (void *)dev_path[i]);
if (thread_creation_result != 0) {
fprintf(stderr, "ERROR; return code from pthread_create() is %d\n",
thread_creation_result);
exit(EXIT_FAILURE);
}
}
// Wait for all threads to complete
for (int i = 0; i < device_count; i++) {
pthread_join(threads[i], NULL);
}
exit(EXIT_SUCCESS);
}
compile
$ gcc enable_gnss.c -o enable_gnss -Wall -Wextra -pthread -ludev
$ ./enable_gnss 1bc7 1031 02
$ cat /etc/systemd/system/le910cx.service
[Unit]
Description=enGNSS_NMEA_LE910Cx
After=ModemManager.service
After=network.target
[Service]
ExecStart=/home/nvidia/gnss/enable_gnss 1bc7 1031 02
Type=oneshot
RemainAfterExit=yes
User=root
Group=root
[Install]
WantedBy=multi-user.target
reboot and check it
//$ sudo systemctl daemon-reload
reboot
$ sudo systemctl start le910cx.service
$ sudo systemctl status le910cx.service
● le910cx.service - enGNSS_NMEA_LE910Cx
Loaded: loaded (/etc/systemd/system/le910cx.service; disabled; vendor pres>
Active: active (exited) since Tue 2025-05-20 08:30:16 UTC; 8s ago
Process: 2228 ExecStart=/home/nvidia/gnss/enable_gnss 1bc7 1031 02 (code=ex>
Main PID: 2228 (code=exited, status=0/SUCCESS)
CPU: 23ms
May 20 08:30:11 tegra-ubuntu systemd[1]: Starting enGNSS_NMEA_LE910Cx...
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Found device: /dev/ttyUSB2
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Loop count: 1
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Creating thread for device: /de>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Thread processing device: /dev/>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Response to 'at$GPSP=1': at$GPS>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: OK
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: Response to 'AT$GPSNMUN=1,1,1,1>
May 20 08:30:16 tegra-ubuntu enable_gnss[2228]: OK
May 20 08:30:16 tegra-ubuntu systemd[1]: Finished enGNSS_NMEA_LE910Cx.
開機啟動
$ sudo systemctl enable le910cx.service
ref:
1. ModemManager-filter
沒有留言:
張貼留言