Got the correct audio set up here. So just one second... There we go. Mr. Liali. Hey, how's it going, John? Good. How are you, sir? I'm good. Sorry about the background noise. Hopefully it's not too bad. No, I think we'll be able to deal with that. Excellent. So it's your show. What can you tell us about fundamentals of diagnostic requests over CAN bus? So this is a new one for me. Not diagnostic over CAN, but trying to present from a garage that doesn't have air conditioning. So wish me some luck. So right now I'm in the Car Hacking Village garage that we're hosting the Village. So behind me, you'll hear the emcee for our 101 talk. So there's a little bit of crosstalk, but once that's over, it'll just be me. Hello, everybody. My name is Robert Liali. I also go by CarPlucker. So you'll find me on the Discord right now. I have actually two different names going on. One so I can get the experience of the user, and one so I have the experience of the Village user. So you'll see two CarPluckers, C-A-R-F-U-C-A-R. So just go ahead and DM any one of those. I should get that if you guys have any follow-up questions. I'm also in the voice channel right now. I've muted myself, but later when I take some questions, I'll make sure I unmute myself so I can hear some of the questions that might be coming up. I won't be able to respond to them because I don't have... I'm as a normal user, but I'll try to type it in there, and maybe as John is rebroadcasting, I'll try to get some of that information out. So let me go ahead and get started and just kind of go over real quickly what is Diagnostics. So I don't have a very perfect presentation here, but I do have an old presentation that I've given before. So let me just close this up and just kind of go over the concepts of Diagnostics as it relates to vehicle systems. So Diagnostics is a layer of communications that allow you to connect and send requests directly to vehicle controllers, right? So one of the challenges though is Canvas has a very limited amount of space that you can put data bytes in. Specifically, you can't send more than eight data bytes. So in order to do that, they need to send... they need to send about 4,095 bytes. You have to use other protocols. So specifically, most Diagnostics in vehicle systems use a protocol called ISO 15765-2. This is sometimes referred to as the transport protocol or ISOTP or CAN ISOTP sometimes. This is the transport protocol that nearly every Diagnostic protocol builds upon. So this is that middle layer or transport layer that bridges the CAN bus limitations to allow for longer message lengths. So a lot of people and a lot of companies GM, Ford, Toyota, everybody uses this... seems to use this protocol now for all of their Diagnostic protocols. Now there are a handful of different Diagnostic or what we call high-level protocols or application layer protocols. I'm going to focus on one. It's called UDS. And the reason why I'm going to focus on UDS is because it's the most well-known and highest used one. Almost all Diagnostic protocols are kind of based off this particular one. So what I want to do is I'm going to take you through a process. Now this is a process that is actually related to OBD2, but this particular process takes apart a Diagnostic CAN message. So here's a flow for Diagnostic CAN messages. Let me just... I have two screens, so this is coming across the wrong screen. So let me just turn that feature off so that we can play this back on the main screen. I would like to run the slideshow from the current slide, but I don't want presenter mode. So let me just find out. No, I don't want that. I want to do that out of the primary monitor and from the current slide. So this should allow us to see this a little bit better. So I'm going to take you through the individual process, just sort of on a slide, and then I'm going to take you through it on an actual vehicle, but actually going to take you through on the vehicles that we're using for the Car Hacking Village challenges today. So here is ISO 15765-2. There are high-level protocols that run on top of it. I'm going to talk about specifically the OBD2, in this case this will be OBD2 Diagnostic Protocol, sometimes called J1979. So this is sort of like a standard protocol family that's across all vehicles, makes and models that are on the road today. So let's take a look at a flow of messages, the example of flow. So I'm going to take a really simple one that really illustrates every facet of this particular protocol, ISO 15765-2, specifically the VIN request. So the VIN request has the two bytes. The two bytes are 09 and 02. So 09 is what we call the service byte. The service byte is essentially the function that we're asking the controller to perform. In this case, we'll ask the engine controller, because every vehicle tends to have an engine controller. We'll ask the engine controller, hey, what is the VIN? And the first part of every request is the service or function you want it to perform. Now there's a list of all of these functions, specifically OBD2 functions. If you just google OBD2 services or OBDIDS, you'll see Wikipedia has a very good list of the individual services called modes that are available for OBD2. So service 09 is a request service. The function or sub-function 02 is the parameter ID for the VIN. So if I were to change that from 02 to 01, I would get the VIN. I'd get something else from the controller. But it'd still be a request, but for a different parameter. So here's my VIN request, 0902. And I'm going to go ahead and move this up to my VIN request. Look at that. Pretty exciting. Kind of came through the video. I'm also monitoring the video. It looks like you guys saw a little bit of animation. Spent a whole two hours working on that animation, so give me some credit there. So there's our request, 0902. Everybody with me? Raise your hand if you are. Okay, good. Excellent. I like to see that. So next, we have our VIN response. So you'll see our VIN response is significantly larger than our request, because it includes our information about the success of the request, information about how the request is encoded, and the actual VIN characters in ASCII databytes. So the VIN on most vehicles, in fact, vehicles in the United States, are 17 characters long. So we'll have at least 17 characters to encode the VIN. In fact, in this case, we'll have 20 characters, 17 characters plus three overhead bytes that are happening. So we see the 0902, but now our response will come back, and you'll see it says 4902. So as you guessed it, that first databyte is, again, a service indicator, so a service byte. But it's not the service or function that we perform, but rather the response or the information that's coming back from the service itself. And rather, will we see a successful request or not? And each time we see 49, or 40 hex more than our original service ID, we know that this is a positive response. So in this case, this request that we're getting, that we sent out, was successful. Our response is positive, because we see 40 hex more than our original request. The 02 is just an echo of the original parameter ID, or PID, in this case, the VIN request. So even if we didn't see what the initial response was, we could tell that this wasn't that, indeed, a response to the VIN request. The next byte, the 01, is actually an encoder to let us know that this is actually an ASCII-encoded parameter. So now one challenge we have here is this is now 20 bytes. If you know anything about CAN bus, CAN bus only allows 8 bytes per frame. So we have to do something about this. If we can't fit this into a CAN bus frame, we have to segment it. We have to break it apart. And so, how do we do that? So we take the first part of the message, we take the second part of the message, then we take the third part of the message and we break them across three different frames. Okay? So now we have three frames of CAN bus messages. So here they are, 490201C. It's the same frames, but we had to break them up into different frames. Okay? So I'm going to go back. I'll reiterate that we are sending this message to the engine control system. So since it's to the engine controller, we need to identify each frame. Each frame identifier is essentially part of the arbitration ID of a CAN message. Every CAN message has an arbitration ID, and inside of that arbitration ID is the identifier for the controller that we're talking to. In this case, we're talking to the engine controller, which is legislated at 7E0 for 11-bit CAN bus system. So since we're talking on an 11-bit CAN bus system, we'll say 7E0 and then we'll say 0902. Okay. Awesome. Here we have a message that goes across. But we need to add some more information specifically about how large this particular message is going to be. So then what we do is we're encoding something called PCI data or program control information. So the PCI data could be the frame type specifically. So here we put a 0 in. A 0 is a frame type. That frame type 0 is a single frame message. So right away in the first data byte of this particular frame, we know that this message is going to be a single frame message because there's a 0 encoded in the first nibble of the first data byte of the frame. The second part of that particular nibble, the second nibble of that byte, is the data length. So it indicates how many bytes this particular message is going to be. In this case, since our message is 0902, we have two bytes in our message, we put a 2 in that data byte, indicating that there's two bytes of data. Really simple. 02. Very obvious if you think about it. Here we have a 020902. And now we'll do one more thing. We'll do this thing called padding. Padding is a way of filling in the data. Not all cars require this, but I always recommend it because some cars do require it. And later what I do in my demo, I'm going to show you guys how that is the case. So now we fill in the rest of this data with padding. The reason why we know it's padding, because again, we go back to the beginning of the data frame, and we see the 02. That 2 is an indicator of how big this particular message is. So we count one, two bytes, 0902. Those are our two bytes of our message. Anything after that must be padding, right? Because it's not part of the message itself. So even if there were data there, we could ignore it because it's not part of the actual message. All right, so we send a request out. Now we should get a response back. Our response ID has got to be different than our request ID, but it's usually just a little bit different. Sometimes it's significantly different, but sometimes it's very much the same. In this case, our response ID will be 8 more than our request ID. So 70E0 returns a 70E8. This is part of the OBD2 specification for emissions-related controllers, so that's just very common, as you'll see. So again, we'll have a frame type, just like we had before in the first nibble of the first byte of the last frame. We do the same thing here. We have a one to indicate that this is indeed a multi-frame message. So one equals multi-frame. Just like before, we had a data length, but since our multi-frame messages can be over 4,000 bytes, 4,095 to be exact, because we can have 4,095 bytes, we need more bits to encode that information. So the four bits, or one nibble, that we saw in the single frame message just before is not enough. We need more bits. So we expand. Now that we use those four bits, but we also use the next eight bits as well, giving us a total of 12 bits, so 2 to the power of 12 total number of values that we can have, so 4,095. So now we know how many bytes we can have. So I put a 014 to indicate that this is actually going to be 20 bytes long. So 014 is hex. That equals 20 in decimal. And then we fill in the rest of the actual message itself. So recall the VIN message starts at the 49, 02, 01, 31, 32, 33. So that's the data that's going to be encoded in the VIN. So we just sent a request to a controller. It just responded back to us saying it's going to send us a multi-frame response. What if we can't handle a multi-frame response? Or what if we need to reserve a buffer and we need to do that quickly? We need to give some feedback to the controller saying, hey, listen, I'm either able to do this or I'm not able to do this. So that's what we call our flow control. So we're going to send another message back to the controller to let it know that the kind of the rules of how it's allowed to transmit the rest of the data back to us. So we'll send a 70, 0 back to the controller. And then we'll send a 3 for the frame type indicating this is a flow control message. So we're going to send a flow control response back to it. Now, we have to do this within 100 milliseconds of the request that we sent. So you have to do this programmatically. You'll never be able to do this by hand or manually. So if you're typing in, like, send flow control or something like that, you'll never get the timing that you need in order to do that. So you have to run some sort of script locally to the hardware or something like that in order to have success with it. Or your hardware needs to support this particular feature. So that's something that's important to keep in mind when you're doing diagnostics. Next, we need to send a... it's clear to send. So we say a 0 in the next nibble of the next bite indicating it's clear to send. If it's not a 0, it's some error. 1 is an error. Hey, you can't send this. So usually that will mean that the controller stops sending its request. The next is the block size. So that's how many blocks or messages or frames, I should say, that you need to... you can send before another flow control is sent back. So normally that's set to 0, meaning I'm not going to send any more blocks. Don't worry about the blocks. I'm just going to... I'm just going to send this one flow control frame. You finish up the rest of the message. You don't need to hear from me again. These are the rules. Sometimes it can be a number not 0, meaning if that was a 0, 1, then the controller would send a message back to me, and then I would send another flow control frame, and then the controller would send another message, and I would send another flow control frame, et cetera, et cetera, et cetera. So that number essentially is how many more frames until I send a flow control. Last parameter here is the frame separation. This is the actual time in milliseconds that we need to wait between... that the controller that's returning the data back to me needs to wait for it to send a message. So 0A equals 10 milliseconds. And again, we have padding as well. Now we get a response back. The next frame comes after the first frame that we got. The next data bytes. In fact, the next seven data bytes. Why seven, not eight in the Canvas frame? Because we still have more PCI data. Specifically, a frame type of two. Two indicates that this is a... what we call a consecutive frame. A frame that happened after a first frame. So if you were just to look at this and just monitoring the Canvas, and you saw that this is a consecutive frame and you didn't have the first part of the frame, you would know that you missed some data. So it's super important to know that if some other system is monitoring it, that this is not the first part of the data. But what part of the data is it? Well, we have this rolling code that comes next, indicating that this is the first consecutive frame. So we have a one there. The next frame will come. It will also be a... have a two as well, because it's also a consecutive frame. But instead of having a one in the rolling code section, it will have a two. And guess what? The next one will have a three and a four and a five, et cetera, all the way until we hit F. And then we just roll back over to zero. So you'll see it go 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, and then go back to zero, 1, 2, 3, 4, 5, et cetera, et cetera, if there were more data or more frames that needed to be sent. But for the most part, this is pretty simple. We've essentially requested the VIN and received the VIN back from the controller, all pretty quickly and pretty simply. So I want to take a little bit of time. So I'm about 18 minutes in. Great. I'm doing well on time. I want to take some time and do a little live demo. Now, you know what live demos are. They're always bad. Don't ever do this. So I'm going to go ahead and do this. All right? So let me bring up all of these. I've got a few different windows open here. Specifically related to our system here. I'm going to go ahead and minimize a few things here. Okay. So this is the this is SocketCAN. If you're not familiar with SocketCAN, we have some really cool information about how to get started with SocketCAN. In fact, everything I'm doing right now, you can come over to the CarHacking Village. Join the virtual.carhackingvillage.com CTF. It's easy to join. Just sign up and you can get into the queue. And you, too, can also plug into this vehicle virtually as well and do all of these same things. So I highly recommend you guys do that if you have time. And we have some pretty good getting started instructions on our web page as well. If I have time, I'll try to go into those as well. So I've taken a few seconds and created a couple different windows. One window looks like some people are having some fun on these networks as well. So we might get some spurious data coming across. Also, I'm going to clear up some of these windows real quick. Let me just cancel out of this and reload it. Okay. So it might be a little tight to see. You might need to zoom in, squint your eyes. Let me just see if I can make this a little bit bigger. Yeah, a little bit bigger. Okay. A little bit bigger. All right. That's a little bit bigger. That's nice. That's very nice. Okay. Same here. Okay. So I made it a little bit bigger. Hopefully you guys can see a little bit easier. So what we have, I have three different windows. One I use for... these are all terminal windows. One I use a program called CanSend. It's a CAN utility. You can download CAN utilities available as well. It's already available on this particular device for you. CanSend essentially takes a few arguments. One is the device that we want to send the data on. We have two CAN buses that are available. CAN0 and CAN2 are available on this particular vehicle. If I send data on CAN2, it's actually on a gateway network. So you can't see the data. This is actually the CAN dump of CAN2. So what we see below here is nothing. So if I were to send a CANSend message, which I'll do. In fact, I'm going to do a bad message. So you guys can see a bad message go across. Of course, I sent it on CAN1, my bad. I'm going to send it on CAN2. Give me a second here. It's a little slow. All right. So I sent a message on CAN2, and then I was monitoring CAN2 using CAN dump. Again, I sent my message. I actually sent what's called a tester present message. So it's a pretty standard way of just kind of pinging the network, just saying, hey, there's a tester here. I sent it on an ID7DF, which is a broadcast ID to a handful of controllers. So hopefully I get one of them to respond back to me. But I malformatted the message intentionally. I sent it with seven data bytes, not eight. And remember, I was talking about padding. Padding is important. So you have to make sure that you pad with all eight data bytes. As soon as I do that, you'll see I get a few more responses back. Again, this is a broadcast message to a handful of controllers. I actually got three different messages happen after my initial request. So here's my initial request right here. I don't know if you can see this very well. But it's essentially 7DF023E, and then the rest are zeros. That is a tester present. And so I get a bunch of responses back from three different controllers, two of which give me what are called the negative response. The negative response is a 7F, and the positive response is a 7E, which is 40x more than my initial request. So I set the request correctly, but I got different responses back. That tells me a lot about the system right away, that these different controllers are configured with different versions of diagnostic request services. So it's quite normal to see that different controllers run different diagnostic request applications, and that's no different here. So you might find something like that. So let's go back to our original concept of sending a request for our engine, or our VIN. So again, 7E0020902, and then I'll fill this in with zeros. So I send that request. Of course, you know how it goes. There it goes. I get my response back. Let me just clear this up a little bit, so it's a little bit harder to see what's going on. I'm sure if it's hard for me, it's even more difficult for you. So let me just try to make this even bigger, so you guys can see it a lot better. Okay, so I'll send my request. I got to actually hand dump. Okay, send my request again. And you'll see we get a request and response. So our response is the first part of the VIN on this particular vehicle. I'm not going to do the rest, because I'm broadcasting live on a real vehicle, and you can get the idea. You know, sending VINs out there, as cool as that is, if you want this VIN, you got to get it yourself. But you know what the next thing is, I have to send a flow control frame. Now, like I told you, I can't do this by hand, right? So if I want to send a flow control, I would need to send a 70, 0, 3, 0, and then space there. It's a little slow here on my end. You guys are going through. But I'd have to set something like this. Now, because of my timing, I'm not going to get this message going out correctly. Even if I wanted to, I'd have to script this in some way. So I could create a bash command to send one and then the other within a certain period of time. I could create a script. I could try to chain them together, maybe with an ampersand. It's, you know, maybe a little time sleep between them. Something like that. I don't know if time sleep has a millisecond control on bash. But if it did, we could do it that way. We just did a little bit of a delay. Maybe there's enough of a delay already. But if we send our flow control a little too fast before the actual controller sends its response, it might ignore our follow-up. So that's kind of how we can send a simple request to these controllers. Later tomorrow, I'm going to have a whole talk related to what I call CMAP. It's a way of mapping out diagnostic requests across the board. Not necessarily using SocketCAM, but using a Python interface that just says, hey, I'm going to sort of copy or take all of this information and broadcast it out to you. I have also here the actual powertrain network that's on the bus. Now, you'll notice there's a lot of data running across there. I'm going to go ahead and cancel this. And what I'm going to do is I'm just going to filter. I want to show you a little bit about SocketCAM filtering. So CanDump, we can monitor the CanDump, but I'm going to monitor a different network, specifically Can0, which is the powertrain network. Can2 is the diagnostic network. So any diagnostic, as I said, I can see they get transferred over to the correct network that is supposed to manage that particular identifier. In this case, 70.0 should get rebroadcast by the gateway over to the powertrain network, which is Can0 in this case. But because there's so much data, it's really hard to see it. So what's cool about CanDump is you can do some filtering. In this case, I'll filter on 70.0, but I also want to capture the 70.8 message. So I'll type in 70.0 here, but what I'll actually do is I'll add a filter mask. If I just want to do 70.0 messages, I put a 7FF, which is an indicator of 11 bits of 1s. If you understand how bit masks work, if you don't, I recommend doing a reading up on bit masks. Specifically, what I'm interested in is I want to see anything that starts with 7E, but I don't care about what the last byte is or bit or nibble, I should say. Now, in this case, I'll just put a 0 there, I don't care what's in those last four bits, I just want to see anything that's 7E, anything. So I just put a 7F there. If I hit enter here, now only messages that have 7E something in them will show up. So I'll go ahead and send my initial request. Now remember, I'm sending on Can2 right now, but I will see it on Can0, which are two separate, physically separate networks, but there is a controller in between them that is gatewaying that information. So go ahead and send that request. Oops, I did not send the actual request. Let me send this one again. And we can see both my message going across, not only on the Can2 network, which I anticipate, right? You monitor a network, you look at it, you're going to get the data back, but you also see it on a separate network as well. That's because I'm actually sending through something. Now, the reason why I set it up this way is I wanted to see if somebody could go through and say, hey, I'm going to scan all possible identifiers on the diagnostic network and see what comes out, see what actually is being forwarded between the diagnostic side and the non-diagnostic side, or the powertrain side. There's probably some correlation. You can learn a lot about how the system works just by poking at it. It's a great way to poke at it. So I've run about 30 minutes, which was my goal today, to hit about 30 minutes. So what I'm going to do is take it there and then see if there are any questions for you from the audience. So I'm going to turn my audio back on so I can hear you guys if you guys are talking in this channel. And go ahead and fire an audio question away. If there's a question inside of the one-on-one chat section, I'll also switch over to that support. So I'm also monitoring the one-on-one chat. So if there are any questions, I'm happy to take them. If not, I'll say, hey, thank you very much. And hopefully you guys get a chance to hack these cars. Please go ahead, anybody. Robert, thank you so much for the presentation. It was fun. Sorry, I have an echo. Sorry, it might be my fault. Let me just fix my end. Just one second. I'm going to mute myself. No, I don't know where it's going. Don't worry, it's okay. Did that exit? No, that didn't exit. No problem. No, it's not my fault then. Actually, I have a question. What kind of tools are you using to do this? Like, what hardware? I typically, I mean, I use Vehicle Spy right now. And Vehicle Spy is? Yeah, I use Vehicle Spy with a value can. I'm a big fan of it because I used to work there. So Vehicle Spy is a commercial tool. So it's not a free one, but it really, you know, when you're first learning this, one of the hardest parts is just setting it up and plugging it in. And if you got the time and the money, you know, to do it, I recommend getting a commercial tool because they work really well and there's support. But, so not everybody has that ability or their wherewithal to do that. So, you know, the obvious tools are out there are the Cannibal by Eric Evacek. He's got the, or not the Cannibal, he's got the Cantac, sorry. He's got the Cantac Pro that just came out. Those are really good tools that I've used several times. There's a lot of different CAN devices out there. You know, in general, you'll probably spend, no matter what you buy, you'll probably be in the $100 range, no matter, like for a good one. So I recommend it. There are tools called Elm, Elm devices. I, you know, for a beginner, I, and somebody who really wants to get into this, I would recommend staying away from them only because they really limit your ability to see what is actually happening. So I feel like you're limited to that particular protocol, which is great. Once you learn how diagnostics works, you can switch over to one of these tools, but I wouldn't start there. I think you're going to not get the full picture. And that's the reason why I like using one of these other tools that kind of, you can see everything on the bus, whereas these other tools are very hard to configure. Just something that you can plug in and just start monitoring right away. And that's the reason why I like the ValueCAN. It's pretty simple to set up. Even it works on Windows, it works on Linux. It works in a ton of different environments. Thank you for the question. Yeah, yeah, of course. And actually, so you have hardware, and then which software is to use most often? Again, Vehicle Spy is my number one tool. And then I write my, like, write your own, you know, Python. Probably Python is my biggest, like, fallback. But I like, I'll prototype it usually in Vehicle Spy, because it's really easy to write scripts and everything like that in it. And then once I'm ready to, like, make a tool and try it again, I'll switch over to Python and write everything there. There's a lot of good CAN libraries in Python as well. Okay, great. Thank you so much. Yeah, there was a lot of discussion going on in the Discord channel about getting involved, getting started, and how to, which hardware, which softwares, and which tools in general. So if you have any recommendations about where you would like to get started, or if you were getting started, I think that would really help everyone. Yeah. You know, my problem is I'm a little biased just because I started working at the company that makes the tools. So I know them really, really, really well. And I also know the people who wrote the tool. So I have a very distinct advantage. So that helps. So not a lot of people have that particular thing. So it's really hard for me to recommend other tools, because honestly, I don't use anything else that much, besides writing my own tools. I typically stay with what I know, not to say that there's not other good tools out there. And I've heard a lot from a lot of other tools, like good news from other tools. So it's really hard for me to answer that. But there's a lot out there. I mean, over the past four or five years, it's just been an explosion of CAN bus tools. It seems like every year DEF CON, somebody's releasing a new CAN bus tool. So, you know, just check out a bunch of talks. Find out what you like. Ask your friends. And, you know, we're using the value CANs attached to Raspberry Pis here at the Car Hacking Village. And so they work really well. Yeah, absolutely. Please. Robert, just one last question. So where do you find all your resources? Like, is there a specific place that you like to look for different links or different, like, softwares? There's not a lot of good resources out there. I've been trying to make them happen. I feel like, you know, there's a couple of good forums out there that I've used before. MHH Auto is a really good forum out there that you can get on. I've also used, that I've been a part of, that are really good. So some private slacks. I don't know if there's one that's public right now. Some car research slacks. I don't know. I'm sure the ASRG has probably some good ones out there, too, that are happening that might be available. But, you know, everything's, you know, the automotive industry is pretty tight-lipped in general about how to access these systems. So you've just got to, like, you know, figure it out, like, with just poking at the vehicle sometimes. There's some good, like, databases that are coming up in GitHub. There's a CAN OpenDBC, I think it's called, GitHub repository by comma AI that I really like. So it's not perfect, but it's a great start. It gives you an idea of what's on the other end. Now, these DBC files are sort of de facto standard for CAN bus normal messages that doesn't really do much for diagnostics. So diagnostics, I typically take existing scan tools, get my hands on them in whatever way I can. I like the eBay borrow method. If you've never had done the eBay borrow method, you buy something on eBay, and then you resell it on eBay after you're done with it for the same price you bought it for. It's a really great way to get into this space for very cheap. These tools used tend to not devalue after you purchase them, and especially if you're using them only for a few months. So it's a great way to, like, be out a few, you know, maybe if it's $1,000, but you can turn around and sell it for $1,000 a few months later. So it's not like, you know, it's not like you're out a ton of money. Even if you're out only 100 bucks, so just borrowing tools, scan tools from different companies, each manufacturer tends to sell their own. Great resources for it. So there's a lot of different ways to do this. Awesome. Well, thank you so much for the talk, the presentation, the demonstration and everything. Robert, I don't think we have any more questions, so I think we're just going to move on to the next talk. The next talk is in 21 minutes.