Rust on Espressif chips – 2023 Roadmap
mabez.dev
mabez.dev
They do a good job of hiding complexity (their compiler supports all of C++20, yet it's really rare to see anything more than a few basic classes).
For projects where I just want to make a thing that works, this is amazing compared to other toolchains.
I really hope rust can offer the same. Even a mediocre coder should be able to start from a blank file and, within 10 minutes, make something that tweets when the sun comes up.
So far, rust on esp32 is far from that... But hopefully they can get there!
If your product needs to do X, Y, and Z, and that can be built in arduino in a few days, and built in some other "professional" toolchain in a few months, then you will save money by building your real product on arduino.
And when the thing you're doing is outside the scope of what arduinos library ecosystem can provide (eg. you need to drive some GPIO pin with cycle accurate latencies), you can still write your assembly to do it.... But you still get the benefits of simplicity for the rest of your functionality.
In my experience, escaping the Arduino system isn't so simple. The Arduino framework pulls a lot of stuff into the global namespace when you include even just one of their headers, some of which is incompatible with standard C++. For example they `#define` all pin names, some of which can be common terms like `led`, `button`, `mosi` or `clk`. Any of those you then can't use a variable name. It's even worse because the pin names differ between boards, and there's no master list of defined terms.
Not necessarily. This is only true if you only think about software. Your "real product" also consists of hardware, and if you can do X, Y, and Z on a certain chip with the Arduino environment, you can most likely do exactly the same on a smaller and cheaper chip without the Arduino environment. Simply replacing Arduino C++ by plain C code without useless abstractions can already give you a very noticeable performance improvement and code size reduction on small microcontrollers. Development time is essentially a one time investment, while the chip price is a fixed cost for every unit you build. It's purely a question of how many units you want to build in the end. And for the record, if you can build it in an Arduino environment within a few days, you can definitely build it in "some other 'professional' toolchain" in just a few more days at most.
It's possible to be beginner friendly and not badly designed. Unfortunately Arduino doesn't really achieve that. The Arduino API is pretty terrible, the "IDE" is awful (they did improve it a lot recently in fairness), and the boards are very expensive.
Mbed is better in pretty much every way except that they screwed around with several different rubbish build systems for years, which meant it was very difficult to set up and as a result very beginner unfriendly. But the actual APIs are a lot nicer than Arduino's.
They do finally have a good IDE (Mbed Studio, based on Theano). I would probably use that for new microcontroller projects.
Arduino is great if you are happy with one single massive file for all your code (I know you can add additional files, but it’s not intuitive).
For large projects that needs to be split up and architected use platformio.
Obviously not advocating plain C for business middleware servers. Those benefit greatly from C++ type of languages.
Naturally having Rust as well is a positive, the more the merrier.
When I got into electronics and embedded development recently as a hobby, I started with Arduino. And for the vast majority of my projects, which are mostly simple smarthome IOT type things, or little toys for my kiddo, the Arduino framework is just fine and the ecosystem of libraries is great.
But when I recently started a project that was much lower-level (in fact, it was your example exactly -- driving pins with cycle-accurate timing), where I didn't need those libraries, I used ESP-IDF without Arduino.
I'm also just lazy, and just don't want to deal with more complexity than I have to.
Arduino is added complexity. In my limited hobbyist experience, going to the datasheets and using official toolchains has been very straightforward. It pays to build the understanding ahead of time rather than debugging some random person's Arduino library when your peripheral stops working.
Arduino's added complexity opened up the space to people who had little-to-no programming experience. People who had a computer with a USB port. People who had no familiarity with binary let alone hexadecimal to set a bitmask so they could turn an LED on. Let alone figure out which bits to flip to set a timer.
Arduino is added complexity to the benefit of accessibility. The complexity of the datasheets and toolschains make most products an unassailable wall. That said, the real gem of Arduino is its community that is welcoming and helpful to beginners.
I'd hazard a guess there is no software that would take "months" to build while it takes days on arduino, unless you count "developer learning C++ properly" in that month.
Most uC platforms provide enough SDK and code generation that you can hit the ground running with simple code and complex code won't be any easier in arduino
It isn't so much that the programming model is less complicated. Sure that's an advantage if you are just noodling around. The Wiring framework has introduced a lot of developers to embedded development. But once you start to do projects that are a bit more than just a demo of something it actually is a bit easier to write software using, for instance ESP-IDF (FreeRTOS port for ESP32). You don't have to reinvent concurrency, for one. And it makes it easier to make use of more of what the ESP32 chips can offer.
The bane of embedded development is awful tooling and people who aren't very good programmers inventing abstractions that do more harm than good. Setting up the ESP-IDF toolchain correctly is much harder than it needs to be. If you haven't done it before, it is going to cost you a weekend to set up. You can probably make something work half-way in a few hours, but to get all the gremlins out...that can take a while. I recently spent on the order of 3 hours getting things to work properly again after upgrading to a newer version of ESP-IDF and it is slightly infuriating how bad the tooling is.
And as if the tooling wasn't bad in itself, the embedded world is in love with Python - which adds yet another dimension of problems. Python isn't a language that lends itself to producing software that behaves predictably on random computers. Please stop using it for tooling. Write tooling in a portable language that can produce proper binaries. Preferably statically linked binaries so that at least you do not have to waste time wondering why your tooling doesn't work. Write tooling in Rust or Go.
(And if you think ESP-IDF is a handful, good lord, pray that you never have to deal with Zephyr.)
I used to hope that Platformio (https://platformio.org/) would help solve problems, but it seems that none of the MCU vendors really give a shit about figuring out a good solution. Everyone wants to make their own mess. (Yeah, it is Python, but not at the amateur software engineer level as the nonsense you see in vendor toolchains)
Yeah, Arduino (or rather Wiring) is limiting. But the "professional" embedded world can sit down and shut up until they can demonstrate something that is even half way as reliable as the Arduino IDE.
IC vendors have a lot of incentive to lock you in their ecosystem. Embedded developers are paid to produce firmware for widgets- their priority is shipping a firmware binary to manufacturer.
Besides, I don't find Arduino IDE that reliable- I still have to figure out which board definition to use (is this a micro or nano? Perhaps nano pro? Why doesn't this blink code not work on RP2040 board? Oh right, I selected adafruit rp2040 board when I was using pico, which uses board pin numbers vs rp2040 gpio numbers)
The industry is starting to understand this, but it takes time to fix. And the industry still needs to shed a lot of managers that lack a basic understanding of engineering software ecosystems and tooling.
Figuring out what board definition to use is a luxuriously simple problem to have. :-) Try to figure out how to configure a bunch of sensors hooked up to anything running Zephyr. It seems to have been put toghether under the motto "there is no problem so complex that we can't make it even more complex".
e.g. you can build and flash any application and open a debug shell with
make BOARD=esp32s3-devkit flash term
The ESP32 toolchain is installed with an easy to use script: https://doc.riot-os.org/group__cpu__esp32.html#esp32_local_t...Well, that's stings. Can you elaborate on your trouble with getting Zephyr working on ESP32? My memory is it's actually one of our simpler platforms, though it's true Zephyr isn't itself an IDE. It's an RTOS with a C API; you're expected to build at the command line with a wrapper tool (west), and to have downloaded and unpack an SDK tarball. But really there's not much more to it than that.
One other frustration is how obtuse the error messages can be. If west encounters a situation it doesn't like, it will often dump stack traces rather than print an error message. If you have something in the wrong directory or a typo etc, the magic in the build system often just fails and prints a generic error message or nothing at all. Maybe you screwed up the DT, maybe you named something incorrectly, maybe it's in the wrong directory, or maybe you typo'd. All of these can produce the same error messages at build time, which gets annoying.
With that said, the zephyr experience is remarkably pain-free and Linux-like compared to some vendor toolchains and generally good enough that I advocate for it internally.
I think both criticisms are reasonable but they aren't from the same perspective. In this case, yes, Espressif (who I'm not affiliated with and don't want to speak for, though I have worked on the ESP32 -- it was actually Zephyr's launch hardware for SMP, as it happens) doesn't quite have everything wired up yet. And that's unfortunate, but not so much a ding at Zephyr as with this particular integration (other chip vendors, Nordic especially, tend to be farther along).
And as for doing devicetree work... yeah, I don't like it either and try to stick to the kernel. But it's a ton cleaner than the kind of cut/paste you see with other solutions (and especially with whole-board IDE/SDKs!). In Zephyr, if you have a part that has an instance of some existing hardware integrated on another platform (a Synopsys DMA controller, some third-party I2C accelerometer, whatever), the configuration is usually/hopefully as simple as a three-line block of DTS with the addresses and interrupts filled in. For platform integrators, it's probably our single most attractive feature.
There are definitely amazing features in the ecosystem. E.g. the fact that you can run a live debugger on this tiny chip is mind blowing.
Use VSCode. It's not great, but it's SOOOO much better than everything else in the embedded space (that doesn't cost $100K+) that it's ridiculous.
As for programmers, any programmer of talent decamped from the embedded world to anything else. As long as semiconductor companies refuse to pay real salaries, this will continue.
> Write tooling in a portable language that can produce proper binaries. Preferably statically linked binaries so that at least you do not have to waste time wondering why your tooling doesn't work. Write tooling in Rust or Go.
No. Just no. Installing random crap in any language on my computer is where things fail.
Use a bloody container. To contain the mess, it's what I have to do anyway. For the love of all that is holy and unholy, distribute a container. This is the kind of scenario that containers were MADE for--lots of random tools that need to be version locked and only upgraded as a single unit.
The only thing that is sometimes faster from idea to deployment is micropython, since it's possible to just copy paste regular python with minor changes and it just works.
https://docs.rs/embedded-hal/latest/embedded_hal/
Here are the supported boards
https://github.com/rust-embedded/awesome-embedded-rust#hal-i...
There's a book on rust on esp https://esp-rs.github.io/book/
Here's how to blink a led using esp-idf-hal, targetting espressif
https://github.com/esp-rs/esp-idf-hal/blob/master/examples/b...
/! Blinks an LED
//!
//! This assumes that a LED is connected to GPIO4.
//! Depending on your target and the board you are using you should change the pin.
//! If your board doesn't have on-board LEDs don't forget to add an appropriate resistor.
//!
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::gpio::*;
use esp_idf_hal::peripherals::Peripherals;
fn main() -> anyhow::Result<()> {
esp_idf_sys::link_patches();
let peripherals = Peripherals::take().unwrap();
let mut led = PinDriver::output(peripherals.pins.gpio4)?;
loop {
led.set_high()?;
// we are sleeping here to make sure the watchdog isn't triggered
FreeRtos::delay_ms(1000);
led.set_low()?;
FreeRtos::delay_ms(1000);
}
}https://github.com/toitlang/toit
I'd love to pick your brain and fully understand your experience with MicroPython. I've been doing programming languages for a number of years now, and I find that it is incredibly useful to understand what developers appreciate (and dislike) about the available stacks.
Do I write MicroPython, a language I know well, but that has a few libraries, or do I stumble my way around C++ in an ecosystem that has basically everything? The choice has been very easy so far.
Toit is evolving rapidly and we find that being fully open source helps our users tweak things where necessary. It's been a fun ride so far and we've got some pretty awesome and sophisticated use cases that involve custom C++ code.
I'm actually really happy to see Espressif progress on this, I just want to throw how I'm feeling the void.
I currently don't see any benefit of using Rust on a MCU if you're a hobbyist, but that doesn't mean that it isn't a wonderful language to use on a Raspberry Pi or anything above that.
I think the matter of embedded programming being difficult was just exacerbated by this. I'm just a hobbyist in the embedded world, and I really find it fun and interesting, but as a hobbyist I only have limited time to be working on my project and I want something to show for my time, besides rustc messages about my unsafe code.
It's been pretty easy to work with, compared to "normal" embedded work with C that in my experience is a lot of tedious work to learn and set up before you get some momentum and can start focusing on the actual application.
All that said... I may not need anything out of glibc for this project so it is probably worth trying without it. I'm calling into c based bsp methods that do depend on glibc but they will be fine. So thanks!
We have been working on it for a few years now, and it gets more and more complete. Recently Espressif started contributing as well (and added Toit to their esp-idf installer).
Some selling points of Toit:
* modern language
* very fast execution for a dynamic language. (more than 10x faster than micropython)
* consistent libraries.
I’ve been using ESP-IDF for 2 years and have 100k lines, but I would love to consider Toit in the future.
The RP2040 in particular is pretty great though, being able to flash firmware with `cargo run--release` is amazing, and I can use tools like probe-rs and defmt to help debug my firmware.
My only "complaint" at the moment is a bit of immaturity in the USB side of things but I get it, USB is a pretty complicated stack and it'll just take time for things to mature.
For reference, here's some keyboard firmware I wrote for the RP2040. To me it feels reasonably high level and concise for something that drives a real world USB device (as simple a keyboards may be)
I think you're right about it being new to rust, and I recognise my folly in learning both embedded and rust in parallel. I wish I could go back and un-burn myself on it.
The answer is "not yet". But, some of their LLVM PR's were accepted recently, which is a big milestone!
Would be cool to see the list (and amount) of pending changes required.
https://discourse.llvm.org/u/andreisfr/summary
This is about as much as I can find.
[0] https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/...
And yes - this is partially a maturity thing, although more in the sense in that the embedded ecosystem is very vendor specific, and you're traveling outside the path the vendor forged for you, so you'll be an early trailblazer - you get to deal with all the integration shit, rather than finding some other random person on the internet that's dealt with something similar.
(And it's not necessarily that the vendors path is all that great, but at least there's a whole collection of other people that's struggled with it and posted about it online).
Our professor in college was livid when we suggested not using the vendors toolchain (back in the early 2000's), "yes $vendor's toolchain is broken and shit, but it's marginally less broken and shit than $non-vendor toolchain".
I am having luck currently with embassy-rs, on rp2040, including Wi-Fi, not BLE tho.
I think overall the space is improving rapidly year-over-year. It’s hard with suppliers focusing on the C stacks and the Rust stuff being all volunteer.. but IMO things are getting better.
What a weird choice, have same "family" name of ESP32 yet completely different ISAs
So for the simple project I am doing right now I'm using a 8051 microcontroller (AT89S51), mostly because it is super cheap to buy locally but also I am forced to deal more with the details of how embedded applications run. But it does feel like learning 8051 is not too relevant today.
A few questions, I saw that the chip itself is surface mount, would I need to make my own PCBs to be able to make more "permanent" projects with it? Also, what kinds of external things I would need to get started, like specific programmer hardware, etc.
If you want to directly use an RP2040 chip: yes, but you can do that for a few $ at e.g. jlcpcb where they can directly assemble the SMD components on the board for you. If you build your own "more permanent" project, you most likely want to build a PCB anyway, to integrate all the peripherals and connectors you need. Otherwise there is the RPi Pico board which is a small PCB with the RP2040 on it. It's the standard "small PCB where you can install a pin header" type of development / eval board.
> Also, what kinds of external things I would need to get started, like specific programmer hardware, etc.
I helped someone else with an RP2040 project recently and apparently all you need is some crystal for the clock, some flash chip for the program code, potentially a voltage regulator, and either a USB connector or an SWD connector (or ideally both). If you have SWD, you can program and debug the RP2040 from another RP2040 based module (there is "SWD programmer" firmware available, there is also the "Raspi Debug Probe" available now), and if you have USB and add some form of boot switch, you can directly program it via USB.
With the ESP32 it seems that this is not something reasonable to do, as most things require extensive setup that is implemented in the framework.
The language runtime is the "OS", and you talk directly to io ports and memory addresses.
Go feels ergonomic in an embedded environment, and if its concurrency could be utilized there it could be very powerful.
Rust is still awesome and I love working with it, but it seems much more daunting in the embedded world. I should probably give it a shot.
In any case, I'm so stoked to see the progress being made on projects like this. The freedom to program in more languages, especially safer languages, will make the embedded world so much more accessible.
In any case, building with an unpatched LLVM is still supported.
I was actually thinking about zig on one of these, but I assume xtensa LLVM must make that easier too.
I personally very much applaud the effort to port Rust to Espressif chipsets.
There are RTOS that allow you to tap into this stuff, but its basically all written in C. I wish there was more available in Rust, not just for the language benefits, which there are many but for more modern tooling.
Assembly still has its use cases but often its not applicable to today’s general purpose microcontrollers unless your doing some intensive realtime calculations, even then it should be scoped to that specific routine.
I'm convinced Rust will fade away like Scala precisely because criticism and bad experiences are met with poorly constructed straw men. It illustrates how immature the language and community are if this is the best argument available.
Virtually every HN thread for years has comments condemning C and C++ developers. Not a single one of those lunatics seems to understand most C and C++ developers have managers and company policies they must navigate. What do you think is going to happen when someone gives this straw man to their boss?
If people are going to be demonized for trying Rust and switching to something else, for whatever reason, the net effect will be people will avoid Rust and find something nicer like Swift, Nim, Pony, and Zig.
I doubt it. Scala choices were directly dictated by JVM. No JVM language managed to outpace Java. Java usually adds most interesting features from other such langs and assimilates them.
Rust doesn't let underlying compiler dictate everything.
There is a huge risk in regards to complexity, but community has (so far) been vocal against such things. Yes, Rust community isn't a cult of hivemind pod people. Imagine my shock!
Honestly as a dev coming from higher levels langs (Java), being able to write sensible non-GC code and it not dying in segfaults, UB sheitfuckery, or null pointer bullshit is a gift. Plus no data races? Sign me up.
Scala gives you a better horse (Java + Haskell), Rust gives you a car with a seatbelt.
The point was non-technical merits matter just as much, if not more.
Yes, borrow checker is harsh. Lifetime's are a pain. But only way out by other languages are:
A) Add GC/RC
B) Sweep it under rug, and pray.
It seems borrow checker is irreducible complexity.
Scala otoh made Trait order implementation matter and allowed custom operators. Compared to those blunders, Rust mistakes are minor.
Issue is. They want code generation to solve coloring problem of async. I asked about using `#[maybe(async)]` syntax but they pointed that attribute macros have deficiencies. Can't differentiate on async-ness or lack of it, plus attribute macros lack exhaustiveness check that `match expr` afford.
Nothing has been said about how to address the pathology seemingly unique to Rust making it suffocatingly toxic. It's the only programming language in recent memory where people scour the Internet condemning people for writing software in any other language. Even when the software is written in Rust, if it's not to some mythical standard, the community will unleash a wave a hate like in the case of Actix. There's memes about the "Rust evangelist strike force" plaguing open-source software authors to "rewrite it in rust". I can't think of any other language that has anything similar. Shrugging this off is an attempt to diminish and minimize the damage, rather than acknowledging the problem.
Presenting a false dichotomy solely on technical merits misses the big picture.
But I talked about syntax already, what do you...?
> Nothing has been said about how to address the pathology seemingly unique to Rust making it suffocatingly toxic. It's the only programming language in recent memory where people scour the Internet condemning people for writing software in any other language
Social aspects aren't immutable facts of language, but exterior ever changing things. It's like finding a game having helpful community. Only for it to become toxic later as player count increases. Same can be possible in other direction if Rust becomes more professional/boring.
And honestly, I don't care about arguing memes and one incident that was widely panned by everyone close to Rust.
RIIR isn't something you get branded with when you write your first Hello World, but something actively discouraged.
That said as someone that's neither here nor there, anti-Rust backlash is palpable and might work exactly to ingrain said "Evanglism Strike Force" mindset into it.
I find myself defending it more, even though I just want to use it. And don't care too much about community.
Initially, yes. But Scala has evolved beyond the JVM, with Scala.js [1] being rock-solid, and Scala Native [2] under development. Neither are truly hampered by the initial JVM roots of Scala.
> Scala gives you a better horse
Weird analogy ;)
[1] https://www.scala-js.org/ [2] https://scala-native.org/en/stable/
I'm reminded of a talk ex-Scala guy had, where he rhetorically asked "Scala, if Java cut their nose... Gods what are you doing?!". Stuff like compareTo being an 32 bit int, rather than just 8 bit, because compatibility, and so forth. I doubt they got rid of those nitpicks.
Although what pushed me of it was order dependent Traits and implicits everywhere. Plus Arcane type signatures.
I haven't kept up with Scala since 2, but if it works for you, it works for you.
> Weird analogy
- Will get you to your destination, intact
- Will shit everywhere (GC)
- Knows few useful tricks (Type system)
- Not made of metal (not low level)