SmingHub / Sming

Sming - powerful open source framework simplifying the creation of embedded C++ applications.
https://sming.readthedocs.io
GNU Lesser General Public License v3.0
1.47k stars 347 forks source link

Port Sming to esp32/Arduino #1333

Closed slaff closed 2 years ago

slaff commented 6 years ago

This issue is created to collect all tasks needed to create a port of Sming on top of esp32/Arduino.

As mentioned from @avr39-ripe here:

Sming itself can be described as several components which together allow us to develop complete standalone app for esp8266:

The main idea is to use esp32/Arduino as a base that provides us with:

As a start we need someone to try and do the following:

slaff commented 6 years ago

@avr39-ripe @frankdownunder I would like to hear your comments.

hreintke commented 6 years ago

@slaff : As you know I moved my applications from Sming to Arduino.

For your first questions : Timing, I presume Timer, see Ticker lib including PR (https://github.com/esp8266/Arduino/pull/4209) Has almost all the Sming Timer functionality. Part of Core.

TCP : Due to ESP32 multiprocessor, lwip has specific implementation aspects. see ESPAsync (https://github.com/me-no-dev/AsyncTCP) and ESPAsyncWebserver (https://github.com/me-no-dev/ESPAsyncWebServer) for tcp options both have Sming like callbacks/events.

Difference between Arduino and Sming is std::function vs delegates. Both serve the callback mechanism, delegates have a subset of the possibilities/options.

avr39-ripe commented 6 years ago

@hreintke Last time I peek esp/Arduino it wasn't async by nature, parts of it is, as you mention AsyncTCP + frewnds, but not framework as whole. May be things had been changed since my peek. Sming's strong side is unified mechanism of timer/delegate for ANY aspect of application - either tcp or serial or wifi or whatever.. So I think porting such "whole system with ASYNC in mind" to esp/Arduino worth efforts. Thank you for pointing to Ticker lib, will take a look on it!

harry-boe commented 6 years ago

As much as i like Swing i don't think it will add a lot on top of the really excellent toolchain and libs that come from Espressif.

They have done an excellent job on the ESP-IDF (Espressif IoT Development Framework). And a port from sting will loose a lot of the features you get with their lib and api's.

You get as much control as you want for low level communication i2C, SPI, Serial .. They have a very modular Boot loader with partition table support - similar to rBoot You get even more option for secure Boot and encrypted flash There is a visual File system with FAT and spiffs support Hardware!! accelerated SSL/TLS

And on top of all this a very easy to understand and modular make based build system, including a nice extendable menuconfig target.

I'm working for a while now withe the Esp-idf toolchain a like it more every day. Of corse, you cannot just plug in some Arduino libraries but that's what esp32/Arduino claims to do.

I will likely spend more time on porting or writing libs on top of the Espressif API's than trying to adopt to the Arduino programming models.

frankdownunder commented 6 years ago

I do not know how many people develop using sming - is it possible to guess from the download stats? Anyway I think it is probably quite a few, and many will have embarked on significant projects that have consumed loads of time. Many of them must be wondering if they will be able to use that code to run on an ESP32. So I think migrating Sming to ESP32 is worth doing.

harry-boe points out that the ESP-IDF has some interesting new features. But ESP-IDF is still just a collection of functions; its is not a well-designed set of classes as is Sming. And while it may be good, I nevertheless do not want to rewrite all my Sming code to run for the new platform.

So I would suggest our aims for the project should provide an easy migration path for SMING based code and also if possible, SMINGRTOS projects.

To do this we should

It will need to be a team effort- there is a lot of effort involved I suspect. Need to canvas support for this amongst the community.

harry-boe commented 6 years ago

i don't agree that the esp-idf is just a collection of functions. If you take a closer look into the components folder if the idf than you will find components for more or less all the low level stuff you ever wanted (from lwip to openssl, JSON to nghhtp ..) not to mention a huge library on examples.

However, i'm happy to support porting for the stuff i contributed. Like the SPI, SDCard and testing on some Displays libs. I assume it will be more like wrapping esp32 api's to the Sming API's than really low level porting.

frankdownunder commented 6 years ago

Slaf said: "As a start we need someone to try and Migrate the Timing classes to Arduino." OK, I have done that, amongst other things, the old API calls to ets_timer_xxxx in the start function needed changing to:

    esp_timer_create(&_timerConfig, &timer);
    if (repeating) {
        esp_timer_start_periodic(timer, interval );
    }
    else {
        esp_timer_start_once(timer, interval );
    }

Just getting things to compile was challenging - and I would like to document the changes I needed to make (if such a list has not already been started).

I note that the docs say this: _"Timer callbacks are dispatched from a high-priority esptimer task. Because all the callbacks are dispatched from the same task, it is recommended to only do the minimal possible amount of work from the callback itself, posting an event to a lower priority task using a queue instead." We probably need the setQueued() function that was added into SmingRTOS to do it properly.

@hreintke I agree about std::function, Slaff seems also to agree, I might put my hand up to do that after Easter

@hreintke I looked at the Ticker class, IMO it is pretty basic compared to Timer. Do we need both? or do we add functionality to Ticker? I guess if we want to make it easy for Arduino code to build then we want to support Ticker class somehow.

I now know much more about the scope of this exercise than before. It will be a substantial undertaking. Ive created a page on the wiki, https://github.com/SmingHub/Sming/wiki/ESP32-migration Its probably a bit premature.

hreintke commented 6 years ago

@frankdownunder Did you look at the Ticker class from the current master or the ticker class including https://github.com/esp8266/Arduino/pull/4209

The ESP32 ticker class will probably get the same options. I am working on a PR for that.

frankdownunder commented 6 years ago

@hreintke OK, I looked at it briefly. Do you think we should encapsulate an instance of Ticker in the new Timer class, inherit from it, replace it entirely, or just get everyone to change their code to use Ticker instead?

frankdownunder commented 6 years ago

This might be interesting: https://github.com/plerup/makeEspArduino

mikee47 commented 5 years ago

Picking up from https://github.com/SmingHub/Sming/pull/1579#issuecomment-483025881 -

...I think we at least need to migrate to the RTOS SDK, though we wouldn't use any of the FreeRTOS code.

IMHO it would be best to stay as close as possible to the current async execution model and use FreeRTOS only for ESP32 and only to run Sming applications as its main task.

We should start describing the code-restructuring needed to have multi-platform code. Ideally Sming v4 should be able to run on ESP8266, ESP32 and some portions of it should run on a Linux machine. The latter is for testing and development purposes. I already have big chunks of the network code compiling under Linux and there I can test it and analyze it better before running it on a real device. Therefore I would love to have that code in one repository instead of keeping the Linux repo in sync with the develop branch.

I agree. I wasn't proposing we switch to FreeRTOS, but that we restructure according to the ESP-IDF-style API (or perhaps we should refer to it as the HAL). Many API calls have just been renamed, so system_get_free_heap_size() becomes esp_get_free_heap_size(), but others (like the Timer API) have been more helpfully abstracted so we'd need some code to do that, and update the framework accordingly. We'd still be using the C libraries from the Non-OS SDK, though.

I guess all we'd need to do to make a start is to create a components directory ($SMING_HOME/components ?) then pick something easy to start with. I could have a go at the timer stuff, see how we get on.

slaff commented 5 years ago

I guess all we'd need to do to make a start is to create a components directory ($SMING_HOME/components ?) then pick something easy to start with. I could have a go at the timer stuff, see how we get on.

I would suggest the following restructuring as a start

And so on.

Once that is ready and everything is still compiling and working :) you can start abstracting the timer stuff. What do you say?

mikee47 commented 5 years ago

Sounds like a plan! We can always revisit but the structure sounds good. Will you take care of the initial restructuring? And I guess (hope) we can continue in develop branch...

slaff commented 5 years ago

Sounds like a plan! We can always revisit but the structure sounds good.

Wonderful!

Will you take care of the initial restructuring?

Honestly your C/C++ skills are way better than mine. Can you try to do the initial restructuring and I will try to help as much as I can?

And I guess (hope) we can continue in develop branch

Yes, this way more people can check if the platform refactoring works as expected.

mikee47 commented 5 years ago

OK, I'll work up a PR for the restructuring. Don't be fooled about my C++ abilities - the STL still baffles me!

mikee47 commented 5 years ago

We also need to deal with the system directory in a similar manner. I don't think it belongs under Platform as it's mostly C code and I think the place where the new ESP8266 HAL code will live.

frankdownunder commented 5 years ago

May I suggest we use cmake? I have been working for quite a while on an ESP32 project, and Ive been using the cmake development way of IDF - I think it has clear advantages over makefiles. "The CMake-based build system will become the default build system in ESP-IDF V4.0. The existing GNU Make based build system will be deprecated in ESP-IDF V5.0." https://docs.espressif.com/projects/esp-idf/en/latest/get-started-cmake/

mikee47 commented 5 years ago

I think initially we can continue with Makefiles, but once the code is structured more closely with IDF we should certainly look at migrating to Cmake. The makefiles need simplifying and now is the time to do that.

Question: Do we remove 'makefile-project' now? I am happy to upgrade it inline with makefile-rboot (I would pull out common parts anyway) but obviously that would create additional work especially for testing.

slaff commented 5 years ago

Do we remove 'makefile-project' now?

Yes, it's about time we do this.

frankdownunder commented 5 years ago

once the code is structured more closely with IDF

cmake will be essential in getting a port to ESP32 done, but cmake has more to offer than just alignment with idf. Speed is one thing - it is very fast. It gets the dependencies right, and it runs on Windows without any Linux layer necessary. Ive seen many posts where a potential Sming user has given up when trying to use Windows. Just my two bobs worth!

mikee47 commented 5 years ago

I definitely believe cmake is the way to go. I had had a fully working ESP32 build system and compiled a sample within an hour yesterday. The visual makeconfig is a great improvement. (It is pretty slow under Windows, of course, as everything runs under MinGW.) Having the build environment as a .zip file made installation easy. I also looked at the ESP8266-RTOS which has a similar .zip. I'll have a look at whether this can be used for our purposes.

What we want is for Sming projects to build for Esp8266, Esp32 plus Windows and Linux targets, so I'm bearing this in mind whilst working on the initial restructure, which should not affect existing apps in any way.

Next step perhaps, for me anyway, is to implement the Windows target which will allow any app to build and run natively under MinGW for debugging, testing and for new users to evaluate. This will involve writing new versions of Sming modules (classes) and/or creating IDF-compatible components to abstract the hardware, where appropriate. I would prefer not to mix platforms within modules (#ifdefs). This should continue to have no effect on building for Esp8266. The 'Timer' abstraction mentioned above would be in there somewhere.

I'm very mindful that the ESP32 has considerably more resources available, especially RAM, so we need to be careful of introducing additional overhead if at all possible.

Sming has its 'modules', but the lower-level code isn't so well structured so migrating this into IDF-compatible components will help considerably. For example, gdbstub would be a component. This means we can also do direct comparison with/merging of code from the ESP8266-RTOS SDK. So the uart driver code will be in the same place, although at present they will be quite different. Others will be much closer, in some cases identical.

I note that Espressif's flashing tool supports both ESP32/8266 so that's helpful.

OK, so where does that leave us with Cmake? Well, doing it right is a fair bit of work though doesn't need to replace existing makefiles yet, can be added as alternative. Can probably start once we're happy with the initial restructure.

Lastly, porting will place additional requirements on users (python + cmake) plus the app. makefile and standard #include will need to change.

frankdownunder commented 5 years ago

Agreed. Happy to help.

frankdownunder commented 5 years ago

as everything runs under MinGW

Not true Mike. You should instead have followed the instructions here https://docs.espressif.com/projects/esp-idf/en/latest/get-started-cmake/windows-setup.html From that page:

The GNU Make based build system requires the MSYS2 Unix compatibility environment on Windows. The CMake-based build system does not require this environment.

Im a fan of Linux, but I dislike MSYS2 and MinGW. I need to rebuild my windows machine it got schtumpfred by Windows Update, but last time I had it running, i had native exes with CMake and Ninja in control and I got I get similar performance to Ubuntu. SUPER fast builds.

mikee47 commented 5 years ago

@frankdownunder Thanks Frank, I'd missed the CMake build preview. Definitely the way to go. It seems that the ESP32 is getting all the love at the moment, hope we can find a way to do the same for the humble ESP8266. (I still think of the ESP32 as an ESP8266 on steroids.)

mikee47 commented 5 years ago

OK so the appveyor build fails on a missing isblank call, an ISO C-99 function declared in ctype.h.

I've attempted (without success) to track down MinGW 5.3.0 to inspect why this is happening and to find the correct fix. My install is 6.3.0, which as far as I can tell is what chocolatey pulls down.

I had a similar issue with std::isinf and std::isnan for the Travis builds, solved by using Xenial (GCC 5.4.0) instead of the default Trusty (GCC 4.8.4) environment.

Perhaps update the version for appveyor?

slaff commented 5 years ago

Perhaps update the version for appveyor?

Sounds good to me. Any update that we make to the Windows CI should also be reflected on the docs and the choco packages so that Windows users are able to compile Sming also on their systems.

slaff commented 5 years ago

build fails on a missing isblank call, an ISO C-99 function declared in ctype.h.

// Checks for a blank character, that is, a space or a tab.
inline boolean isWhitespace(int c)
{
  return (isblank(c) == 0 ? false : true);
}

It used to succeed on the same code with the same compiler. I guess there is a problem with the includes and not in the compiler version.

mikee47 commented 5 years ago

Any update that we make to the Windows CI should also be reflected on the docs and the choco packages so that Windows users are able to compile Sming also on their systems.

I've haven't risked a choco install on my dev. system yet, but inspecting the scripts looks like the mingw install should be the current one, not the old one provided on appveyor. Needs confirming, of course.

mikee47 commented 5 years ago

I already have big chunks of the network code compiling under Linux and there I can test it and analyze it better before running it on a real device. Therefore I would love to have that code in one repository instead of keeping the Linux repo in sync with the develop branch.

@slaff So far my work on SHEM is only to get it to build, so no network functionality. I'm happy to to take a look at your Linux code to see how it could be integrated?

slaff commented 5 years ago

So far my work on SHEM is only to get it to build, so no network functionality. I'm happy to to take a look at your Linux code to see how it could be integrated?

You are reading my mind :). I will try to post the details here later today.

slaff commented 5 years ago

Here is what I did in order to be able to use LWIP in Sming on Linux.

That produced ports/unix/proj/lib/liblwip.a, ports/unix/proj/minimal/liblwipapps.a and ports/unix/proj/minimal/liblwipcommon.a libraries. And I used them in Sming Linux.

After that I used the unix minimal example and based the lwip initialization code on it. You should set the IP that will be used from the app. I couldn't find a way to set it automatically so I had to set it manually. And that IP must be in the same network as the tap interface. So I decided to go for 192.168.13.2 and I set it by

struct netif netif;

/* (manual) host IP configuration */
static ip4_addr_t ipaddr, netmask, gw, dns;

int setup()
{
        /* startup defaults (may be overridden by one or more opts) */
    IP4_ADDR(&gw, 192, 168, 13, 1);
    IP4_ADDR(&ipaddr, 192, 168, 13, 2);
    IP4_ADDR(&netmask, 255, 255, 255, 0);

    /* use debug flags defined by debug.h */
    debug_flags = 0;
#ifdef LWIP_DEBUG_ALL
    debug_flags |= (LWIP_DBG_ON | LWIP_DBG_TRACE | LWIP_DBG_STATE |
                    LWIP_DBG_FRESH | LWIP_DBG_HALT);
#else
    debug_flags |= LWIP_DBG_STATE;
#endif

    strncpy(ip_str, ip4addr_ntoa(&ipaddr), sizeof(ip_str));
    strncpy(nm_str, ip4addr_ntoa(&netmask), sizeof(nm_str));
    strncpy(gw_str, ip4addr_ntoa(&gw), sizeof(gw_str));
    printf("Host at %s mask %s gateway %s\n", ip_str, nm_str, gw_str);

    lwip_init();  // <!-- This must be called first before using LWIP

    netif_add(&netif, &ipaddr, &netmask, &gw, NULL, tapif_init, ethernet_input);
    netif_set_default(&netif);
    netif_set_up(&netif);
#if LWIP_IPV6
    netif_create_ip6_linklocal_address(&netif, 1);
#endif

   // ...
}

In my setup I had a problem with setting up a DNS server that I could not solve. Also I had issues with UDP packets. So those might be connected. For the DNS I have tried using the code below, but that did not work for me.

    // @TODO: DNS resolution is still not working...
    // @TODO: IPv4 Packet forwarding is also not working...
    IP4_ADDR(&dns, 8, 8, 8, 8);
    dns_setserver(0, &dns);

Once the setup is done the ticker should be started. What I used in my setup was the following:

while (1) {
        /* poll netif, pass packet to lwIP */
        tapif_select(&netif);
        sys_check_timeouts();
    }

My code and setup is not elegant. I just needed to have it running on Linux and was able to achieve this. But that helped me immensely in finding issues with our network code or just testing new code before testing it on the device. Not to mention the advantages of using valgrind and static code analyzers to discover potential problems with the code.

mikee47 commented 5 years ago

@slaff Thanks for the info, very helpful. I've got it integrated now so have a play and see what's broken!! (I haven't looked at DNS or UDP yet, by the way.)

Update: Added default DNS server but as you say no response to UDP packets...

slaff commented 5 years ago

have a play and see what's broken!!

Fantastic :) Shall I try PR #1692 or your feature/Arch-Host-DEV branch? Did you rebase PR #1692 on latest develop?

mikee47 commented 5 years ago

Things are still in flux so shall we work from my feature/Arch-Host-DEV branch, then when we're all happy with it I can tidy it up and push it all back to Arch-Host.

slaff commented 5 years ago

So far the Host architecture looks very-very promising :) I started from zero and added some notes as a WIKI entry: https://github.com/SmingHub/Sming/wiki/Host-Emulator that will be updated over time.

