Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

emulation on host: Add full UART driver emulation. #5785

Merged
merged 12 commits into from
Feb 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions tests/host/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ CORE_CPP_FILES := $(addprefix $(CORE_PATH)/,\
libb64/cencode.cpp \
libb64/cdecode.cpp \
Schedule.cpp \
HardwareSerial.cpp \
)

CORE_C_FILES := $(addprefix $(CORE_PATH)/,\
Expand All @@ -74,7 +75,7 @@ MOCK_CPP_FILES_COMMON := $(addprefix common/,\
Arduino.cpp \
spiffs_mock.cpp \
WMath.cpp \
MockSerial.cpp \
MockUART.cpp \
MockTools.cpp \
MocklwIP.cpp \
)
Expand All @@ -95,7 +96,6 @@ MOCK_C_FILES := $(addprefix common/,\
noniso.c \
)


INC_PATHS += $(addprefix -I, \
. \
common \
Expand Down Expand Up @@ -231,7 +231,7 @@ ARDUINO_LIBS := \
WiFiServerSecureBearSSL.cpp \
BearSSLHelpers.cpp \
CertStoreBearSSL.cpp \
) \
)

OPT_ARDUINO_LIBS ?= $(addprefix ../../libraries/,\
$(addprefix ESP8266WebServer/src/,\
Expand All @@ -251,7 +251,7 @@ OPT_ARDUINO_LIBS ?= $(addprefix ../../libraries/,\
DNSServer/src/DNSServer.cpp \
ESP8266AVRISP/src/ESP8266AVRISP.cpp \
ESP8266HTTPClient/src/ESP8266HTTPClient.cpp \
) \
)

MOCK_ARDUINO_LIBS := $(addprefix common/,\
ClientContextSocket.cpp \
Expand All @@ -263,7 +263,7 @@ MOCK_ARDUINO_LIBS := $(addprefix common/,\
MockEsp.cpp \
MockEEPROM.cpp \
MockSPI.cpp \
) \
)

CPP_SOURCES_CORE_EMU = \
$(MOCK_CPP_FILES_EMU) \
Expand Down
12 changes: 11 additions & 1 deletion tests/host/README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ WiFiClient+WiFiServer/ClientContext and WifiUdp/UdpContext using socket
posix API. Further work will optionally propose native lwIP library
instead.

Serial UART0 and UART1 are emulated via stdin/stdout/stderr. Therefor
stdin is connected to UART0(RX) and stdout is connected to UART0(TX).
UART1(TX) writes to stderr. Reading from stdin happens in non-blocking
raw mode, that means each character is directly injected into the UART
FIFO without any buffering in the console. The command line switch -c
can be used to stop the emulation from intersepting CTRL-C (SIGINT).

How to compile and run a sketch
-------------------------------

Expand Down Expand Up @@ -73,7 +80,10 @@ Options are available:
-h
-i eth0 bind servers to this interface (WIP)
-l bind Multicast to the above interface (WIP)
-f no throttle (possibly 100%CPU)
-c ignore CTRL-C (send it over serial device)
-f no throttle (possibly 100%CPU)
-S spiffs size in KBytes (default: 1024)
(negative value will force mismatched size)

TODO
----
Expand Down
5 changes: 5 additions & 0 deletions tests/host/common/Arduino.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ extern "C" void yield()
{
}

extern "C" void optimistic_yield (uint32_t interval_us)
{
usleep(interval_us);
}

extern "C" void esp_yield()
{
}
Expand Down
2 changes: 0 additions & 2 deletions tests/host/common/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,6 @@ extern "C" {
void init(void);
void initVariant(void);

int atexit(void (*func)()) __attribute__((weak));

void pinMode(uint8_t pin, uint8_t mode);
void digitalWrite(uint8_t pin, uint8_t val);
int digitalRead(uint8_t pin);
Expand Down
77 changes: 69 additions & 8 deletions tests/host/common/ArduinoMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,58 @@
#include <user_interface.h> // wifi_get_ip_info()

#include <signal.h>
#include <unistd.h> // usleep
#include <unistd.h>
#include <getopt.h>
#include <termios.h>
#include <stdarg.h>
#include <stdio.h>

bool user_exit = false;
const char* host_interface = nullptr;
size_t spiffs_kb = 1024;
bool ignore_sigint = false;
bool restore_tty = false;

#define STDIN STDIN_FILENO

static struct termios initial_settings;

static int mock_start_uart(void)
{
struct termios settings;

if (!isatty(STDIN)) return 0;
if (tcgetattr(STDIN, &initial_settings) < 0) return -1;
settings = initial_settings;
settings.c_lflag &= ~(ignore_sigint ? ISIG : 0);
settings.c_lflag &= ~(ECHO | ICANON);
settings.c_iflag &= ~(ICRNL | INLCR | ISTRIP | IXON);
settings.c_oflag |= (ONLCR);
settings.c_cc[VMIN] = 0;
settings.c_cc[VTIME] = 0;
if (tcsetattr(STDIN, TCSANOW, &settings) < 0) return -2;
tty_restore = true;
return 0;
}

static int mock_stop_uart(void)
{
if (!restore_tty) return 0;
if (!isatty(STDIN)) {
perror("isatty(STDIN)");
//system("stty sane"); <- same error message "Inappropriate ioctl for device"
return 0;
}
if (tcsetattr(STDIN, TCSANOW, &initial_settings) < 0) return -1;
printf("\e[?25h"); // show cursor
return (0);
}

static uint8_t mock_read_uart(void)
{
uint8_t ch = 0;
return (read(STDIN, &ch, 1) == 1) ? ch : 0;
}

void help (const char* argv0, int exitcode)
{
Expand All @@ -48,9 +94,10 @@ void help (const char* argv0, int exitcode)
" -h\n"
" -i <interface> - use this interface for IP address\n"
" -l - bind tcp/udp servers to interface only (not 0.0.0.0)\n"
" -c - ignore CTRL-C (send it via Serial)\n"
" -f - no throttle (possibly 100%%CPU)\n"
" -S - spiffs size in KBytes (default: %zd)\n"
" (negative value will force mismatched size)\n"
" (negative value will force mismatched size)\n"
, argv0, spiffs_kb);
exit(exitcode);
}
Expand All @@ -60,13 +107,15 @@ static struct option options[] =
{ "help", no_argument, NULL, 'h' },
{ "fast", no_argument, NULL, 'f' },
{ "local", no_argument, NULL, 'l' },
{ "sigint", no_argument, NULL, 'c' },
{ "interface", required_argument, NULL, 'i' },
{ "spiffskb", required_argument, NULL, 'S' },
};

