 OK, so I'd like to introduce my talk now. This is embedding Ruby. So I'm going to be talking about taking Ruby, whatever applications you want, and dealing with them in an embedded environment, so microcontrollers, where you've got limited CPU, RAM, IO, all that kind of stuff. Just a quick side note before I really get started. Last night, I was putting this together. It didn't come with Keynote or anything. And I had this idea in my head of what I was going to do. I wrote a quick Ruby script using Figlet. I'm calling it Slider. That's what's doing all my slides. It was just kind of a fun project, if anybody's interested, at the end of the talk. I'll show you where to get it. So my name is David Goodlad. You can find me all over the place as D. Goodlad. Nice, easy. Nick to find me as on Twitter, IRCA, wherever. Why am I qualified to talk about this? I've been working with Linux for a bajillion years since I was 12 years old. I've worked with it from the context of installing or pre-built distributions all the way down to building Linux from scratch systems for a couple of years. So I've done most everything. The only thing I haven't done is kernel development. I've been working with Ruby for, I was trying to remember, it was four or five years now. Significant amount of time. And I've done a wide variety of projects with Ruby. And lately, for the past year or so, I've worked for a company in the town where I live called Techmar. And they design and manufacture HVAC controls, thermostats, boiler controls, solar heating controls, all sorts of stuff to do with heating and cooling and building maintenance. And we've been doing a lot of work with integrating their sort of really old school mentality. It's all embedded devices and taking those devices and making them accessible on the web. So you've got building managers that can monitor the state of the building remotely from one room. They don't even have to go to the boiler room and get all dirty anymore. So that project has been called the Gateway Project. And I'm going to be referring to it a few times through this. The Gateway Project, as it stands, is a regular Rails app running on a Mac mini for clients. But the work that we've been doing has been working to having the whole system be embedded on an ARM9 or maybe ARM11 system. So why would you want to run Ruby on an embedded machine? You can do a huge range of things. You can do home automation, media center applications. So you've got a touch screen on your wall and you say, I want to watch DVDs. Boom, done. You don't have remote control. Or maybe you have it for pause and play and that's about it. Like really, really nice systems that way. You can run Ruby on phones. If you've got a jailbroken iPhone, you can get Ruby on there now. And I believe you can interface with the Cocoa stuff. You can do robotics with it. There's people talking about doing robotics and music and all sorts of stuff in embedded systems, which is really cool. But the problem is you've got a lot of constraints when you're developing in an embedded system. You have limited resources. You're stuck with a slow CPU. You're stuck with limited RAM. RAM is not cheap for embedded systems. And it's a pain to wire. Building up the circuit boards to wire 256 megs of RAM is not very fun unless you're doing it single chip and they're not cheap. I.O. speed, a lot of people think magically if you've got flash, it's fast. But it's not. You've got limited bus speeds and write speeds generally aren't that quick. The other thing that you've really got to deal with is reliability. In an embedded situation, you've got a box on the wall. If you're a hobbyist and you're the only person at home and this is your toy, great. You can take your laptop over there and plug your serial debugger in and play with it when things go wrong. If you've got the wife at home who just wants to watch TV or just wants to, you know, it's too cold in the room and doesn't know anything about this, you don't want to have the thing crashing and spitting out a stack trace. You can't have that kind of stuff happen. It's got to be very reliable. So there's a lot of things that you can do to deal with that. You can run a watchdog process. You know, if something does fail, just restart it. But generally, you've got to be really careful when you're coding these apps to make sure that it's not going to fail. You deal with all the edge cases right when they come in and then make sure to test a lot. GUI depends what you're doing. You can have a touchscreen interface. You can have just buttons with a simple digital LCD readout to no interface on the box at all and just a web interface. That's really where I've been concentrating and haven't done very much GUI work. Although really, if you're talking about an embedded system running Ruby, you've got Linux, you can run any number of different GUI systems on top of a lightweight X server and you should be fine. So really, a large chunk of my talk today is going to be about embedded Linux and how Ruby fits into that environment because there's really two scenarios that I envision. One, you're a hobbyist doing this for fun and you've got some development kit or a gum stick or something like that. You're gonna need to know how to build your system and finding that information in a cohesive way on the internet is tough. So you need to know how to build an embedded Linux system from scratch. If you're in a commercial environment and you're the Ruby programmer on the project, you still need to understand what's going on in the background or you won't know how to talk to the hardware when you need to. Embedded systems, you're always talking outside of your own hardware. You're always dealing with sensors or other systems. So the base of an embedded Linux system on a microcontroller, it's gonna automatically jump to a specific address and flash or wherever storage ROM that's got a bootloader on it. It may be some custom bootloader that the manufacturer has said this is the bootloader and there's no source code or anything, but that's it. Most modern microcontrollers that you'll get, you'll get the Atmel 1891, which is really common. That's an ARM9 processor, runs at about 200 megahertz and it's generally pretty good. It's fairly quick or NXPs, LPDs, 7A, 404. Anyway, whatever chip you've got, most of them will run with either Apex or Uboot. If you Google for those, there's tons of resources on how to use those bootloaders and those allow you to get the kernel up and running in Linux. And once you're at the kernel, you've got to worry about custom patches. The kernel developers have been really good about setting it up such that you can run on lots of different architectures, different processors, different memory configurations, all sorts of stuff like that. However, dealing with every possible permutation is very difficult. So for example, the device that I have actually with me today, I won't be able to demo, but if people wanna see it later, this guy here, this is a development kit from a company called Logic Product Development. They use an NXP chip on there. And this guy requires 25 patches, about three megs worth of patches to the kernel in order to get it all the hardware running. You've got patches for Ethernet, you've got patches for the Compact Flash adapter. There's a couple of patches for Ethernet, Compact Flash, SD, all sorts of peripherals, mainly. So you're gonna need some custom patches, there's gonna be some finangling around there. And then you're gonna need a libc, the standard C library, everything in Linux, the base system, links against libc. There's no getting around it. You've traditionally got two options, glibc, which is what you're gonna have on any normal Linux system, or muclibc, which is microclibc. Muclibc is becoming less important for these newer microcontrollers because they've got a memory mapper. Muclibc was built for ARM7 and below, and other similar architectures, where you didn't have an MMU, and therefore glibc didn't work. You couldn't map areas of memory. Muclibc, the only real restriction that I know of is you can't fork, I believe. But there's a couple goofy things in muclibc that really don't matter much anymore if you're dealing with a processor fast enough to run Ruby anyway. And then I like to use busybox. Busybox is really neat. It's a package that in one binary encompasses 90% of the standard Unix tools you'll ever see out there. You've got LS, you've got AUK, you've got VI. So everywhere from the very, very base to some simple editors, grab more or less. And you can configure this however you want. You can use, include each individual utility, and you can say, I want access to this command line option. If you don't need it, you can get rid of it and save 2K, right? It's not really that big of a deal, like I said on these sort of newer microcontrollers, but it can be worth it in some situations. So busybox is really good that way. But how do you really put this stuff together? You can do it all by hand. I've done it and it's really worthwhile to learn. It's not worthwhile in the long run. It's not fun. You'll spend hours and hours and hours testing out different combinations of compiler, C library, but binutils. And it's worth it to understand the process. It's not worth it in the long run. So we're gonna call that Yuck, no way. Second option is Crosstool. Crosstool is a really cool package that uses the Linux kernels build system. They've sort of hacked it out of the kernel and made it possible to use that to configure what version of GCC you want, what version of binutils you want, what architecture you're compiling for. It puts it all together and you run make and the whole system builds up to sort of glibc and you'll get busybox. That's about it. It doesn't deal with building an image for you if I recall correctly. Nothing like that. It's basic. It's a pin in the butt because it offers about 10 different versions of GCC, 10 versions of glibc, tons of versions of binutils. Binutils has four minor versions in his version number. I think it's 2.16.99. It just goes on forever. It's ridiculous and almost impossible to find a working combination by guessing. It's very, very difficult and there's no documentation. This version of GCC works with this version of binutils. On some architectures it might work with one. Some architectures it might work with another. It's just a nightmare. So Cross Tool is good. It's a cool system. If you know what you're doing, great. But I don't like it, ick. Open Embedded is the way I've gone. It's the way most embedded developers have moved. The OpenMoco cell phone, if you've seen that at all, the Neo 1973 or whatever, their whole system uses Open Embedded to build their base Linux. So it's really neat. What happens is it's a toolkit for building a whole distribution, not just the base. It's got an enormous category of package recipes. So if you think of Mac ports, I'm sure a lot of you are familiar with Mac ports or Gentoo's portage system, anything like that. It's a bunch of recipes for how to build packages and you can define at a very high level how you want the system to work, what architecture you're using, and then just say, give me a basic image and a bunch of packages that are optional to install. And it works very much like the Debian app system once the image is built. And it builds the whole image. It builds the kernel for you. The whole shebang is ready to go. There's a lot of preconfigured stuff for common embedded systems. It works very, very well. And the whole thing is in Git, so it's easy to keep up with. You can see it at openembedded.net and they've got a really good wiki. They've kept it fairly up to date lately. They were bad for a while, but the wiki is pretty good. How does open embedded fit with Ruby though? That's what most of you are really concerned about. There's a package called Ruby all ready to go. It hacks the config.mk and a couple of other files so that it cooperates with cross-compiling. All the tests will pass when it compiles. It works very, very well. They've done a really good job. It's quite simple or quite short for that matter. I'll show you that in a minute. However, the things that it does are so important and so hard to find how to do that on your own. So the Ruby package is quite good. Right now it builds 185, but it's easy to adapt to 186, 187, whatever you need. And because we're living in an embedded world, you're bound to need extensions, whether they be your own extensions or maybe SQLite, MySQL, if you really want to go there. Hapercot, whatever. There's a open embedded class called Ruby extension that you just include in your build script that you can write yourself fairly easily and it just makes it work. It cross-compiles it properly for you and works incredibly well. I've been quite impressed. So let's take a look at open embedded briefly and we're gonna play in Vim for a minute. What you get is a sample configuration file that they say, please go through and read this whole thing and you have to because at the very bottom there's a line that you have to uncomment or the whole thing just breaks. They really don't like people with silly questions that they just haven't read the config file. It's very well commented. So if you don't read it, do bad. So there's only a couple really important variables in here to configure. One is what kind of machine you're building for. So you've got in my case an LPD7A404 that's that board there. I also have an Atenel development board which is commented out above. There's a large selection of machines. I'll show you that in a minute. They're very easy to define. The LPD7A404 I define myself and I think it's like 10 lines copy and pasted with a couple tiny modifications really. It just defines where memory starts and a couple of packages that you want to say I want to use this instead of this. The next variable here is distro. Open embedded like I said is used for building multiple distributions. You can build open mocha with it. You can build a minimal distribution that's just sort of stock. Not very well maintained but good starting point. Angstrom is sort of their generic build that will build most packages and give you a GUI if you want it. It'll give you a console if you want it, whatever. I use Angstrom. It's easy. It's well maintained. That means they've chosen appropriate package versions for integration and ease of use. So the last line, like I said, remove this line. Normally it is uncommented and will cause a fatal error. Read the comments in your conflocal.conf. That's a nasty one. It's easy to miss too because they space it out like that. So don't miss that when you're editing. You'll just do the classic face palm. This is the machine definition that I spoke about. So it's just in, you can see at the very bottom, hopefully you can read that conf slash machine slash lpd7a404.conf. This I just copy pasted. I don't remember which one from, but it's quite simple. All I've told it is it's an ARM processor, ARM v4t, which is the overall architecture. I want a Zed image for the kernel. I don't want to use virtual terminals because I know I have no display. All I have is a serial port. So I turn off virtual terminals. I prefer to have certain things enabled. USB host, USB gadget, the serial console will run on a specific console number at a certain speed. Minimal, minimal stuff. And I say require the tuning file for that architecture. I don't have to go too far into it. If you're really interested, you'll dig. The other thing is the distribution. Anxtrum, I'm not even gonna scroll down. You can dig at it. All of these config files are organized nicely under conf slash machine or distro or classes or whatever. Then, just as a simple thing, what have I done now? This is the minimal distribution. I won't go through that one. This was my hacked up file to make the kernel build custom patches for this machine. I just wanted to show it real quick to show that Open Embedded is quite good for customizing for specific machines. And you can, if you're targeting four machines, four different architectures. For example, the Linksys routers that started with custom firmware. You can now build that firmware for 50 different routers and they all have slightly different hardware configurations. You can actually just put all the machine definitions in here, run build again, and it'll automatically sort out what it needs to rebuild. And you can have custom patches. Notice the online 13 there, source URI, append for only the machine LPD 7A404. So this is kernel version 262.19. And I've got all those patches listed. There's 25 I think there. And I've just copied them in there. So it's very comprehensive that way. If I look at now something a little more interesting for us Ruby people, that's how a package definition looks. So if you're building your own Ruby app, you can write a package definition like that that doesn't even compile anything. All it does is copy some files. This one compiles Ruby, requires Ruby Inc, which I can load up. And is fairly simple. All it's got is a patch to extmk and disables one feature that causes issues, compiling or cross-compiling Ruby for ARM processors. Otherwise, it does a quick said edit for, which file does it do it on, common mk. Anyway, it does some tricks to make Ruby cross-compile. So you're building on your PC, you're building for ARM, which is likely little Indian rather than big Indian. And you've got a completely different architecture. This takes care of it. Ruby doesn't work well, doesn't use auto tools to build. So it can be a little troublesome if you're doing it by hand. What is going on here? Sorry, I had a bunch of marks set and this one doesn't seem to be set. I'll skip that one. The Ruby extension class, you can just include it. So really, I'm going to jump back out of OE. It's too hard to give a real good overview of open embedded in 45 minutes and it's straying away too far from Ruby anyway. So we're going to talk about more with integrating Ruby apps with outside systems. When you're in an embedded environment, most applications are going to require you to talk to external sensors. In my case, I'm talking to this USB device that integrates with this thermostat network. I need to deal with that. You're going to have packed binary data. You're going to have low level kernel calls to make. You're going to have all sorts of goofy stuff which may or may not be appropriate to do in Ruby depending on your application. I've chosen to do it all in Ruby because I like Ruby and it worked well enough. I use a gem called bin data which you should really, really look into if you're going to do any low level binary manipulation. It works very well even for reading GZIP files. If you know the structure of a GZIP file, you can do simple things defining a GZIP file and you say there is an int16 foo. Int16 is a method call. It's just like active record has, belongs to a class level methods. Int16 says the first field is a 16 bit integer and we're calling the field foo. You can pass parameters, for example, in a string. The field name is bar and the length of the string is foo. If you've got binary data, you have to know the length or you can say it's not terminated. Lots of cases you've just got a length field before. So when reading it will automatically read the appropriate length based on what it read from the first field foo. When writing it'll do the opposite, it'll automatically fill the field foo with the length of the string that you've put in Ruby. It's really, really handy. It's reasonably fast, fast enough for my applications and really simplifies how you define your data structures for binary data. But remember C is your friend. It's my friend. I curse at it sometimes, but it gets me through a lot of tough situations. If you're really desperate, drop down to C. Ruby inline, Brian back there wrote Ruby inline, I believe. Ruby inline works well. If you really want to dive down and write a full on Ruby extension, a C extension, whatever you need to do to interact with a slow level data, just do it. But Ruby is there to take care of the higher level, more interesting logic, right? That's how I've treated it. Ruby has allowed me to make a maintainable, nice, pretty system that can deal with this low level, ugly, but fun stuff too. So just remember, you've always got access to C. Interface-wise, I'm really gonna concentrate on web interfaces. If you're running an embedded system, you don't wanna be running Rails on it. Not funny, you've got 64 megs of RAM, maybe, so by the time you booted, you're using four or five megs of RAM, and you run this app and boom, you're swapping. Swapping on Flash is not fun. It will destroy your Flash card real quick, and we're talking about reliability. You don't want this thing running out of the ability to write to Flash because the blocks are starting to go bad within six months because you've been swapping in and out. So Rails, bad idea. So what do you do instead? There's lots of options. Everybody knows camping, why I made a big deal about it a few years ago. Small, lightweight framework to find everything in one file. Works pretty well, not for me, but it's an option. You've got RMAs, which I know another gentleman I was talking to earlier is doing a talk about RMAs. I discarded it early in my process for my app. It wasn't gonna suit it, but I've heard really good things about it. So take a look at RMAs. Sinatra is what I've ended up choosing to use. It's very lightweight, but similar enough to Rails that in my case, I've already got a Rails app that I'm trying to run on an embedded system. I can take a large chunk of my Rails code, copy, paste, and it just sort of magically works. A lot of the active record stuff. If I run active record, or if I don't run active record, I don't want to, I'm gonna have to replace. But a lot of the controller view stuff tends to work fairly well in Sinatra. And on that note, I'd like to go to an example project, the project that I've been working on. It's the gateway system that I talked about before. So existing, on a Mac Mini that these customers have sitting in their boiler room, you'll have two processes, both running Rails. One is the web server and one runs the TN4 system. TN4 is the network protocol that this company runs. It's 300 bod, it runs over four wires, but over ridiculously long distances at 24 volts. Like it's totally custom. I've got a box that they've built for me that plugs into this network, gives me a USB connection, and lets me see their network traffic in binary. So I've got this process that sits and watches the network to see what's happening, writes to the database. So that's why it needs Rails, because I've got all my model code there. And then I've got the typical Rails, Mongrel web server running, also accessing the same database, reading that state, and writing to it so that the TN4 process can go off and send more information back to the network. So I'm gonna flip back to them here. And start with this guy. So I've got a, I've got this system that runs TN4. It's really simple. I start up my message queue, I start up the gateway process, and line 39 there, gateway run, process messages. That's all it does. The gateway reads messages coming off when it's found one, sends it off to this message processor task, and it gets dealt with. This thing, each process eats up a ton of RAM. It's ugly. And I'll talk about that more in a second. So we've got this gateway run process that sits there, processing information, and then processing inbound messages. It's pretty simple. I'm gonna blast through and look at this because it's a good example of how I use BIN data to integrate with these systems. The constants at the top are not really meaningful for you guys, but I say it's little indian. I've got an eight bit value service port timestamp, and then I define this new thing called a bit field that has packed data into one, two, in this case, four bytes. You've got off on the right there, you can see six bits for the data length, six bits for the node, eight bits for subnet. They've got this weird networking protocol and the header is all packed into four bytes. And then I use this choice data type, and I've defined a bunch of message types. Each one is one of these types of classes, a BIN data class that has a different format. So the payload of this network packet can be all sorts of different things. That's how I use BIN data. Really, this is a little bit heavy for what I'm doing, but the way I've worked the new stuff and the sad thing is I'm not allowed to show code for the new stuff is I've taken this sort of TN4 process and I've factored it out. I've brought out a bunch of the parsing code into C or some of it is just pre-processed. I run a script on it and dumps out a Ruby script so it's not so dynamic and therefore doesn't take up as much RAM. This stuff requires a fair bit of RAM to hold all the information about all these field types and there's a lot of complexity there to parse this out because it's so nice to read. So I've gone ahead and done the pre-processing work on a very similar DSL to spit out a class that can automatically parse it. So that is what I've done there is instead of having this huge Rails process on one side dealing with it, I've minimized the overhead in dealing with the TN4 networking information and then I've moved to Sinatra on the website and it has made a huge improvement to the system. I can actually run it on this little board now where before it was taking 400 megs of RAM and eating up CPU on this Mac Mini. It was ugly, but it worked. So that's what I've done with my system and it works quite well. Really the strategy for me was cut as much stuff as possible, drop down to see where I needed to and run profilers. So embedding Ruby, it's not exactly easy. I've blown through a ton of stuff. There's so much to cover if you're gonna understand how to build the whole system. But it's not really that hard either. When you've got all these tools available, Open Embedded makes the build system so much easier, makes building Ruby itself and extensions for Ruby quite a bit easier. Deploying these kinds of applications on your own without a tool like this is a monumental task. I just last comment about Slider, this tool that I wrote just for fun. If you want it to play with, help me make it better. Go ahead. I don't actually have my GitJour server running. I can in a second, but I actually pushed it to GitHub earlier. So if you'd like a copy, just go to my GitHub page and grab it. It's ridiculously simple and small. All it requires is a program called Figlet from MacPorts. Any questions? So I've got a bit of time left. Yeah. How about unit testing? Will you use your code as usual or do you find challenges that assist you? Unit testing, sorry. He was asking, what about unit testing? Any challenges or do I just go on as usual? It really depends what you're doing. My binary data parsing stuff is fairly easy to unit test. I can take a dump of the data that I normally get, excuse me, over the serial line and run it through and verify that I get the appropriate messages and I get the appropriate information in the various fields. Unit testing, the build, hard to do, right? But unit testing, your actual Ruby app is fairly easy to do, especially if you've got access to an emulator, which I didn't actually mention. QEMU works very well for emulating ARM and is actually used by Open Embedded to build the G-Lib C locales, the binary locales in G-Lib C. So if you wanna unit test your app within the ARM environment, you could just run it in an emulator. You don't necessarily have to run it on the hardware. You could have some QEMU virtual machine set up in your build system to automatically run your unit tests within a virtual machine from a new image build. There's all sorts of ways you could go about that, but really unit testing is the same old just with some wrappers. Anybody else? Yeah. How much do those ARM boards run? It really depends. You can find a lot of them used now. You can find the Atmel ones used on eBay. I don't know what kind of pricing. This guy I think is about $400, but that's ARM board plus a bajillion peripherals. The Atmel boards generally come with touchscreens, which are really nice, because then you can play. Open Embedded will let you build an image that has OP or GPE or Enlightenment on it. So you boot up and you've got a full window manager that works with the touch screen and set up nicely. It's really cool. But back to your question of how much? 400 bucks. I think the Atmel ones are around 300. You can buy gumsticks for a couple of hundred. Wilson? The cool legal board thing I was talking about last night is $150. So you can get a partial of those prices Okay, yeah, Wilson just said he's using a board called Beagle. It's about $150 for one board. What processor does it use? 600 megabits on 11. Oh, nice. 600 megabits on 11. Where do you get it from, Wilson? Digikey sells it. Digikey. Yeah, Digikey's pretty good. Beagleboards.org, I think. Beagleboards.org? Okay. Yeah, so that's actually a really good option. I was lucky and this is through work, so it's free. But if you're gonna play the hobby game, gumsticks are really cool. They're under $200. And like Wilson said, the Beagleboards. I just noticed that it's implicitly supports extra amount of embedded sound. Okay. Cool. Anybody else? Yeah? Do you have any tuning of Ruby's garbage collector? I haven't. No. I've left it alone. I'm scared to touch it because I will break things. Anybody else? Yeah? Okay, any of those ports called Open Hardware? Does it support Open Hardware? Is that good? I believe the gumsticks have some information available, but I don't think they're fully open. The open moco phone is fully open. There's Eagle PCB designs for it available. Otherwise, off the top of my head, I can't think of anything fully open hardware now. Most of this stuff is fairly easy to design because everything's in the microcontroller. You've got a couple of passive components and some RAM and a couple peripherals to hook up, but you need board fab capabilities. So, yeah? What extent are there good Ruby wrappers to the APIs? He's asking about Ruby wrappers for APIs for peripherals. It really depends on what you're talking to, right? For me, I've been working with custom stuff. In terms of like a touch screen, you would be working through X, so whatever facilities you have for dealing with the mouse and X. Otherwise, what kind of peripherals are you talking about like servos, all sorts of cool stuff? It really depends. There's GPIO ports on most of these, which you can write a really simple kernel driver for. I haven't dealt into that. In terms of Ruby wrappers, I don't know. It really depends on what you're dealing with and I haven't dealt with a lot of different peripherals like that. Last one and then looks like people are getting ready for the next talk. Anyone? Yeah? He's asking about experience with compressed file systems. I actually use JFFS2, which is a journaling flash file system. It works incredibly well. It's read only, if I recall correctly. It's been a little bit since I looked at that. I use that for my root file system. It generally keeps something that would be 16 megs down to five or six megs in flash. So with a 16 meg nor flash on board, you've got lots of space for the kernel and lots of root file system. And then I just use a compact flash card. This guy's just got a CF card and I use it for all the data. But the root file system that you build, the base is maybe 10 megs tops and that includes X, I believe. So it's pretty easy to get down fairly small with compressed file systems. I've chosen JFFS2. It works quite well, so. I'm gonna have to cut it off. So thanks everybody for coming.