First question - the library code and the application code seems to be optimized and debugging it by default is a bit more inconvenient. Shall I specify a env. variable or would it be better to have the code non-optimized by default for the Host architecture unless SMING_RELEASE is specified?

slaff commented 5 years ago

@mikee47 The Sming Host Emulator, even at this early stage, is awesome! I played with it yesterday night and it was real fun to use it. Some years ago there was another simulator that I was planning to use as a base for our own https://github.com/afnid/espsim but you did much better.

Hints

socat  -x -v -d -d  /dev/ttyUSB0,raw,echo=0,crnl,b9600 /dev/ttyUSB1,raw,echo=0,crnl,b9600 2> >(tee /tmp/output.txt)

Something similar can be used also in the simulator because socat can listen on TCP ports and forward them to usb. Check these examples.

Suggestions for Improvements

modprobe tun ip tuntap add dev tap0 mode tap user "YOUR_USER_NAME_HERE" ip link set tap0 up promisc on ip link set tap0 master br0

sysctl net.ipv4.ip_forward=1 sysctl net.ipv6.conf.default.forwarding=1 sysctl net.ipv6.conf.all.forwarding=1

iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -i tap0 -o wlan0 -j ACCEPT



* Networking on Windows - have a `makefile` target to setup the virtual network.
mikee47 commented 5 years ago

