Embedded can be a tiny little msp430 with RAM measured in bytes, or it can be the latest and greatest intel flagship - it's a description of how the chip is used more than the chip type.
As CPUs get more powerful in general, so too do the little development boards. I can spend $10 on a board with multiple cores and megabytes of ram to drive my blinky lights and do wifi - that's enough power to run a full unix.
I’m a fairly loyal customer of adafruit. I really like their stuff and I’m not ashamed to say I really like circuit python. So much of embedded work is writing drivers and circuit python has many many already written and ready to go.
The Raspberry Pi foundation recently (about a 18 months ago) released their own dual-core ARM chip - the RP2040 - and a corresponding dev board - the Pi Pico. The Pico dev board runs about $4 plus shipping from authorized resellers.
Within the past few weeks, they introduced a wireless version - the Pi Pico W. It's about $6 from authorized resellers, though stock is hard to find at the moment.
They're looking at this from a higher level for defining the API and programming model people will use to write event-based code. An implementation might run using interrupts and a timer to drive an event loop on a single core, or it might scale itself out to run on multiple cores where available like they mention.
Not really, the processor has to be designed to support it that way to make it practical. With interrupts, execution can be interrupted at any point, and you have to be able to restore the registers to the correct state before letting the process continue from that point. It's technically doable, but with a chip that has only 128 bytes of RAM, you'll be pretty limited in how many processes you can run. Even Windows, prior to Win95, used the event loop style for multitasking.
Arduino's framework divides main() into two functions:
Setup() and loop(), but they're essentially the same as whatever comes before the while loop, and the while loop itself, in a standard embedded c main function.
Then you just have interrupts setting states, and the loop responds to the change in state.
It's not quite the same as a while loop, because when you return from the loop() function, how long it takes before it gets called again isn't defined. Using arduino-pico, for example, it does some USB I/O handling (when using TinyUSB). If you write an actual while loop and don't return from the loop function often enough, you might starve the USB port implementation.
This adds enough of a delay that I moved some code to the other core on the Pico to get precise timing.
That's a fair point. I admit, I moved away from the Arduino framework pretty rapidly after embedded development became my job, well before I had to contend with everything else under the hood.
I never ran into an issue of quasi-hidden behaviors like that, because I never pushed the thing that hard. It feels a little bit like the Arduino toolset (that is pre IDE 2.0) was designed to discourage people from pushing things that hard.
Well, it's behavior specific to one board, and perhaps to TinyUSB. But it looks like Arduino has a yield function [1] that you can use to run periodic tasks if you write your own loop. And Teensy calls yield() between calls to loop(). [2]
Since switching to VSCode and PlatformIO, I read the source more. (Arduino 1.x discouraged this due to not having easy ways to navigate between callers and callees.)
Interrupts work up to a point, but the correct way to do it is with an RTOS. Looking at the GitHub discussion it seems they are looking at mbed, FreeRTOS, or some other varieties too.