 Alright. Happy Friday, everyone. How's it going, Beata? Looks glad the YouTube chat is actually catching up. I had to switch... switch tabs around and refresh in order to get the go live button. I wasn't sure if the old chat there was actually going to stay active, but it looks like it is. Got sound, but I'm going to readjust the microphone just a little bit. And then... I'll shuffle the chats around and we'll get this show on the road. Not bad, thanks. Nice. Glad to hear it. Alright. So hello, everyone. My name is Tim. I go by FOMAGuy on GitHub and Discord. This is the deep dive program. This is a weekly live stream program where we are working on CircuitPython-related stuff, broadly speaking. Sometimes we work on different parts of the projects. It could be inside the core or the libraries, or today we're actually going to be focused on Blinka, which is the CircuitPython compatibility layer. Before I jump straight into what I'm going to work on today, though, just for folks that might be new, or if you're watching this video in the future and you don't know really what I'm talking about. CircuitPython, like I said, is kind of the main thing that we work on on this live stream, different aspects of the CircuitPython project. CircuitPython is an implementation of Python that runs on tiny computers called microcontrollers. There's a bunch of pictures of them over here on the downloads page. Basically, all these different devices are essentially really small computers and we are running Python code on these things and they can interface with different types of hardware. There's lots of different shapes and sizes of these microcontrollers. They all have different capabilities built in. They all have different hardware that might be built in. There are different shapes and sizes and things like this. The common thread that ties all these things together, though, is that all these devices on this downloads page here, they run CircuitPython, which means that you can connect them to your computer. They will show up like a thumb drive. You can edit the Python code that's on that thumb drive and it will execute the code for you on that microcontroller. You can interact with whatever you have connected, be it Cherry MX keyboard switches, alligator pin pads to connect other things, RGB, Neopixel, LEDs, buttons, sensors, speakers, buzzers, pretty much any kind of little electronic doodad that you can imagine. You can connect up with these microcontrollers and then interact with through this Python code. CircuitPython is an open-source project, but it is primarily funded by this company, Adafruit. This is their website, Adafruit.com. They are a hardware and software company based out of New York. They are the company that's paying the folks who work on the CircuitPython project. They pay a team of folks to work on the project full-time. They pay some other folks like me to work on the project part-time. All of that is made possible by selling hardware. For anybody who is interested in this type of stuff, if you want to get yourself some toys, head over to Adafruit.com. Thank you if you want to purchase hardware from them. You are also helping support the CircuitPython project. Thank you from all of us that work on it. Today, like I mentioned, I am going to be diving into Blinka, which is actually the CircuitPython compatibility layer. Before I do that, let me catch up on the chat as well here. I actually shuffled my OBS back on top of the chat. Let's see here. How's it going, DJ Devon? As well as Paul SK and Axel Magnus over on the Discord there. I should mention, yeah, I think that's linked down below me still, right? Yeah. The Discord is the chat that's directly below me there on the screen, and then below that is the YouTube chat. If you like to ask questions or interact during the live stream, you can head to either one of those two places. Like I said, today I'm going to be working on Blinka, which is the name of the... Well, it's actually the name of a few things, but in the context that I'm working on today, it's the name of the compatibility layer that allows CircuitPython code to run on single board computers like Raspberry Pis, as well as even run on some desktop computers in some instances, depending on what you want to do with it. In particular today, we're going to be looking at Blinka Display.io, which is what allows those Raspberry Pis and single board computers to use the Display.io CircuitPython APIs. So I have a Raspberry Pi here. I think it's Raspberry Pi B+. It's not necessarily one of the newer ones. I think it's a 3B+, or something like that. But it's got a display on top, and we'll be using Blinka Display.io to interact with this display. Today, we're going to test out a PR that's into the Blinka Display.io repo. We'll maybe look at a PR that's into the Blinka repo as well, and then I have in mind to work on some other stuff in Blinka Display.io, depending on how the testing goes and stuff like that. So we'll see where we end up getting to, and then dive into that stuff later on. How's it going? See Grover and Niradak. Happy Friday to both of you. Thanks for tuning in. Let's see. So this should be a pretty straightforward one. I'm not anticipating this being particularly tricky, but sometimes I get tricked by them, and they're more tricky than I had originally thought they would be with some of these different PRs to test. In this case, though, all this is doing is just setting a thread back to none. So to level set a little bit here, we are looking at inside of Blinka Display.io. Let me back up, actually, one step. Before we talk about what the actual problem and what the fix is in there, let's take a quick step back. So Blinka Display.io, this is allowing you to use Display.io APIs on Raspberry Pi and other single board computers in the Display.io API. So we could pull up Python docs maybe and go to Display.io. There is a concept called Auto Refresh that the displays can have. So by default, Auto Refresh is on typically, but you could turn it off and then you can manually refresh whenever you want. But by default, it is on. So in Circuit Python, if you don't specify, where is, why is display? Oh, did I, oh, display, okay. It's like, is it not at the top of the list? I did not search for the whole thing. If you leave the Auto Refresh on, then you can basically just draw stuff on the display, move it around, change its properties, change its color, do whatever you want to it, and the display will just automatically refresh itself and show the latest version of whatever your thing on the display is after you update it. Here's display object and then somewhere on there. So Auto Refresh, it has listed as a property right there. I think you can pass it maybe as an argument as well. So it's going to be in this list somewhere. Also, I think, but there's the property one for it. So that is in Circuit Python, normal Circuit Python on the microcontroller. There's this idea of Auto Refresh. So jumping back to the Blink-a-Display-IO world, the way that that is implemented in Blink-a-Display-IO, it uses a thread, which is something we can't do in actual regular Circuit Python. But of course, Blink-a-Display-IO is the compatibility layer. It runs with normal Python, C Python. It has access to the things that Circuit Python may not, including things like threading. And in fact, it uses threading for that Auto Refresh capability, which makes perfect sense because we want that display to keep refreshing without the user having to call refresh. A thread is kind of the only way we can keep executing code like that over and over, even though the user is actually doing other stuff and not calling functions for us. Inside of init for the display, it's going to set up this thread somewhere. Maybe we can search. It starts it out on none. There's our Auto Refresh property, which is going to determine whether or not we're using this thread. Here's our setter. So inside of here, when Auto Refresh gets set to either true or false, I guess actually it doesn't matter if it's true or false. But when it gets changed, it will create this new thread right here. And then it will start the thread or stop the thread based on whether or not you said true or false for your Auto Refresh there. And the crux of this is, so that's kind of how the threading comes into the mix. That's what it's used for is the Auto Refresh. The thing that it's actually fixing is the fact that the code as written right now can technically reuse the thread. So if we kind of follow the logic a little bit, this is, I believe this is right here. Close this one. I didn't mean to close that one, of course. You're going to need seven. So basically, if we kind of follow through the logic of this, if you were to set, so this is inside the setter for Auto Refresh. So this is when the user says something like display.autorefresh equals true or equals false. It's going to come inside of here. It's going to set the actual Boolean. It is going to say if the thread is none, then we make a new thread. And up to this point, it doesn't matter whether we pass true or false, but then here is where it starts mattering because we say if value, value is our Boolean. So if value was true and if the thread is not alive, then go ahead and start the thread else. If not value, so if value was false and if the thread is alive, then it's going to do refresh thread.join, which I'm not super familiar with Python threading, but I guess this is the way you stop a thread. It feels weird that you would call join in order to stop a thread, but I guess you join it and therefore it's no longer in the background. It's kind of on the main thread now. I don't know exactly how that works, but I did see that... Well, and so if we keep stepping through the logic here at this point, so like let's imagine we set this to false, right? So we've said auto refresh false, it's going to do this. And then let's say afterwards, you know, 30 seconds later or whatever, we're going to set it back to true. We come back in here, we call this with true. This time around, it's going to go to here because value is true and because thread is not alive because we stopped it before. And it's just going to call start to start the same thread again. But it turns out I think that you are not actually supposed to reuse the same thread. I did not get super far into the research here, but I did plug this into Google and saw this one, which says you cannot restart a thread, you must create and start a new thread with the same configuration. Curious to see if this talks about stopping. Maybe we can confirm the join. I assume that's right because it works, but it just feels like a weird... Next, the main thread joins the new thread, which will block, not return, until the new thread has terminated. Okay, yeah, I guess that's how we stop it, is by joining it from the main thread. And then in this example program, it's going to try to start it again, but what this is telling us is it's going to throw an error or something, I guess, yeah, runtime error because it tries to start the same thread again. So basically, we could essentially, we should see that error if we set auto-refresh to true and then set it to false and then set it back to true, or I should say it starts on true by default. If we set it to false and then back to true, we should be able to get that error. So I have now in my pie charm here in my editor, I am actually connected to a copy of Blinka display IO, the repo that is actually living on that Raspberry Pi. So the project I have open in pie charm actually is on this pie that's down here. It's not on my local computer, it's actually on that pie. I'm connected to it across the network through an SSH thing. So all this code that's in here, all these files, all this stuff, this stuff is all on the actual pie itself. And I also actually have SSH already connected in the terminal down here, so this terminal is actually the pie as well. So I'm just going to copy the simple test. We're going to make a reproducer that should raise the error. So we're going to say, let's say auto refresh error, auto refresh error. We don't want to add it because I just have a million of these that are not added. I should make a repo where I keep these things, but right now they live here. Before I go and add the code that's going to break it, I'll just run that by itself without that maybe. In order to run it, I'm just going to Python 3, ILI. So I already have Blinka set up on here. I already have Blinka display set up on here. A bunch of other stuff to make all this work. The spy bus, all that stuff has already been done. If you're setting up Blinka for the first time, you do have to do a fair amount of setup stuff, but then once you get it all set up, you don't really need to keep doing it. One more thing though, actually before I run that, how do we, you can check versions. Check versions with pip, pip check version. Is it pip show? I think it's pip show, right? I'm going to try it. Python pip show. Oh, pip. Maybe do I need pip 3? Or maybe, oh dash m. The one thing is like this is all going through the network, so it does tend to take it an extra second compared to like if I was just doing this locally on my computer. 6.13.1. I would assume there's a new one. We should probably go ahead and update to the newest one, whatever it is. I'll probably update to the newest Blinka display I.O. as well before we run this. What does this say? Orange pie. Yeah, definitely newer. 8.12. Wow, we have a much newer. Much, much, much newer. So I wonder, we may run into trouble? We'll see. Let's go back to here. We were on 6.13.1. I guess we could always come back to that if we do run into trouble, but let's try python dash m pip install dash dash upgrade for Blinka that run pip list. Interesting. Pip list for the versions. Can you do like pip list and then a single package and it will show you the version for that package or does it give you the whole list or just the versions I got just so it doesn't show you the rest of that stuff? Yeah, that screen did, or that command I should say, it did print a bunch of extra stuff. So there's 8.12. We'll do, let's try list this time. List the fruit. Good. Display. Oh, it does all. Okay, let's see. This will be back to the A's. 010.12. 010.2? Okay. So this is unedited still, which means it's basically just simple test, which happens to be this script that draws these two rectangles of different colors and then a label with hello world. What we're going to do though is mess with the auto refresh down here before we go to the main loop. We should just have display as a variable, I think. Yeah, display. It's auto refresh false. We could change something about the text and then if we just run it like this, we should not see the green text. It should still be yellow, so it should actually basically just look the same, yeah. And then nothing breaks because we didn't set this back to true yet. So now this theoretically raises the runtime error for starting the thread. Yep. Boom. Okay. So to test the fixed one, maybe not. You could use grep maybe. If you did pip list, and then you could do grep with the one you wanted, then you would get the single line with the version on it there. I probably want to do that. I guess we'll just check it out with... Well, I don't... This probably doesn't have GH, does it? Let's have GH. I was surprised it didn't tell me there's a newer one available at credentials over here for a minute. Oh, that's not going to work. Probably not going to work, but can I do it from a different computer? Oh, my goodness. Not letting me. Okay. Like GitHub, if you're going to use your own app for two-factor, it has to be more reliable than it is. If I could be quite frank and brutally honest. Otherwise, we should just use an existing two-factor thing because now I'm stuck with the browser telling me to two-factor on my phone and my phone being like, nah, there's no two-factor requests right now. Nothing to do. No problem. Well, like the notification came through, then I clicked it and then it was like no requests. Okay. Congratulations, you're all set. Let's hope that's true. It isn't actually clear, apparently. Okay. Where are we at? GHPR list. I grep a lot. I'm trying to get in the habit of using grep more. Lots of times where grep would probably be helpful to me and I don't use it enough. 103 is what we want. There is a much newer version of this, but I'm not going to mess with that right now. PR, check out 103. So now I'm going to install Adafruit Blinka from the repo that I'm inside of. So just a minute ago, I installed it from PyPy. Whatever the released version is. 0.10.2, I think it was. But now we're going to install it from this repo. I'm actually going to uninstall it first. I don't think I would really have to do that, but I'm going to, because I'm superstitious. Is this case sensitive? I noticed in that list they were all lowercase. Okay, uninstalled, which means if we try to run it again right now, it should just fail. Let's do that just to make sure that we're actually, like, don't have some other display.io in the mix here. Okay, I see a good thing we did that because... Oh, I guess it would have been interesting. Yeah, okay, so what it did is it took the... Yeah, I forget about that. Python, it looks in the directory you're in. So something said import display.io. It looked inside site packages. There was no display.io there. But it looked inside the path where we actually are at. And there is display.io there, and it's the new display.io, the modified one that's in the PR, which means that's what it ran, and it does not crash anymore, and we do see the green text, which means that our auto refresh did successfully take hold after we changed the color. So I was going to install it, but we don't actually have to. It will just run it from where it's at here. Let's not do color, let's do text. But we do know it's this one because it didn't crash. Name time is not defined. Oh, weird. Why can't I... Interesting. It doesn't have a bunch of the helper stuff. I'll change the deep dive there. So further proof that our auto refresh is on and working after the fact. And then at this point we could... We should be able to switch it back and forth over and over and it shouldn't matter. This is not particularly practical, but we could do something like this. Maybe let's do... I guess let's do a counter. And I guess after we set that, let's go... So this will just flop it back and forth every second. And I guess we'll see every other number, essentially. We'll kind of skip every other number since the display won't actually refresh after we set it. That was part of the point there, too. We could count up. Invert and shortcut for the win. Okay, so things worked out for the best by uninstalling. Yeah. Oh, right. So this is Blinka and not circuit Python, which means we actually have to run our code. We cannot just save it and have it start running automatically. So yeah, basically every other number gets skipped because technically it gets set, but auto refresh is false. And then the next time that it gets a chance to refresh is essentially right, like, the instant before it changes the text again. So like, technically I suppose there could be a split second where you might see that previous number before the next one shows. But it's happening so fast that it doesn't look like it's there. And it may not, even if you had, like, high-speed camera or something, it may not actually still be there, too. Because it also depends, like, that thread in the background is also doing the refresh at its own speed or whatever. So it is also possible to just make a change and then make another change before it has had a chance to go. But everything looks fine to me here. This is running exactly how I'm expecting auto refresh, true, false. Definitely is not an issue anymore. This is probably horrendously inefficient because we are creating threads over and over and over and over and over. But it's not crashing anymore. And that is what we set out to prove, and that does seem to be the case. How's it going, my friend, over on YouTube? I probably will unfortunately butcher your name, so I'll just say ABD if that's all right. But thanks for tuning in. That is going crazy. There's, like, packing material or something, and he's, like, digging around inside of it. Oh, well. So let's... Yeah, I think that's probably fine. I should say, too, hug report to this person. BabLockB. They have not only put this PR in, but really the place where this stemmed from is they were working on my Pygame display... Blinka display or Pygame display thing. And they... For those who have been watching for a while, remember, maybe a month or two ago, I was working on my Pygame display thing. We discovered that it did not work on Python 3.10 on my new computer. We discovered the threading issue, was the root cause of it, and I changed around the API to kind of hack it together and make it work with duct tape and stuff. BabLockB actually found a much better solution that brings the threading back into the mix and makes the Pygame display thing behave a lot more like it did originally, where it's not... Essentially, when I changed it a month or two ago, we kind of cheated the whole idea of auto refresh. It no longer really existed as auto refresh. It was technically just manual, but we hid it inside of a property that you were encouraged to call every iteration of the main loop anyway, so it was manual, but you were calling it every iteration. Not the best, but thank you to BabLockB. There's actually a new version floating around, which I will get merged in hopefully this weekend that brings back the threading and actually makes it work on Python 3.10. And while they were working on that, that is when they noticed this issue put this PR in to fix it. So our reports this week, for sure, are in order on time. Remember that, yeah. It doesn't work in... That was right after I got the new PC, and I was like, this isn't working on my new PC, which was terrible. We managed to get a hack together one, but I'm much happier with the way that they've actually done it with the threading and stuff. Even if anybody did catch that stream, the whole time I was doing it, I was like, I do not like this. This is not the way I want to do this, but it is a way that will work. Let's see, raised when you set auto refresh for the second time. I also successfully tested the fix. Tested the fix. Confirmed. I think it's Pi 3B+, isn't it? That was like one of the popular ones, right? Pi 3B+, with latest versions of Blinka and Blinka display IO on a ILI 99... I don't know the actual display. 9 something. 9341. Aw, come on. 341. But that was during the Galio game. I have... Yeah, I don't remember if that was on this... was on this PC or not. Well, that was around Circuit Python day. I have this new PC by then. I come a bit later than that, I think. I'm not actually sure, though. I'm gonna say sometime in November. That's when I got this one. Uh, yeah. I'm gonna call it auto refresh threading fix. Yeah, that's true. Definitely can do that. Let's see, this release... Year 10.3. Year 10.2. Year 10.3. Okay. Some more stuff. I want to do, I think, in Blinka display IO land. But before that, I actually want to go up sort of one more layer, I guess, so to speak, to Blinka land rather than Blinka display IO. And over on the Blinka repo, there's also a PR. Also by BabelockB. Continued hug report. This one was adding a placeholder for generic Linux inside of Blinka so that you can be allowed to write import board on a generic Linux PC and have it not raise an exception. It does not implement anything else for the generic Linux PC just allowing you to import board without raising the exception, which is actually pretty nice, in my opinion. I will leave a review on this one as well after we test it. But this one, I think, is a bit more like I'm less inclined to just leave a review and merge it. I think we probably want Melissa to take a look. I would probably defer to Melissa more so on this one. The other one was a bug fix. We were able to test it. We know that it definitely fixes. We definitely saw that it also does is a bug because it raises the exception. This one is a bit more I guess opinionated. It's not really fixing a thing that's just a bug. It's kind of allowing Blinka to be used in additional way that it's not really meant for. I'll be it. I'm all for it because it works very nicely with my Pygame display. I am already on this branch, but after I checked it out, so I'm actually just going to switch back delete it, then update again and oops, 4.7. So to start with, I guess let's go should we use our grep super grep powers here? Python 3, pip, list. Try lowercase. Right, right, right, right. Oh well, grep. It's open file. Right. For whatever reason, dash M is like killer for me with that. Okay, so it's currently is installed. It's 0.10.2 which is the last version, the one before the one we just released. I'm going to uninstall it just because I don't know if it's like the released one or if it's the one from this PR when I had it yesterday or what. We're just going to go pip uninstall Blinka display. But honestly, the truth is I don't know why I'm doing Blinka display. Oh, we really need to be doing Blinka. So, yeah. Appears to be not installed currently. Hmm. I don't know if I trust this somehow this might be it might be the same thing where this is like somehow this is using this folder even though I did a CD back. I'm just going to go all the way out here. No, I definitely have something that's allowing me to import board. Weird. Didn't work with list for some reason. So why did they see uppers? So, hmm, okay, those are case sensitive and some are upper and some are lower. That seems odd. Okay, it is there though. So we will remove it. I wish that would be such an amazing superpower if you could like grep your memory just fine stuff from the terminal. Oh, man. That would be amazing. Close captioning. There you go. That's pretty close, I guess. Yeah, I hadn't considered that. So it does, YouTube, I guess, has automatic closed captioning, I think, right? So if I got that text put it into a bunch of text files then I could be grepping it. That'd be kind of interesting actually. Somebody was it, I think maybe AskPectureW at one point was looking into the YouTube closed captioning API. That's that's not a bad idea actually. I know, yeah, like you say, it's not, it's not going to be perfect. So it won't work for everything, but I mean like as long as it's a main theme of the dive or the livestream, I should say, as long as I say it a bunch of times, whatever it is, then hopefully at least one of them will get caught correctly. Should no longer be able to just do import board, I think. It failed, but it didn't fail because I don't have Blinka. Is this like does it cache? Where's that coming from? Oh, it says dist packages. Why did the uninstall not remove that? Weird. It was installed in both places apparently and permission denied license. Is that a pseudo problem? Okay, that's what we're expecting. This is why it's always good to like delete the thing you're testing and then make sure that the error you get is that the thing you're testing is gone. I cannot tell you how many times I have sat down to test something thought I was using the modified version thought I was actually testing what I thought I was testing and just wasn't just had a different version installed was running that the whole time didn't even touch the code I was trying to. Okay, now we have none. No Blink is installed. We will I'll actually I'll install from pip so that we can see the error state Python three actually for me it's just Python dash in pip install so can you do this lowercase yes but it doesn't now I like it changed my history I can never have that command back that is so weird that you can install it one way and then it doesn't find it. I've never noticed that before. Okay, that's there we're looking for that means that basically we tried to like we installed Blinka on a platform whatever platform it is we tried to use it by importing board but the platform that we are on does not actually have support for Blinka which is what this error is telling us which is true and then the actual PR changed it just minimally such that you can import this and you won't get the error which is nice because that means that if you want to make portable code that runs both on like a microcontroller let's say as well as on a regular computer like my Linux computer using the pygame display output you would have to wrap this and try catch probably because you would want to catch this error when you ran it on the Linux computer with the newly changed Blinka though you'll be able to actually run import board you won't get the error which means you don't have to have try catch you don't have to have special logic the same code can now work on both platforms or I should say it's bringing together bringing closer together the code for each platforms there are still differences of course you need to set up your display differently for pygame display versus like a built in display on a microcontroller or whatever so like there are still going to be some logical differences but this is eliminating one of them and the fewer that we can have certainly the better as far as I'm concerned I already checked that out I'm just going to delete it and check it out again I don't the last change I think was not functional but I do want to make sure that I have the latest version anyway 647 I do I think I did already do this actually that's all right Oh is that insensitive case insensitive interesting it's a good tip thank you yep take it easy paul sk good night also helps cut down on code yep that's true as well but for bug for a py3 that was big of his deal well I mean if your goal is to basically take the same code pie and run it into places then saving code you know even code that's on the PC side if you're if you're running the same code on the microcontroller it's still helping you there as well so cutting down the code will help if you're actually trying to do the the full on right once run everywhere type thing now you could have if you just made slightly different variations then you could kind of cut out some of the stuff the microcontroller doesn't need that would also help you save some additional space in the case of that but if your program is not very big and you value the portability over the the efficiency then you can write it in such a way where it will just run on both because it has the correct kind of like logic checks and try catches so we'll uninstall blinker again python-m pip uninstall it's time we should only have one so it should no longer it should be back to no module yep so now I'll do pip install dot which means install this directory directory that we're inside of which is the repo checked out to the branch that the PR is on and after that we can go python again we can go import board we now have board it does not raise an exception this was already taken care of we can do it as a we can do it as an approve I'm still gonna not actually merge it I'm still gonna wait for Melissa but let's see just some of the chats back here youtube captions oh paul cutler worked on the captioning there's a python script for recorded videos those 100% accurate I mean I can download the video use openai's whisper it was better than commercial but only worked if there was only one person openai's whisper I haven't heard that one before I'll look into that I was impressed by how accurate the captions were compared to youtube's yeah I have I've definitely run across some wonky ones on youtube occasionally as well okay I think that is good for now so we have two we have two choices what am I feeling next so we can either go to ispy displays I got the ispy ribbon cable breakout um got delivered this week I got one soldered up and I want to hook it up and play with it and then the other option is gonna be staying in blink of display irland I actually think it might be a fun exercise to work on implementing bitmap tools which is a thing that exists in the core but not blinka yet I think I'm leaning towards that one I think maybe we'll do ispy displays tomorrow for those that don't know I stream usually on Saturday mornings as well 10 a.m. central time um for that stream so I think tomorrow morning for that stream at 10 a.m. central I'll work on ispy display for now I'm gonna keep going on blink of display I.O. but we'll work on something different right we got our testing done the next thing I want to do is actually coding I'm gonna start implementing bitmap tools so um again for people who don't know the display I.O. world too much or the different apis bitmap tools is a module that's inside the core of circuit python it's a module that has a couple of different functions that allow you to manipulate bitmaps in various ways um I'll run through what the functions are real quick here so roto zoom is kind of a very Swiss army knife like thing it's basically copy pasting um but with modifications and translations as well so you can basically cut a rectangle out of one bitmap paste it into another bitmap and specify the location of it um you can crop it intrinsically you know clip parts of it out you can rotate it uh which is actually really cool because this is like the only way really to achieve rotation uh in circuit python display I.O. um you can scale it uh even scale it by floats I believe as well so if you want to scale something by like one and a half times for instance um you can't do that with a group the group scaling in display I.O. is integer only but roto zoom if the thing you're trying to scale is a bitmap you can use roto zoom to scale it by a float value so you can go 1.5 or something like that so that's what roto zoom is it's probably the most complex one so it's probably not the best one for me to start with um alpha blend this is for basically combining two images together so if you imagine uh that you had two images and then you kind of made them both partially transparent and then stacked one one on top of the other and they're both you know partially transparent so you can kind of see both of them essentially blended together I believe that's what alpha blend is um that one is also I'm going to guess on the more complex side so I'm basically going to try to be starting from the most basic ones first um fill region this is basically drawing a rectangle into the bitmap so that sounds like low hanging fruit this will basically let you give it the bitmap you want to draw into give it the x y position where you want to start drawing give it the height and width of the rectangle you want to draw and give it the color which is actually a palette index that you would like to color it in as so this is essentially just drawing a rectangle by setting uh values inside of a two dimensional array which is basically what a bitmap is um I should mention also in circuit python these things exist as a core module in order to speed them up uh because they're a core module they're implemented in c code which means it runs faster than python code so you could obviously use for loops to draw a rectangle uh inside of a bitmap you know for loop inside python that would work fine it would just be slower than doing it from c code in the core on a microcontroller uh in blink of display oh land though you know we're not going to be writing c code for it blink of the display I was all written in python anyway um so we'll have what ends up being a python implementation of this probably with two for loops or something similar um but the nice part about this is like in blink of display oh it won't necessarily give us the same speed up that we get in core circuit python but what this will do is actually getting back to the same thing I was just talking about this will increase portability this will mean that if your code uses these bitmap tools functions and you want to run your code on a pc or a raspberry pi once we implement these things you'll be able to actually utilize those functions which today you can't if you had code that used this function you try to run it on a raspberry pi it would just tell you this function doesn't exist you can't do that um so if we create it then it will exist and you will be able to do that let me catch up on the chat here um oh I'm not too far back I bet somebody could come up with arch text with a rotating each glyph in a word that would be kind of cool like uh kind of that that number going on yeah you could do you could do rotations you could also do x y's that would be kind of interesting so draw rectangle that seems pretty easy fill region is the name of that one boundary fill this is like I actually implemented this one in the core this was pretty much my first major contribution to the core that was actually like code c code this is like a paint bucket if you imagine old school like microsoft paint or even gimp really I think pretty much every paint program has a bucket fill right so if you have like solid shape or whatever solid ish and then you use paint bucket like this fills inside of it that's pretty much what boundary fill is you give it the bitmap that you're filling into you give it the x y pixels of where you want to start your filling you give it the color that you're gonna fill and then you give it the color that you want to replace so you also pick like you can have it only fill a certain background color if you want so it could like paint bucket fill and only fill in gray with black and then like if there was other colors in there they would have remained so since I actually did this one and I believe I actually essentially kind of like looked at a python implementation while I was doing it that should be another relatively low hanging fruit one although not as easy as fill region draw line that should also be pretty straightforward draw line it's relatively self-explanatory you give it a bitmap you give it two points x1 y1 x2 y2 you give it a color which is again that index inside the palette and it's just gonna draw a straight line between those two points for you array blitz this one gets into the kind of more complex land again this is basically taking data from like a byte array or a two-dimensional array or something like that and putting them into a bitmap be honest with you I'm not exactly sure not exactly sure how this one works I'd have to maybe go try and find a case where it's used I think the core idea though is like you have a data blob like an array of data whether it came from a sensor or it came from like you know micro lab processing or whatever you have like an array of data and you want to plot that data into a bitmap just as like you know pixels of certain colors or whatever this is letting you kind of like copy it from that data array into the actual bitmap array that's a bit more on the complex side so that's probably not what we're going to start with the last one I think nope there's still a few more the next one is read into this is basically a helper that will read a binary bitmap file into a bitmap object for you so this is basically just used for loading bitmaps generally speaking at the user code level you wouldn't use this probably you would just use the image load library but if you look inside the image load library it will be using this and that one is also a bit more on the complex side because it's also another one I'm not entirely sure like I'm not entirely sure how that works I don't know that I've used it directly like enough to figure out how it works I know it's in the image load library I think maybe it's in bitmap font as well it's used to load fonts and things like that so it's talking about PCF which is a font file I don't have much experience with it so that's probably also not a good one to start with dither is an interesting one I have not actually played with the dither yet this is basically like what does that do do level output image dither it's like changing the edges right it's like making the edges less sharp basically something like that not entirely sure we'll have to actually so when it comes time to do that one I'll have to actually run it and see what it does so we can make sure we're getting it right so I'm thinking the lowest hanging fruits are definitely draw line fill region and boundary fill so I'm going to start with those and I'm just going to stay on the Raspberry Pi for now probably at some point we'll get back to doing this on the PC and I will use the Pi game display stuff but now I'll just stick to the Raspberry Pi and I'm going to make a file called bitmap tools in the root of this because that is where it would need to be it pretty much goes alongside with font IO and parallel display and terminal IO it's kind of like it's not technically inside of display IO but it's like only really used with display IO which is a similar kind of state of existence as these files here one thing I don't know are those like specified inside of here somehow are just Pi modules okay so we'll need to make sure to add it to here as well oh I didn't know you could have both of these interesting I've always seen that as either or bitmap tools I will make a new let's go cancel I'll make a new thread or not thread but um branch this is also says Adafruit get remote-v does have foamy guy on it though get check out dash B bitmap tools starting starting bitmap I guess we just call it bitmap tools well no let's call it starting I don't know if they'll want to have I don't know if they'll want to implement everything all at once before merging excuse me before merging any of it or if they be open to having a PR with the easiest ones getting that reviewed and merged and then working on the harder ones we'd have to see so I'll do starting bitmap tools and we'll see where we get to so check out and then that's going to be from Adafruit main no track switch to new branch starting bitmap tools now we can add this now let's go simple test copy that again and call it bitmap tools let's do fill region first I think okay and then before I actually start coding it up on the pie I am just to make sure that I actually understand how it works I am going to run it on microcontroller first good old feather TFT let's turn that on let's get device workspace here we go let me catch up on the chat here I believe Dither is making colors with less color ah okay making colors with less colors by mixing different color pixels next to each other so that from a distance they blend together to the human eye to look like a different color and then this is quoted from somewhere a dithered bitmap consists of a number of few colored pixels set next to each other carefully in order to create the visual effect of a full range of colors ah okay I see so it's kind of like this is an algorithm that you would use if you had a color image and you wanted to make a gray scale or a black and white version of it you would use dithering to essentially tone down the colors to the point where they were all black and white or I mean if you wanted to you could change it to all blues or reds or whatever you want I suppose but what is that secrets connecting uh MQTT test of some sort this is I think this is probably a um I think this is probably an example I don't think we need to keep that I'll just do it from scratch I was going to go copy from learn guide but it honestly it'd be good practice to just do this from scratch so straight up we're going to go import board import display oh excuse me import display oh do we need anything else we're going to start with just that we'll we'll see what else we might need so I'm going to go display equals board display we're going to go main group equals a new display io.group we are going to go display .show main group we could start adding stuff to main group now let's go BMP equals bitmap display io I don't know the order and this doesn't want to tell me oh interesting control click works but oh okay I was you don't have to hover I guess or you don't have to control I guess width height and then value count so let's just go the full size of the display display dot with display dot height value count is the number of colors I'm just going to say 10 doesn't really matter when I grow up I want to be you I love you Tim that's quite the compliment I guess thank you for saying that I appreciate you saying that Mario over there on youtube says that it's very kind of you my friend if you put a red pixel and blue pixel next to each other from far away it would appear as a different color kind of like faking RGB in an algorithmic way that's crazy that people figured out how to do that not only figured out that that worked because of the way our eyes work or whatever but then figured out how to automate it trunk through a whole image and do it okay so that's our bitmap we'll go our tile grid we need as well tile grid equals display oh tile grid this one takes I think a bitmap first oh we can't get it on that one would I do that oh oh it's scanning maybe that's was it doing that the whole time oh first of all I have a spell drawn I think it was scanning the whole I don't know if it's actually going to finish might not finish so this one is bitmap so the ones we need to do are going to be bitmap pixel shader tile width tile height actually those should those should be fine as default actually actually I think just bitmap pixel shader that might be all we need bitmap equals bitmap BMP and then pixel shader and we don't actually have it yet but I'm going to call it palette palette equals palette palette 10 colors have to do this part but it'll make it more fun let's go for I and range 10 palette I equals rainbow color wheel let's see I think this goes to 255 so I'll say I times 25 almost the whole range okay so I've created a bitmap I've created a palette I think I have put 10 colors sort of relatively equally spaced throughout the rainbow into my palette I have a tile grid I need to add the tile grid to the main group so we actually say main group dot append tile grid we already showed it so we can go while true pass theoretically this should show a blank screen I think you do have to look at the actual camera people only had like 16 colors for a while so they got creative that's a good point I think it stems from newspaper printing images originally though harder PC is even being invented that appears to have failed because we still have a serial output I can't actually make out what that says I could pull it closer and look I'm gonna actually just connect to you though and do this invalid syntax line 15 we get the colon in there there we go no something weird is going on because the display is not updating that's all old stuff it's not in codepie codepie guess it's not I mean I guess it doesn't have is it transparent by default doesn't make sense does it maybe we do need size let's go BMP dot fill 0 no difference I'm gonna restart this because I don't know what that thing is doing I was gonna say it doesn't seem to have made a difference but it actually did I don't know that was weird no idea what that was doing no idea why we needed to restart it we're there now and it turns out that index 0 is red if we wanted to we could do this time is not defined I don't know why this is stuck updating we don't actually need the rainbow thing going on though let's just go past there let's go to red by default and then what we really want to be doing is now bitmap tools and then let's say bitmap tools dot BMP dot fill region destination bitmap so that's BMP x1 y1 value so let's go x1 y1 so I'm assuming that's probably top left point of the rectangle we want to fill so let's say 10 and then let's say display dot width minus 10 so this will fill basically everything except the 10 pixel border and then the color we want to fill it with let's say anything but 0 let's say 5 that'll be something other than red there we go okay so that's our fill region there perfect so this code I essentially want for Blinky Display I.O some of the differences though are the way the display is going to get set up main group so when we run this the first time it will just crash because there is no fill region so you can see we could actually use fill region on the simple test once we have it it uses for loops I think or it might use two bitmaps actually I don't think it might be two bitmaps that I'm thinking about it okay I do have a clear screen so I'll run that that will just blank this back out Rainbow I.O is not defined we don't need text anymore so we don't need this we don't need this bitmap tools is not defined perfect although I am a little bit surprised that it didn't take it from here I guess is that because we don't have knit but I mean terminal I.O imported probably from there is it maybe it's because we have or do we actually we still have blink of display I.O installed doesn't look like it kind of surprised well oh oh oh oh oh oh duh got our import okay that's what we're expecting that is exactly what we're expecting how to refresh off no I didn't I did not see actually when you said that so I'm not sure what I had going on but it was not anything related to how to refresh I don't think it was basically I forgot my import here I forgot one of the other imports before that as well so at a baseline we just need def fill region we want to take all the same arguments as well type definition while we're here I'm kind of bad about doing type definitions on my first pass of code so this is a chance to try to be better let's figure out how this behaves in the less than happy paths so what if this was negative 10 does it crash or what does it do looks like it just ignores outside so no out of bounds error essentially we just any pixels that happen to be outside we ignore what would happen if we gave it backwards does that break anything so basically now we gave it bottom right and then top left rather than vice versa didn't seem like it made a difference can't I scroll can't I scroll back same surprised by that X pixel pixel position of the first corner Y pixel position of the first corner X pixel position of the second corner X pixel position of the second excuse me Y I think I said X again that one's Y and value I guess there's just code that that that has an if statement to determine which one is higher would be my assumption can take a peek at the C implementation although not the best at reading C so it's right there it might be nice to have example code here okay layout area this is like an internal class I think an internal type this is not exposed I don't think that's circuit python layer but it has area which is I guess like a rectangle object basically it looks like area canon I don't know what that does map area compute overlap area okay compute overlap okay so this rectangle represents the actual bitmap and then this rectangle represents the rectangle that the user asked to fill in that we find the overlap of that I would assume this is actually how we are ignoring the outer the pixels that are outside the bitmap my assumption would be is this function when it finds those pick get down from there cat trying to climb the window is exactly what we need um yeah anyway I would assume this is throwing out the pixels that are outside update the dirty rectangle this is an internal thing that it does to tell it that it needs to refresh and then it's actually just looping from there so I guess the other thing that happens is inside of here this must also it must also account for the fact that sometimes the user could pass top left first and sometimes the user could pass bottom right first I mean you could do bottom left and top right I suppose as well if you wanted okay so our code will be a bit different I think what we're gonna do is for loops that's not where we need to be one is um so we yeah we need to know which we want to do this we need to know we need to basically loop over the difference between the two points we want to loop over x1 minus x2 or vice versa either way and then we want to do the same for y the difference so x counter equals 1 if x1 is less than x2 else y counter equals 1 if y1 this one's actually trickier right because it's backwards kind of so if we are taking the top left bottom right version then y2 is gonna be larger than y1 means we're gonna want to count up so if y1 is smaller than what would we do for equals let's just do equals we might be off by 1 because of that but I don't actually know that one's basically backwards we flipped a sign to make it backwards so if y2 is bigger then we'll be counting down and if y1 is bigger then we'll be counting up but that sure sounds backwards so maybe I'm wrong we'll see I'm a lot more of a guess and checker than I am an actual figure it out the right way the first time plus it's always nice when you're a guess and checker with graphical code sometimes you get to see happy little accidents that look fun there's always a fun sign fun thing to see I should say so those are our counters so then basically we can now go that's just gonna tell us whether we're counting up or down so I'm just gonna go for y pixel in range and then we're gonna go y1 y2 and then we're gonna go y counter as our counting variable x pixel in range x1 x2 x counter if so we need to do the bounds checking basically so if it's in bounds which we'll say if 0 is less than or equal to x pixel is less than or equal to is less than destination bitmap.width do something y pixel height do something the something is no we actually want and these don't need to be two separate things I need this basically that's saying if the pixel is in bounds then destination bitmap do you do a tuple or do you do a a double square brackets you do whatever we just did there tuple x pixel, y pixel equals the value which is value that works but I actually know I shouldn't say I think it works I think it's the right like for loops I reasons why it won't work but starting with invalid syntax on line 5 are you not supposed to do these you're not supposed to do those we made it farther we have read something not quite right let's say let's just print some stuff out print fstring x counter x counter, y print x pixel y pixel is in bounds else is out of bounds let's see what if our loop is actually running or not print after loops print y range x range okay part of my problem for sure is that in order to go from 10 to 230 we need to count upwards but we're actually counting downwards which means we got this backwards there we go and it's very slow doubly triply especially slow because it's printing it's printing you know I guess only one line but it's printing one line for literally every pixel that it's doing so slow it's also like we talked about before we don't have the benefit of the C code to speed us up so if we turn off the print it should be a fair bit faster however still likely slower than the microcontroller I would assume we basically saw it chunk through where like a refresh hit as it was partway done drawing and then another refresh hit when it was further and another refresh hit finally at the end whereas on the microcontroller you kind of like you don't really see it draw exactly it just kind of goes that fast this linux computer like while it is a faster computer it's also working at a much much much higher level right it's got pil in the mix got blinka sending stuff via spy through the pins it's doing all the other stuff for a raspberry pi like got an entire like graphical user interface that's running I don't have hdmi plugged in so I can't see it but like it's there I know it's there because I plugged it in before I started the stream so that I could get the IP address so why did these not need to be backwards I'll be honest this feels wrong to me but I guess it's not I don't know why that something about that feels wrong to me though alright let's test the other thing so let's go negative 10 negative 10 ideally that should fill up the top and left side walls in addition to everything that's filled currently because this will basically make it ignore some of the pixels on the outsides to the left and top there perfect got all of our out of bounds we do still have and that would also slow us down actually let's turn that one off as well in case we don't need the else but okay let's do the backwards one so let's go back to 10 10 cut those over here duplicate well that stands to reason I guess I don't know why I'm so I know why I'm so enamored by that but it filled backwards right it chunked up from the bottom this time instead of chunking down from the top which does make sense right because we're counting backwards this time so that's kind of cool it did work we tested outside the bounds we tested backwards we need to test anything else don't forget to retitle the stream well blink a display I really we stayed on I will let me see if I I will try to remember to change it afterwards the only one I actually know how to change is inside restream and it will theoretically change both but in that title I didn't actually put the stuff I'm doing so that I'm guessing is in YouTube separately I need to probably fix that one so I'll do it after I can and then if I I don't want to like accidentally turn the stream off too early so I'll wait to click around on that page until after I'm done but thank you for the heads up um I think we're good on this one fill region I think it's working I mean the only other thing I could even think to test would be like doing bottom left and then top right instead of what we did before guess theoretically that should work the same bottom left would be 10x and then display height minus 10y and then the next one would be display width minus 10 and then y would be 10 bottom left top right mix it up with the colors a bit maybe I'm tired of teal and red let's do whatever number 8 is that did work the same still chunked up from the bottom which I guess is mostly to do with the fact that we have y as our outer loop right there is there a is there any way it can be more efficient can it somehow loop is there a way it can only loop once I don't think so that that springs to mind for me at least okay I will I didn't really mean to add this one so I guess what I'm gonna do is remove that cached following file has staged content different from both the file and head I know what that means we make a copy and then I guess let's try I mean can you do dash f with cached or I don't know if it worked it doesn't seem like it worked but I will say too though I guess because we're like file system through SSH it could be going slow it doesn't just look like it didn't work interesting okay so at this point high term is out of sync with git not entirely sure how to do anything about that for now I'm just gonna uncheck that so that it doesn't get committed I guess we still have a lot of prints cool that should now be okay draw line draw line yeah draw line I think is what we'll look at next I don't know if we'll have enough time to do it I'm gonna look at the implementation here see what it's looking like looks easy enough I'll take a stab not I may wait oh there's already a line okay fair enough let's look there actually this is a bit different because it's a an object not the easiest to understand actually let's see it might be cool to have a draw polygon inside bitmap tools potentially you have draw line just drawing a very particular type of polygon limit loops using counters loops using counters to not loop as far probably that's back when I asked about not using both loops somehow you know one thing we could do actually is something that would speed it up in the case where you gave it stuff outside the bounds we could use max and min inside of here so that we don't bother looping over pixels that are outside the bounds right now we're just looping over all the pixels that the user gave to us with their x1, y1, x2, y2 and then for each one deciding whether it's inside the bounds or not but we could actually we could actually use max and min here and throw away the ones that are outside the bounds and just not even loop through them anymore and then we wouldn't need magic here which would make it more efficient in those cases where they gave us a rectangle that's outside the bounds safe draw non-bitmap p0, p1 so that's just tuples for 0.1, 0.0 are the x1, y1 pairs in fact they get put into those variables here although they're zero based instead of one based point to on safe draw, bitmap and safe draw what is that safe draw bitmap, bitmap, point, color x, y equals point if, okay so safe draw is essentially the same logic as this this is just checking to make sure the point is inside the bounds so line on says define point on which is just draw at a given point as long as it's inside the bounds then it goes into here all this logic so we've got if those two are equal there's some stuff to do those two are equaled and there's some stuff to do so that's if it's a perfectly vertical or a perfectly horizontal line that's what these two cases cover and then else it would be a diagonal line so in that case it's calculating the it's calling it steep but it's the slope is it rise over run or whatever I don't know if it's actually the slope I might be wrong but anyway it's the steepness of the angle which slope is another kind of way to express so maybe it is maybe it isn't if steep well no it's well it's definitely not calculating the slope because it's a Boolean expression I guess it's greater than so that's saying is it steep yes or no but basically it looks like it's comparing maybe the slope of each a pair of pair of points or no I don't know deciding if it's steep if it is steep then it's basically swapping the X's for Y's and the Y's for X's then if X0 is greater and it's also swapping the ones for zeros which is interesting so basically the way they've done this is like check these things and then swap the inputs around that way we only need one case for how to count through it because they could have like had multiple different cases for like if this is true then count this way if that's true then count that way instead they've just said if that's true then change the points and if that's true then change the points and then once we've changed the points then we just always count the same way DX equals X1 minus X0 DY absolute ERR equals DX over 2 and what that would be it sounds like error right but what would it be ERR error if Y then use Y step okay that looks pretty familiar Y step we did something like that for X in range X0 X1 plus 1 if the point on else point on ERR minus equals DY if ERR less than 0 Y0 plus equals Y step ERR plus equals DX I do not follow the logic all the way there ERR slope okay it is the slope it must be short for error right error minus equals DY which is distance Y I don't know I'll have to step through this at a later time and probably add a crapload of print statements to it so that I can follow what's going on and figure out how to do it although worth noting I guess theoretically we should be able to just steal this function come to think of it we don't necessarily need to know we don't necessarily actually need to understand that it should be told because this function has almost the same signature it's slightly different this is tuples instead of four ints we would change that we would probably change the name of the variable the arguments to like desk bitmap and value instead of color but beyond that it's actually the same function so yeah good news is we don't necessarily actually have to understand it all the way from the from the geometry level we could actually I think just reuse it by swapping up the arguments and I think that's gonna be it but we will have to see that next time so I will call it a night for now thank you to everybody for watching I gotta say thanks again to Bablok B for all the work that they've done on Blinka display as well as the pie game display stuff thank you to everybody who has helped out in the chat including DJ Devin I will change the title after I get off of here so thanks for the reminder on that um hope everybody does have a good night as far as tomorrow so I'll be back tomorrow morning at 10am for the next stream over on my own channel I'll probably work on some iSpy stuff I think tomorrow and then at some point I'll get back into this but we'll see how far along I get into the iSpy displays but that's what we'll start with tomorrow morning so if you are gonna be around in the morning and you want to come join me over a cup of coffee and watch along with some more python programming then feel free to do so I'd be happy to have you I think that's gonna be I think that's everything for tonight thanks again everybody for watching hope everyone does have a good night I'll see you all next time thanks