void save ()
void cleanup ()
{
mock_stop_spiffs();
mock_stop_uart();
}

void control_c (int sig)
Expand All @@ -76,7 +125,7 @@ void control_c (int sig)
if (user_exit)
{
fprintf(stderr, MOCK "stuck, killing\n");
save();
cleanup();
exit(1);
}
user_exit = true;
Expand All @@ -90,7 +139,7 @@ int main (int argc, char* const argv [])

for (;;)
{
int n = getopt_long(argc, argv, "hlfi:S:", options, NULL);
int n = getopt_long(argc, argv, "hlcfi:S:", options, NULL);
if (n < 0)
break;
switch (n)
Expand All @@ -104,6 +153,9 @@ int main (int argc, char* const argv [])
case 'l':
global_ipv4_netfmt = NO_GLOBAL_BINDING;
break;
case 'c':
ignore_sigint = true;
break;
case 'f':
fast = true;
break;
Expand All @@ -128,16 +180,25 @@ int main (int argc, char* const argv [])
// setup global global_ipv4_netfmt
wifi_get_ip_info(0, nullptr);

// set stdin to non blocking mode
mock_start_uart();

// install exit handler in case Esp.restart() is called
atexit(cleanup);

setup();
while (!user_exit)
{
uint8_t data = mock_read_uart();

if (data)
uart_new_data(UART0, data);
if (!fast)
usleep(10000); // not 100% cpu
usleep(1000); // not 100% cpu, ~1000 loops per second
loop();
check_incoming_udp();
}

save();
cleanup();

return 0;
}
14 changes: 13 additions & 1 deletion tests/host/common/HostWiring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,23 @@ void pinMode (uint8_t pin, uint8_t mode)
case WAKEUP_PULLDOWN: m="WAKEUP_PULLDOWN"; break;
default: m="(special)";
}
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "gpio%d: mode='%s'\n", pin, m);
#endif
}

void digitalWrite(uint8_t pin, uint8_t val)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "digitalWrite(pin=%d val=%d)\n", pin, val);
#endif
}

void analogWrite(uint8_t pin, int val)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "analogWrite(pin=%d, val=%d\n", pin, val);
#endif
}

int analogRead(uint8_t pin)
Expand All @@ -67,11 +73,17 @@ int analogRead(uint8_t pin)

void analogWriteRange(uint32_t range)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "analogWriteRange(range=%d)\n", range);
#endif
}

int digitalRead(uint8_t pin)
{
#ifdef DEBUG_ESP_CORE
fprintf(stderr, MOCK "digitalRead(%d)\n", pin);
return 0;
#endif

// pin 0 is most likely a low active input
return pin ? 0 : 1;
}
Loading