 Okay. So I want to welcome everyone to this month's meeting. Our speaker today is Joey Hess. Many of you know him. He is most well known for his work with Debian, which he did for a very, very long time. And as a Debian user myself, I'm very grateful for all the work that he did. I don't think without his efforts, I would be using Debian. Beyond that a number of years ago, he started programming in Haskell and he's produced a number of very, very valuable software packages that are in pretty wide use in Haskell. And I reached out to him and asked if he would give us a talk. And he said that he wanted to talk about the work that he's been doing on using Haskell to program Arduinos. So with that, I'm going to turn everything over to Mr. Joey Hess. Thank you very much. Okay. Thank you, Claude. So yeah, I'm going to talk about programming Arduinos using Haskell, which as somebody already commented seemed like two completely disjoint topics that didn't fit together. And but luckily NASA has kind of swooped in and made it almost possible. So I'll talk about how I've built on that. And for anybody who doesn't know, I think most of you will. Arduino is a little tiny computer like this. This is a clone. And it runs a very small embedded processor with like 32 kilobytes of RAM and well, I'm sorry, a flash is really embedded and maybe two kilobytes of RAM, something like that. So the problem with running Haskell on such a constrained environment is that you know, GHC and most other Haskell compilers are built to target modern machines. They have a big chunk of runtime that comes along with every program. If you go build your simplest Hello World program in Haskell, it will be like half a megabyte or something of binary. You could probably strip that down quite significantly, but it's nowhere near small enough to fitting on these boards. And even if it was, Haskell isn't really designed to be a resource constrained programming language. Anybody who has written Haskell code and has had a fold L or a fold R and has been like, wait, which of these should I be using right now so that I don't eat up all the memory in the world when I operate over this list realizes that Haskell is not perfectly ideal for controlling your use of memory. It's great at other kinds of control, controlling effects. It's great at types, but memory is kind of its bugaboo. So trying to use it on an embedded system is especially problematic. And also the Haskell runtime itself does a whole lot of allocation. It's constantly allocating new values and then freeing old values. And so again, not a good fit. So I would have said that it was impossible. And that was what I thought when I first started this project. And then I actually did a little bit of research yesterday. Let me start sharing my screen and make sure that works. Okay. Y'all should see my web browser. You got it? Okay. So I did a little bit of research. I found this page that is like seven years old that talks about just this problem. And actually down here under full compilation, it talks about AJHC, which actually targets the ARM Cortex M3 and M4, which is actually the processor that is on some Arduinos. So it's actually been done. I think this project was abandoned. And this is also not GHC Haskell. It's not exactly the Haskell that most Haskell developers would probably want to run. And you're probably still constrained what you can do that. This also gave a good overview that the basic approaches are you're going to have some kind of full compilation or a limited system that you have a very limited subset of Haskell code or a hosted EDSL. Now what is an EDSL? I have a question. Maybe you're going to get to this. Why is it necessary to run Haskell on an Arduino? What's a good question? I'll jump right into that. And I'm happy to take questions. Yeah. I'm sorry. I made a spoken over your little because of my lag, but I'm happy to take questions. And that's a good point. Why am I even thinking about this? So the reason is I do, I live off grid. I have done a fair amount of home automation with Haskell all using ARM computers, which is fine. Running Linux on an ARM, you can write Haskell programs that do things like keep track of how many watts your house is using. If you can see down here at the bottom of my screen or the voltage of your batteries, how many battery percent you have that can do things like automate when this and that turns on and off. But also, your typical ARM board isn't a really robust embedded system that you really can be guaranteed is going to keep working. And I was thinking about putting in a hot water heater. And I was thinking about, well, I'm going to try to program this thing to some extent. I want to be able to at least control when it turns on and off, even though I'm not going to defeat all the safety apparatus that is on a standard hot water heater because I've seen the videos where they come out at the top of a house and I don't want it to happen a month. But I would still prefer to be using a platform from the beginning that is a proper embedded platform. And Arduino isn't perfect in that regard. It's a hobbyist computer. But at least it's a start. And I really have enjoyed using Haskell, though, especially functional reactor programming in Haskell with lots of strong data types, just to keep track of all the complexity of when you're reading from all these different sensors, they all have different units, you know, just stuff like that. So I was sort of thinking about it. And what I realized is, yeah, it can kind of be done using this EDSL technique that this page talked about several years ago. And if you don't know what a DSL is, it's a domain specific language. So you're basically you have a Haskell program and embedded within it is another program. And the idea is the Haskell program is going to write a program that is written in C that then gets compiled targeting the Arduino. So luckily, around this time that I was thinking about this, a guy named Ivan Perez who works at NASA was promoting this project that NASA had developed and released a street software called Copilot. And the idea with Copilot is basically exactly to be that domain specific language. And what they're really actually my understanding doing with Copilot is while they're working, I believe mostly with drones right now, but maybe also with rockets, I can't speak to what NASA is doing internally. But the basic idea with Copilot is that NASA has their regular software that controls their rocket or their drone, which is probably written in C because that's how NASA rolls. And it's probably very complicated. And maybe it's hard to, you know, guarantee that its behavior is completely perfect in every respect. And the idea here is, well, we're going to hang a little thing off the side of a program. And every time we go around our main loop or something, we'll just say, hey, Copilot, could you go check that everything's okay? And it will look at various values that our C program was looking at, say maybe altitude and how much battery is left, stuff like that. And it will do enough calculation to figure out if it needs to take over, you know, if it needs to grab the wheels that were and say, oh no, you know, everything is going wrong. And I'm going to try to get us into at least a safe situation. And so it is just a small little domain specific language. They're continuing to develop it. And when I saw this, and then I looked at actually another presentation from a fellow in, I believe, Australia, or maybe New Zealand named Anthony Crowley, who had actually taken Copilot, used it to build some C code and then manually written the rest of enough code to get that C code to actually do something on our Arduino. So he was still writing, you know, 20 or 30 lines of C, whatever, but at least he could then, you know, use Haskell to generate the rest. So that was cool. But he hadn't taken it any farther. And I thought, well, okay, I'll do this. So I went off and did that in 2020. Early 2020. And this is the result, Arduino Copilot. It's a package which you can get on Hackage if you're a Haskell developer. And if anybody would like to follow along in the talk, if you go to this page, just look up Arduino Copilot on Hackage. And if you clone the source code, either with Git, or if you just ask Cabal to unpack it for you, you'll have all the examples I'm going to use for the rest of the talk. Okay. So let's, without further ado, actually look at how this looks. So I'm going to be mostly just using existing examples. This is the simplest program that everybody writes, you know, on the Arduino to begin. We're going to blink an LED. There's really not much to it. The LED is blinking. That is like the most important thing about it. And you'll notice there's like an equals colon operator here. And that's just a made up operator that says basically assign whatever the value of blinking currently is, which is either on or off to the LED. And there's also a delay. And the delay just, you know, makes the LED not blink continuous, you know, very fast. And so it's a 100 millisecond delay. Now this program is an Arduino program. And this Arduino, which we're going to call from our main function, goes off, generates the C code for the specification that's inside of it. And then you can compile that C code yourself. So the rest of this is just your typical boilerplate. This is the module that everything is in. And this is the module we're making. And this up here, rebondable syntax I'll talk about later. Okay. But basically it's, it's where they embedded in embedded domain specific language comes. Let me show you how this works. Does anybody have any questions? Yes, I have a question regarding it. Does blinking have any side effects? Because it seems a bit weird that it automatically flips between two states. But then you also need to insert the delay. Right. So the way to think about this is, this is the behavior of the LED. Every time we go through some kind of a loop, basically the Arduino main loop, right? The LED will switch to the next state. So we could make this be a slow blink, like it's on for, I don't know, 150 or 200. And then it's off for 200 on off. We could do it that way. But we can also just throw in an actual delay, which is kind of easier, because that way if you have a bunch of different things happening, you can keep them all happening at the same rate. And yeah, this does, I mean, this is all side effectal code in a way. We haven't, the only pure code here really is blinking, which is the only code actually written in copilot. The rest of this business here is all Arduino copilot. So we'll get a bit more into doing more interesting pure code in a few minutes. But right now, all we have is the simple function blinking, which is either on or off. And every time through the loop, it alternates, basically. Okay. So if you were to call it two times, then there would be no blinking at all, because it would immediately... If I signed to this twice, that's a good question. Let's see what happens if I do that. I like that question. Let me try to actually highlight this. Okay. So I'm just going to go through the readme file, which says how to build it, which is a bit weird because it's inside anyway. Normally, this would be building it with stack or with cabal in your normal way. So it built. Now we can upload it through Arduino here with make upload. Now, but first let's actually look at the program because what it's done is it's created blink.ino, which is a C program. And there's that main loop that I was talking about down here. Every time through the main loop, it does a step. This is a step. And in here, we start getting code that was written by copilot. In fact, let me actually pull this up to an editor so I can highlight. So this part here was written by Arduino copilot. And then this block in the middle here, all this is copilot just for blink. And you might be like, wow, that's a lot of code just for on or off. We'll get to there in a minute. But and then this is again generated by Arduino copilot. So I just wrapped the copilot with enough to make it work. Now, the nice thing about this example, though, is it's a lot simpler than it looks. You can do a lot of constant elimination and folding and some really basic optimizations that every compiler will do. And you'll get a much simpler program. But let's see what it does first. So okay, so I'm going to make upload. I have my board here. And it started. Now it's still up. And there it goes. Now it's blinking. So I believe to answer your question, and I'm actually not 100% sure since it has been two years since I wrote all this, and I haven't thought about that particular point since, but I believe that it only happens one time. It only, if you assign to it multiple times, it doesn't change the behavior. It's a good question. And it should be documented what happens there and it isn't. So thank you for that. Anyway, so I said that all this gets simplified down. I actually have a webpage somewhere where I went through manually and manually, you know, did all the basic compiler stuff. And it basically comes down this one line. You can see here is the modulus mod two. And that's all the copilot ends up to compile to is this is mod two, and we assign to it and update it every time to the loop. And if you notice here when it built the program, this is the total size of the program 966 bytes, the data is 13 bytes. So it's fairly small. It uses 2% of my Arduino, which isn't a very big one. It's a red board. Okay. Okay. So that's a simple example. I wanted to show something that would give you a little bit more of a feel for how how copilot works, not just Arduino copilot. So this is an example that includes a button. And here we're reading from an input whether we're reading from pin 12. In fact, a but is the button pressed. And then the LED is on when the button is pressed or blinking. So it keeps blinking. But when the buttons press it just stays on. Okay. So this or here is is happening in copilot in the DSL. But also there's a delay and I changed the delay to be longer and longer. So longer and longer is a value expressed in the copilot DSL. And it's a stream of numbers. Everything in the copilot DSL is in fact a stream of something. Blinking is a stream of bull. This is a stream of word 32s. And I basically just took an example straight out of the copilot tutorial that shows how to build a stream that increments and then resets when the stream of bull gets to true. So basically, this just counts up to 64. Well, this just counts up forever. When it gets to 64, it becomes a true value. This other counter uses the inner counter to decide when to reset. And that makes it that makes the delay go up to 64 and then reset back to zero. So I can demo this, but I don't have a button hooked up. But I can still show you the basic blinking effect. You can guess what it looks like. I need to build that other thing. And if all the stream manipulation looked a little hard to understand, it's a little hard to understand for me too, to be honest. I've read the manual and I understand, I can generally get copilot to do what I want, but it can take a little bit of thought. I don't know if y'all can see the blinking effect here, given the limitations of camera technology, but it is kind of, it starts out fast and then it gets slower and slower, and then it goes back to fast. Okay, simple. And if there was a button, you'd see that. Okay, I'm going to turn that over. That's a very annoying effect. Okay, so I have some other demos that show off other things. One that I like to show off is serial port because it shows a little bit more than just read from pin, write to pin. So this is an example that does output to a serial port. And I'm not going to go through the entire detail of it, but if you look here, there's a little bit of setup for the serial port. It also supports XP, which is a wireless serial device. If it happens to be there, it will use it also. And all this program does is it reads from A0, which is the analog input of the board. And I'm sorry, was there a question? No, okay. So it reads from A0. It says what value it read. And that's, and I believe it lights up the LED if there's input on either the serial, on the serial console. So that's the basics of it. The thing I like about this example is that since A0 is just kind of a, on my board, it's not connected to anything, I can actually demo this without any hardware just by putting my finger near to it and changing the value of the ADC happens to pick up out of the other as it were. So let me show you that. If y'all are, if you think that it would be useful to show you this, I don't want to overwhelm you in silly little demos that don't really do much of interest. At least it will stop my board from doing the annoying blinking. Okay. So I'm going to listen to the board to see what it has to say. Figure out where A0 is. And as I run my finger along, where is A0 over here? I think so. There it is. It's over there. So, oh, you can't see. I'm sorry. Yeah, I'm just moving my finger right over on the pins here slightly shorting them out or making there be a little bit of a current there and it reads it. How do you get out of screen? I have no idea. Okay. Good. So does that kind of make sense? I know that that's a lot of stuff I've thrown at you really quickly. The important things are it can input from a pin. It can output to a pin. And also, I didn't really talk about how it outputs this message, but you see here that there's a list of values. And it's basically just creating a string out of them. And some of these values can be like the C can be values that are sketches or streams. Okay. So this is kind of, yeah. You might have shown how did you set what the sample rate is again for that ADC. So we once again have a delay down here. Oh, the ADC sample rate? Yeah. Oh, okay. I'm just using whatever the Arduino default ADC sample rate is. And I think I may need to make that configurable. I was a pretty naive Arduino program when I wrote this and I didn't realize that you might want to alter that. But yeah, it's just sampling for whatever the default is when you do a read. If we look at the code, serial port, let's see, it was A081. I don't know. Somewhere in here it does the actual read. Oh, here we are, analog read from pin 14. That's the entirety of the code that I had it generate for that. So if you needed to do something more complicated, like read for a certain amount of time interpolate or something like that, you would either have to do it write new C code that does it basically or try to do it with copilot with the DSL itself. Does that answer your question? Okay. Yes. Yes. Cool. And maybe I need to revisit that, but I'll get to that toward the end of the talk why I need to. Okay. So the other example I wanted to show you, because this is Haskell after all, and this is also kind of my motivating example is the hot water heater. And the good thing about this example is so far we've only seen basic data types. We've seen bulls. We've seen various kinds of words. Nothing that a nothing to see program wouldn't pretty much have in it. Finally, we're seeing a couple of real honest to goodness, well, almost honest to goodness data types. Now, unfortunately, copilot, the language from NASA isn't it doesn't have like the entire Haskell type system available at runtime. And again, the reason is Haskell's, you know, making Haskell's types all work at runtime would be difficult. Because you need to be able to unpack them and traverse them and whatever. And copilot doesn't do that. And in fact, I wish the copilot did a little bit more with types. And I've kind of hacked this on on top of it to at least have a little bit of more typing. So the reason that these are data PSI and data Celsius and there's not like an equals PSI, you know, like that is that these aren't values, they're just phantom types. And a type behavior PSI float, for example, is the same as a stream of floats. But it's got this PSI attached to it and the compile the Haskell compiler can tell Oh, I'm operating on a PSI, whatever else I'm operating on, if I'm adding to them together, for example, it better be a PSI as well. So it's just basic phantom types, which is something that you can do on top of copilot pretty easily. I won't go through this thing in detail, but you can see that it has some, you know, some values, a maximum minimum water temperature, which are in Celsius. And it has here is the temperature safe, and there's a lift B2, that's lifting the less than which is a copilot operator in this case, into or it's lifting it to operate on a type behavior. So we don't need to unpack everything manually, basically. And, you know, we can also read in, say from an ADC that is somehow connected to, you know, or a value in Celsius, that's probably unlikely, there's actually a sensor that works that way. But if we had one, we could read it this way, or we could read in a value from the ADC, and then use this function to get a type behavior from Celsius of Celsius. And it just does the basic math to convert a raw nominal ADC value into Celsius. There's an unsafe cast here, because we're converting I believe from, oh, I don't know, from something to float and it wouldn't work without it. But it's not actually very unsafe, we're converting between two different numeric types that have the same memory representation. And copilot does, I believe, make sure that you don't do anything really unsafe with it, like between two things that don't have the same memory representation or something like that. So yeah, here we're, you know, we can read from A1 and read from A2, which are the two sensor elements in a water heater, convert them to Celsius, read a PSI, and decide if it's safe to run the water heater or not, and just turn on the different elements depending on the temperature or something like that. So a simple water heater example, actually copilot itself has a some kind of water heater example on its front page, which I thought was amusing. It doesn't have strong types here, it just has floats and words. Okay, I wanted to show you all one more example, which is where it gets a little bit more looking like a non-trivial program. And this is a simple line follower. And if you've seen a line follower from Arduino, this pretty much looks like every single other one. The thing I wanted to show is that you can use if, then, else. And now this isn't like your typical Haskell if, then, this is lifted using rebindable syntax into various, well, this is in what's called a sketch, which is Arduino copilot's idea of what an Arduino program is. So we're assembling together little sketches like this one that stops the robot, this one that goes forward, turns left, turns right. All these sketches are getting composed together here to decide what to do, and it's going down the line and reading from two sensors and swerving left and right is needed to stay on the line. Pretty typical little thing. I've never actually put this together with actual hardware, so it's all just a general idea of what it would look like. The other thing I wanted to point out is that you might notice there's some code duplication in here. This is basically the same function two times with a few minor differences. And you could totally refactor this at the Haskell level and make a function that took, like, a direction and say, you know, left and right and do like this, do like that, put your direction in here, you know, you know, like this, you know, that do something like that. And so you can pretty much do normal Haskell programming without much real thought about it. If you're a Haskell programmer, this is all pretty natural. You would just refactor stuff like this to eliminate the duplication. I left it in to keep it simple. And I'm going to build this but not run it, because I don't have the hardware. But I wanted to show you the size of this little example. Once it gets compiled, I'll just build it into an Arduino binary, but I won't upload it. Oh, I must have skipped a step. That's what happens when you forget the step up here. Okay, I guess I've lost something else. Well, that's what happens when you do too many demos, I guess. Anyway, this, I guess my point, I'm sorry that I'm not able to show you that, is that this compiles into a fair amount of code. A lot of it is generated by co-pilot. And it's really not too optimal. All those if-thens under the hood aren't the most optimal representation. They don't get compiled into the most optimal code. And that's really the first time that we've seen this, seen that problem in these examples, I think. Okay, so I think we've had kind of enough examples. If anybody has any questions about you know, Arduino co-pilot or anything else that I've touched on, be happy to answer them. If not, I can show you some other cool stuff. Have you seen any other languages that have been sort of created DSLs or things like that that convert to co-pilot or use co-pilot to push to Arduino? No, I haven't. And I mean, co-pilot is a high school library. So, hmm, but I was wondering if anything I may touch on this later in a minute. But what I was thinking you might be asking is, have you seen any other functional languages that can compile to the Arduino? And I have. Rust can compile to the Arduino. And I haven't, I haven't written anything for it. I've only briefly looked at it. Obviously, Rust is a much better suited language for programming on Arduino in general. If you want it to be like banging on a serial port, you know, to do output and you have to get all the timings just right, something like that. This is still a little too abstract at the high school level to be doing that. But you could write that in Rust. My understanding is you tend to get into a slightly restricted subset of Rust, which makes sense. And you probably want to avoid anything that allocates much. A nice thing about co-pilot is it always allocates a fixed amount of memory. So, your program is always going to use the same amount of memory. It's, I believe that it's guaranteed to not go into infinite loops except for the one that it's stuck in all the time, obviously. You know, it's got some pretty good guarantees. It also has, NASA has built some things on top of it, something called co-pilot spec that, or co-pilot proof, I forget which, that lets you do theorem proving about your co-pilot. I believe it's co-pilot's proof. It lets you do theorem programming using maybe a SAT solver or something. I haven't looked at it, but it lets you prove theorems about your program, do things like that. They also have, I believe, a new project that lets you do some kind of a GUI to build a co-pilot spec. And all that stuff would work with a reno co-pilot, I suppose. I haven't tried to do it. So, that's cool. You just asked, yeah. Any other questions? I'd like to ask about, I will just go ahead. You ask afterwards. So, I would like to ask if you have looked at Clash and how does it compare to co-pilot? Because Clash is also a DSL written in Haskell, right? I haven't really looked at them. This Haskell embedded blog, which I only actually found today and I wish I had known about earlier, does have a few, it has a resources page somewhere with several different DSLs. I know that Galois had a DSL or two that they wrote that are targeting embedded systems or maybe could be used for embedded systems, at least. But no, I haven't looked at anything else. When I found co-pilot, I was happy enough to actually get something that worked and I haven't actually built my water heater yet. So, but I was at a conference recently and I believe it's the Executive Director of the Linux Foundation mentioned a project that I was like, why have I not heard of this project? And that's where we get over into Zephyr. Zephyr is kind of to the Arduino, you know, it's like a grown-up version of the Arduino runtime system and all the Arduino libraries, which are all hobbyist grade, you know, you go off, if you want to use some hardware in the Arduino, you find some software that somebody dumped off on a webpage at some point and hopefully it works, right? Zephyr is a real-time embedded operating system that targets something like, I believe, yeah, 350 different boards, embedded boards. It's kind of targeting to be a platform that any kind of company that's trying to build an embedded board and just wants an operating system and Linux won't do because it's too big, can go use Zephyr and Zephyr has developed a lot like Linux. It has threads, it has device drivers, it has networking, it has a device tree, but it all targets fairly small embedded boards. So when I found out about this, I was like, well, gosh, can I run co-pilot on Zephyr boards? And that was two weeks ago and so here's Zephyr co-pilot. So all I did is I took Arduino co-pilot and filed all the Arduino edges off and got something that was, you know, fairly general purpose, refactored that out and then built Zephyr co-pilot and now Arduino co-pilot on top of this abstraction that I came up with, but yeah, this is very, very similar to Arduino co-pilot, right down to pretty much all the functions and everything. Actually, you know, I've skipped one thing about Arduino co-pilot that I did want to show you, which is that it has a library for each board in it that is supported. Currently, there's only two of them and you don't have to use these libraries. You can just import co-pilot to Arduino and then write to an LED or something, but most of these examples have been importing a specific board and the advantage there is you get all the pins and other information about the board. So then and also I wanted to point out that these pins, which are a type pin, but they have a, this is a Haskell syntax for a type-level list of types. So digital I.O. is a type, PWM is a type and this board module knows what the Uno is capable of and which pins can do pulse width modulation and which ones can't. And actually, if I go back over here to the robot, let's see, I think it was the line sensing problem about wheel motor. Okay, yeah, let's switch this to pin four and pin five, right, and build that and see what happens because, you know, you could just be picking random pins and be like, I'll wire up the robot later. Well, the nice thing is since we're using this module that knows which pin is which, it actually says, oh, you're trying to use a pin for pulse width modulation, but it doesn't support it. And this is a Haskell type error message and I've overridden the ugly message with a pretty one. And yeah, so you get a little bit of strongly type programming there. I'm sure there'd be a possibility to do a whole lot more. Anyway, the reason I only have two modules is I haven't bothered to research what all the pins are of the other ones. And if anybody has an Arduino, and they want to add one more, you know, a module for some other piece of hardware, you're more than welcome to do it, I would be happy to help out. Or even if you have one that you just want to me to test, you know, I'd be happy to just throw some code at you and see if it works properly, something like that. It's easy to do. Very simple code to make those board modules. Anyway, Zephyr Copilot, the reason I mentioned that is it only currently supports two boards, which are just the two that I had happened to buy first. I'm not going to buy all 350. And these boards that I picked were from Adafruit. Here's one of them. It's the Feather Board. It's kind of smaller than the other Arduino board was. And this thing is probably a lot more powerful than the Arduino. I believe it has about half a megabyte of flash or something like that. And I thought I'd just show you a quick demo of Zephyr just to, you know, we'll see that it does basically the same thing. I'll go ahead and jump directly to the Feather one if you don't mind, just to save completely overloading everybody with demos. So this is, again, simple Copilot program. This time we're using Copilot Zephyr board, Arduino foot, our Adafruit Feather. And we have an input from pin nine. And then we have an LED that is lit if that input isn't connected. And we have some various other pins that are blinking. These pins are blinking in a different frequency. So somebody asked earlier, why isn't blinking controlling the frequency? And it could be. It could just be frequency two is all that is. So yeah, this is like what I used to test that this board module basically worked. And I haven't implemented everything else. I don't have analog to digital converters or pulse width modulation or serial output, yeah, or serial input or threading. I mean, there's, you know, Zephyr has a huge amount of things that I could add. And I doubt I'll ever add them all. But I wanted to at least show you how this looks when it builds. So we'll run this and we'll get a program. Oh, it's in generated now because Zephyr needs a fair amount of like cruft. So it needs to turn on GPIO when it builds its kernel. It has, this is a device tree overlay that Zephyr copilot has generated that defines all the pins that it needs for this particular program. And then here's the generated C code, which gets a bit, sorry, let me highlight again, this gets a bit grody looking, it's digging around in the device tree at compile time to find the different pins that it's using. So this is a lot of boilerplate that doesn't really do much at all. And then here's your, here's our copilot block. And then down here is the actual pin setup and reading from the input and so on. All right. So if I go ahead and build this, yeah, I've already got the right command line. I'm going to use this other terminal because Zephyr is kind of hard to get all set up and in your path easily. So it's building the whole kernel basically, we put up here. And so that little program now, it turned into 232 kilobytes of flash. So a bit bigger, but almost all of this is a Zephyr operating system. And it's using 32 kilobytes of ram also mostly the Zephyr operating system, probably. Now we can flash this to a board. I'm going to plug it in. Hopefully, please let me know if my mic goes out at this point because I have no idea what's going to happen when I plug in another board. So okay, I have a board plugged in. I think I need to put it into flash mode by hitting the reset button a couple of times. Let me see. Yeah, there we go. It's in flash mode. And now it's running or it probably is running. Of course we can't tell because this is not hooked up to a pin, is it? There we go. Now we're getting some action. I hooked it up to the, I hooked this wire up to the pin, one of the blinky pins. Here's a slower blinky pin. I'm sorry, you can't see it. Slower blinky pin with the wire hooked up to it. I just used this to test that I had the right pins. It all worked because unfortunately to implement that board module, I had to go look at the data sheet for the design for that particular board and figure out which pins were hooked up to what. Actually let me, wrong. So I had to go in and figure out that I need like port A11 for pin zero and port A10 for pin one and so on. It's still not very hard to write, but I did have to scare the schematic for a while. And unfortunately, Zephyr doesn't seem to define things like GPIO pins for every board. It often has an LED and maybe a switch that's on the board. And that's about it. And in fact, I wanted to show you the blink example also. I'm not going to run it, but I'll just show it to you. Same code that you saw at the beginning except a different import here. And if you build this and flash it to a Zephyr board, it's using the generic board. So it'll work on anything that has led LED zero, which is a kind of standard name that Zephyr has. And the neat thing about that is that here's the boards that that Haskell program runs on. It's something like 200, I don't know. So and yeah, that's pretty much all I wanted to show you, but I would be really happy to take questions and talk about it further. Great. Thank you so much. So yes, let's open up for questions. I have a question. I'm a little bit more familiar with Scala and its effect systems, where it keeps everything pure until the last moment where it actually does the impure part. Does that make sense in an embedded environment where you you just really build a program to actually set all those LEDs and do all those impure things? And then the last bit is, I mean, or is it just takes too much RAM and resources to do that separately? Yeah. So the way to think about this is I've showed a couple of examples of the generated C code. And there's always a co-pilot block in the middle that's a bunch of gobbledygook. All that code is in fact, essentially the pure code. Now, obviously, it's been compiled into C, and the C is assigning the variables and maybe mutating them. But the Haskell code that generated that particular block is all the pure code. And most of the examples I've shown have been very impure, because of course, we're just fiddling around with pins. And we're reading and writing, and we're trying to we're trying to affect the outside world. And obviously, at some point, you get to the impure code there. However, it's not like this, you know, you saw main equals Arduino do and then some look like impure code. But that doesn't go off and run IO immediately. It's running on your computer. It's in fact, generating a co-pilot spec, which is a data type. Co-pilot is then going off and looking at the spec, checking maybe that it's sane in various ways. I don't know. It's trying, it's working on compiling it into the C code. So everything up until that point is in a way pure on the Haskell side. Now, once you get down to the C side, I don't know that just down to the C side. Once you get down to the C level, I don't know that, you know, the purity matters, particularly. But you can certainly write pretty large amounts of co-pilot code in streams, you know, manipulating streams and stuff, and it will all be pure code. Did I answer your question? Yeah, thanks. I have another question. So this is written somewhere in NASA, just offhand, is that NASA Houston or could we talk to those people? Yeah, let me reshare my screen and show you their page again. So, because they have the details here, where to go? NASA Langley. Yeah, I think it'd be great if you could get, if you get even Ivan or even Perez, who is I believe the lead or one of the leads on this project to speak, because I'm sure he could tell you so much more about the Stream DSL that I have no idea about. It seems like a really neat project, all the things that are doing on top of it, although so far it says monitoring test flights to drones. I don't know if any spacecraft are going to be running this. That would be really neat. My final question is do future bugs lead to your cabin exploding with a bad water heater? No, because I'm not going to defeat the safety that's built into your typical water heater. It will be completely by the book installed, and then we'll have things added on around it that further decide about whether I have enough battery power to heat the water yet or things like that. So, I'm planning not to have a water heater launched out of the roof of my cabin through my solar panels. That would probably be bad. What's your network connection? It is a VSAT satellite link right now, and I have a minimal off-grid system consisting of four solar panels that can produce one kilowatt of power, which is not very much. You can see down here that my house is currently drawing 130 watts of power for the lights on the internet. That's pretty typical. You're not a Starlink customer with the power draw for Starlink? I only wish because the power draw is less for Starlink. I'm currently paying about 90 watts for the internet. I think Starlink is down to 50. They haven't shipped my order yet. Well, you're not in the winter. I thought in the winter it took 130 years. Oh, you might be right. You may well be right. I haven't seen that detail for the newest model dish yet. I'd be curious to know. What state are you located in? I'm in Tennessee. That's what I thought. Okay, great. Moving the dream. Yeah, that's cool. I should add that my day job right now is writing Haskell code. I wrote something called GetAnX, which is a Haskell program that lets deal with large data in various ways that it turns out a bunch of scientists have found useful. A bunch of computational neurologists and people like that are funding basically a lot of my work on GetAnX, which makes it possible to live in a cabin in the woods in Tennessee. Okay, I have to ask what's between a neuroscientist and a computational neurologist? That's the word I was looking for. I'm not a scientist. What do I know? So it is neuroscience, not neurology. I think it was a specialty in medicine. Right. It's a whole lot of MRIs and then processing them using a whole lot of code, is my understanding. Any other questions about co-pilot before I stop the recording? Go ahead, Rashad. I was just going to ask because I wasn't sure if I missed it at the start of the talk, but what other kinds of tools do you use for home automation besides Arduino? Do you write those in Haskell, too? I do. I use an embedded or an ARM box running Linux and I use Haskell on that. I actually use something called reactive banana, which sounds like a wonderful thing to automate your house with. It's a functional reactive programming system for Haskell. I realized, well, I can just read from sensors and write to switches and stuff instead of reading from mouse and writing to the screen. It's all the same, really. Functional reactive programming, I find is a good way to deal with these effects that isn't really, it abstracts it away a little bit and lets you deal more with pure values. And Arduino co-pilot, while I would struggle to say it's actually functional reactive programming, when you look at these things where you're reading from pin A1 and then you're doing some computation and assigning that stream value, which is a varying value that varies over time, to a pin or to something else. It looks a lot like functional reactive programming to me, although I think that a purist would say that there needs to be a continuous function or something like that. But anyway, I've used pretty much exclusively reactive banana to automate my house. Awesome. That's another talk. Yeah, that actually kind of answers it. Yeah, I may be switching over to Arduino's because keeping these armboards up can be a little bit tricky. USB tends to glitch out or power fails or something. Now somebody else had another question, maybe. Yeah, no, you actually kind of answered my question. I was just going to ask about how FRP is kind of utilized in co-pilot Arduino. But I do have another question, if you don't mind. It was just about the input function that I saw you use and some of the do blocks and some of the demos. And I noticed you were passing I missed the name of the function that you said. The input, I believe it was called input with an apostrophe at the end. Oh, yeah, yeah. Oh, good example. Oh, I mean, good question. Let me look at an example. I skipped over something interesting. So this could be as simple as input from pin 12, right? And this you'll probably just write it as like the reason that it's written this way and it's a neat little feature of co-pilot is that if I run this thing, but I don't actually tell it to output C code, instead I pass C5 or maybe C4. Let's see. Oh, dash I, I4. What this is going to do is it's going to take those input values that were provided as dummy input values. And it's actually running the co-pilot spec on my local computer, not on Arduino. And it's saying, here's what's going to happen at the different steps of that thing. So here's what it's going to write out to that pin based on the input values. So the nice thing about that is, yeah, here we are. So false, false, false, true, true, false, true. It depends because it's blinking, of course, so it's on and off anyway. So I don't know if this actually demonstrates what it's doing, but you can, you know, you can use this to just simulate something before it hits the actual hardware and see what happens. And I've actually found this pretty useful when we get over to working with eProms, which I haven't touched on because it's a pretty complicated API. But we do have support in Arduino co-pilot for eProms. You can write values to them and read values from them. You can operate over a range of values and change to a new value each time you could use for data logging or something like that. And I really found this input really helpful there in this simulation, very helpful because I could see exactly what happened at every step to every eProm cell. So, you know, for that kind of thing where you need to really look at your program in detail and you don't have it a debugger for the Arduino, obviously, well, maybe there's one, I don't know. This is a really nice thing that's in co-pilot. And so a lot of the examples that I have do use, I don't know, yeah, here's one, so dummy clock input even. It's inputting from, it's inputting the current time, but then it's dummying up the various time steps, for example. So, yeah, I use that a lot, just to understand what's going on. And it's a great feature of co-pilot. Yeah, and I was also going to ask, I guess the answer would be, I'm going to guess that it's no, but could you pass an infinite list into... So, while these stream values that co-pilot uses, let me go back to the button example. So, it's a stream of word 32. What is this thing actually? It's not an infinite list, but you could think of it as an infinite list because, you know, just think of it as an infinite list as being garbage collected without any garbage collection, because obviously there's no garbage collection running on an Arduino in two kilobytes of memory. But what it actually is, is the current value of the stream at every step depends on the co-pilot program. So, you can think of it as an infinite list, but there's no actual list happening here. And co-pilot does support some C structures, and it supports arrays of fixed size. So, you can have values that are like a stream of... I'm not sure what the syntax is because I've never actually used it, but it would be something like this, but not this. Probably a stream of some size array, array size three or something, like probably something like this very vaguely. If you want to know the details, you could take a look at the co-pilots documentation. They have a manual, which is pretty decent to get an idea of... Here's the manual, to get an idea of how the streams actually work. Okay. Or you can read the C code, which is also pretty easy to understand if you look at a simple example, and you'd be like, oh, so the stream is just modifying this value. Okay. Interesting. Thank you. Yep, that's all. That's all the questions I have. Oh, these are great questions, guys. Thank you for them, David. These streams, from the perspective of Haskell, like IOMONAD sort of implementations or... Yeah. Normally in Haskell, yeah, a stream would be some kind of infinite list, probably, or something vaguely similar to an infinite list. But since co-pilot isn't actually running the Haskell runtime system, it's compiling... It's generating C code, which is very simple. It doesn't have access to lists. It doesn't have access to laziness. It doesn't have all the things that the Haskell runtime uses to do these structures, which is why it uses a simple stream abstraction as its main data type, as it were. So I actually have one more question, because we're talking about memory and stuff like this. So recently, I'm sure you know, Haskell has recently gotten linear types. Could that somehow be utilized in co-pilot somehow, or does that offer anything? That is a really good question. I bet it could be used in co-pilot itself. In Arduino co-pilot, I'm not... I'm sure there's something, actually. I haven't thought about it. It's a wonderful question. I would be curious if the co-pilot developers have thought about it at all yet. It might be worth looking around in their issues and seeing what they have. You know, maybe they can give better guarantees about their programs. Sorry. I'm hearing myself echoing back on something. All right, last call. Any other questions? I mean, we'll continue the discussion after I stop the recording, but anybody want to get in a question before? Okay. I saw you use Rebindable Syntax. Did you think about Qualified 2, which was added in, I think, GC9? I guess, since this is just a syntax transformation, both of them would work, but Qualified 2, I guess, would not really offer anything because you always only have one monad, right? So you don't really need to specify which monad you're talking about. So I don't know if I actually understand Qualified 2, to be honest. The use of Rebindable Syntax is mostly by co-pilot and not by Arduino co-pilot. What it does is it's what's letting you say if foo then bar and things like that for people who don't know. Basically, co-pilot throws out a lot of the Haskell preload and then re-implements it with its own version and uses Rebindable Syntax to get as close as possible to what looks like natural Haskell programming even though you're not using the normal data types or anything like that most of the time. And Arduino co-pilot, it does have a monad, which is of course where the, you know, read from this right to that happens isn't a monad. And it uses Rebindable Syntax only also for if then else, which it has its own implementation in that monad using Rebindable Syntax. But yeah, I don't know about Qualified Do's application here. Sorry. So I think I'm going to stop the recording now. Before I do, I want to thank Joey so much for this talk and the discussion. This was really, really fun and interesting. And we'll continue discussing in a second. Well, thank you, Claude. I appreciate the opportunity and I've really appreciated all the questions, some of which are made me think. So I really appreciate that. So thanks to everybody who showed up.