@slaff Happy you like it! Thanks for starting the Wiki page. I did have a scan of https://github.com/afnid/espsim before all this but sometimes it's better starting from scratch I think, especially with all the progress made with Esp32/IDF.

First question - the library code and the application code seems to be optimized and debugging it by default is a bit more inconvenient. Shall I specify a env. variable or would it be better to have the code non-optimized by default for the Host architecture unless SMING_RELEASE is specified?

If you build with ENABLE_LWIPDEBUG=1 that should produce debuggable code. It also enables the various debug flags (if selected) in lwipopts.h. I guess it makes sense to make that the default for a Sming DEBUG build, in which case there's probably no point in having ENABLE_LWIPDEBUG at all.

* `Bridging` communication between real UART/USB device and our serial console.

If socat works as a stop-gap then great, but I'd envisaged ultimately extending the uart_server class to support serial port access directly for better real-time emulation. It would also properly deal with flow control, breaks, etc. which may be more important when adding support for the Esp32.

* As far as I saw SSL with axtls is still missing. Maybe it is a good idea to add it too before merging the PR.

I'll have a look, I guess we want to pull in as much existing Esp8266 code as possible.

* Networking on Linux:

1. Maybe we should have a `makefile`  target that setups TAP networking for us

Probably implement that in a shell script I guess.

