emosenkis / esp-rs

Script for installing/running toolchain for building ESP8266 firmware in Rust
MIT License
329 stars 19 forks source link

How to find what API's are available? #25

Open torntrousers opened 5 years ago

torntrousers commented 5 years ago

Now that I finally have this compiling, how do I find what ESP8266 API's are available to use from Rust?

The generated example uses things like esp8266_hal::OutputPin and LED_BUILTIN, where are they defined?

How would I do something like start Wifi and connect to an access point?

emosenkis commented 5 years ago

These are calling bindings to the ESP8266 Arduino SDK that are generated automatically using bindgen. You should in theory be able to use any function in the SDK but in practice I've found that bindgen doesn't always succeed. Try looking up ESP8266 Arduino tutorials and calling the same functions from Rust. The build script should know based on what you use which bindings are needed.

torntrousers commented 5 years ago

Thanks for the quick reply. Ok, so a simple ESP8266/Arduino example might connect to Wifi with:

    WiFi.mode(WIFI_STA);
    WiFi.begin("someSSID", "somePassword");

So I add those to the lib.rs example and it fails with:

Running cargo check
    Checking my-project v0.1.0 (/ant/my-project)
error[E0425]: cannot find value `WiFi` in this scope
  --> src/lib.rs:25:5
   |
25 |     WiFi.mode(WIFI_STA);
   |     ^^^^ not found in this scope

error[E0425]: cannot find value `WIFI_STA` in this scope
  --> src/lib.rs:25:15
   |
25 |     WiFi.mode(WIFI_STA);
   |               ^^^^^^^^ not found in this scope

error[E0425]: cannot find value `WiFi` in this scope
  --> src/lib.rs:26:5
   |
