Submind YouTube summaries
Thumbnail for Deep Dive w/ Tim: CircuitPython Turbo Sand Particle Simulation

Deep Dive w/ Tim: CircuitPython Turbo Sand Particle Simulation

Watch on YouTube

Video summary

In this episode of the Deep Dive series, Tim introduces his latest project: a high-performance sand particle simulation running on CircuitPython using Turbo. The demonstration showcases a physics engine that calculates gravity and collisions for numerous particles while maintaining smooth rendering at approximately 30 frames per second. This performance is achieved by combining Viper-compiled native modules for the mathematical logic with PicoGame for efficient display rendering, offering a significant speed improvement over standard CircuitPython implementations. Tim explains that Turbo allows Python code to be compiled into machine code, bypassing the interpreter loop and enabling direct CPU instructions for integer math, which is crucial for computationally intensive simulations like this one. A major portion of the discussion focuses on the technical nuances between different compilation methods, specifically the differences between Viper, Native, and standard MicroPython. Tim clarifies that while both Viper and Native compile functions to machine code, they differ in their approach to data types; Viper is a typed system closer to C where variables have fixed types like 32-bit integers, whereas Native preserves full Python object semantics with less speed gain but greater flexibility. The conversation also delves into the complexities of the Turbo shim layer, which manages fallback mechanisms for older firmware versions that cannot yet run compiled modules. Tim questions the utility of silently falling back to slow pure Python code when a project is specifically designed for Turbo, arguing that raising an error might be more helpful for users to identify compatibility issues rather than confusing them with unexpectedly slow performance. Beyond the core simulation, Tim addresses several areas for future improvement and community support. He plans to refactor the codebase to use clearer variable names and remove unnecessary dependencies like USB video handling, aiming to make the project more accessible and efficient. Additionally, he highlights ongoing work on other Turbo demos, including simulations for fluids, coral growth, and cloth physics, which will be available soon. The episode concludes with Tim sharing updates on his hardware development, such as getting USB host cameras and Bluetooth dongles working with Feather boards, while reminding viewers to check out JP's Product Pick of the Week later in the day to support Adafruit's open-source initiatives.
Read the full video transcript
All right. Hello. Good morning. Uh, what is up to Liz over in the Discord chat and forgot to put my keys away. Put them there for now. Uh, let me more of these windows wrangled. All right, there we are. So, yes. Good morning, good day. Welcome. Uh if you are new and don't know, my name is Tim. I go by Fomy Guy on GitHub and Discord. I stream at this time each week, uh Tuesday mornings at 10:00 a.m. Central time. Uh that is 11 a.m. Eastern time here on the Adafruit channel, working uh most typically on stuff related to Circuit Python. Uh you can learn more about Circuit Python at the main website for the project here, circuityon.org. This is basically a version of Python that runs on these tiny computers called microcontrollers. Uh there are a bunch of pictures of them over here. Um the sort of highlevel quick version idea of what Circuit Python is. Uh when you use it on these devices, typically the way it works, you plug it into your computer with a USB cable. Uh so they'll have either USB micro or USBC. You plug it into your uh computer. It shows up like a little thumb drive. You can edit a Python code file on that drive. And when you save it, the microcontroller actually will interpret and execute that Python code for you. And the code itself can interface with whatever other uh peripherals you have hooked up to your microcontroller. So that might be things like displays or buttons or lights or sensors, uh inputs, outputs, uh robotics stuff, you know, servos, all that sort of stuff can all be hooked up to these microcontrollers as well as loads of other uh types of hardware peripherals and your Python code. uh in this case can interface with those peripherals. So you can write Python code that draws whatever you want to on the display for instance. Um if you would like to help support Circuit Python, one of the easiest ways to do it is by purchasing hardware from Adafruit. Uh this is their website adafruit.com. They pay a team of folks including myself to work on the Circuit Python project. It's an open source project. Everything is uh published on GitHub under permissive open source licenses. So anyone is allowed to take that code uh repurpose it, use it for what you want uh port it to your own devices. Um all of that stuff is allowed uh and Adafruit uh again pays a team of developers including myself to to continue working on that. So if you would like to help support them uh in Circuit Python as well as uh other um open- source initiatives that they pay people to work on that are outside the Circuit Python world. Okay, they pay people to work on Arduino drivers and other things as well. Uh so if you uh would like to support open source software and the work that they are uh you know paying people to do one of the easiest ways again is purchase hardware from them. Adafruit.com you can do that there. Uh looks like uh the shop is getting loaded up with new uh little breakouts and sensors. Uh shout out to Liz. Uh the libraries for these uh TOF sensors are in and there is ambient light up now as well and then a hall effect sensor which shouldn't need a library I think right cuz it'll just be on off. So uh all of those things as well as many other microcontrollers and many other bits and bobs uh to make fun and interesting projects with you can get them over at adafruit.com. Uh, and if you check back later today, 300 p p.m. Eastern time, 12:00 p.m. Pacific time, you will find JP's product pick of the week, where you can not only help support uh, Adafruit and the open source software they support, you can get yourself a, uh, wonderful discount on whatever the item selected for this week is. So, uh, make sure to do that. And diving in for me this week, I'm headed over to the learn pages and in particular I have the turbo learn guide open. Uh and that is where that is what I am working on right now. So I have uh this demo for sand simulation which I showed a little bit on my stream this past weekend. And I am now working on a guide for although it is going to be a bit different too. I added the logo to it this morning. Okay. So there we now have Adafruit logo. Bit blurry. Take the camera a second to uh to focus in. So we have this uh sand demo now running under circuit Python using Viper compiled uh native modules or Viper compile. So one thing I need to figure out I'm still a little unclear on is the difference between Viper and native. That's actually something we're going to ask the robot friend to explain here in just a minute. All right. So, we have the sand demo uh with all these different particles of sand. And we have the math for this is now running in one of those compiled uh Viper modules. And then in this case, the display rendering uh is using Picoame, which allows it to render a little bit more efficiently than display IO does. And those two things combined allow us to have this demo uh with all of these really nice smooth snappy um sort of gravity and collision simulation uh mathematics going on but still rendering smoothly. I think this one renders around uh maybe 30 frames a second or something. I think it's even printing on this side. We were to open up the terminal. Yeah, 30 34 32 38. Uh so it depends on how many particles are uh moving, right? If if you kind of just leave it sitting um depending on the orientation of your sensor, they will all just settle uh to one spot. And so when they're all settled, the FPS goes much higher. And then of course when they're all moving the FPS drops down a bit but is still um you know perfectly good for being able to get a nice smooth reaction out of the gravity on it. And certainly miles and miles and miles better than uh we would have been able to get in Circuit Python pre Turbo and uh Pico game. Let's see this actually. And so I will need one more link. Oops. Okay. I do actually want to uh ask about the difference between native and Viper. I'm I'm there's kind of two different things and I'm unclear like I think sort of that Viper is faster but maybe chips dependent and native is not. I'm I'm not sure though. I don't understand the difference actually. So let's figure that out. Uh-huh. Can you tell me the difference between Viper and Native? So native, I mean before turbo stuff had come up, I would have said native to me. I interpret that to mean essentially like a C module but I think with what was added there are decorators. So so what was added essentially is from upstream microython there are decorators like at microython.viper and at microython.native native. So, this is also something I'm a little unclear on is this turbo directory and the shim. Where's the native path? So native is maybe native is faster. It prefers the native path. This is a hypothesis at this point. Viper and native both compile a Python function to machine code instead of by code. The difference is whether it still behaves like Python. With native Python semantics, no interpreter loop. Every value is still a full Python object. MP object G A plus B becomes a call into the same runtime routine as the interpreter uses. What it saves is the bite code fetch decode dispatch on each OP. Almost any Python code works unchanged and ins can still grow to big end. Speed up is modest commonly around 1.5 to2. Okay, interesting. So, this sounds more like what we had before, just sort of what I would think of as regular MPY, but that may not be the case either. This may still be on top of and in addition to quote unquote regular MPY. So then Viper though is faster. So Viper is typed. It's closer to C. Locals and arguments get machine types. So int and u in 32 or no u int and u 3 32-bit registers that wrap on overflow. There are no big ins. So we can't no integers bigger than 32 bits. Boolean pointer pointer 8 pointer 16 pointer 32. Anything else is an object. So we have int uint boolean pointer the four pointer types and then object integer math on typed value become single CPU instructions with no runtime calls. Pointer buffer uh pointer 8 buffer I and pointer 32 array I become direct memory loads and stores. There's no bounds checking, no objects boxing. So that's one thing to be careful with. Um no bounds checking. So you need to make sure your code is, you know, staying within the lines, so to speak. Uh, overflow wraps silently, out of range, corrupts memory or crashes. Variable can't change types. So yeah, Viper is faster and more seike. Mixing typed values with object values, drops back to slow runtime calls. Speed up on tight integer buffer loops is often 10 to 50. Both emitters share one implementation. Compile picks either native or Viper around there. It generates code. Viper annotation. How this shows up in the repo. Sand is textbook Viper kernel. Okay. So my next question then is about the shim board doesn't Right. Right. Right. Yeah. Oh yeah. I mean the board interprets plain Python still from code pie. It doesn't necessarily compile anything. I would say that's true. Uh only a thumb emitter is wired up. Only a thumb emitter. I don't know what that means. It's machine code from py turbo build runs. Mpy cross. Uh so I have not actually used the turbo build either. I've been with the turbo stuff that I have done. I have been just using MPY cross directly and I do have at well I've been using okay yeah that's what I haven't used turbo viper decorator I've been using at mic python viper decorators but actually one second what is The what is the sand demo? Shout out to Mikey as well. Hug report to Mikey Scalar gave me this uh implementation for the matrix portal. So I worked on uh this version and I should say also when I say worked on I mean huge huge hug reports uh go to Phil B uh originally for this code. Phil B created this a long long time ago uh for LEDs. There have been Raspberry Pi versions over the years. There's Arduino version. There's 13 by 9 or whatever LED Featherwing versions. Um Phil B wrote the core code that that does that does this simulation been adapted to a few places over the years and now it is uh able to run in Circuit Python. So in sand this is actually at turbo. Okay. So yeah, one thing that is something I have not looked into yet is the sort of circuit python at turbo layer I have been doing all. So for instance like this code here uses this at turbo viper compared to for instance this code which is the TFT one which I did I adapted again from PhilB's code. This is what's running on here right now. This is washed out as well. They're rainbow colors. Think maybe you've been able to see that as I tilt it. But uh this is what's running on here. And this code just uses MicroPython Viper, not Turbo Viper. So, I'm still not quite caught up on the the actual Turbo CLI and the shim. Okay, it sounds like maybe we do still get a an improvement by using native um over what I will refer to as the quote unquote normal MPY, the old normal MPY. what NPY has done normally up to this point. It's like maybe native is still a little bit faster, but I'm not positive on that. Uh, but Viper definitely you just the thing with Viper is you do need to rewrite it and it really is only useful for sections of code that are um doing math inside of a loop on integers. Basically, they can be pointer integers. So you know you can have lists and things as well but ultimately it boils down to math on integers. Um okay and so then in this sand demo again thanks to Mikey for this one. So here it is using lib turbo which has this gem. So this is where I'm not I'm not quite clear on why this exists exactly. So Turbo runs compiled MPY where the firmware can source everywhere else. Your Python I don't really like it's not really a complete sentence. I don't know what that's supposed to mean. puts the native arts from the front of cis path source right be so we need to have source I'm a little unclear on that part as well um source has the sand. Okay, one more here too. You tell me what the turbo.py doing. Although while that's working, we can read through the code as well. But it does have this dictionary of archetypes which are just numbers to names. Sounds like cis.implementation.mppy. report. Sis 114. So this is going to get that value and then shift it. sure what that did that take out did it leave us with just this I don't really have a clear picture of uh like a mental picture of bit shift math all the Yeah. Bits one and zero or bits 10 and up. the table. Okay, so we got 10. It's not I don't think grabbing the 10 from there. It's going to binary first. But 10 here maps to this. Okay. So this is not I think I'm starting to understand this turbo pie shim layer. This is not necessary. This is not necessary for the speed up. But what it is is in this transmission period where we have like this isn't even officially turned on yet for instance for the matrix portal. I have uh did I say anything about the matrix portal yet? I don't think so. Um I have the matrix portal over here which is hooked up as well. Um, but for instance, it's not actually. So, right now, I think officially in released builds, it's in RP 2040 and 2350 only. And then there's a few more peers in now. I'm not sure if they've been merged yet or not. Uh, but I I don't think they were in the release we just had if they are merged. Although, again, I'm not sure, like I said. So I think M M uh 4. So uh SAMD51 is coming up. Um and then espressive is coming up as well and some other ports uh also are going to get enabled but but technically it's not enabled yet. So, what the shim is doing is like handling the fact that the system we're running on might not be able to use this yet because if it's on an old version of Circuit Python, it would not be able to import the new Viper compiled thing. I don't know exactly what would happen if it I assume it raised an exception. I'm not sure, but it would not work one way or another. So this is making it so that it will have a series of fallbacks. Basically it will try the Viper one. Although it didn't it say prefers native. It prefers native. Run compiled MPY where the firmware can source everywhere else. Pure Python. I guess this is this this sentence is referring to its own file. It's saying this file is pure Python maybe though I don't think it's necessary. Puts the native arch under that in front of that with source right behind it. So a module missing from the arch still import source identity decorator. Okay. I don't know where I saw that. Functions cannot be Yeah, I don't know where I saw that. somewhere. I thought I saw uh something about preferring native, but that doesn't actually seem to be the case in that file at the very least. This read me. Did I see that native loader? Okay. I don't know about this. I don't know about this being hardcoded to source because I can imagine a scenario where you want uh also what's up uh Fed2 sorry I saw your message just a minute ago I meant to say hi what's up my man uh nice to see you my friend I think it was in the explainer for the difference between native and viper from the cloud session that could be that could Okay. Yeah. And I was talking about faster by code. They hand them the So, so this is interesting as well. I have to I'm uh I have to look into decorators. Decorators are a thing that I've used several times but never really looked into the mechanics of in Python. They're very interesting though. Um, I feel like we're missing a potential fallback. I feel like we're missing a fallback. I wish this would stop opening. I So yeah, I think a few things. One, this hardcoded as source. I think this could get unwieldy if you cuz if you have the same board and let's say you have like four different Viper projects on it, right? projects that use Turbo, then you're going to end up with source containing presumably four or more if they have multiple modules each um Python code files that are now for different projects. I would almost like to see this either just use the root or ideally if it's possible to be like I mean I like to do them per project. So, so if I'm making the sand demo project, then I would have like sand demo as the folder name or you know something descriptive as the folder name instead of source and it would have all the helpers all the Python modules that that project uses could go inside there and then they're still separated. But I do. So then the other thing is I think we're missing a layer here because Yeah. I don't know the way this would have arch in it is a little weird to me too because on the same like the devices archetype architecture architecture will never change. So you would only ever have one of these. So, it's like lib, turbo, and then whichever one you're on, and there will only be one. It in my mind, it could just as easily be in turbo, or it could even just as easily be in lib. But, but remember, we did find that difference between Viper and native. They're both different MPY formats. one of them. Well, no, they both need the flag, though. They do both need the flag. They do both need the flag. And then I guess because of the way this works currently, it would fall back to source. You could put sand.mppy in source. You could have uh you could have a non turbo MPY file. You could have an old style MPY in here. It would actually load that. Okay. So, we're not really Yeah, we're not really missing a layer like a fallback. I was thinking we were going straight from if the Viper one doesn't exist, then we are forced into Python directly, which I will say the way this is documented makes it seem like that a little bit. Um, but in reality, the way it will behave is you could actually have sand.mppy. You could have a old nonviper, non-native, non-turbo at all, just regular old MPY, whatever the standard MPY was before all this. You could have that here. It would still be it would still be what gets imported. As long as you don't have both. If you have both, then the plain tech and the plain text one will get imported. At least that's the way it is for lib and root. I assume it's that way for every folder. I think for I think uh it's for packaging so that a release could contain MPY and ESP from the release perspective in a release. That that does make sense to me. Like a release of a project that runs on multiple devices. That would Yeah. Yeah. And that would explain Arch, but you would only you don't really need all of those on your one device, right? in this in in a hypothetical project that supports different architectures, you don't need or necessarily want the other architectures compiled code on the devices that don't support it. So, you do still end up only with one. And I think like in an ideal world, we will have hooked up the uh I don't actually have an example of the button, but like on the code pages where there's a get get uh what is it? Project download project bundle. That's project bundle, right? Yeah, download project. So on the I think the idea is with this button there's a tool behind this called bundlefly which could package up everything for the device you're on as long as it knows which device you're on. Also saw in the code it asked about what's the arch to load that specific. Yeah, that's that's another thing that's a little weird to me is like well I mean ultimately that boils down to the same choice of having the folder with the architecture name right the reason why we need to have this mapped is because it's these are the folders that it is wanting inside of Turbo here. So, X to win. Yeah. I don't know if that needs to live on the I don't know if that needs to live on the device, though. This feels like extra complexity for the final device to me cuz the final device is only one architecture. If the bundler, the thing behind this button can package it without the architecture folder, then there's no need to have this mapping because we never need to know the name of this folder. We still can check just yes or no like is native supported or not. And we can still um do this this path trick to to put in the different the different things in the path there. But then we don't end up with this cuz to me this code reads like it's picking with like picking from available architectures. I don't know. I just it this feels like a packaging detail, not a running on the device detail to me. It feels like a a a thing we don't it doesn't need to be on the device. This shim that's like what do I do for all these different architectures when that shim is only ever going to run on that that one? Well, I mean, the same shim runs on different ones, I suppose, but if it were refactored a little bit, it would not need this whole idea of which one is which. And it doesn't seem to do any enforcement either, I don't think. Right. So, like this this module is not helping you if you have the wrong one of these. It's not like telling you, hey, we're on X to win, but you have ARM 7, therefore we fall back to Python code or something. It doesn't seem to be doing that either. For saving space, you only need Yeah. Extensa and that's it on the board. ease of instructions. You just say copy these to lib. Circup uh totally I think is part of the answer to this as well. Yeah, good call uh Fede for sure. I think circup is part of the answer to this to this to this problem to the scenario as well. Circup I view circup as very similar to this the bundle thing that's behind this button basically. Uh and so circup I would love uh and I think it will as well. we'll we'll get to the point where it is built in to support this stuff as well. The other thing that it's telling uh is is that telling a user if you have ESP you need to remember extensib. Yeah, I think but I think where I land is it's easier to just tell the user to copy it to lib and not worry about the architecture at all and have the bundling tools have the bundling tools pull the correct architecture. So instead of having an extin folder on my matrix portal for instance, it would have just either just lib like it does today or if we want to keep turbo then it could be lib turbo but it would have no extensive whim and instead it the bundler. So the thing that's behind this button and circup both would know to check whenever you install a module. I will say circup is a bit weird because it's for library code more than project code. Um, but idea being the project bundler and circup or or some tool would know would would check your device and they would say, "Hey, you're on an extensive wind device. So, I'm going to give you compiled code for that, but I'm just going to put it in lib turbo or just lib by itself. Either one. Okay. But I understand it a lot better which is good. Um and then these are the decorators which do what again? Identity decorators. Turbo Turbo Native and Viper are markers for the Oh, okay. They don't do anything in Python code. Okay. And then these are only these must be only for if you use the turbo actual turbo CLI as well. Yeah. I don't know. I at the end of the day I think uh it's worth a discussion. It's not really just it's not really just up to me. I could see some like upside to having it document the architecture there. Uh, but I can also see some confusion where people are like, do I need to have all these or or if the bundler gives them all of them, they copy all of them. That I don't think we want. Ideally, we want the bundles to know which device you're you're running on. Circuit, it's easy because it can just check the device. The bundler here is a little trickier because obviously it can't just check the device. But what we have working for us here is these tend to be on a guide page which tend to be for a project that is for a specific like board. Uh in this case it's not right because it's an example of this breakout. But this is not something that would really benefit from turbo anyway. We would almost, you know what we would need is like we would almost need a comment or something at the top of the file that like declares for the bundler what the architecture is supposed to be. And I think we need that either way, right? Whether we keep that folder or we don't. Somehow the bundler needs to know what architecture it's need it wants to bundle for. Yeah. And I think some kind of like comment or something near the top is probably the easiest way. Or maybe like a hidden file or something in the same folder. Okay. Yeah. I don't I don't know about this about this shim necessarily. I think the main part of this shim in my mind I think I part of the I guess where I'm stuck a little bit is this shim kind of it implements behavior that we sort have already with because because like Circuit Python by default will choose the right import when you have multiple ones available. Well, what it calls the right I I guess what this does is it Yeah. Okay. What this does is it it's really back to what I was talking about at the very beginning, which is the fact that we don't have support on old versions, right? So if you have a matrix portal running old circuit Python, it's not updated yet, and you run a project with this shim on it, the project will have the capability to fall back to raw Python. So it will do something instead of error, but it will run way slower than it would if you had newer circuit Python, which is a tradeoff. I almost wonder if it would be I almost wonder if it would be worth like do we want it to go do we want it to fall back to slow? I could almost see a case for like if you have a thing that is from a project for Turbo, the whole point of it is to run that section in Turbo and run faster. I can see the upside in it not crashing if you're on an old o uh version of Circuit Python, but you are still missing out on what I would argue is kind of the main point of the project, right? because it's at this point it's a turbo project meant to take advantage of the new stuff. So you are then running it in the old way where it's slow. I could almost see it being better or less confusing, I guess, if it were to just crash instead of run slowly. Because if it crashes, you know, immediately visually from the serial terminal, there's going to be an error that's like, we tried to import this thing and it failed. I I presume some kind of error like that will happen as compared to with this shim. It silently picks a slower one so there's no like I envision a person coming to the help with channel and being like I'm running this turbo thing and it's going super slow. What's the problem? Whereas if it errored out in this scenario they would be running this turbo thing. They would see the error. They may still come to the help and be like, I'm running this thing and it's crashing. What's the problem? But they may also see the error and be like, oh, the import's not working and maybe we have an FAQ or something. And it points them in the right direction. It it it feels like easier breadcrumbs to follow if it just raises than falling back to the slow one because if it falls back to the slow one, it's on the user to like notice by looking at it that it's slower than it should be. And the user may have never seen the fast version in person, right? Like presumably they maybe have seen the guide which has GIFs or videos, but they maybe found it on GitHub and haven't even seen the guide or they maybe have seen only the guide and never seen it in person. They're like trying to make a judgment call of is it as fast as it's supposed to be um to know if it's working or not, which feels tough to me. Thinking of uh let me see here quick related topic tested yesterday circuit Python rest with mandelroad example 7.5 times faster than turbo on an M7. Wow, that's pretty crazy. Currently porting and testing the sans code. Oh, nice. Thinking about porting ULAB to Rust as well. Interesting. And FedEx says, "Yes, I would not like a fallback if the idea is to have the screen move in the sand faster than have instructions." Yeah, I think I'm I think I'm coming around to that stance as well. I think I lean like right now, I would say I lean the direction of I think the exception is probably more helpful, but I'm not fully decided all the way, but I am like I'm headed in that direction. the slow the the the slow fallback. Again, I can see the upside of it because you get something instead of a crash, but I feel like the something you get might be tough for a person to actually know that what they're looking at is a consequence of it not working how it's supposed to. Uh, I think the fallback is more for Turbo as a dev project than the guides that use Turbo. Yeah. And it does. Yeah. Yeah, it definitely does help. It does make it a lot easier to go back and forth. Okay. Okay, so these are I mean these are a couple of things I'll uh discuss a little bit with Mikey I think and the rest of the team as well. Maybe this would be a good in the weeds in the weeds thing to dig into a little bit in the future. Um yeah. Yeah. Yeah, I think the exception makes makes a little bit more sense in my mind. Um, but I can see it both ways too. Uh, Okay, one thing I did on this one earlier is make these a lot easier to Understand all of this physics before was like uh single and double letter variable names. There's some room for improvement still with the constants which are basically indexes. They mostly have comments. I might still go through and rename these honestly. But again, this is all basically Phil Bee's code from which I actually didn't when I first started working on this, I did not I hadn't found it in this browser. I had not found this actual pixel dust repo. When I first started this the other day, I had not this code. It was animated sand. I think this is the code I started from or yeah was either that or a panel. actually. I think it might have been this one or no cuz this doesn't have code. That's right. This is the handles and this is hourglass which is a little bit different. Yeah, I think it was the 13 by9 actually this one. But I believe the code in this one was based on pixel dust cuz it also was fil but I did later find that there is actual pixel dust library as well which it does look like is the code that was running in that guide. That was just the handles actually. I do think the constants would be good even though they have comments here. IP. key. I'm not sure why P is what is what's prepended to all of these. E parameter might be parameter because it does pass by index basically. Some of these could still be improved for sure. like these. So, nothing in here says what He is this speed and it should I guess it's the I don't know nothing in there says what it This fantasy. Nothing actually calls it from here. We would have to look in code pie. Back up. Take this part out for the published one. I used USB video before I had a display connected. Where does P get assigned? So, we have this P here, but it's okay. Still, there's definitely still room for improvement. Uh, this got a lot better, but should be still better. Like these used to be two letters, one letter. There's a seed or random was used a lot or LCG or something I think is an acronym that was used for random. But especially where code pie is calling into this, it could be a lot better. And Code Pi itself could be a lot better as well. So I think that's what I will do next. And also rip out the USB video stuff. We don't need that. Uh it doesn't need to check targets. It can just assume the ILI because this is kind of targeting Metro with the TFT shield. So okay, I'm going to clean that up a bit more. But that is basically uh where my head's right now is Turbo uh all the things. I have the sand demo that I've been working on. This is the first uh guide that I will publish for Turbo stuff, but I do have a couple of other uh demos that will come out perhaps after that or I'll throw them up on Playground or somewhere if they don't uh become official guides. There's like a fluids one, there's a coral one, there's a cloth one which is pretty cool. Um, if folks are interested in those, you can check back on my channel uh from the stream this past weekend on Saturday. I played with those a bit. Um, and before I go, I will leave you with the reminder for JP's product pick of the week. Uh, later on today, you can get yourself a great discount on a wonderful product, whatever it is. And uh, as always, when you purchase from uh, Adafruit, you are helping them support open source and all of us that they pay to work on it. Uh, congrats again on USB camera. It seems very interesting. Yeah, camera I think is going to be cool as well. I did get uh I did get USB host camera working. Um, that was something I mentioned in the meeting yesterday. Uh, for fruit jam, there are some changes in upstream picop USB um that we'll need to either get merged in or we'll have to use our own fork for that. And actually last night uh which I don't think I didn't mention in the meeting I don't think but last night one of the things I worked on was um USB host Bluetooth. So the ability to use a Bluetooth dongle. Um I'm working with a specific one. I forget the ex it's a realtech one. Um and I'm focused mainly on that one specific one. Not a generalized thing, but uh I was able to get a Feather USB host with a Bluetooth dongle talking to a Circuit Playground Bluefruit. So, uh pretty cool stuff going on there as well, I think. Um so, yeah, keep keep uh keep a lookout for that stuff coming up. Um like I said, JP later today. That is at 3:00 p.m. Eastern, 12:00 p.m. Pacific. And I should be back next week at the usual time here on Tuesday. uh 10 a.m. Central time for my stream as well. Until then, I will bid you so long farewell. Thank you for watching and hope you have a wonderful day and a wonderful week.