* Networking on Windows - have a `makefile` target to setup the virtual network.

Perhaps a powershell script, once we've sorted out what a 'virtual network' looks like for Windows, possibly some kind of bridged configuration as you suggest. The SystemClock_NTP sample works perfectly, just can't use a local web browser - I fired up my laptop for that and it works a treat. I've added autodetection to the Windows host_lwip implementation so we can select adapter based on IP address.

slaff commented 5 years ago

If you build with ENABLE_LWIPDEBUG=1 that should produce debuggable code.

Right. I have added the following to the wiki page:

-- cut --

If you plan to use a debugger make sure to set the following environmental variables before compiling the code:

export ENABLE_GDB=1  
export ENABLE_LWIPDEBUG=1  # <!-- this will compile also LWIP with debug symbols

-- cut --

Probably implement that in a shell script I guess.

I can provide one. I was able to access my DNS and Internet with packet forwarding. Those additional lines made it work:

# The following lines are needed if you plan to access Internet
sudo sysctl net.ipv4.ip_forward=1
sudo sysctl net.ipv6.conf.default.forwarding=1
sudo sysctl net.ipv6.conf.all.forwarding=1

export INTERNET_IF=wlan0 # <!--- Make sure to replace wlan0 with the network interface connected to Internet

sudo iptables -t nat -A POSTROUTING -o $INTERNET_IF -j MASQUERADE
sudo iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i tap0 -o $INTERNET_IF -j ACCEPT**
slaff commented 5 years ago