26 |     WiFi.begin("someSSID", "somePassword");
   |     ^^^^ not found in this scope

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0425`.
error: Could not compile `my-project`.

What am I doing wrong?

torntrousers commented 5 years ago

As this issue has been closed now should I open a new one for the Wifi question?

emosenkis commented 5 years ago

Did you run build.sh again? What's in bindings.rs now?

I don't have much time to invest in supporting this project right now so if anyone else is able to help, please jump in.

torntrousers commented 5 years ago

Yes I did run build.sh again. This is whats in bindings.rs:

root@240e8789861a:/ant/my-project# find . | grep binding
./src/bindings.rs
./vendor/esp8266-hal/src/bindings.rs
root@240e8789861a:/ant/my-project# cat ./src/bindings.rs
/* automatically generated by rust-bindgen */

#![allow(non_snake_case,non_camel_case_types,non_upper_case_globals)]
extern crate libc;

pub const LED_BUILTIN: u32 = 16;
extern "C" {
    pub fn delay(arg1: libc::c_ulong);
}
root@240e8789861a:/ant/my-project# cat ./vendor/esp8266-hal/src/bindings.rs
/* automatically generated by rust-bindgen */

#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals)]
extern crate libc;

pub const HIGH: libc::c_uint = 1;
pub const LOW: libc::c_uint = 0;
pub const INPUT: libc::c_uint = 0;
pub const OUTPUT: libc::c_uint = 1;
pub type __uint8_t = libc::c_uchar;
extern "C" {
    pub fn pinMode(pin: u8, mode: u8);
}
extern "C" {
    pub fn digitalWrite(pin: u8, val: u8);
}
extern "C" {
    pub fn digitalRead(pin: u8) -> libc::c_int;
}
torntrousers commented 5 years ago

Is it because this only supports the digital pin operations so any other ESP8266 functions would need some similar code implemented in somewhere like the esp8266-hal crate?

emosenkis commented 5 years ago

No, that's just a crate of wrappers for convenience and compatibility with the embedded-hal ecosystem. It's written using the same build script and its bindings are generated in exactly the same way.

torntrousers commented 5 years ago

I'm a bit of a Rust newbie. I've no idea how this works, so can you or someone give me any hints?

I can have WiFi.mode(WIFI_STA); in my Arduino C program because I also have #include <ESP8266WiFi.h> which drags in this file, which includes a bunch of other things including this which defines the mode function implementation.

How is Rust supposed to work out from just WiFi.mode(WIFI_STA); in my Rust src that its supposed to call the mode function from ESP8266WiFiGeneric.cpp? It feels like I'm missing something and need more than just WiFi.mode in my Rust source, (making up some syntax) something like ESP8266WiFi::WiFi.mode(WIFI_STA); ?

apriori commented 5 years ago

@emosenkis @torntrousers I've also tried to get the WiFi example running. So far, I'm out of luck.

What I could gather so far is that build.sh is grep-ing all #include in src/main.ino, and feeding them into bindgen, which then supposedely should create bindings but will fail miserably.

First I had to upgrade the SDK to 2.4.2 (force pip2 and add parallel builds to not wait for ages on each full rebuild)

diff --git a/build.sh b/build.sh
index 1862853..e3996fc 100755
--- a/build.sh
+++ b/build.sh
@@ -3,7 +3,7 @@
 set -e -u -o pipefail

 readonly MRUSTC_VER='b5b7089'
-readonly SDK_VER='2.4.1'
+readonly SDK_VER='2.4.2'

 readonly INSTALL_DIR="${HOME}/.esp-rs"
 readonly MRUSTC_DIR="${INSTALL_DIR}/mrustc"
@@ -57,7 +57,7 @@ function install_toolchain() {
     fi
     if ! platformio --version &>/dev/null; then
         echo 'Installing platformio...'
-        pip install platformio --user
+        pip2 install platformio --user
     fi
     if ! [[ -d "${INSTALL_DIR}" ]]; then
         mkdir "${INSTALL_DIR}"
@@ -65,7 +65,7 @@ function install_toolchain() {

     checkout_git_revision 'https://github.com/thepowersgang/mrustc.git' "${MRUSTC_VER}" "${MRUSTC_DIR}" 'mrustc'
     echo "Building mrustc/minicargo@${MRUSTC_VER}"
-    ( cd "${MRUSTC_DIR}" && make RUSTCSRC && make -f minicargo.mk PARLEVEL=$(nproc) LIBS )
+    ( cd "${MRUSTC_DIR}" && make RUSTCSRC -j $(nproc) && make -f minicargo.mk -j$(nproc) PARLEVEL=$(nproc) LIBS )
     checkout_git_revision 'https://github.com/esp8266/Arduino.git' "${SDK_VER}" "${SDK_ROOT}" 'ESP8266 Arduino SDK'
     if ! [[ -d "${TOOLCHAIN_ROOT}" ]]; then
         echo 'Installing PlatformIO ESP8266 Arduino SDK...'

I specifically had to add a bunch of defines in

lib/generated/generated.h:

#define TCP_MSS 0
#define LWIP_IPV6 0
#define LWIP_FEATURES 0
#define LWIP_OPEN_SRC 0

for bindgen to continue parsing.

The main.ino looks hacky at best as well:

// Include an empty header to make platformio compile the generated code

#include <generated.h>
extern "C" {
    #include "../lib/generated/libesp_rs_project-0_1_0.hir.o.c"
}

#include <lwipopts.h>
#include <lwip/opt.h>
#include <IPAddress.h>

#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

decltype(setup_rs()) main;

void setup() {
    main = setup_rs();
}

void loop() {
    loop_rs(&main);
}

The resulting log of the execution of build.sh shows more issues:

/home/someuser/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: warning: 'Client::flush' hides overloaded virtual function [-Woverloaded-virtual]
/home/someuser/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Print.h:93:22: note: hidden overloaded virtual function 'Print::flush' declared here: different number of parameters (0 vs 1)
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:47:12: warning: 'BearSSL::WiFiClientSecure::write' hides overloaded virtual function [-Woverloaded-virtual]
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClient.h:59:18: note: hidden overloaded virtual function 'WiFiClient::write' declared here: type mismatch at 1st parameter ('uint8_t' (aka 'unsigned char') vs 'const char *')
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:53:12: warning: 'BearSSL::WiFiClientSecure::write' hides overloaded virtual function [-Woverloaded-virtual]
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClient.h:59:18: note: hidden overloaded virtual function 'WiFiClient::write' declared here: type mismatch at 1st parameter ('uint8_t' (aka 'unsigned char') vs 'Stream &')
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:51:34: error: use of undeclared identifier 'strlen_P'
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:80:7: error: use of undeclared identifier 'memcpy_P'
tools/sdk/libc/xtensa-lx106-elf/include/stdlib.h:122:44: warning: unknown attribute '__warning__' ignored [-Wunknown-attributes], err: false
tools/sdk/libc/xtensa-lx106-elf/include/stdlib.h:133:65: warning: unknown attribute '__warning__' ignored [-Wunknown-attributes], err: false
tools/sdk/libc/xtensa-lx106-elf/include/sys/pgmspace.h:59:3: warning: 'register' storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register], err: false
tools/sdk/libc/xtensa-lx106-elf/include/sys/pgmspace.h:66:3: warning: 'register' storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register], err: false
/home/someuser/.platformio/packages/framework-arduinoespressif8266/cores/esp8266/Client.h:37:22: warning: 'Client::flush' hides overloaded virtual function [-Woverloaded-virtual], err: false
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:47:12: warning: 'BearSSL::WiFiClientSecure::write' hides overloaded virtual function [-Woverloaded-virtual], err: false
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:53:12: warning: 'BearSSL::WiFiClientSecure::write' hides overloaded virtual function [-Woverloaded-virtual], err: false
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:51:34: error: use of undeclared identifier 'strlen_P', err: true
/home/someuser/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h:80:7: error: use of undeclared identifier 'memcpy_P', err: true

I doubt this is how the project is supposed to be used. Are there more current pointers?

apriori commented 5 years ago

Further analysis shows we hit a limitation of rust-bindgen here. It cannot bind to inline functions. So I guess we kind of have to rewrite this C++ stuff from scratch, maybe using C dependencies.

emosenkis commented 5 years ago

@apriori very interesting that inline functions were the underlying problem... it seems like it would be useful for bindgen to support that use case...

emosenkis commented 5 years ago

It looks like it actually does have some options for making inline functions work: https://rust-lang.github.io/rust-bindgen/faq.html#why-isnt-bindgen-generating-bindings-to-inline-functions