[{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe goal of this project is to build a Python based handheld and battery powered scientific calculator the size of a cigarette box (well, pocket calculator). Scientific, as in \"reasonable precision\". Float32 (single precision) is certainly acceptable for a display precision of, say 6 or 7 digits, but the follow-up rounding errors are not - at least not for me. I experimented with Decimal math before but ended up having to fight memory constraints with jepler-udecimal (\u003ca href=\"https://github.com/jepler/Jepler_CircuitPython_udecimal\"\u003ehttps://github.com/jepler/Jepler_CircuitPython_udecimal\u003c/a\u003e) and my own extensions even on a Feather RP2040 with 256kB of user RAM. So I eventually decided to make a custom CircuitPython build and try to enable float64 (double precision) math. (Thanks to the Adafruit folks for their help!). Needless to say that float64 is entirely handled in C without the help of a potential floating-point unit (FPU) but then this approach is still much more CPU and memory efficient than implementing everything in Python. \u003cbr\u003e\u003cbr\u003eThe code for this project is on \u003ca href=\"https://github.com/h-milz/circuitpython-calculator/tree/main\"\u003ehttps://github.com/h-milz/circuitpython-calculator/\u003c/a\u003e and will be discussed in this article.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"If you want to build a similar project with float64 math enabled, you should be familiar with building custom CircuitPython images, the C language, patches, diffs, and other low-level stuff. If not, be sure to hop over to https://learn.adafruit.com/building-circuitpython/build-circuitpython first. \n\nPlus, you should know how to hold a soldering iron the right way.","metadata":{"class":"element alert-element build-alert alert-info","markdown":"","alert_type":"info"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe hardware is a Keyboard Featherwing V2 (\u003ca href=\"https://www.solder.party/docs/keyboard-featherwing/rev2/\"\u003ehttps://www.solder.party/docs/keyboard-featherwing/rev2/\u003c/a\u003e) from arturo182 (\u003ca class=\"postlink\" href=\"https://github.com/arturo182\"\u003ehttps://github.com/arturo182\u003c/a\u003e), an Adafruit Feather M4 Express (\u003ca class=\"postlink\" href=\"https://www.adafruit.com/product/3857\"\u003ehttps://www.adafruit.com/product/3857\u003c/a\u003e) and a 2000mAh LiPo battery.\u0026nbsp; The provided diff is against CircuitPython 9.0.0 - the build script automates the build process, and sits on top of \u003ca href=\"https://learn.adafruit.com/building-circuitpython/build-circuitpython\"\u003ehttps://learn.adafruit.com/building-circuitpython/build-circuitpython\u003c/a\u003e. If you want to build a similar project with float64 math enabled, you should be familiar with building custom images, patches, diffs and other low-level stuff. On the other hand, most math stuff is likely to work with float32 (single precision) math as well, but it's less fun I suppose.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/747/original/IMG_20240321_140458.jpg?1711027286","metadata":{"caption":"Keyboard Featherwing with the modfied Circuitpython image"}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eThe terminal window displays a couple of features of this tiny machine, from top to bottom: calculating pi using arctan(1), the arsinh of a complex argument, multiplying two numbers with uncertainties, and multiplying two fractions including reducing it to lowest (positive) denominator. \u003cbr\u003e\u003cbr\u003eThe dongle on the lower right is a PCF8523 RTC hanging off the Keyboard Featherwing's STEMMA Qt connector (which, sadly, is unusable if you want a handheld with a back cover ... What did arturo182 think?) \u003cbr\u003e\u003cbr\u003eOn the backside you can see the Adafruit Feather M4 Express and the LiPo battery, which is fixed to the Featherwing using a double-sided adhesive foam strip.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/748/original/IMG_20240321_111032.jpg?1711027593","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eRemoving the Feather M4 Express reveals some solder hacking on the Featherwing. The small copper wire connecting the keyboard interrupt line to pin 12 is not required by Arturo's BBQ10 I2C keyboard driver (which is a polling driver) but is a leftover of some experiments I made with a GiantBoard running Debian 10.\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eUpdate 2024-03-24:\u003c/strong\u003e The 2x 470k resistor divider brings the voltage at the USB pin down to the ADC measurement range 0..3.3V. The exact resistor values are irrelevant as long as they match by a percent or two. If in doubt, use a digital multimeter. The voltage is then measured at A2 and provides an automatic read/write remount of the flash if USB is not connected if the pin voltage is below ~4.2V.\u0026nbsp; See \u003ccode\u003eboot.py\u003c/code\u003e.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/761/original/IMG_20240323_194227_s.jpg?1711297920","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eSo let's have a look at some of the code, shall we?\u003c/p\u003e\n\u003ch2\u003ecode.py\u003c/h2\u003e\n\u003cp\u003eThe main file running the keyboard and display, as well as command dispatching. The UI looks and feels much like a real Python prompt but is fully terminalio emulated because you cannot simply run a local, interactive REPL. The UI runs Python expressions, statements and compound statements. The eval() and exec() calls are not yet hardened but I'll do that sooner or later (https://lybniz2.sourceforge.net/safeeval.html). (On the other hand, this is my machine, and why would I not be root on it?) \u003cbr\u003e\u003cbr\u003eMost of the code is a mess but pretty straightforward imho. The display is connected via SPI and controlled by an ILI9341 chip and the standard Adafruit driver. Keyboard and touch are I2C connected and driven by arturo's own drivers. Likewise the RTC, which runs on the Adafruit driver again. \u003cbr\u003e\u003cbr\u003eNext is some code concerning the command line history. All commands are appended to a Python list and supposed to be written to flash to make it permanent. I still have to figure out how to mount the flash read/write by default if USB is not connected. Maybe I'll use an additional SD Card for this. \u003c/p\u003e\n\u003cp\u003eThe following code parts try to determine if a command is an expression, a statement or the first line of a compound statement. The \u003ccode\u003eprocess()\u003c/code\u003efunction sends the command to \u003ccode\u003eexec()\u003c/code\u003e or \u003ccode\u003eeval()\u003c/code\u003e and returns the result accordingly.\u0026nbsp;\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eupdate_status()\u003c/code\u003e is invoked every couple of seconds from the keyboard polling loop. Here you can see what I mentioned above concerning the battery voltage. The value \u003ccode\u003emmax\u003c/code\u003e was measured with a full battery, and \u003ccode\u003emmin\u003c/code\u003e is the corresponding value for 3.4V. \u003cbr\u003e\u003cbr\u003e\u003ccode\u003edate()\u003c/code\u003e sets or reads the RTC. To set the clock, simply invoke date(\"2024-MM-DD HH:MM\") from the command line. \u003cbr\u003e\u003cbr\u003eSince you cannot run an interactive REPL easily on a local display, I emulate a REPL using \u003ccode\u003eterminalio\u003c/code\u003e. The first line consists of the Blinka logo and a status line, and the rest of the display is the command window. (The Blinka logo was shamelessly stolen from the CircuitPython source tree as a bitmap, put into an XPM file and converted to BMP using netpbm on Linux.)\u0026nbsp;\u003cbr\u003e\u003cbr\u003eThe terminal block cursor showing the inverted character was made as follows: In \u003ccode\u003etools/gen_display_resources.py\u003c/code\u003e (see info box) there is a script that gets run during each image build. It creates the Blinka logo and the builtin terminal font. I added some code at the end to expand the character map: the glyphs from 128-191 contain the same glyphs as 32-127, but bit inverted. Now if character 0x37 + 0x60 is displayed, it is actually inverted as shown on the image below (the '7').\u003cbr\u003e\u003cbr\u003e\u003cbr\u003e\u003cbr\u003e\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"alert","content":"All path names from the CircuitPython git tree mentioned in this article are relative to the top level.","metadata":{"class":"element alert-element build-alert alert-info","markdown":"","alert_type":"info"}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/750/original/IMG_20240321_145411.jpg?1711029616","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003cp\u003eWith the help of the 5-way joystick, I can move the cursor left/right or move through the history up/down as you would expect. Backspace deletes the character left or the cursor, and typing characters inserts text just left of the cursor. To take the image above, I simply moved up the history 2 steps up and moved the cursor left a couple of ticks.\u003c/p\u003e\n\u003cp\u003e(By the way does someone know where to get a rubber cap for the 7x7mm joystick? The ones in the Adafruit shop won't fit because they are to big. TIA!)\u003c/p\u003e\n\u003cp\u003eMost of the keyboard evaluation loop should be pretty straightforward. Bad spaghetti code.\u003cbr\u003e\u003cbr\u003eI find the ANS function on some pocket calculators very useful, so each time a usable result is returned, it is copied to the \u003ccode\u003eans\u003c/code\u003e variable, which you can pick up in the next command.\u003c/p\u003e\n\u003cp\u003eThe Featherwing's keyboard controller (Arm Cortex-M0) is still running the stock firmware, but the 4 rubber buttons in the top row are freely assignable. I assigned the rightmost button to SYM because with the stock firmware, the lower SYM button is unused and the keyboard map is sparse. With the help of the custom SYM button I could assign some more popular characters like [] {} \u0026lt;\u0026gt; and whatnot. See keymap.py in the git repo to see the mapping. \u003cbr\u003e\u003cbr\u003eIf you're wondering what I did with the \u003ccode\u003ekbd.backlight2\u003c/code\u003e in the key polling loop - I expanded Arturo's BBQ10 driver to also accept settings for the TFT backlight, in order to dim it down after a timeout of about 1 minute. See the code in the repo.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eumath.py\u003c/h2\u003e\n\u003cp\u003eumath.py is a simple wrapper to make real / complex handling fully transparent. Basically, each function simply checks the argument's type, and if it's complex, the complex routine is invoked from the cmath module, otherwise the real routine from math. Since MicroPython/CircuitPython's cmath is pretty sparse (I did not feel like patching \u003ccode\u003epy/modcmath.c\u003c/code\u003e), some complex routines are written in Python, making use of the real math functions. Exceptions are \u003ccode\u003esin()\u003c/code\u003e, \u003ccode\u003ecos()\u003c/code\u003e, \u003ccode\u003esqrt()\u003c/code\u003e, \u003ccode\u003eexp()\u003c/code\u003e and \u003ccode\u003elog()\u003c/code\u003e which are available in cmath. \u003cbr\u003e\u003cbr\u003eSome math and physics constants are provided by the namedtuple on top, which makes sure I cannot inadvertently overwrite pi with a 3 and thus invent an entirely new kind of math. Eventually I may add some conversion constants as well. \u003cbr\u003e\u003cbr\u003eThe special functions like \u003ccode\u003eerf()\u003c/code\u003e or \u003ccode\u003egamma()\u003c/code\u003ecurrently do not support complex numbers but then this is usually not something you do with a pocket calculator. But then, why not.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003euncertainty.py\u003c/h2\u003e\n\u003cp\u003eThis is a simplified version of the popular uncertainties module from Python, which has a rather large memory footprint and does some clever things which I don't need on such a small device. For example, I'm fine with the tuple representation and don't feel like parsing an \"a+/-b\" format. But the basic algebra is there, as well as most functions from the \u003ccode\u003emath\u003c/code\u003e module. The basic algebra represents Gaussian error propagation, and the derivatives of the math functions are analytical except for \u003ccode\u003egamma()\u003c/code\u003e and \u003ccode\u003elgamma()\u003c/code\u003e. This module was developed and tested on CPython 3.10 so it should work pretty much everywhere.\u003c/p\u003e\n\u003cp\u003eUsage example:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"from uncertainty import ufloat as u\n\na = u(3.14, 0.01) * u(2.71, 0.02)","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eufractions.py\u003c/h2\u003e\n\u003cp\u003eThis is a simplified version of Python fractions.py which does not run on CircuitPython out of the box due to a number of missing dependencies. I felt like re-writing instead of porting so ...\u003c/p\u003e\n\u003cp\u003eThe module is not complete as of this writing but the basics are there. Some work remains to be done.\u003c/p\u003e\n\u003cp\u003eThis module was developed and tested on CPython 3.10 so it should work pretty much everywhere.\u003c/p\u003e\n\u003cp\u003eAs fas ar input formats, there's the tuple format with \u003ccode\u003efr(nominal, deviation)\u003c/code\u003e, \u003ccode\u003efr(float, number of decimals)\u003c/code\u003e, and \u003ccode\u003efr(\"float as string\")\u003c/code\u003e. \u003cbr\u003e\u003cbr\u003eUsage example:\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"code","content":"from ufractions import frac as fr\n\nb = fr(125, 375) * fr(27, 81)\nc = fr.from_float(3.1415926, 7) # number of decimals to fend off float64 rounding\nd = fr.from_float(\"3.1415926\")","metadata":{"language":"python","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n        \u003ch2\u003eNumeric Integration\u003c/h2\u003e\n\u003cp\u003eI extended \u003ccode\u003eulab.scipy\u003c/code\u003e by an \"integration\" attribute to provide four numeric integration methods:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003equad (tanh-sinh, exp-sinh and sinh-sinh methods)\u003c/li\u003e\n\u003cli\u003equadgk (Adaptive Gauss-Kronrod)\u003c/li\u003e\n\u003cli\u003eromberg (Romberg' method)\u003c/li\u003e\n\u003cli\u003esimpson (Adaptive Simpson's rule)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eUsage example below. There is no \u003ccode\u003ehelp()\u003c/code\u003e available as of yet. \u003c/p\u003e\n      \n\n\n\n\n","metadata":{}},{"element_type":"code","content":"\u003e\u003e\u003e import ulab.scipy.integrate as i\n\u003e\u003e\u003e dir (i)\n['__class__', '__name__', '__dict__', 'quad', 'quadgk', 'romberg', 'simpson']\n\u003e\u003e\u003e f = lambda x: x**2 + 2*x + 1\n\u003e\u003e\u003e i.quad(f, 0, 5)\n(71.66666666666669, 4.040179044031267e-14)\n\u003e\u003e\u003e i.romberg(f, 0, 5)\n71.66666666666667\n\u003e\u003e\u003e i.simpson(f, 0, 5)\n71.66666666666667\n\u003e\u003e\u003e i.quadgk(f, 0, 5)\n(71.66666666666667, 8.38549416476104e-14)","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n        \u003cp\u003eSynopses:\u003c/p\u003e\n      \n\n\n\n","metadata":{}},{"element_type":"code","content":"//| def quad(\n//|     fun: Callable[[float], float],\n//|     a: float,\n//|     b: float,\n//|     *,\n//|     levels: int = 6\n//|     eps: float = 1e-14,\n//| ) -\u003e float:\n//|     \"\"\"\n//|     :param callable f: The function to integrate\n//|     :param float a: The left side of the interval\n//|     :param float b: The right side of the interval\n//|     :param float levels: The number of levels to perform (6..7 is optimal)\n//|     :param float eps: The error tolerance value\n\n//| def romberg(\n//|     fun: Callable[[float], float],\n//|     a: float,\n//|     b: float,\n//|     *,\n//|     steps: int = 100\n//|     eps: float = 1e-14,\n//| ) -\u003e float:\n//|     \"\"\"\n//|     :param callable f: The function to integrate\n//|     :param float a: The left side of the interval\n//|     :param float b: The right side of the interval\n//|     :param float steps: The number of equidistant steps\n//|     :param float eps: The tolerance value\n\n//| def simpson(\n//|     fun: Callable[[float], float],\n//|     a: float,\n//|     b: float,\n//|     *,\n//|     steps: int = 100\n//|     eps: float = 1e-14,\n//| ) -\u003e float:\n//|     \"\"\"\n//|     :param callable f: The function to integrate\n//|     :param float a: The left side of the interval\n//|     :param float b: The right side of the interval\n//|     :param float steps: The number of equidistant steps\n//|     :param float eps: The tolerance value\n\n//| def quadgk(\n//|     fun: Callable[[float], float],\n//|     a: float,\n//|     b: float,\n//|     *,\n//|     order: int = 5\n//|     eps: float = 1e-14,\n//| ) -\u003e float:\n//|     \"\"\"\n//|     :param callable f: The function to integrate\n//|     :param float a: The left side of the interval\n//|     :param float b: The right side of the interval\n//|     :param float order: Order of quadrature integration. Default is 5.\n//|     :param float eps: The tolerance value","metadata":{"language":"auto","linenums":false}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eCustom image\u003c/h2\u003e\n\u003cp\u003eLet's have a look at the diff representing my changes for float64 and complex math as well as the block cursor. \u003cbr\u003e\u003cbr\u003eSince I experimented with the internal LIBM first and then managed to also activate the internal LIBM_DBL, there are some leftovers which should still work. I enable the internal LIBM using the \u003ccode\u003eINTERNAL_LIBM\u003c/code\u003e switch as available, and introduced a new switch \u003ccode\u003eINTERNAL_LIBM_DBL\u003c/code\u003e to also compile and link the code in \u003ccode\u003elib/libm_dbl\u003c/code\u003e. After I got it to work, I noticed some subtle rounding differences compared to the GNU libm, so I ended up to setting \u003ccode\u003eINTERNAL_LIBM=\u003c/code\u003e and using the toolchain libm. Compiling with the internal libm initially threw some warnings as errors so I threw out \u003ccode\u003e-Werror\u003c/code\u003e for float64. But in the latest versions, this is no longer required (I should clean this up, really.) \u003cbr\u003e\u003cbr\u003eIn \u003ccode\u003eports/atmel-samd/boards/feather_m4_express/mpconfigboard.h\u003c/code\u003e, I switch on all the stuff that are required to configure float64 and cmath:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003ccode\u003eMICROPY_FLOAT_IMPL_DOUBLE\u003c/code\u003e makes sure the internal type for floats is 64 bit instead of 32.\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eMICROPY_OBJ_REPR_A\u003c/code\u003e forces CircuitPython to keep floats on the heap instead of in the pointers (keyword: object representation).\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eMICROPY_PY_MATH_SPECIAL_FUNCTIONS\u003c/code\u003e enables a number of math functions like \u003ccode\u003egamma()\u003c/code\u003e or \u003ccode\u003eerf()\u003c/code\u003e.\u0026nbsp;\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eMICROPY_PY_MATH_FACTORIAL\u003c/code\u003e together with \u003ccode\u003eMICROPY_OPT_MATH_FACTORIAL\u003c/code\u003e should enable the use of the internal \u003ccode\u003efactorial()\u003c/code\u003e function but this seems not to work (another ticket candidate I suppose) so I implemented it in Python.\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eMICROPY_PY_CMATH\u003c/code\u003e, well, enables the use of \u003ccode\u003epy/modcmath.c\u003c/code\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eULAB_SUPPORTS_COMPLEX\u003c/code\u003eshould enable ulab to support arrays with complex components but it does not. There is an open ticket for this. (\u003ca href=\"https://github.com/adafruit/circuitpython/issues/9052)\"\u003ehttps://github.com/adafruit/circuitpython/issues/9052) \u003c/a\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003ccode\u003eMICROPY_PY_BUILTINS_STR_UNICODE\u003c/code\u003e switches off Unicode support which should be the default on most boards anyway. This is required for my block cursor to use the extra half of the font bitmap.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIn \u003ccode\u003eports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk\u003c/code\u003e, I disable some modules which I don't need, mainly because linking the GNU libm requires some space in flash.\u003c/p\u003e\n\u003cp\u003eThe raspberrypi section for the Feather RP2040 is abandoned at the moment. The code compiles fine but there is a strange linker error in the last stage that I was not able to rule out for now. Anyway, as you can see, I add some RP2 SDK parts to enable float64. The other changes are similar to the ones for the M4 Express. Just configuration.\u003c/p\u003e\n\u003cp\u003eIn \u003ccode\u003epy/circuitpy_defns.mk\u003c/code\u003e, I make some compiler switches depend on float64 enabled or not. If not, the image should build like a stock image.\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003epy/unicode.c, shared-module/terminalio/Terminal.c\u003c/code\u003e, and \u003ccode\u003eshared-module/fontio/BuiltinFont.c\u003c/code\u003e contain extensions for the extra font glyphs for the inverted block cursor.\u003c/p\u003e\n\u003cp\u003eThe patch in \u003ccode\u003eshared-bindings/rgbmatrix/RGBMatrix.c\u003c/code\u003e was needed because the return value of the function \u003ccode\u003ergbmatrix_rgbmatrix_get_brightness_proto()\u003c/code\u003e is hardwired to float. It should be \u003ccode\u003emp_float_t\u003c/code\u003e, I suppose.\u003cbr\u003e\u003cbr\u003eAnd as mentioned before, the patch against \u003ccode\u003etools/gen_display_resources.py\u003c/code\u003e as mentioned before, extends the builtin font bitmap by the inverted glyphs.\u003cbr\u003e\u003cbr\u003e\u003cstrong\u003eUpdate 2024-03-24:\u003c/strong\u003e\u0026nbsp; The added files under \u003ccode\u003eextmod/ulab/code/scipy\u003c/code\u003e provide the numeric integration as mentioned above. \u003cbr\u003e\u003cbr\u003eIf you want to use the patch or build a similar custom image, you can try the build script in the repo.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eHardware hacking\u003c/h2\u003e\n\u003cp\u003eSorry whoever designed the Feather M4 board but I had to hack the LiPo charger. I'm using a 2000 mAh battery and would like to charge faster, like the RP2040. The schematics (\u003ca href=\"https://learn.adafruit.com/assets/57242\"\u003ehttps://learn.adafruit.com/assets/57242\u003c/a\u003e, see top right corner) reveals that adding a second resistor in parallel to R8 does the trick. I soldered a 5.1k 0805 SMD part (marked \"512\") piggyback on the 10k part marked \"10C\" and now the board charges about 3x as fast (with ~300 mA). I did not want to go higher because even if the schematics says 1000 mAh max, the data sheet for the MCP73831 chip says 500 mAh, and if I have learned something in 40+ years of electronics, it's sticking to official data sheets ;-) .\u0026nbsp; If Adafruit ever makes a new board revision please make LiPo charging faster as well. Or selectable by solder jumpers.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/752/original/IMG_20240321_163039_s.jpg?1711035181","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003e\"Roadmap\"\u003c/h2\u003e\n\u003cp\u003eThere is no fixed roadmap as such, but I have a number of ideas I would like to work on.\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSome cleanups are badly needed, and I would like to get the RP2040 port to run, also because the Feather RP2040 has a Stemma/Qt port for the RTC and the Feather M4 Express has not. Maybe I'll solder a Stemma socket on the M4 Express free space or something else which makes it pluggable.\u003c/li\u003e\n\u003cli\u003e\u003cspan style=\"text-decoration: line-through;\"\u003eNumerical integration using various methods like Simpson's rule and such. ulab.numpy allows to sum up stuff really efficiently.\u003cbr\u003e\u003c/span\u003e\u003c/li\u003e\n\u003cli\u003eThe number of significant digits on the display should be selectable, also the number format SCI or ENG.\u003c/li\u003e\n\u003cli\u003emore help functions\u003c/li\u003e\n\u003cli\u003ethe uncertainty module needs an output format where nominal value and standard deviation share the same exponent, like \"(3.14, 0.01)e+6\".\u003c/li\u003e\n\u003cli\u003eufractions may need some more functions, like more input formats. But for now it's okay.\u003c/li\u003e\n\u003cli\u003eplotting. Maybe for some simple cases, creating an image and displaying it using displayio. Ideally with zooming and panning using the touchscreen.\u003c/li\u003e\n\u003cli\u003erunning short Python scripts from flash or SD card. Think of it like math or EE solution libraries.\u003c/li\u003e\n\u003cli\u003eCAS or symbolic math would be way cool, but I'm afraid MCUs like the Cortex-M4 or the RP2040 are \"slightly\" underpowered as far as CPU performance and amount of memory. The Giantboard I mentioned earlier (running Debian Buster on ARM Cortex A5 with 128 MB of RAM, sadly discontinued and largely unsupported) can handle giac, octave-cli, or Python/Numpy/Sympy just fine albeit slow for complex calculations, and I also occasionally use Mathematica on a Raspberry Pi Zero 2W. Maybe pymbolic could help me to get something done, but you have to write the backend code yourself, which I am not savvy enough for. For Arduino mode, there are some really powerful C++ libraries like exprtk (\u003ca href=\"https://www.partow.net/programming/exprtk/\"\u003ehttps://www.partow.net/programming/exprtk/\u003c/a\u003e), but then I would have to write the whole frontend myself and not rely on Python to lift the heavyweight. Well ...\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eAnd there will be a simple back cover 3D printed with clear resin and polished to transparency. The thing will look like the following screenshot from FreeCAD.\u003c/p\u003e\n      \n\n\n\n\n\n\n\n","metadata":{}},{"element_type":"user_image","content":"https://cdn-learn.adafruit.com/user_assets/assets/000/000/751/original/Bildschirmfoto_vom_2024-03-21_14-34-36.png?1711034280","metadata":{"caption":""}},{"element_type":"text","content":"\n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n  \n        \u003ch2\u003eSummary\u003c/h2\u003e\n\u003cp\u003eThis should pretty much be it. The handling of the emulated REPL command line should be familiar for everyone who knows a Python command line.\u003cbr\u003e\u003cbr\u003eComments and contributions welcome! \u003c/p\u003e\n      \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n","metadata":{}}]