Further idea comes to my mind: Show the developers how to use terminal multiplexer as tmux or screen and provide in one terminal window a split view with the output from UART 0, UART1 and the simulator itself.

slaff commented 5 years ago

Are there any generic device emulators available? For example, to simulate specific types of SPI slave

I have found this on Internet: http://codelectron.com/how-to-setup-virtual-spi-in-linux/. Maybe this can be a good starting point.

mikee47 commented 5 years ago

Are there any generic device emulators available? For example, to simulate specific types of SPI slave

I have found this on Internet: http://codelectron.com/how-to-setup-virtual-spi-in-linux/. Maybe this can be a good starting point.

@slaff Thanks for this slaff, an interesting idea but not sure how it would fit. This is quite a complex area so I've opened a separate issue #1710 to discuss further.

slaff commented 5 years ago

@mikee47 Are you ready to try ESP32 port :)) ? This way we can prove in practise if current structure is good enough to add multiple new architectures.

mikee47 commented 5 years ago

In a sense I suppose as I reckon the next step is to add partition support #1676, which is a pre-requisite. It would get rid of all those hard-coded flash memory locations, also be very helpful for things like #1781.

slaff commented 5 years ago

In a sense I suppose as I reckon the next step is to add partition support ...

Go for it :)

mikee47 commented 5 years ago

