mirror of
https://github.com/iNavFlight/inav.git
synced 2025-07-12 19:10:27 +03:00
6.1.0 sitl interop (#8913)
* fix getopts 'has_arg' usage (iaw getopt_long(3)) * Rexec the SITL on reboot * Allow hostnames, facilitate compilation on non-GNU OS (e.g. *BSD), add IPV6 [xplane.c] * add required interop header files [simple_soap_client.c] * add required interop header files [simple_soap_client.h] * update serial_tcp (headers, IPv6, lookup etc) * conditional for pthread_attr_getschedpolicy availability * fix error in xplane socket familiy * remove unnecessary added headers [xplane.c] * fix gcc 12 warning is osd.c * update docs * fix for older gcc without closefrom(3) * add AI_V4MAPPED|AI_ADDRCONFIG to ai_flags (to support V4 only hosts)
This commit is contained in:
parent
4fa7e508ed
commit
064a809ad2
9 changed files with 343 additions and 133 deletions
|
@ -27,12 +27,13 @@ The following sensors are emulated:
|
|||
|
||||

|
||||
|
||||
Select "FAKE" as type for all mentioned, so that they receive the data from the simulator.
|
||||
Select "FAKE" as type for all mentioned, so that they receive the data from the simulator.
|
||||
|
||||
## Serial ports+
|
||||
UARTs are replaced by TCP starting with port 5760 ascending. UART 1 port 5760, UART2 6761, ...
|
||||
By default, UART1 and UART2 are available as MSP connections.
|
||||
By default, UART1 and UART2 are available as MSP connections.
|
||||
To connect the Configurator to SITL: Select TCP and connect to ```127.0.0.1:5760``` (if SITL is running on the same machine).
|
||||
IPv4 and IPv6 are supported, either raw addresses of hostname lookup.
|
||||
|
||||
The assignment and status of user UART/TCP connections is displayed on the console.
|
||||
|
||||
|
@ -41,11 +42,11 @@ The assignment and status of user UART/TCP connections is displayed on the conso
|
|||
All other interfaces (I2C, SPI, etc.) are not emulated.
|
||||
|
||||
## Remote control
|
||||
Joystick (via simulator) or serial receiver via USB/Serial interface are supported.
|
||||
Joystick (via simulator) or serial receiver via USB/Serial interface are supported.
|
||||
|
||||
### Joystick interface
|
||||
Only 8 channels are supported.
|
||||
Select "SIM (SITL)" as the receiver and set up a joystick in the simulator, details of which can be found in the documentation for the individual simulators.
|
||||
Only 8 channels are supported.
|
||||
Select "SIM (SITL)" as the receiver and set up a joystick in the simulator, details of which can be found in the documentation for the individual simulators.
|
||||
|
||||
### Serial Receiver via USB
|
||||
Connect a serial receiver (e.g. SBUS) to the PC via a UART/USB adapter. Configure the receiver in the Configurator as usual.
|
||||
|
@ -61,8 +62,8 @@ For this you need a FT232 module. With FT-Prog (https://ftdichip.com/utilities/)
|
|||
|
||||

|
||||
|
||||
For SBUS, the command line arguments of the python script are:
|
||||
```python tcp_serial_redirect.py --parity E --stopbits 2 -c 127.0.0.1:[INAV-UART-PORT] COMXX 100000```
|
||||
For SBUS, the command line arguments of the python script are:
|
||||
```python tcp_serial_redirect.py --parity E --stopbits 2 -c 127.0.0.1:[INAV-UART-PORT] COMXX 100000```
|
||||
|
||||
Note: Telemetry via return channel through the receiver is not supported by SITL (yet).
|
||||
|
||||
|
@ -70,15 +71,15 @@ Note: Telemetry via return channel through the receiver is not supported by SITL
|
|||
For the OSD the program INAV-Sim-OSD is available: https://github.com/Scavanger/INAV-SIM-OSD.
|
||||
For this, activate MSP-Displayport on a UART/TCP port and connect to the corresponding port.
|
||||
|
||||
Note: INAV-Sim-OSD only works if the simulator is in window mode.
|
||||
Note: INAV-Sim-OSD only works if the simulator is in window mode.
|
||||
|
||||
## Command line
|
||||
The command line options are only necessary if the SITL executable is started by hand, e.g. when debugging.
|
||||
For normal use, please use the SITL tab in the configurator.
|
||||
For normal use, please use the SITL tab in the configurator.
|
||||
|
||||
The following SITL specific command line options are available:
|
||||
|
||||
If SITL is started without command line options, only the Configurator can be used.
|
||||
If SITL is started without command line options, only a serial MSP / CLI connection can be used (e.g. Configurator or other application) can be used.
|
||||
|
||||
```--path``` Full path and file name to config file, if not present, eeprom.bin in the current directory is used. Example: ```C:\INAV_SITL\flying-wing.bin```
|
||||
|
||||
|
@ -108,9 +109,9 @@ It is recommended to start the tools in the following order:
|
|||
|
||||
## Compile
|
||||
|
||||
### Linux:
|
||||
### Linux and FreeBSD:
|
||||
Almost like normal, ruby, cmake and make are also required.
|
||||
With cmake, the option "-DSITL=ON" must be specified.
|
||||
With cmake, the option "-DSITL=ON" must be specified.
|
||||
|
||||
```
|
||||
mkdir build_SITL
|
||||
|
@ -120,5 +121,26 @@ make
|
|||
```
|
||||
|
||||
### Windows:
|
||||
Compile under cygwin, then as in Linux.
|
||||
Compile under cygwin, then as in Linux.
|
||||
Copy cygwin1.dll into the directory, or include cygwin's /bin/ directory in the environment variable PATH.
|
||||
|
||||
#### Build manager
|
||||
|
||||
`ninja` may also be used (parallel builds without `-j $(nproc)`):
|
||||
|
||||
```
|
||||
cmake -GNinja -DSITL=ON ..
|
||||
ninja
|
||||
```
|
||||
|
||||
### Compiler requirements
|
||||
|
||||
* Modern GCC. Must be a *real* GCC, macOS faking it with clang will not work.
|
||||
* Unix sockets networking. Cygwin is required on Windows (vice `winsock`).
|
||||
* Pthreads
|
||||
|
||||
## Supported environments
|
||||
|
||||
* Linux on x86_64, Aarch64 (e.g. Rpi4), RISC-V (e.g. VisionFive2)
|
||||
* Windows on x86_64
|
||||
* FreeBSD (x86_64 at least).
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# X-Plane
|
||||
# X-Plane
|
||||
|
||||
Tested on X-Plane 11, 12 should(!) work but not tested.
|
||||
|
||||
|
@ -20,21 +20,25 @@ In the settings, calibrate the joystick, set it up and assign the axes as follow
|
|||
| Roll | Roll |
|
||||
| Pitch | Pitch |
|
||||
| Throttle | Cowl Flap 1 |
|
||||
| Yaw | Yaw |
|
||||
| Channel 5 | Cowl Flap 2 |
|
||||
| Channel 6 | Cowl Flap 3 |
|
||||
| Channel 7 | Cowl Flap 4 |
|
||||
| Channel 8 | Cowl Flap 5 |
|
||||
| Yaw | Yaw |
|
||||
| Channel 5 | Cowl Flap 2 |
|
||||
| Channel 6 | Cowl Flap 3 |
|
||||
| Channel 7 | Cowl Flap 4 |
|
||||
| Channel 8 | Cowl Flap 5 |
|
||||
|
||||
Reverse axis in X-Plane if necessary.
|
||||
|
||||
## Channelmap:
|
||||
The assignment of the "virtual receiver" is fixed:
|
||||
The assignment of the "virtual receiver" is fixed:
|
||||
1 - Throttle
|
||||
2 - Roll
|
||||
3 - Pitch
|
||||
4 - Yaw
|
||||
|
||||
The internal mixer (e.g. for flying wings) cannot be deactivated without further ado, therefore always select "Aircraft with tail" in INAV.
|
||||
The internal mixer (e.g. for flying wings) cannot be deactivated without further ado, therefore always select "Aircraft with tail" in INAV.
|
||||
For the standard Aircraft preset the channelmap is:
|
||||
```--chanmap=M01-01,S01-03,S03-02,S04-04```
|
||||
|
||||
## Other applications
|
||||
|
||||
[fl2sitl](https://github.com/stronnag/bbl2kml/wiki/fl2sitl) is an open source application to replay an INAV Blackbox log through the INAV SITL via `blackbox_decode`. The output may be visualised in any MSP capable application, such as the INAV Configurator or [mwp](https://github.com/stronnag/mwptools). fl2sitl uses the X-plane protocol.
|
||||
|
|
|
@ -33,6 +33,10 @@
|
|||
|
||||
#include <sys/socket.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "common/utils.h"
|
||||
|
||||
|
@ -43,6 +47,68 @@ static const struct serialPortVTable tcpVTable[];
|
|||
static tcpPort_t tcpPorts[SERIAL_PORT_COUNT];
|
||||
static bool tcpThreadRunning = false;
|
||||
|
||||
static int lookup_address (char *name, int port, int type, struct sockaddr *addr, socklen_t* len )
|
||||
{
|
||||
struct addrinfo *servinfo, *p;
|
||||
struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = type, .ai_flags = AI_V4MAPPED|AI_ADDRCONFIG};
|
||||
if (name == NULL) {
|
||||
hints.ai_flags |= AI_PASSIVE;
|
||||
}
|
||||
/*
|
||||
This nonsense is to uniformly deliver the same sa_family regardless of whether
|
||||
name is NULL or non-NULL
|
||||
Otherwise, at least on Linux, we get
|
||||
- V6,V4 for the non-null case and
|
||||
- V4,V6 for the null case, regardless of gai.conf
|
||||
Which may confuse consumers.
|
||||
FreeBSD and Windows behave consistently, giving V6 for Ipv6 enabled stacks in all cases
|
||||
unless a quad dotted address is specificied
|
||||
*/
|
||||
struct addrinfo *p4 = NULL;
|
||||
struct addrinfo *p6 = NULL;
|
||||
int result;
|
||||
char aport[16];
|
||||
snprintf(aport, sizeof(aport), "%d", port);
|
||||
if ((result = getaddrinfo(name, aport, &hints, &servinfo)) != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result));
|
||||
return result;
|
||||
} else {
|
||||
int j = 0;
|
||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if(p->ai_family == AF_INET6)
|
||||
p6 = p;
|
||||
else if(p->ai_family == AF_INET)
|
||||
p4 = p;
|
||||
j++;
|
||||
}
|
||||
if (p6 != NULL)
|
||||
p = p6;
|
||||
else if (p4 != NULL)
|
||||
p = p4;
|
||||
else
|
||||
return -1;
|
||||
memcpy(addr, p->ai_addr, p->ai_addrlen);
|
||||
*len = p->ai_addrlen;
|
||||
freeaddrinfo(servinfo);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *tcpGetAddressString(struct sockaddr *addr)
|
||||
{
|
||||
static char straddr[INET6_ADDRSTRLEN];
|
||||
void *ipaddr;
|
||||
if (addr->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 * ip = (struct sockaddr_in6*)addr;
|
||||
ipaddr = &ip->sin6_addr;
|
||||
} else {
|
||||
struct sockaddr_in * ip = (struct sockaddr_in*)addr;
|
||||
ipaddr = &ip->sin_addr;
|
||||
}
|
||||
const char *res = inet_ntop(addr->sa_family, ipaddr, straddr, sizeof straddr);
|
||||
return (char *)res;
|
||||
}
|
||||
|
||||
static void *tcpReceiveThread(void* arg)
|
||||
{
|
||||
tcpPort_t *port = (tcpPort_t*)arg;
|
||||
|
@ -50,7 +116,7 @@ static void *tcpReceiveThread(void* arg)
|
|||
while(tcpThreadRunning) {
|
||||
if (tcpReceive(port) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
@ -58,6 +124,7 @@ static void *tcpReceiveThread(void* arg)
|
|||
|
||||
static tcpPort_t *tcpReConfigure(tcpPort_t *port, uint32_t id)
|
||||
{
|
||||
socklen_t sockaddrlen;
|
||||
if (port->isInitalized){
|
||||
return port;
|
||||
}
|
||||
|
@ -66,14 +133,29 @@ static tcpPort_t *tcpReConfigure(tcpPort_t *port, uint32_t id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
port->socketFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
uint16_t tcpPort = BASE_IP_ADDRESS + id - 1;
|
||||
if (lookup_address(NULL, tcpPort, SOCK_STREAM, (struct sockaddr*)&port->sockAddress, &sockaddrlen) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
port->socketFd = socket(((struct sockaddr*)&port->sockAddress)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
if (port->socketFd < 0) {
|
||||
fprintf(stderr, "[SOCKET] Unable to create tcp socket\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int one = 1;
|
||||
int err = 0;
|
||||
#ifdef __CYGWIN__
|
||||
// Sadly necesary to enforce dual-stack behaviour on Windows networking ,,,
|
||||
if (((struct sockaddr*)&port->sockAddress)->sa_family == AF_INET6) {
|
||||
int v6only=0;
|
||||
err = setsockopt(port->socketFd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only));
|
||||
if (err != 0) {
|
||||
fprintf(stderr,"[SOCKET] setting V6ONLY=false: %s\n", strerror(errno));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int one = 1;
|
||||
err = setsockopt(port->socketFd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
|
||||
err = fcntl(port->socketFd, F_SETFL, fcntl(port->socketFd, F_GETFL, 0) | O_NONBLOCK);
|
||||
|
||||
|
@ -82,14 +164,10 @@ static tcpPort_t *tcpReConfigure(tcpPort_t *port, uint32_t id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t tcpPort = BASE_IP_ADDRESS + id - 1;
|
||||
port->sockAddress.sin_family = AF_INET;
|
||||
port->sockAddress.sin_port = htons(tcpPort);
|
||||
port->sockAddress.sin_addr.s_addr = INADDR_ANY;
|
||||
port->isClientConnected = false;
|
||||
port->id = id;
|
||||
|
||||
if (bind(port->socketFd, (struct sockaddr*)&port->sockAddress, sizeof(port->sockAddress)) < 0) {
|
||||
|
||||
if (bind(port->socketFd, (struct sockaddr*)&port->sockAddress, sockaddrlen) < 0) {
|
||||
fprintf(stderr, "[SOCKET] Unable to bind socket\n");
|
||||
return NULL;
|
||||
}
|
||||
|
@ -99,57 +177,53 @@ static tcpPort_t *tcpReConfigure(tcpPort_t *port, uint32_t id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[SOCKET] Bind TCP port %d to UART%d\n", tcpPort, id);
|
||||
fprintf(stderr, "[SOCKET] Bind TCP %s port %d to UART%d\n",
|
||||
tcpGetAddressString((struct sockaddr*)&port->sockAddress), tcpPort, id);
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static char *tcpGetAddressString(struct sockaddr *addr)
|
||||
{
|
||||
return inet_ntoa(((struct sockaddr_in *)addr)->sin_addr);
|
||||
return port;
|
||||
}
|
||||
|
||||
int tcpReceive(tcpPort_t *port)
|
||||
{
|
||||
{
|
||||
if (!port->isClientConnected) {
|
||||
|
||||
|
||||
fd_set fds;
|
||||
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(port->socketFd, &fds);
|
||||
|
||||
|
||||
if (select(port->socketFd + 1, &fds, NULL, NULL, NULL) < 0) {
|
||||
fprintf(stderr, "[SOCKET] Unable to wait for connection.\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
socklen_t addrLen = sizeof(port->sockAddress);
|
||||
port->clientSocketFd = accept(port->socketFd, &port->clientAddress, &addrLen);
|
||||
socklen_t addrLen = sizeof(struct sockaddr_storage);
|
||||
port->clientSocketFd = accept(port->socketFd,(struct sockaddr*)&port->clientAddress, &addrLen);
|
||||
if (port->clientSocketFd < 1) {
|
||||
fprintf(stderr, "[SOCKET] Can't accept connection.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[SOCKET] %s connected to UART%d\n", tcpGetAddressString(&port->clientAddress), port->id);
|
||||
fprintf(stderr, "[SOCKET] %s connected to UART%d\n", tcpGetAddressString((struct sockaddr *)&port->clientAddress), port->id);
|
||||
port->isClientConnected = true;
|
||||
}
|
||||
|
||||
|
||||
uint8_t buffer[TCP_BUFFER_SIZE];
|
||||
ssize_t recvSize = recv(port->clientSocketFd, buffer, TCP_BUFFER_SIZE, 0);
|
||||
|
||||
// Disconnect
|
||||
if (port->isClientConnected && recvSize == 0)
|
||||
{
|
||||
fprintf(stderr, "[SOCKET] %s disconnected from UART%d\n", tcpGetAddressString(&port->clientAddress), port->id);
|
||||
fprintf(stderr, "[SOCKET] %s disconnected from UART%d\n", tcpGetAddressString((struct sockaddr *)&port->clientAddress), port->id);
|
||||
close(port->clientSocketFd);
|
||||
memset(&port->clientAddress, 0, sizeof(port->clientAddress));
|
||||
port->isClientConnected = false;
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (ssize_t i = 0; i < recvSize; i++) {
|
||||
|
||||
|
||||
if (port->serialPort.rxCallback) {
|
||||
port->serialPort.rxCallback((uint16_t)buffer[i], port->serialPort.rxCallbackData);
|
||||
} else {
|
||||
|
@ -159,7 +233,7 @@ int tcpReceive(tcpPort_t *port)
|
|||
pthread_mutex_unlock(&port->receiveMutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (recvSize < 0) {
|
||||
recvSize = 0;
|
||||
}
|
||||
|
@ -199,7 +273,7 @@ serialPort_t *tcpOpen(USART_TypeDef *USARTx, serialReceiveCallbackPtr callback,
|
|||
return NULL;
|
||||
}
|
||||
tcpThreadRunning = true;
|
||||
|
||||
|
||||
return (serialPort_t*)port;
|
||||
}
|
||||
|
||||
|
@ -254,7 +328,7 @@ uint32_t tcpTotalRxBytesWaiting(const serialPort_t *instance)
|
|||
uint32_t tcpTotalTxBytesFree(const serialPort_t *instance)
|
||||
{
|
||||
tcpPort_t *port = (tcpPort_t*)instance;
|
||||
|
||||
|
||||
if (port->isClientConnected) {
|
||||
return TCP_MAX_PACKET_SIZE;
|
||||
} else {
|
||||
|
|
|
@ -22,12 +22,15 @@
|
|||
|
||||
#include <pthread.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define BASE_IP_ADDRESS 5760
|
||||
#define TCP_BUFFER_SIZE 2048
|
||||
#define TCP_MAX_PACKET_SIZE 65535
|
||||
|
||||
typedef struct
|
||||
typedef struct
|
||||
{
|
||||
serialPort_t serialPort;
|
||||
|
||||
|
@ -39,8 +42,8 @@ typedef struct
|
|||
pthread_t receiveThread;
|
||||
int socketFd;
|
||||
int clientSocketFd;
|
||||
struct sockaddr_in sockAddress;
|
||||
struct sockaddr clientAddress;
|
||||
struct sockaddr_storage sockAddress;
|
||||
struct sockaddr_storage clientAddress;
|
||||
bool isClientConnected;
|
||||
} tcpPort_t;
|
||||
|
||||
|
|
|
@ -429,7 +429,7 @@ static void osdFormatWindSpeedStr(char *buff, int32_t ws, bool isValid)
|
|||
}
|
||||
|
||||
osdFormatCentiNumber(buff, centivalue, 0, 2, 0, 3);
|
||||
|
||||
|
||||
if (!isValid && ((millis() / 1000) % 4 < 2))
|
||||
suffix = '*';
|
||||
|
||||
|
@ -444,7 +444,7 @@ static void osdFormatWindSpeedStr(char *buff, int32_t ws, bool isValid)
|
|||
*/
|
||||
void osdSimpleAltitudeSymbol(char *buff, int32_t alt) {
|
||||
|
||||
int32_t convertedAltutude;
|
||||
int32_t convertedAltutude = 0;
|
||||
char suffix = '\0';
|
||||
|
||||
switch ((osd_unit_e)osdConfig()->units) {
|
||||
|
@ -2471,7 +2471,7 @@ static bool osdDrawSingleElement(uint8_t item)
|
|||
displayWriteCharWithAttr(osdDisplayPort, elemPosX, elemPosY, SYM_SERVO_PAN_IS_OFFSET_L, elemAttr);
|
||||
} else {
|
||||
panServoTimeOffCentre = 0;
|
||||
|
||||
|
||||
if (osdConfig()->pan_servo_indicator_show_degrees) {
|
||||
tfp_sprintf(buff, "%3d%c", panOffset, SYM_DEGREES);
|
||||
displayWriteWithAttr(osdDisplayPort, elemPosX+1, elemPosY, buff, elemAttr);
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
# include <netinet/in.h>
|
||||
# include <netdb.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/select.h>
|
||||
|
||||
|
@ -50,7 +52,7 @@ bool soapClientConnect(soap_client_t *client, const char *address, int port)
|
|||
if (setsockopt(client->sockedFd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
client->socketAddr.sin_family = AF_INET;
|
||||
client->socketAddr.sin_port = htons(port);
|
||||
client->socketAddr.sin_addr.s_addr = inet_addr(address);
|
||||
|
@ -79,7 +81,7 @@ void soapClientSendRequestVa(soap_client_t *client, const char* action, const ch
|
|||
return;
|
||||
}
|
||||
|
||||
char* requestBody;
|
||||
char* requestBody;
|
||||
if (vasprintf(&requestBody, fmt, va) < 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -89,16 +91,16 @@ void soapClientSendRequestVa(soap_client_t *client, const char* action, const ch
|
|||
action, (unsigned)strlen(requestBody), requestBody) < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
send(client->sockedFd, request, strlen(request), 0);
|
||||
}
|
||||
|
||||
void soapClientSendRequest(soap_client_t *client, const char* action, const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
|
||||
|
||||
va_start(va, fmt);
|
||||
soapClientSendRequestVa(client, action, fmt, va);
|
||||
soapClientSendRequestVa(client, action, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
@ -159,10 +161,9 @@ char* soapClientReceive(soap_client_t *client)
|
|||
if (size2 <= 0) {
|
||||
return NULL;
|
||||
}
|
||||
size += size2;
|
||||
size += size2;
|
||||
}
|
||||
|
||||
recBuffer[size] = '\0';
|
||||
return strdup(body);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#define SOAP_REC_BUF_SIZE 256 * 1024
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
#include <stdarg.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
|
@ -63,13 +66,14 @@
|
|||
static uint8_t pwmMapping[XP_MAX_PWM_OUTS];
|
||||
static uint8_t mappingCount;
|
||||
|
||||
static struct sockaddr_in serverAddr;
|
||||
static struct sockaddr_storage serverAddr;
|
||||
static socklen_t serverAddrLen;
|
||||
static int sockFd;
|
||||
static pthread_t listenThread;
|
||||
static bool initalized = false;
|
||||
static bool useImu = false;
|
||||
|
||||
static float lattitude = 0;
|
||||
static float lattitude = 0;
|
||||
static float longitude = 0;
|
||||
static float elevation = 0;
|
||||
static float agl = 0;
|
||||
|
@ -92,7 +96,7 @@ static float barometer = 0;
|
|||
static bool hasJoystick = false;
|
||||
static float joystickRaw[XPLANE_JOYSTICK_AXIS_COUNT];
|
||||
|
||||
typedef enum
|
||||
typedef enum
|
||||
{
|
||||
DREF_LATITUDE,
|
||||
DREF_LONGITUDE,
|
||||
|
@ -126,12 +130,12 @@ typedef enum
|
|||
DREF_JOYSTICK_VALUES_CH8,
|
||||
} dref_t;
|
||||
|
||||
uint32_t xint2uint32 (uint8_t * buf)
|
||||
uint32_t xint2uint32 (uint8_t * buf)
|
||||
{
|
||||
return buf[3] << 24 | buf [2] << 16 | buf [1] << 8 | buf [0];
|
||||
}
|
||||
|
||||
float xflt2float (uint8_t * buf)
|
||||
float xflt2float (uint8_t * buf)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
|
@ -152,18 +156,18 @@ static void registerDref(dref_t id, char* dref, uint32_t freq)
|
|||
memcpy(buf + 9, &id, 4);
|
||||
memcpy(buf + 13, dref, strlen(dref) + 1);
|
||||
|
||||
sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
|
||||
sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, serverAddrLen);
|
||||
}
|
||||
|
||||
static void sendDref(char* dref, float value)
|
||||
{
|
||||
char buf[509];
|
||||
strcpy(buf, "DREF");
|
||||
memcpy(buf + 5, &value, 4);
|
||||
memset(buf + 9, ' ', sizeof(buf) - 9);
|
||||
strcpy(buf + 9, dref);
|
||||
memcpy(buf + 5, &value, 4);
|
||||
memset(buf + 9, ' ', sizeof(buf) - 9);
|
||||
strcpy(buf + 9, dref);
|
||||
|
||||
sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
|
||||
sendto(sockFd, (void*)buf, sizeof(buf), 0, (struct sockaddr*)&serverAddr, serverAddrLen);
|
||||
}
|
||||
|
||||
static void* listenWorker(void* arg)
|
||||
|
@ -171,13 +175,13 @@ static void* listenWorker(void* arg)
|
|||
UNUSED(arg);
|
||||
|
||||
uint8_t buf[1024];
|
||||
struct sockaddr remoteAddr;
|
||||
struct sockaddr_storage remoteAddr;
|
||||
socklen_t slen = sizeof(remoteAddr);
|
||||
int recvLen;
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
|
||||
float motorValue = 0;
|
||||
float yokeValues[3] = { 0 };
|
||||
int y = 0;
|
||||
|
@ -189,10 +193,10 @@ static void* listenWorker(void* arg)
|
|||
motorValue = PWM_TO_FLOAT_0_1(motor[pwmMapping[i] & 0x7f]);
|
||||
} else {
|
||||
yokeValues[y] = PWM_TO_FLOAT_MINUS_1_1(servo[pwmMapping[i]]);
|
||||
y++;
|
||||
y++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sendDref("sim/operation/override/override_joystick", 1);
|
||||
sendDref("sim/cockpit2/engine/actuators/throttle_ratio_all", motorValue);
|
||||
sendDref("sim/joystick/yoke_roll_ratio", yokeValues[0]);
|
||||
|
@ -203,11 +207,11 @@ static void* listenWorker(void* arg)
|
|||
sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[2]", 0);
|
||||
sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[3]", 0);
|
||||
sendDref("sim/cockpit2/engine/actuators/cowl_flap_ratio[4]", 0);
|
||||
|
||||
|
||||
recvLen = recvfrom(sockFd, buf, sizeof(buf), 0, (struct sockaddr*)&remoteAddr, &slen);
|
||||
if (recvLen < 0 && errno != EWOULDBLOCK) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (strncmp((char*)buf, "RREF", 4) != 0) {
|
||||
continue;
|
||||
|
@ -216,7 +220,7 @@ static void* listenWorker(void* arg)
|
|||
for (int i = 5; i < recvLen; i += 8) {
|
||||
dref_t dref = (dref_t)xint2uint32(&buf[i]);
|
||||
float value = xflt2float(&(buf[i + 4]));
|
||||
|
||||
|
||||
switch (dref)
|
||||
{
|
||||
case DREF_LATITUDE:
|
||||
|
@ -230,7 +234,7 @@ static void* listenWorker(void* arg)
|
|||
case DREF_ELEVATION:
|
||||
elevation = value;
|
||||
break;
|
||||
|
||||
|
||||
case DREF_AGL:
|
||||
agl = value;
|
||||
break;
|
||||
|
@ -274,7 +278,7 @@ static void* listenWorker(void* arg)
|
|||
case DREF_FORCE_G_AXI1:
|
||||
accel_x = value;
|
||||
break;
|
||||
|
||||
|
||||
case DREF_FORCE_G_SIDE:
|
||||
accel_y = value;
|
||||
break;
|
||||
|
@ -290,7 +294,7 @@ static void* listenWorker(void* arg)
|
|||
case DREF_POS_Q:
|
||||
gyro_y = value;
|
||||
break;
|
||||
|
||||
|
||||
case DREF_POS_R:
|
||||
gyro_z = value;
|
||||
break;
|
||||
|
@ -302,36 +306,36 @@ static void* listenWorker(void* arg)
|
|||
case DREF_HAS_JOYSTICK:
|
||||
hasJoystick = value >= 1 ? true : false;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_ROll:
|
||||
|
||||
case DREF_JOYSTICK_VALUES_ROll:
|
||||
joystickRaw[0] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_PITCH:
|
||||
case DREF_JOYSTICK_VALUES_PITCH:
|
||||
joystickRaw[1] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_THROTTLE:
|
||||
case DREF_JOYSTICK_VALUES_THROTTLE:
|
||||
joystickRaw[2] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_YAW:
|
||||
case DREF_JOYSTICK_VALUES_YAW:
|
||||
joystickRaw[3] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_CH5:
|
||||
case DREF_JOYSTICK_VALUES_CH5:
|
||||
joystickRaw[4] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_CH6:
|
||||
case DREF_JOYSTICK_VALUES_CH6:
|
||||
joystickRaw[5] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_CH7:
|
||||
case DREF_JOYSTICK_VALUES_CH7:
|
||||
joystickRaw[6] = value;
|
||||
break;
|
||||
|
||||
case DREF_JOYSTICK_VALUES_CH8:
|
||||
case DREF_JOYSTICK_VALUES_CH8:
|
||||
joystickRaw[7] = value;
|
||||
break;
|
||||
|
||||
|
@ -375,8 +379,8 @@ static void* listenWorker(void* arg)
|
|||
0, //(int16_t)round(-local_vy * 100),
|
||||
0
|
||||
);
|
||||
|
||||
const int32_t altitideOverGround = (int32_t)round(agl * 100);
|
||||
|
||||
const int32_t altitideOverGround = (int32_t)round(agl * 100);
|
||||
if (altitideOverGround > 0 && altitideOverGround <= RANGEFINDER_VIRTUAL_MAX_RANGE_CM) {
|
||||
fakeRangefindersSetData(altitideOverGround);
|
||||
} else {
|
||||
|
@ -391,7 +395,7 @@ static void* listenWorker(void* arg)
|
|||
imuSetAttitudeRPY(roll_inav, pitch_inav, yaw_inav);
|
||||
imuUpdateAttitude(micros());
|
||||
}
|
||||
|
||||
|
||||
fakeAccSet(
|
||||
constrainToInt16(-accel_x * GRAVITY_MSS * 1000),
|
||||
constrainToInt16(accel_y * GRAVITY_MSS * 1000),
|
||||
|
@ -425,7 +429,7 @@ static void* listenWorker(void* arg)
|
|||
if (!initalized) {
|
||||
ENABLE_ARMING_FLAG(SIMULATOR_MODE_SITL);
|
||||
// Aircraft can wobble on the runway and prevents calibration of the accelerometer
|
||||
ENABLE_STATE(ACCELEROMETER_CALIBRATED);
|
||||
ENABLE_STATE(ACCELEROMETER_CALIBRATED);
|
||||
initalized = true;
|
||||
}
|
||||
|
||||
|
@ -435,22 +439,113 @@ static void* listenWorker(void* arg)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int lookup_address (char *name, int port, int type, struct sockaddr *addr, socklen_t* len )
|
||||
{
|
||||
struct addrinfo *servinfo, *p;
|
||||
struct addrinfo hints = {.ai_family = AF_UNSPEC, .ai_socktype = type, .ai_flags = AI_V4MAPPED|AI_ADDRCONFIG};
|
||||
if (name == NULL) {
|
||||
hints.ai_flags |= AI_PASSIVE;
|
||||
}
|
||||
/*
|
||||
This nonsense is to uniformly deliver the same sa_family regardless of whether
|
||||
name is NULL or non-NULL ** ON LINUX **
|
||||
Otherwise, at least on Linux, we get
|
||||
- V6,V4 for the non-null case and
|
||||
- V4,V6 for the null case, regardless of gai.conf
|
||||
Which may confuse consumers
|
||||
FreeBSD and Windows behave consistently, giving V6 for Ipv6 enabled stacks
|
||||
unless a quad dotted address is specified (or a name resolveds to V4,
|
||||
or system policy enforces IPv4 over V6
|
||||
*/
|
||||
struct addrinfo *p4 = NULL;
|
||||
struct addrinfo *p6 = NULL;
|
||||
|
||||
int result;
|
||||
char aport[16];
|
||||
snprintf(aport, sizeof(aport), "%d", port);
|
||||
|
||||
if ((result = getaddrinfo(name, aport, &hints, &servinfo)) != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(result));
|
||||
return result;
|
||||
} else {
|
||||
int j = 0;
|
||||
for(p = servinfo; p != NULL; p = p->ai_next) {
|
||||
if(p->ai_family == AF_INET6)
|
||||
p6 = p;
|
||||
else if(p->ai_family == AF_INET)
|
||||
p4 = p;
|
||||
j++;
|
||||
}
|
||||
|
||||
if (p6 != NULL)
|
||||
p = p6;
|
||||
else if (p4 != NULL)
|
||||
p = p4;
|
||||
else
|
||||
return -1;
|
||||
memcpy(addr, p->ai_addr, p->ai_addrlen);
|
||||
*len = p->ai_addrlen;
|
||||
freeaddrinfo(servinfo);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char * pretty_print_address(struct sockaddr* p)
|
||||
{
|
||||
char straddr[INET6_ADDRSTRLEN];
|
||||
void *addr;
|
||||
uint16_t port;
|
||||
if (p->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 * ip = (struct sockaddr_in6*)p;
|
||||
addr = &ip->sin6_addr;
|
||||
port = ntohs(ip->sin6_port);
|
||||
} else {
|
||||
struct sockaddr_in * ip = (struct sockaddr_in*)p;
|
||||
port = ntohs(ip->sin_port);
|
||||
addr = &ip->sin_addr;
|
||||
}
|
||||
const char *res = inet_ntop(p->sa_family, addr, straddr, sizeof straddr);
|
||||
if (res != NULL) {
|
||||
int nb = strlen(res)+16;
|
||||
char *buf = calloc(nb,1);
|
||||
char *ptr = buf;
|
||||
if (p->sa_family == AF_INET6) {
|
||||
*ptr++='[';
|
||||
}
|
||||
ptr = stpcpy(ptr, res);
|
||||
if (p->sa_family == AF_INET6) {
|
||||
*ptr++=']';
|
||||
}
|
||||
sprintf(ptr, ":%d", port);
|
||||
return buf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, bool imu)
|
||||
{
|
||||
memcpy(pwmMapping, mapping, mapCount);
|
||||
mappingCount = mapCount;
|
||||
useImu = imu;
|
||||
|
||||
sockFd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
|
||||
if (sockFd < 0) {
|
||||
if (port == 0) {
|
||||
port = XP_PORT; // use default port
|
||||
}
|
||||
|
||||
if(lookup_address(ip, port, SOCK_DGRAM, (struct sockaddr*)&serverAddr, &serverAddrLen) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(0);
|
||||
sockFd = socket(((struct sockaddr*)&serverAddr)->sa_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (sockFd < 0) {
|
||||
return false;
|
||||
} else {
|
||||
char *nptr = pretty_print_address((struct sockaddr *)&serverAddr);
|
||||
if (nptr != NULL) {
|
||||
fprintf(stderr, "[SOCKET] xplane address = %s, fd=%d\n", nptr, sockFd);
|
||||
free(nptr);
|
||||
}
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 1;
|
||||
|
@ -462,13 +557,7 @@ bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, bool
|
|||
if (setsockopt(sockFd, SOL_SOCKET, SO_SNDTIMEO, (struct timeval *) &tv,sizeof(struct timeval))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bind(sockFd, (struct sockaddr *) &addr, sizeof(addr));
|
||||
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_addr.s_addr = inet_addr(ip);
|
||||
serverAddr.sin_port = htons(port);
|
||||
|
||||
if (pthread_create(&listenThread, NULL, listenWorker, NULL) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -508,4 +597,4 @@ bool simXPlaneInit(char* ip, int port, uint8_t* mapping, uint8_t mapCount, bool
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,20 +66,23 @@ static bool useImu = false;
|
|||
static char *simIp = NULL;
|
||||
static int simPort = 0;
|
||||
|
||||
static char **c_argv;
|
||||
|
||||
void systemInit(void) {
|
||||
|
||||
fprintf(stderr, "INAV %d.%d.%d SITL\n", FC_VERSION_MAJOR, FC_VERSION_MINOR, FC_VERSION_PATCH_LEVEL);
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
fprintf(stderr, "[SYSTEM] Init...\n");
|
||||
|
||||
#if !defined(__FreeBSD__) // maybe also || !defined(__APPLE__)
|
||||
pthread_attr_t thAttr;
|
||||
int policy = 0;
|
||||
|
||||
pthread_attr_init(&thAttr);
|
||||
pthread_attr_getschedpolicy(&thAttr, &policy);
|
||||
|
||||
pthread_setschedprio(pthread_self(), sched_get_priority_min(policy));
|
||||
pthread_attr_destroy(&thAttr);
|
||||
#endif
|
||||
|
||||
if (pthread_mutex_init(&mainLoopLock, NULL) != 0) {
|
||||
fprintf(stderr, "[SYSTEM] Unable to create mainLoop lock.\n");
|
||||
|
@ -89,7 +92,7 @@ void systemInit(void) {
|
|||
if (sitlSim != SITL_SIM_NONE) {
|
||||
fprintf(stderr, "[SIM] Waiting for connection...\n");
|
||||
}
|
||||
|
||||
|
||||
switch (sitlSim) {
|
||||
case SITL_SIM_REALFLIGHT:
|
||||
if (mappingCount > RF_MAX_PWM_OUTS) {
|
||||
|
@ -108,7 +111,7 @@ void systemInit(void) {
|
|||
fprintf(stderr, "[SIM] Mapping error. RealFligt supports a maximum of %i PWM outputs.", XP_MAX_PWM_OUTS);
|
||||
sitlSim = SITL_SIM_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (simXPlaneInit(simIp, simPort, pwmMapping, mappingCount, useImu)) {
|
||||
fprintf(stderr, "[SIM] Connection with X-Plane successfully established.\n");
|
||||
} else {
|
||||
|
@ -119,13 +122,13 @@ void systemInit(void) {
|
|||
fprintf(stderr, "[SIM] No interface specified. Configurator only.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
rescheduleTask(TASK_SERIAL, 1);
|
||||
}
|
||||
|
||||
bool parseMapping(char* mapStr)
|
||||
{
|
||||
char *split = strtok(mapStr, ",");
|
||||
char *split = strtok(mapStr, ",");
|
||||
char numBuf[2];
|
||||
while(split)
|
||||
{
|
||||
|
@ -158,7 +161,7 @@ bool parseMapping(char* mapStr)
|
|||
return true;
|
||||
}
|
||||
|
||||
void printCmdLineOptions(void)
|
||||
void printCmdLineOptions(void)
|
||||
{
|
||||
fprintf(stderr, "Avaiable options:\n");
|
||||
fprintf(stderr, "--path=[path] Path and filename of eeprom.bin. If not specified 'eeprom.bin' in program directory is used.\n");
|
||||
|
@ -169,21 +172,26 @@ void printCmdLineOptions(void)
|
|||
fprintf(stderr, "--chanmap=[mapstring] Channel mapping. Maps INAVs motor and servo PWM outputs to the virtual receiver output in the simulator.\n");
|
||||
fprintf(stderr, " The mapstring has the following format: M(otor)|S(servo)<INAV-OUT>-<RECEIVER-OUT>,... All numbers must have two digits\n");
|
||||
fprintf(stderr, " For example: Map motor 1 to virtal receiver output 1, servo 1 to output 2 and servo 2 to output 3:\n");
|
||||
fprintf(stderr, " --chanmap=M01-01,S01-02,S02-03\n");
|
||||
fprintf(stderr, " --chanmap=M01-01,S01-02,S02-03\n");
|
||||
}
|
||||
|
||||
void parseArguments(int argc, char *argv[])
|
||||
{
|
||||
// Stash these so we can rexec on reboot, just like a FC does
|
||||
c_argv = calloc(argc+1, sizeof(char *));
|
||||
for (int i = 0; i < argc; i++) {
|
||||
c_argv[i] = strdup(argv[i]);
|
||||
}
|
||||
int c;
|
||||
while(true) {
|
||||
static struct option longOpt[] = {
|
||||
{"sim", optional_argument, 0, 's'},
|
||||
{"useimu", optional_argument, 0, 'u'},
|
||||
{"chanmap", optional_argument, 0, 'c'},
|
||||
{"simip", optional_argument, 0, 'i'},
|
||||
{"simport", optional_argument, 0, 'p'},
|
||||
{"help", optional_argument, 0, 'h'},
|
||||
{"path", optional_argument, 0, 'e'},
|
||||
{"sim", required_argument, 0, 's'},
|
||||
{"useimu", no_argument, 0, 'u'},
|
||||
{"chanmap", required_argument, 0, 'c'},
|
||||
{"simip", required_argument, 0, 'i'},
|
||||
{"simport", required_argument, 0, 'p'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"path", required_argument, 0, 'e'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
|
@ -192,7 +200,7 @@ void parseArguments(int argc, char *argv[])
|
|||
break;
|
||||
|
||||
switch (c) {
|
||||
case 's':
|
||||
case 's':
|
||||
if (strcmp(optarg, "rf") == 0) {
|
||||
sitlSim = SITL_SIM_REALFLIGHT;
|
||||
} else if (strcmp(optarg, "xp") == 0){
|
||||
|
@ -210,7 +218,7 @@ void parseArguments(int argc, char *argv[])
|
|||
}
|
||||
break;
|
||||
case 'p':
|
||||
simPort = atoi(optarg);
|
||||
simPort = atoi(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
useImu = true;
|
||||
|
@ -227,12 +235,12 @@ void parseArguments(int argc, char *argv[])
|
|||
printCmdLineOptions();
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (simIp == NULL) {
|
||||
simIp = malloc(10);
|
||||
strcpy(simIp, "127.0.0.1");
|
||||
strcpy(simIp, "127.0.0.1");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -261,7 +269,7 @@ uint64_t microsISR(void)
|
|||
|
||||
uint32_t millis(void) {
|
||||
return (uint32_t)(micros() / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void delayMicroseconds(timeUs_t us)
|
||||
{
|
||||
|
@ -273,10 +281,17 @@ void delay(timeMs_t ms)
|
|||
delayMicroseconds(ms * 1000UL);
|
||||
}
|
||||
|
||||
void systemReset(void)
|
||||
void systemReset(void)
|
||||
{
|
||||
fprintf(stderr, "[SYSTEM] Reset\n");
|
||||
exit(0);
|
||||
#if defined(__CYGWIN__) || defined(__APPLE__) || GCC_MAJOR < 12
|
||||
for(int j = 3; j < 1024; j++) {
|
||||
close(j);
|
||||
}
|
||||
#else
|
||||
closefrom(3);
|
||||
#endif
|
||||
execvp(c_argv[0], c_argv); // restart
|
||||
}
|
||||
|
||||
void systemResetToBootloader(void)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue