MicroPython solves this with the machine module. The machine module is a pain in the ass to write. You have to come up with an abstraction for the hardware that is universal and that people agree on. Then you have to implement it for every single microcontroller-family individually. Every family has it's own C API, or register layout if you do it like that. You get zero free lunch. By my counting, the machine module is only fully implemented for the STM32F4 family, the CC3200 and for the ESP8266 and ESP32. I'm saying "only", because that is just a very small handful of chips, at the same time it is a pretty huge achievement. Implementing the machine module for a certain platform is hard and long work. But its power is magnificent. When I write a driver for a sensor, it works across different microcontrollers, because the machine module is consistent on all of them.
Except when it isn't, and this finally brings me to the reason I'm writing all this: CircuitPython. I love Adafruit and I love the Feather boards and when they picked up MicroPython, I was extremely happy. The Pyboard is a nice board, too, but it is just one form-factor and one form-factor doesn't fit all. It fits my needs very badly. The Feather boards fit my needs amazingly well. But all the joy kinda went down the drain when Adafruit decided that the didn't want to implement the machine module. They gave a reason for doing so. They are saying that CircuitPython is aimed at beginners and therefore they want a simpler API.
I don't believe that CircuitPythons API is simpler. Actually, it is kinda the same. It's just different enough to break compatibility. Let's look at an example: I2C. I am just looking at the methods here. A sensor driver would take an I2C object as an argument when the object is created, so I don't really need to care about the differences in creating the I2C object. I even think that it may be valid that this object creation is different on different platforms, as there are some different approaches in the hardware: STM has a number of I2C busses on specific pins. Atmel has SERCOM busses that can be used for I2C on different Pins.
MicroPython has the following methods for I2C:
Code: Select all
init
deinit (WiPy)
scan
start (ESP8266)
stop (ESP8266)
readinto
write
readfrom
readfrom_into
writeto
readfrom_mem
readfrom_mem_into
writeto_mem
Code: Select all
deinit
scan
try_lock
unlock
readfrom_into
writeto
CircuitPython:
Code: Select all
readfrom_into(address, buffer, *, start=0, end=len(buffer))
Code: Select all
readfrom_into(addr, buf, stop=True)
The APIs are almost the same. The simplification argument falls short. CircuitPython may have less methods for its I2C object, but they are actually harder to use. The differences don't improve anything. They are just differences.
The good thing is, none of this unsolvable and none this is happening in bad faith, so we can actually try to fix it. One, very brutal, way would be to write a machine-wrapper around the CircuitPython API that restores compatibility. We could also fork their fork and rework their API into the machine module. A softer method would be to start at the class level and send them small pull-requests, fixing the compatibility issues of the methods. But I think that is just half of the work. The other half is to figure out how we can make sure that everyone who adds a new target for MicroPython happily implements the machine module, instead of something incompatible. We could also think about how we can get more Pyboard form-factors into the MicroPython shop and make Damien some money.
What do you think?