I guess we could also start looking at building an Esp32 Arch tree, even if it's not functional would give some direction. I have a recurring thought about getting Sming running on bare-bones Esp32, without any freertos. I have yet to use the Esp32 myself, so cannot speak from experience, but the ROM code doesn't seem to be RTOS-dependent. It would be crazy fast :-)

slaff commented 4 years ago

@mikee47 ESP-IDF v4.0 is officially released (https://github.com/espressif/esp-idf/releases/tag/v4.0). I guess we can start implementing SMING_ARCH=Esp32 :)

mikee47 commented 4 years ago

@slaff It's in progress, but bored with it. Python build script crashes and takes terminal with it. Tedious..

slaff commented 4 years ago

It's in progress, but bored with it.

@mikee47 create a PR and put WIP in the name. Let's try together to see if we can advance faster.

mikee47 commented 4 years ago

@slaff Not ready for a PR of any kind yet but I've pushed my working branch to dev/esp32 in my repo.

I'm keeping notes in the Arch/Esp32/README.rst and as you can see things haven't got very far, having stalled on the first bit, 'getting a reliable build system'. I'm working from tests/esp32/hello_world.

Last time I looked at this when I ran the python builder it just crashes and shuts down the terminal, which kind of makes debugging a little awkward. Also wading through issues with the python builder.

P.S. Working with ESP-IDF release 4.1 branch.

mikee47 commented 4 years ago

Just pulled a fresh IDF in and re-run install. Previously idf.py menuconfig just crashed, but now it works fine so something's been fixed!