 Hello, everyone, and welcome to this year's Circuit Python Day chat with Jeff, Dan, and Katny. We get together every year, have a little chat about something Circuit Python related. This year, we're going to talk about Circuit Python features you didn't know existed, or you may not know existed, but probably should. Essentially, we're going to talk about things that Circuit Python can do that a lot of people don't know about, but might find interesting. We are here live. We're paying attention to the Discord, the Adafruit Discord live broadcast chat channel. If you have any questions, please post them at any time. You can get to Discord by going to adafru.it slash discord and join at any time. So like I said, feel free to post questions any time. We're definitely going to do a Q&A at the end, no problem answering questions throughout the whole thing. I will introduce myself first and then hand it over to Jeff and Dan to do the same. I'm Katny. I am sponsored by Adafruit to work on Circuit Python. I handle a lot of the library stuff and a lot of the guide work. So every time we make some new hardware and it has Circuit Python support, we want to make sure that goes into a guide so that when you pick that up, you can use it with Circuit Python without having to figure all that out on your own. I have no idea. I have two cats and a new workbench. I guess it's my exciting things at the moment. So with that, I will hand it over to Jeff to introduce himself. Hi, yeah, while muted, I was just ooing over your new workbench, which I'm surprised I didn't notice, but tunnel vision kicks in. Yeah, so I'm Jeff Epler or Jeff Epler. And like Katny, I'm paid by Adafruit to work mostly on Circuit Python, a little bit of everything, which is one of the things that I like about it. And my favorite drink is chai tea, like a milk tea. And I've got some right here. So pardon me if I slurp during the stream. All right, Dan, why don't you introduce yourself? All right, I'm Dan Halbert. I'm also paid by Adafruit to work on Circuit Python stuff. I'm one of the Circuit Python core developers, along with Jeff and Scott will be streaming later. And I've been working for Adafruit for just almost exactly six years. I think it was August 17th or something like that, 2017, when I first started being paid by Adafruit. So it's been wonderful and I'm gonna continue to do that. And what else could I say? I don't know, I've had a bunch of other jobs, but this is my favorite. And it's also a job that's really, it's a success. We're working on things that are successful, which is really nice. We're working on software because often people who work on software work on things that turn out not to be successful. So this is great. Also Dan wrote the Unix command more. That's right, I wrote the Unix command more in 1978. Which is also software that works. But I didn't write the version that's out there right now, but I wrote the version. No, probably not. All right, so with that, let's jump in. Jeff, why don't you start off with one of your features and then we'll just kind of go around and trade off on who's gonna discuss. So I think kind of the theme, yeah. And this is something that I actually just added to my list. So if you wanna bring up my screen, one of the things that we do in Circuit Python is offer translations of the error messages to different world languages. We obviously are all three American, living in America speaking English as our first or only language, but a lot of people don't surprise. And so we are using a service called Weblate where anybody can log in and offer translations of Circuit Python. And then we build that into a different version that you can download. So when you're on circuitpython.org, there's a box to pick what language you want when you download and we've got a number of languages here. So this is the Weblate website. So if you wanted to update a translation, it turns out the Brazilian Portuguese translation is fully complete, because you all down in Brazil are pretty awesome, but you would sign in and then find untranslated languages and you would just type the translation in your local language here and click save and continue. And now you've contributed to Circuit Python and made it make it work better for people who speak your first language instead of just English. So I think that's really cool. I'm the one who proposed using Weblate as a way to let community members do this because otherwise you have to like edit PO files in a text editor on your local computer and it's and learn, get and all those things. This is literally you, once you've signed into the website, you type it in and you hit save and now you've contributed to Circuit Python. So I think that's really cool. Yeah, having an approachable way to be able to do this is super critical because there are tons of folks who would be happy to contribute translations, but probably don't know see. If I knew other languages, that would be the Boat I'm in. So I completely understand. This was a good one. Thank you for bringing this one up. I hadn't even thought about this in a long time, which is why it is almost certainly a feature that you may not know about, but probably should. You don't have to understand like a limited amount of technical stuff, like what a percent Q means, but the learning curve is relatively quick to get up to the top of. And as with everything, we are on our Discord and would like to help you learn how to do these things and to contribute. Yeah, and we have a number of languages and we have a number of folks working on different languages as well. So if you get into a situation where you're unsure about something, you can talk to the other folks who have been working on that translation and work through that. And what was the other thing I was going to say? It's super helpful because it also makes CircuitPython more approachable for folks who don't speak English as well. And these are getting used. We get folks coming into Discord posting their tracebacks in other languages, which is fantastic. Obviously, sometimes we need a little help to know what exactly it says, but we're definitely seeing them getting used. And so you are helping other folks when you do this and that's a great thing. Yeah, we are like limited in terms of like, especially with display, I owe what we can display. So for instance, our Chinese translation is using Latin characters, which is not the ideal, but it's kind of a compromise. And because of the limited nature of CircuitPython, there are compromises like that. But yeah, we have all these languages that people have contributed varying amounts to and the ones that are reasonably complete, we build into the files that you can download every day from CircuitPython.org. But if you are someone who speaks in rights Hindi or Korean or Turkish or Russian, Indonesian, it would be great for you to step forward and kind of bring these translations up to a level where we're like, this is useful and we'd like to give this out to our world users. For sure. Excellent. Or languages that aren't listed here, we can add more. Oh, absolutely. Yep, that's the other thing. Start a new one if you find one that you know, but is not present. Thanks, Jeff. Dan, why don't you go ahead and talk about one of your features? Okay, sure. So I was gonna talk first about buttons and balancing. You can go ahead and, so one thing that's true about mechanical buttons is that if you just try to, if you press them, they actually bounce up and down when you press the button and this is actually, there's a plot here of that happening so that you don't get a clean on off. So there's a lot of ways to do the bouncing and people who don't know this assume that when you turn the switch on, it's on and when you turn it off, it's off but in fact, there's this mechanical contact bounce. So there are a bunch of ways to do the bouncing. They're really simple ways just like, you just wait a little while, you wait long enough so that the bounce stops to make sure that there are no other, there are no bounces happening before you decide that switch has changed state. And there's a learn guide here you can find in the chat about, which is kind of all about the bouncing and it's about the bouncer library that's written in Python in circuit Python that you can use. And we use this for a while but then it turns out, you also had, you wanted to be able to do kind of fancier button inputs and the bouncer was kind of one button at a time. So a few years ago, we started working on something called the keypad module, which is, lets you treat single buttons, also key matrices, which is how your typing keyboard works and also something called shift register keys, which is how things like Nintendo controllers and the like work, they scan a bunch of buttons and then transmit that serially up to the host computer. So if you use keypad as built into all, to all of the absolute smallest sort of Python builds, it's not on the same B21 builds, but it is on everything else. And keypad sends events that are like, this button went up, this button went down and also it has time stamps in there. So you can even tell how long a button has been held down. For instance, Jeff added the timestamp stuff, which was great. So in general, if you use keypad, it's a little simpler to use than the debouncing library. If you have multiple keys and it's a little more compact and you get these nice events. So I would say, look at both of these guides. I'll put a link for keypad module in. Also look at the debouncing guide to kind of understand the problem and also look at the keypad guide to see how you can use this in your everyday programming. And it's really easy. There are a lot of examples in these guides and you can just copy and paste them into your program. Yeah, even with the debouncing library, the code is always longer. And in fact, before the debouncing library was in to keep the button pressed from spamming, usually wrote a bunch of code that was like sort of state tracking and like had a state tracking variable that said, okay, well, if it's pressed, but it's not true, like then it's just been pressed and it hasn't been released yet and all this stuff. And so keypad basically took what was just a bonkers, chunk of code that was the sole purpose of it was to make it so that it was known that the button was pressed and then released and turns it into just a few lines of code where not only can you track press and release, like you can track that it's held, you can track how long you can do multiple at once, because that's something that the code that I used to write would never have handled. It wouldn't have handled multiple buttons. It was always duplicate chunks of code to do that. So this was super excellent when this came around. I was really happy about it. Yeah, thank you. And that point about not having to track the state in your own code, it sort of didn't really occur to me to just express it that way, but it does. It keeps track of, like it'll tell you something, not that it was something went down. And so you don't really have to keep that state a lot of the time, which you used to have to. So that works out really nicely. All right. Sometimes you can tell your code is clunky, but you can't identify just what would make it better. And then it turns out, keypad can make some of those clunky things, it'd be expressed much better. Yeah, exactly. What Kenny was saying is concretely that. So I guess I'm repeating myself for repeating us. And you can, some people are worried, you can set the bounce time in keypad. So how fast it scans. It's not exactly the debounce waiting time, it's really how fast it scans. So if you have key switches that are really bouncy, you can lengthen it or if you don't have that, you can shorten it. So you can tune it to your own uses. And somebody actually also just added an addition to the shift register stuff to allow tracking multiple inputs from multiple shift registers at once, which is an interesting thing to do. So there's a question in the chat, which is, is it possible to listen for multiple buttons being pressed at the same time, like button one plus button two? So the answer to that is to say not directly. I mean, this perfectly well handles that different buttons are pressed, but you would get a button one is pressed event and then you'd get a button two is pressed event or vice versa and your code would have to track that in some way itself. Right, an event contains whether it's pressed or released and also the number of the button, which is just an index into the list of buttons. And as you were saying, the timestamp. And the timestamp, right, right, right. So you could do if button one, or if key number one is pressed and then if key number zero is pressed, you could have that as an if and, and then have an event occur, like have something occur based on those two buttons being pressed with those two events happening. Right, I don't know if I have an example. I don't think I have an example of that, but you'll be easy to figure out. Yeah, like if you looked at a more complete keyboard example where you're interested in, like if shift is held, it changes what the A key does. The way I've written that is when shift is held, I set a flag in my code. And then when a key that produces a letter is pressed, it looks at the shift and maybe changes what that key, what that ASCII value is, if that makes sense. That makes sense, right. So someone else is asking, do you need to debounce the new key I2C breakout? And I think the answer to that is no. Yeah, I'm pretty sure the answer is the CSEL microcontroller is doing a debounce for you. Right, it's not using keypad, it's using its own internal code to do that. Keypad works excellently with it, but the debouncing bit is not a necessity. Can you explain real quick how you would instantiate a basic keypad object? Sure, there's, well, there's an example right here. There we go, this is as big as possible. So I'm just, I import board and keypad, and then I create a keypad.keys object here. And I give it a list or a couple of all the pins that the keys are on or the buttons. Then I say whether or not when you press it, the key goes, the button is gonna go to high or to low and often buttons are tied to ground rather than plus 3.3 volts. So often value when pressed is false. That is when you press the button, it will go low. And then it takes care of doing the pull-ups. So you don't need to worry about setting the pull-ups, which you have to do if you handle buttons by yourself with a digital in-out. And then you say whether or not you actually do want it to handle the pull-ups because sometimes a keyboard comes with its own pull-ups and you don't want to interfere with those. So if you just work through the examples that are in this guide, I think you'll get the hang of it really quickly. And there's an example like the MacroPad, which a lot of people really like about it. There's a very simple MacroPad example here that does not use the MacroPad, the, what's it called, the library that. Oh, yeah, I don't remember either MacroPad library, but MacroPad library, which is used in the guides, which is very complicated. This is a simpler example, which does not use that, which is you had to do it at the lowest level. And this turns your MacroPad into a numeric keypad, basically. So that maps, where's the picture like this? One thing about keypad is in your loop, you will always have the event object, is that correct Dan? That you create that variable that is looking for the events. Yeah, but event might be, if there's no, if no button is, if no event has happened, event will be none. None. So you have to keep polling it over and over again. Eventually when we have like add maybe more async IO features, we'll make it so that you don't have to keep doing this polling yourself. Might add some async kind of. Yeah, I have some code somewhere. It may have bit rotted that lets you say await keys.events or something similar to that. So that you can wait for some key to be pressed or released and then the code of your choice runs. Yeah, I think you forgot you added that. So that's another thing here are the features that we might not know about, but you should check into. So, yeah. And that that's that thing I was just talking about is in this category of there really should be a library but we haven't created it yet. And that's things that would make things work with async, which we may touch on async more in a little bit. I don't know. All right, I will talk about async a little bit later, but I think this is we can move on back. Okay. Well, before we move on from, from keyboards, if you just want to pull my screen share back up, this is I'm not going to like explain this line by line, but this is the code running on the keyboard that I'm typing on right now that I use every day. And it is using the keypad like we were talking about. So it's got a bunch of rows and columns and it sets up the key matrix. And then there's all this complicated stuff. So I can send my volume keys and map my keys and all this, but it just works like a keyboard. And that's one of the most exciting things. It's the circuit Python project I use the most and it's using this code. Wow, that's very nice. Okay, yeah. So you don't have to use KMK or something you could use, is this code in a guide somewhere? No. I know Scott has written a very simple one that occupies like one screen full. And I think that works with keypad, but I don't know that we have a full keyboard example in the guide, we should. I've got, I do have a number of keyboard guides on Adafruit, I'm not sure. Yes, one of them does use this. I will find the link to that and drop it in the chat while y'all start moving on to the next topic. All right, time to read. All right. So one of the things that we, I think a few of the things we're gonna talk about fall under Python features that are also in circuit Python, which we keep adding new ones or subsets of them. For example, microlab is a subset of NumPy, which is not really a Python feature, but it's a Python thing. And so sometimes we add like more limited versions of Python features. One of the features that circuit Python supports that is, you know, from, there's also part of regular Python is generators. I think not a lot of folks use them, but also I'm not sure how many folks know that generators are supported. I weigh early on in my aid of free experience, wrote a guide about hacking Ikea lamps, which this uses the circuit player and express. I hope the code still runs on it, but the fun thing is when you look at just stuff like lamps or, you know, creatures or whatever, especially Ikea stuff, think about whether you can cram a microcontroller and some LEDs in the bottom of it, because it turns out a lot of their stuff is very easily modified with not a lot of effort. This little, hopefully there's an actual image of it. I'm trying to click on the screen share, seriously. This little creature thing had a whole little bulb, bulbous plastic deal inside of it. It was just like a rubber cover and there was a groove that would hold the plastic piece in there and the groove was exactly the right size for a circuit player and express, which was a complete coincidence, but worked out very, very well. So just as a arbitrary side note, let yourself think about being able to modify stuff that you may not otherwise have considered doing to turn it into something much more fun. So this particular project used generators to be able to cycle through the rainbow cycle, which at the time anyway was a very blocking piece of code. It runs through the whole rainbow cycle and then for, you know, however short of a nanosecond or whatever, it would let you do something if you hit that exact moment and then it cycles through the rainbow cycle again. And so what I wanted to be able to do was have it make a rainbow, but be constantly looking for other input to move on to do another thing. Now, this is something that I believe async.io completely handles, but generators were complicated enough for me at the time. I had a lot of help with this code. So I don't know. You should also add that the LED animation library was not yet. That's also true. And what's entertaining about that is the LED animation library came out of me complaining that rainbows were too slow. And so we just, because of that, decided to write something that would make rainbows faster and that's what that actually came out of. And this is one of the projects where I would have complained about rainbows being too slow. So yeah, so this chunk of code here, this wheel code is blocking. Like I said, has to run through that whole thing before it, so through the whole set of colors before it stops and then lets you do a thing. So in here, there was a color sequence created and then range 256 is the rainbow cycle. And it had two generators because we needed basically a throwaway generator that just stepped through a series of elements. And then the rainbow lamp one is what actually turned on the neopixels and then stepped through turning them on. So I ended up doing this with two generators. I don't remember how many are in the final code. This was a shorty example to just explain the generators, but notice if you tap it, it goes into the next color sequence, which is just a single color or at the bottom it would cycle through six colors or seven colors rather. And then if you shake it, it changed the pace at which the speed of things. And then I think, I'm trying to think, one of them I think did, at least in the final one did brightness, but I think that's not what this one did. Anyway, point being this particular thing because we didn't have async.io and we didn't have the LED animation library was what was necessary to have a non-blocking rainbow without having it pause every time it would run to give you a chance to do a thing. So we've supported generators for years and I'm assuming from the beginning because I came into this not long after CircuitPython became a thing and was able to do this example within not long after that. Yeah, it was February 20th, 2018, so. Anyway, so that's a feature that you may not know about and might be interested in is using January. Yeah, you talked about how you didn't understand this code when you started the guide. How do you feel now these years later about generators? Did you get to a comfort level with them or are they still kind of- There's so many, entirely because I haven't used them. Yeah. That's, when I use things, I obviously, the more it sticks in my brain and I just haven't had an opportunity really to use generators. Like being able to insert an existing thing, an existing generator into a piece of code that I could probably do simply because I kind of understand the gist of what's going on, but like writing one up myself that does a thing I need. No, I still can't do that. This one just was something, it was a feature that I worked for what I wanted and the friend of mine that was helping me with it basically said like, this is the only option for that. And I said, well, let's do it then. And I never got an opportunity to use that piece of information really at all, specifically enough to hang on to it. So, all right, I think we're good there. Jeff, what else have you got? Yeah, so let's see. I'll close up that tab with the keyboard in it. So let's talk, I really should have brought the project down because I have the project, but let's talk about wrapped text display. And I guess I can bring up the related guide page. So we've got displays on circuit Python, but they are not as big as your computer monitor. And so when you want to display bulk text, it is kind of a problem. So this is a project I did with open AI and it plays a Zork style game. But the problem I had was, especially with the smaller PyPortal pint, the amount of text returned by open AI wouldn't fit on the screen. And this GIF is not demonstrating it, but what it'll do is it'll actually display it and then it'll pause and scroll it up one by one line and up by one line until you've seen everything so that you can see all of the block of text. It takes care of the text wrapping and handles new lines, some of which is built into the bitmap label library, but some of it isn't. So anyway, this is a candidate for something that should be a library or should be in a library, but isn't. And it's this class called wrapped text display from the guide code. And so you can like replace all the text, you can add text on incrementally because this may not be the ultimate version of the code. There was another guide which actually demonstrated the open AI, it goes a word at a time feature that like adds incrementally. So this isn't the ultimate version. And then you can scroll to the end, scroll by one line with wrap around, and then it like does some trickery to make it work reasonably fast because performance is often a limitation with display IO. And so there was figuring out what makes it perform the best. So yeah, then when you want to use it, you just set the text to your selected text and down here somewhere while it is waiting for your input, which by the way uses the touch screen. Where's the code? The organization is not always the best, is it? Well, I'm not 100% sure where this is. There should be some code here which makes it scroll by one line and sleep and wait for the touch, but I'm not seeing where that is. But that's the idea is when you want to display a lot of bulk text and let the user go by lines or let it automatically scroll by lines. And so somebody should grab this code and make it into a library because there aren't enough hours in the day for me to do it sadly. No, this is fantastic because this is an issue especially with the really itty bitty displays. Obviously to display something that works on a pipe portal on a feather display would be a bit difficult but it would be doable with that library versus being completely impossible. Yeah, so this is an example. This is the more full version I think. We may actually see the text. It was tricky to get it to update and be relatively fast. And then I hope one of these shows the scrolling but maybe not. Well, I guess not. So, Jeff, are you able to drag with your finger? Like drag, I saw you added touchscreen. I was gonna ask you. I think the touchscreen is only to tap to do the next thing. In the first program, the Zork, you're always presented with choices one, two, three and four and you touch the screen in one of the quadrants to make your choice. Oh, okay. It doesn't implement like drag scrolling. That would be great. But you could implement drag scrolling because you have the API go up and down. So it's a matter of calibrating the drag scrolling. So that would be a very easy project. There is no, do the PyPortals have a touchscreen? I don't think. The PyPortals, I think all have touchscreens. Okay, so I don't know how well it works because they have resistive touchscreens properly. Yeah, it worked really well to select the screen by quadrant but anything more fine grained. But if it's relative motion, you're interested in the calibration is not as important as if you're trying to pick a spot on the screen. I think it would actually be a better use for resistive because it's about how far does it move during the event? That would be a cool addition. That would be pretty interesting. Like even to have a little bit of history in the terminal window, for instance, so you can go back or something. That's an idea. Here's a feature. I love giving people ideas. It isn't a feature. Potential features, right? We'll do another. So to step back a moment, we have a question from two questions, but the first one from the YouTube chat is, can we say something about the overlap of general Python to and circuit Python? Yeah. So is that with the questions that I'm not looking at the YouTube chat? Yes, that is the question. So a lot of the base language features are similar, okay? A lot of the things that you, what you might call introspection in Python, like finding out things about functions and classes, not all of that is present because we afford to micro Python or micro Python tries to save space and time by not implementing some things. Like the ability, for instance, when you control C to be able to get to certain variables in the current scope, for instance, or change some things on the fly in Python, you can sort of change your program out for under itself. You can't do that very much, but we have all these other features, like generators and async await and dictionaries. What we also don't have is we don't have a lot of what's called batteries included libraries in Python, Python out of the box when you install it comes with a lot of libraries. And so those libraries take up a lot of space. We don't have room for most of the libraries. So we have a few of them are implemented like the regular expression library. Some of those libraries are implemented in C to save space and the JSON library, but there are a lot of other things like, say, the CSV library or some network libraries or something which are just not gonna be, they're not in CircuitPython and they might even be too big for CircuitPython. MicroPython has a lot of these, there's something called the MicroPython lib repository, which has a lot of cut down versions of these libraries. And most of them don't are not MicroPython specific. So they probably work on CircuitPython basically almost right away. So that's a difference kind of in the library environment of the two things. But otherwise, we try to make CircuitPython be a proper subset of what's available in regular Python so that you can take a piece of code that you wrote in CircuitPython and run it in regular Python. And if it includes things that are specific to CircuitPython like manipulating the pins on the board, then we have this compatibility library, this emulation library called Blinka, which will emulate that stuff on things like the Raspberry Pi and other single board computers that have pin support of various kinds and bus support and things like that. So in that sense, we're not a proper subset because we have native modules that aren't in Python, but we have this shim library that would allow you to do that. So does that answer, if the person in YouTube, does that have any follow up? And the basic control flow and that sort of thing is all the same. So the second question or follow up question, could I go out and find some super new library and expect to import it into a CircuitPython project? The answer to that is no. Libraries for CircuitPython are written in a certain way to work with microcontrollers. There's some stuff that is more general, but still it's possible to port, but it won't just be a drop in situation. I mean, it's just kind of a segue into my next topic, which is I did a port of the decimal Python package into CircuitPython. And I went and looked at what it was and there is a lot of code in there compared to say the capacity of, I think I was working on an NRA 52 and just like the bulk of the decimal.py file was more than you could load on the device's RAM. And so it was a matter of saying, well, what's important to me? Which features do I need? And leaving those and deleting the rest of the code and then making it work again. But if you need like arbitrary precision decimal arithmetic, you can do that in CircuitPython. It is largely compatible with the standard decimal library but not 100% but it hit the notes that I needed. And then I added some other stuff. I added an implementation of trigonometry functions which are not in the standard Python version. And so this, if you ran this and just said from decimal import decimal, it would be the same. And that's kind of the philosophy of CircuitPython. We'd like it to be close. We'd like it to be as much as possible but because of the capacity of one of these versus a desktop computer or a Raspberry Pi 4 you can't have everything yet. That's exactly right. As for the last follow-up question, how can I find out without a big experiment that I shouldn't expect some library to work with my CircuitPython project? I mean, you can try to use it for one but honestly, your best bet is to talk to the community. Somebody might have not only tried the thing that you're trying to do, they may have written some code that makes it work. There's a help with CircuitPython channel on our Discord server that a lot of folks hang out in. That would be the place I would start if I had some library for some function that I wanted in CircuitPython, I would go there because these are the people who are working on community libraries and all this other stuff that Adafruit might not focus on but it's still super important and helpful. So, I would check with our community before trying to put in a lot of work and not to mention it may not be that much work to try to make it work and that's what these folks can also help you with is the how to port things versus not bothering because maybe it's something that just isn't gonna function. Yeah, if it's something that is coded in C and then it's wrapped into Python, that is gonna be very difficult. If it's something that depends on a lot of other packages or libraries, like if you say pip install my cool package and it says, great, I'm installing 200 other things, you've got a tall order. But if it's somebody wrote 100 lines of Python and it does something really cool, then that is where you're gonna probably have some success. That's just based on my experience and my work on the core. I mentioned the thing about C and C++ and other compiled languages because although the Python language to code in is pretty much the same, the APIs that you have when you are interfacing to it from C are totally different. They're totally unrelated. And so compiled code doesn't come over, but Python code does. Excellent. Thanks for answering these questions with me. So, Jeff, anything else you wanna talk about with decimal? So, I mean, I can talk about continuing the discussion around what is the same and what is different. This here, this is a sub module that does testing and this wouldn't run on circuit Python, but I can run it during the CI process and test the Python code to make sure it works. And so that is a big benefit of having code that runs on a desktop platform as well as on the microcontroller. The parts that interface with hardware, they're really hard to test, but the parts that are doing a computation or performing an algorithm, you can structure your code so that you can, you know, test it on your desktop computer without necessarily a piece of hardware. Yeah, and then, you know, just examples. Like, apparently I decided it was important to support money formats. I don't remember actually why that was, but you can format your money in circuit Python if you need to. But yeah, I don't think I have anything else specific to say about U decimal, but we've got a couple of these. This is in the community bundle, by the way, and I know that Melissa did a reduced version of the date time library and that is in the main bundle. And so it's a similar thing. There's just a lot to the date time library, which it's not so much that you ever think about how big it is on a desktop computer, but it's enough that we had to select what subset of it was suitable for circuit Python in order to, I think it was some kind of web project, you know, where you wanted to deal with times and dates. And yeah, so she did that, and that is in the community bundle. It's not a part of circuit Python proper. You install it with CircUp, which is probably another tool that we should make sure everybody knows about. It's like PIP for circuit Python. It's called CircUp, and a community member wrote it. So that's pretty cool. I can speak to that in a bit. Dan, what do you have up next? I was gonna talk about, you are communication with your program or maybe USB serial communication, kind of two different things that are kind of related. You know that if you've used like the serial window in Mu or anything else, you can talk to the REPL in circuit Python and type at it and have things happen. And there are things in Python like input and print that read and write in that context. But they're always kind of influenced by the fact that you're going through the REPL. So if you, for instance, control C, you'll stop the program and maybe you don't want that to happen. So one thing that we added when we added more general USB support was the ability to have a second serial port that goes through USB. So right now, if you just plug in your sort of Python board, then on a Windows computer, it'll show up as a COM port and you can type it the REPL on Linux. It'll be DEV TTY something or other like ACM something. And on Mac, it'll be like DEVCU.something, okay? And those are all ways of talking to the REPL. But in, you can also, let me put a link in the chat, you can also, on many boards, you can turn on a second USB channel so that instead of coming up with just one COM port, it'll come up with two. The first one will be the REPL. And the second one will be strictly for exchanging data, binary data back and forth or ASCII data back and forth between you and the host, between this board and the host computer. And so any, you can type control C in that and when errors happen, they won't show up in that window, in that channel. It's just a completely separate serial channel that you can use for talking to the board. And so this is really handy if, for instance, you wanna take data and report it back up to the host computer where maybe you've written a Python script or a C-sharp script or something like that to read from this port. And so if you look in the customizing USB devices and circuit Python guide, and I posted that in the chat, then it describes how to turn this second channel on. So the first one is called the console port because that's the REPL port. And the second one is called the data port because you use it for exchanging data. And unfortunately, we don't have really, some really good examples of that. There are a few examples in the learn guides. If you search for console, you might be able, first for USB CDC.data, you might find it. But look in this guide, the customizing USB devices guide on this circuitpy MIDI and serial page. Look, here it is. And you can read more about this. And often people don't realize that this is available, so they spend a lot of time trying to use input and things like that, which are a nuisance, especially when you're trying to send small bits of data or you want to send binary data back and forth. So check that out. Now, unfortunately on the expressive boards, their USB hardware doesn't support enough USB devices to make this very practical, but it works fine in RP2040 and the SAMD boards and many other boards. It's just expressive, those other boards have like eight. You can have sort of like, what are called eight USB endpoints and on expressive, you can only have like five or six, kind of, and they're already used up for the circuitpy drive and other things. So the guide also explains how you can get around that problem, but at the expense of maybe not having a circuitpy. But check this out if you want to talk to the board because as you're connected to a host computer. And then kind of in the same vein, there's a guide here about how to talk between two circuitpy on boards. Let me make this smaller. Your communication between two circuitpy on boards. And this is a slightly complicated example, but often people say like, how do I talk between boards? Maybe I try to use I2C and one of them is the I2C controller and the other is the I2C peripheral. You can do that, but it's more trouble than it's worth in many ways. Just using UART to talk between two things is fine. And here we're talking about real UART, busio.ur. So check out this guide if you want an example of that. And it's a really simple way of communicating between two boards immediately without getting into a lot of fancy protocols. It wasn't in circuitpython, it was in Arduino, but I did this for the CPM emulator project because the display was on one board and the USB keyboard was on one board and they had to talk to each other for the project to work. And it's really simple and handy because you send a byte, you get a byte. It's conceptually pretty easy to work with. So that's another thing that people sort of would think about right away that I wanted to talk about. Excellent. Okay. So CirCup, let's talk about that for a bit. CirCup is a utility that, as Jeff so aptly said, is kind of like PIP for circuitpython. Let's see. It was written by Nicholas Tolarvy, who is one of our longtime community members. And there is a guide for it, which I will send to the chat right now. Just go to that. Okay, I'm not gonna send it to the chat. So there's a guide on using it. There's, you need to have Python installed and you do use PIP to install it. So be aware of that. And then it is installed using PIP, which is simple and also remember to upgrade regularly when there's new info. And- CirCup will tell you when there's a new upgrade of it available. Just like PIP. So you can see everything that it can do when you do dash dash help or just type CirCup and it will display all of its options. So like PIP, freeze will show you a list of everything that's there. Install, you provide it a module name and it will install the specific thing on the device. List is all the out-of-date modules apparently. Show is available modules in the bundle and update is update the modules on the device. You can do an all with that and it will, if you have a project you did a year ago and you want to just make sure you're running the latest everything, you can do update dash dash all and it will update everything. Just be aware of any breaking changes that affect your code at that point. But yeah, so it works a lot like PIP. And remind me, I'm now forgetting. Yeah, install and update are the ones you'll use every day though. Yeah, so if you, I'm trying to remember, if you have code and you want to, there it is, that's what I was looking for. Thank you, Tim. So CirCup install dash dash auto is what will take your code.py. It'll see all of the modules that are the libraries that are required and it will automatically install those libraries for you. So inside of the Adafruit Learn System, we have the project bundles, which provides you with your code.py and all the libraries in a folder and any other assets that you need. That's super convenient, except what happens when you're not using the Adafruit Learn System. You would normally have to download the bundle and then drag, drag, drag and wait for your serial console to tell you the cascade failures of the ones you're missing and so on, which is what used to be the case. And instead, you can do CirCup install auto and it will grab everything that is required by your code.py file and drop it right on there. I guess be aware, because it does dump it in the library folder. So even if there are boards that already have those libraries frozen in, it won't affect that. The only effect you may have is that sometimes there is space issues on very, very small boards where you don't want to install all the libraries. So you would want to do individual ones at that point, but yeah, auto is fantastic. The other caveat about auto is if you split your code into code.py and another file that you import, it doesn't look inside of the file that code.py imports that's also on your CirCup Python drive to figure out imports that are needed, which for most people is a really minor problem because your project isn't big enough to split into multiple files, but it can kind of come up if you're, for instance, in the midst of developing something that you want to turn into a library, CirCup doesn't figure that out and install a library that's needed by the other file that you use and import from your code.py. I don't know if that makes sense. No, it totally makes sense. I was gonna say, there are also people who will use other files as classes, basically. And so it's not just when it gets too big, it's just some folks use it for organizing and so you're very good idea bringing that up because it's not just like there are people who will run into that and it's worth, it's worth being aware of. David asks if there's a way to work with requirements.text or something similar to distribute the list of libraries needed. I don't believe so. So that's something that I think if you have your own project that has requirements.text, it would require an update to CirCup to be able to do that feature. However, there may be something, someone interested in doing that. So here's another feature that could be added to a thing that isn't a feature yet is allowing CirCup to be able to work with just a list, a separate list. Interesting, there is an open issue suggesting that and they think that potentially you could use the same requirements.text as for standard computers, you would just say, you just put a note that says system equals circuit Python essentially. Oh. So that's an interesting idea, but it's just an issue or a feature request. Nobody's actually written the code. Gotcha. And Tommy Guy says it does have a dash dash requirements argument. Yeah, the guide is old then. Which is obviously- Oh, that is the way of guy- I know, we wrote it early on. Wait, there are thousands and we have to keep them all updated. So if there is a problem with a guide and you notice that there's a link scroll, so it's right by my finger. Hold on. I can't- That's right. Yeah, a little further. A little further. Keep going. Keep going. There we go. Click that link. And as it promises, the Adafruit elves will correct the problem. Yeah. It's helpful to mention the specifics of the thing that is the issue because the way that that feedback works is it only tells you the page. And sometimes if you're on the wrong page when you click it or you're viewing the whole guide as one page, so it's super helpful to just add in there, hey, it's this section or something of that effect. But honestly, it is super helpful because we fix the things when we know about them, but we don't always know about them. So yeah, leave that feedback and we do get to it. And yes, so the community bundle also works with SirCup. So any library that you grab, any SirCup Python library period, as long as it is in a bundle will be installed by SirCup if needed. So the UDESMA library that we just talked about is in the community bundle, right? So if you used that plus an Adafruit SirCup Python library, both of them would be installed by SirCup. So you don't have to figure out grabbing the extras as well. I think that's what I've got. Jeff, what's your name? All right, well, since we're talking about bundles, I want to tell you about a whole bundle that you may not even know about. It's not the Adafruit bundle, it's not the community bundle, it's the keyboard layouts bundle. So community member Naradak responded to a need for, so like one of the things you do with your keyboard projects are you want to type a word or a phrase. And if you're not running on an American keyboard, like this spot on the keyboard is always the same identifier in a USB HID message, but on a French keyboard, it's A, not Q. So if you need to type quick, it won't come out right if your keyboard layout is French. So like they're in one of the libraries, there's a send string function. And so if you're not using an American keyboard, what you need is keyboard layouts for circuit Python, which Naradak, I think, grabs some resources from somewhere else and turns it into Python files. And each of those Python files becomes a separate library within the keyboard layout bundle. So once you've added it, you can install just the Windows French keyboard layout files onto your computer. And then you use that similar to any other thing that you do with USB HID, you just, when you're sending a string, you send it using something within that library. So if you want to write, and depending on the keyboard layout, you can write things with accents. I'm aware that there's code in there, like if you needed a e-circumflex, you could do that and it works if the underlying keyboard supports it. So that's a very cool thing that again, international users care a great deal about. As Americans, we don't think about it a whole lot. Anyway, that's really cool. And I appreciate the effort that went into crafting this, into creating a bundle into putting it all together and serving a whole world full of people or many more people than we served with just the US keyboard layout. Absolutely. And we get this a lot, questions about this. So this was an excellent one to bring up. Regularly, we have folks say like, hey, I am looking for, because all of our guides refer to the US layout for obvious reasons. And so we'll get folks showing up saying, hey, I need a German layout. What can I do? And for a very long time, it was... We're sorry, we're American. Question mark. And now we can just immediately point to this and say, hey, look, this has many layouts. And thank you so much to Naradoc for continuing to support this. This is something that Naradoc supports specifically. And that's so incredibly appreciated as a community member to keep something like this going that is so helpful to folks. So, excellent. Dan, what else? So a discord question. Thinman asks, any chance you can talk about ESP now for Circuit Python? Such a small section in the Remy docs. Is this cutting, is this two cutting edge works great in Arduino? That is something that I also want to learn more about. A community member whose name I don't know offhand contributed it and I've played with it a little bit but I haven't gotten to the point of having a project to do with it. And I don't think we have any projects in the learn system about it. But it is a cool feature. It is a mesh networking feature that works with expressive microcontrollers. And you don't need an access point or to assign IP addresses or anything. It's just if the two devices have, I think they have to share a secret key or something then they can communicate when they're in proximity. And Anecdata has helpfully provided a link to an example on GitHub, a GitHub gist. So that's cool, I'll be taking a look at that after the stream. I pitched the idea of making a wireless keyboard with ESP Now and two expressive microcontrollers but we decided not to do it because we just didn't know enough about it to know if that's even an appropriate choice like what about, just what about all of the things. And so it's nothing I've done yet, but maybe someday. And Anecdata says encryption is optional. So that's good to know. I think for a keyboard you'd want the encryption because you don't wanna be sending your password in plain text when you type it in. And ThinMan says I use ESP Now to talk between a weather station, wind speed and direction and a display showing the speed, temperature, humidity and direction, very cool. That's using Arduino, I guess. So I would say try it because it has not gotten very shaken out in sort of Python above to have more testing and know what bugs to fix. And if you wanna work on it to fix the bugs, that would be great too. Oh, and then Jose David has a link to another open source e-bike project with CircuitPython. What? Okay. Wow, very cool. Yes. All right, do we leave off with Dan? Dan's next thing. Yeah, I have like five more things which I'm not gonna talk about in any way. Are we at our time box? No, we still have to 25 minutes. Okay. So I think we should talk about async.io just in general, I think this is an excellent feature that we could move to because it's so helpful. All right. Just me from having to use generators with Rainbows, come on. Well, if you're into Python history, there's like a deep connection between async.io and generators that I'm not fit to explain to you, but they're like what if generators were just a little better to use and then async came out. Nice. And thank you, ThinMan, for the photos of your project. I love seeing finished things that work. Anyway, but back to async.io, I'll mute. So I would say we have async.io support in CircuitPython. It's really inherited from MicroPython. It's not as, there are a bunch of things that we haven't tested in it, including a lot of error recovery kinds of things, but it is great at, if you were trying to do a bunch of things simultaneously that aren't synchronized in time, you could write one big loop for doing that, but it's an awful lot of trouble to keep track of the time in the big loop. So async.io will let you create a bunch of smaller loops in front independently and can do their own timing and they cooperate. In other words, they take turns. So one will, they're polite to each other and when one is not busy, it will give up and go back to the parent loop, which will then ask that somebody else run. Dan, show your little blanket image. Where is my image? I don't know. I should be in the guide, I think. Oh, there we go. Yeah, there it is. There it is. This is like the essence of async.io. So there's a scheduler here and there could be multiple tasks running and they are all controlled by an event loop that you don't see that you don't have to write. A task could be running. Here's a running task. A task could be ready to run, but not running at the moment. Only one task runs at a time and a task could be not ready to run. It's waiting for something to happen. Like it's waiting for a button to be pushed or something like that. It's waiting for some network event or something like that. So these are the three kinds of tasks and what async.io is that lets you have all these kinds of tasks, not just three tasks, any number of tasks, these are just examples. And so you can write these tasks individually and you can also set up limited communication between them like by having them share an object where they might pass information back and forth. And all this is explained in the guide that I posted, the cooperative multitasking guide. And it's called cooperative because these tests have to cooperate. If one wanted to run forever, it would run forever and nobody, none of the other tasks could do anything about it. So it's important that they know when to take turns. So read this guide for how to do this. And some simple examples, mostly what you have to do with blinking lights which is kind of the simplest example of things that you might want to do that aren't quite the same. And they don't have, they don't necessarily operate in lock step. And try it out. And this is an example of something that as Jeff mentioned has evolved for a long time in regular Python. It's gone through a lot of iterations. It's gotten simpler. It used to be more complicated but a lot of the tutorials about async.io are five or six years old and they're immensely complicated and hard to understand and they're not best practice anymore. So in this guide, I tried to distill what is kind of best practice right now. What we're gonna try to add is something that was added to CircuitPython, and not CircuitPython, regular Python 3.11 called task groups which are even make it even better about how to use sort of more constrained and less likely to cause errors. And we'll be adding that eventually. It's just been added upstream in MicroPython and we'll try to take that back into CircuitPython soon. Is that in 120 or 119? No, I think it's past 120. In fact, I'm not even sure it's merged yet or it might have just been merged. That'll be exciting though. I'm looking forward to that. There are features that are in Python 3.11. There's something called exception groups because they felt in order to do task groups, they felt like they needed to do very fancy exception handling. And exception groups are quite complicated and are probably not gonna show up in MicroPython anytime soon, but you don't really need them to use task groups. You can kind of work around that. So, yes, I hope that we can have task groups and now we write this guide to use task groups. And we will also, we're also thinking about how to make things work more nicely with Async.io like reading from a sensor where you don't have to busy wait or as we talked about earlier, we're keypad where you don't have to do any of the things. Eight of Fruit requests is a big one. Yeah, Eight of Fruit requests, right. That's really, right, that's an important thing. Yeah, it would be really exciting if someday we could keep an LED animation going all the time a network request is happening and right now, that's not yet. Right. Not possible. People have asked, for instance, clever in Discord mentions about having threads, we have deliberately decided not to have enable threads in Circuit Python because threads are easy to misuse and are complicated and we rather come up with a paradigm like Async.io that is more restricted and easier to use and less likely to cause people to get all confused. If you'd like to use threads, that's great. You can use threads in MicroPython, but right now we're kind of reluctant to support something like threads is kind of the assembly language of scheduling and we'd like to leave that out for now. So I'll just say, go ahead and read about Async.io but there's a lot more that's kind of in the, we're thinking about for Async.io in the future and that might be Circus Python 10. Yeah, not having threads is kind of a very opinionated thing and we understand that a lot of people have a different opinion, but we also ask you to be graceful about accepting the choices we made in Circuit Python because we have thought about them. It's not thoughtless, it's the result of much discussion. So Todd asks, is there something like absolute timing for an Async.io task? He wants to be able to run a task every five milliseconds, 100 milliseconds, 105 milliseconds, 110 milliseconds, not just Async.io sleep, five divided by a thousand. I have an answer to that, but I don't think it fits within the time we have available. So Todd, how about you and I talk about this a little later? The answer I think is there's kind of a hidden way to do it and there's not a clear way to expose it that's compatible with standard Python and so it's hidden at best. Uh-huh, hidden features. Yeah, so if you take advantage of this, it might break in the future because it's not really a published guaranteed feature. Uh-huh, I don't know if it's incompatible. An Async.io, but maybe I'm just un-formed about that. I mean, you might be able to say schedule this not until a certain time has been reached, but you can't necessarily schedule this to run it exactly this time. Well, yeah, it might run late. If something runs for 20 milliseconds, then you're hosed or maybe you run it four times as quickly as possible, I'm not sure. Oh, and Cgrover has a calculator with the micro decimal library in action. Yeah, I wrote that for a guide on the Adafruit Learn System, which also the display isn't on it, but it used this custom keypad and the display goes on top of the board here, but this got switched around, but this was a fun one. Referred to as the Claculator, which I still think is hysterical. Yes, it is the Claculator. Okay, let me give you some more quality audio. All right, sorry about that. If you had headphones on. We all need clicky keys in our ears. Well, that's great. Anybody have one last shorty thing they wanna talk about? I'll just mention a couple things, go ahead, go ahead. I was gonna say MIDI Maniac says, RP2040 has PIO which can handle offloading of IO work. Yeah, there's a tremendous amount of stuff that you can do on that microcontroller using PIO and we've got a guide that covers some of the things, but people are inventing new stuff all the time. PIO is the technology that makes the HDMI feather work and the USB host feather and the Feather Scorpio, which has eight independent Neopixel outputs that all run like fantastically fast, is very cool. So if you wanna know about PIO, check out that guide and go find people who are doing cool stuff in Arduino or Pico SDK with it. And if you can't do it in circuit Python, come talk to us about it because we want to make that first rate. Anyway, that's PIO, which is just on the RP2040 microcontroller. And I will say the RP2040 data sheet, which was just linked to on Discord is an excellent data sheet as far as data sheets go, just FYI. If it's the first data sheet you ever dig into, it is a good baby step. It's very well read. But that just means everything else is downhill from there. It does mean that, but I'm saying, if you really need to know how to get into a data sheet, start there, because other ones are confusing and sometimes wrong. So hey, Dan, what was your couple of things? Yeah, I was just, and also I would echo the thing about the data sheet. A lot of data sheets, you really have to read between the lines. They say a lot of things by omission. So if it doesn't say something, it might not work. That kind of thing. And the RP2040 data sheet is much better about saying what's true. In a complete way. So it does a couple of other minor things that we often talk to people about. One is count IO, which counts, can count pin transitions. It's not really suitable for buttons because it doesn't do debounce or anything. But if you have something that's changing state fast and you want to count how many times that's happened, that's really helpful. Count IO, pulse in does something similar rotary IO does something similar for rotary encoders. Sometimes it won't work on really fast rotary encoders. We have to think about that. And then finally, another thing, there are a lot of examples of writing to SD cards that use the Adafruit SD card library, which is a Python library. But for years, we've had native modules for writing SD cards. We have SD card IO and we have SD IO IO, which is Jeff worked on. And so I have a, I thought I have a link to that. Here we go. Yeah, this guide explains how to use SD IO IO, not all boards supported there or you need a better breakout. But SD IO as opposed to play is faster than regular SD card IO. And so these are both preferred of the Adafruit SD card library, which as I said, is in Python and has a lot of like issues of its own. It's a lot better to use the native modules. So instead of using Adafruit SD card, go ahead and look at SD card IO. And that I think is pretty much, yeah, that's that exhausts. Oh, no, I was gonna talk about, also look at the safe mode guide, if you haven't already, which talks about why you might go into safe mode and what the safe mode means and how you can recover from going into safe mode. But I will not talk further about that right now. I will also say, if you get into safe mode, let us know. We often will have folks file issues for certain safe mode situations. Sometimes it's just something in your code. Other times it's something more serious. And obviously if you're dealing with continually dropping into safe mode and this guide does not really get you to a point where you're not, come find us on Discord and ask for sure. And we can help you figure it out and also sort out whether or not it's something that we need to look into. Yeah. Yeah, my keyboard uses the safe mode.py to make sure it always is usable even after something bad happens. And it works real good. Yeah. See Grover asked, given the success of Synthio, do you have any thoughts about text to speech for CircuitPython? I think it would be awesome. I don't know if I'm the right person to work on it. I heard, I think it's the micro bit that they have a speech synthesis module in their micro Python port. And that might be a thing to look at whether that can be brought over to CircuitPython. Someone, yeah, someone at Pycon was telling me about that. I haven't experienced it for myself. All right, it's getting to be about time. Yeah, we'll let people stretch their legs before the next stream starts in about 11 minutes. Yeah, thank you so much everyone for joining us. This has been fun. We had a couple of fun comments on the YouTube chat. One of them is the community is really powerful. And there was something else that I'm now not seeing, but just to pull people in from other places that they're talking as well. So we have a tempers, does it 10% off? I think it's 10% off. Yeah, a discount code in the fruit shop that is valid today and through the weekend. It is CirquePyday, C-I-R-C-P-Y-D-A-Y. And you can punch that in and get 10% off your order. Stick around, we have a full pack day of things all the way through asking an engineer tonight at eight to nine. And there's obviously a ton of stuff in between. So thanks again everyone for joining us and have a wonderful rest of your circuit Python day. Bye.