Imagine that you could talk to the physical world as easily as you can read and write files on your computer. Imagine you could read a file, and read the temperature of an experiment on your bench or of your compost pile outside. Imagine you could simply write “on” to a file, and equipment across the room (or across the house) would power up instantly. Imagine those ideas were just the start… If I’ve got your attention, MRBFS – the MRBus Filesystem – is what you’ve been waiting for.
So first, a little credit where credit is due. The idea for MRBFS was strongly influenced by Paul Alfille’s 1-Wire Filesystem (OWFS). OWFS is a filesystem wrapped around Dallas Semiconductor’s (later Maxim’s) 1-wire network, allowing each IC on the bus to appear as a directory, with the files in that directory corresponding to things the part could do and measure, such as temperature, voltage, counters, or memory. Michael and I both used OWFS for years for home automation and telemetry purposes. When we started converting much of that functionality to MRBus nodes instead, wrapping a filesystem interface around it was only a natural step.
MRBFS (referred to by its lazy creator as “murb-fuss”) is built upon the FUSE – Filesystem in Userspace – project. At the moment, it’s only known to work well on Linux. It probably would work under Mac OS X. I’ve never tried it, but since OSX is built on top of BSD, it looks a lot like Linux and other Unix variants. The chances of working under Windows are currently pretty small. I’m not ruling it out – there are projects out there to get FUSE running under Windows – but MRBFS uses a large number of Unix constructs internally and likely would need heavy modifications to work well. (If somebody does get it running, please let us know and we’ll mainline your patches.)
You should be warned that MRBFS is more or less a hack in progress. It is stable and works well enough that Michael uses it for home automation control, and I use it for home automation and model railroading, and neither of us has issues. However, not all MRBus nodes are supported, and the those that are have varying driver support between “complete” and “bare minimum”.
Installing MRBFS
Let’s walk through installing MRBFS on a typical Ubuntu-based system. This should be basically the same for any Linux distro based on Debian, and easily adapted to other variants. Personally, I spend most of my day on one Ubuntu machine or another, so it’s what I know. It’s also the environment in which MRBFS, and for that matter most of our products, are developed.
First, you’ll need a few prerequisites – a Subversion client for retrieving the MRBFS source code, and then the packages containing the FUSE code – typically fuse, libfuse-dev, and libfuse2 – that MRBFS requires to work. This should be old hat for most of you, but here’s the commands to be run:
sudo apt-get install subversion sudo apt-get install fuse libfuse-dev libfuse2
Next, you’ll need the mrbfs source code. I’d recommend putting this all in its own directory to start with, just to keep it contained. The easiest way is to just grab the most up-to-date version from our SVN archive. Once you’ve got the source code, building it is as easy as going into the mrbfs directory and typing “make”. Again, I’ll save you the trouble of figuring it all out – here’s the commands:
mkdir mrbfs-build cd mrbfs-build svn checkout svn://mrbus.org/mrbus/trunk/mrbfs mrbfs cd mrbfs make
Building should complete without errors. Other than the libfuse stuff we installed previously, there aren’t many external dependencies.
Getting Started with MRBFS Configuration
Once you’ve got it built, now you need to give it the specifics of your particular setup. Configuration takes place through a file called mrbfs.conf. There’s an example included in the SVN repository called “mrbfs.conf.example” – I’d strongly recommend starting with that.
The first thing you should change is where it’s logging to – in getting all this set up, the log that mrbfs outputs will be your best friend in trying to figure out what’s gone horribly wrong. There are two directives that you need: log-file, which tells MRBFS where to log output, and log-level, which is an integer from 0-9 that tells it how verbose to be (0 is just errors, 9 is more than you ever wanted to know).
The other thing MRBFS needs to know is where to find its driver modules. Each node type creates a driver library that’s loaded at runtime. By default, running make on MRBFS puts these all in a modules/ directory, but you can move it if you like. For now – and I’m sorry – you’ll have to provide the fully qualified directory to the module-directory directive.
# log-file tells mrbfs where to store its log data log-file = "./mrbfs.log" # log-level is 0 for just errors, 1 for warnings, 2 for info, and 9 for max debug log-level = 9 module-directory = "/some/dir/path/mrbfs-build/mrbfs/modules"
The first thing you’ll notice is the big block of things in curly braces. The MRBFS configuration file uses each of these blocks to describe a thing, and they’re all in the format of:
thing-type friendly-name { bus = 0 driver = "somedriver.so" address = "0xXX" required-thing = "required-value" required-thing2 = "required-value2" option option-name { value="option-value" } }
thing-type is typically either “interface” or “node”. Interfaces are how MRBFS talks to that bus. Nodes are things hanging off the bus that collect or consume data, like a MRB-BD42 block detector or a MRBW-RTS quad temperature sensor. The “friendly-name” is some name by which you think of this. It’s not as interesting for the interface, but if you have a few of the same node type on the bus (such as temperature sensor nodes), you might name them “kitchen-temps”, “mad-science-lab-temps”, and “outdoor-temps”. (Note that spaces aren’t allowed…)
You’ll note each section has a bus number. MRBFS isn’t limited to talking to a single MRBus network, but can actually handle up to 16 at a time. The bus number tells an interface or a node which bus it’s attached to so that packets from one bus don’t get filtered to a node on a different bus. That’s an advanced use case, though, so we’re skipping it here. So for now, just make these all 0.
Also, each node must have a unique address. These are the addresses you assigned to the nodes when you set up the network. (Also, I should note that interface drivers use “interface-address” rather than just “address” because I’m a very inconsistent person and this is still a work in progress.)
The next thing you’ll need to specify for each block is the driver. This is how MRBFS knows how to communicate with and interpret data from a given device type. These are the things that live in the modules/ directory you specified earlier, so if in doubt as to the name, look around in there.
Beyond that, almost all node types have options that you can specify, and they can be different for each node. For example, if I wanted my MRBW-RTS board to specify temperature in fahrenheit rather than celsius, I’d add option temperature_units { value = “fahrenheit” } to its block. Another one – say the one we named “mad-science-lab-temps” earlier – might have its units specified to “kelvin” instead.
Configuring the Interface
So, typically you’d start by telling MRBFS how it’s going to be communicating with the bus. Currently MRBFS supports two methods – via an ISE MRB-CI2, which connects to your computer via USB and emulates a good old serial port, or wirelessly via a Digi XBee on a board like a SparkFun XBee Explorer or similar. To do this, you need an “interface” section. We’ll be using the MRB-CI2 already on the demo board for our connect. Here’s the interface configuration you’ll need – have a look through it and I’ll cover each line below.
interface my-ci2 { bus = 0 driver = "interface-ci2.so" port = "/dev/ttyUSB0" interface-address = "0xFE" option rtscts { value = "on" } }
So here, we’re creating an interface for a MRB-CI2 on bus 0 which will use the address 0xFE. (MRB-CI2 modules don’t have a “built-in” address as they’re basically a pass-through device, so MRBFS needs to know what address it should use when sending packets.) Also, it’s on /dev/ttyUSB0 on our computer, and we want to use RTS/CTS flow control to avoid serial overruns.
Configuring Nodes
Obviously configuring MRBFS to just be able to use an interface isn’t much fun. You can’t do anything useful with that! So, now we need to start adding nodes that actually do something. For our example, I’m going to add a MRB-ACSW relay switch and a wired, quad-channel MRBW-RTS temperature sensor board to the demo board I used in “An Introduction to MRBus“. The ACSW will be at address 0x8C, and the RTS will be at 0x81.
So, with the hardware physically connected, we need to create configuration blocks for each of these nodes. We’ll call the ACSW “light-controls” and the RTS “workbench-temps”. Here are configuration blocks for both of them:
node workbench-temps { bus = 0 driver = "node-rts.so" address = "0x82" option temperature_units { value = "celsius" } # option suppress_units { value = "yes" } } node light-contols { bus = 0 driver = "node-acsw.so" address = "0x8C" }
Once you’ve added this to your mrbfs.conf, go ahead and save it. For the lazy, like myself, I’ve placed a copy of this sample configuration here. Now it’s time to do something fun.
Let’s Get This Party Started
Okay, enough tinkering with configuration files. It’s time to get MRBFS started and see what it can do. First thing we need is a mount point to become the root of our MRBFS filesystem. For now, I’d recommend just creating a directly called “mnt” in your mrbfs-build/mrbfs directory. That’s what I’ll assume in the samples below.
While in the mrbfs-build/mrbfs directory, run “./mrbfs mnt” Assuming you don’t see any errors, congratulations – you’ve just stared up a MRBFS instance! It’s time to start exploring what’s in there.
Here’s a listing of the directories you’ll find:
./bus0: ./bus0/0x81-workbench-temps: ./bus0/0x8C-light-controls: ./interfaces: ./interfaces/my-ci2: ./stats:
There’s one common directory for interfaces, called appropriately enough, interfaces. Under that, you’ll find a ci2 directory with data about our demo interface.
For each MRBus attached, you’ll find a busX directory with all of its nodes underneath. As both of our nodes exist on bus0, you’ll find their directories under bus0. The nodes are represented by directories named 0xXX-friendlyname, and individual parameters of each exist as files in those directories.
Stats is a placeholder for future expansion – for now, just ignore it.
Reading Temperatures
In the bus0/0x81-workbench-temps directory, you’ll find a bunch of “files”. These aren’t real files, but rather pieces of data that MRBFS represents as files so you, the user, can interact with them more easily. If you do an ‘ls -la’ you’ll see something like this:
dr-xr-xr-x 2 ndholmes ndholmes 0 Mar 1 18:51 . dr-xr-xr-x 2 ndholmes ndholmes 0 Mar 1 18:51 .. -r--r--r-- 1 ndholmes ndholmes 1 Mar 1 18:51 eepromNodeAddr -r--r--r-- 1 ndholmes ndholmes 8 Mar 1 18:52 mrbus_voltage -rw-rw-r-- 1 ndholmes ndholmes 1 Mar 1 18:52 rxCounter -r--r--r-- 1 ndholmes ndholmes 1800 Mar 1 18:52 rxPackets -r--r--r-- 1 ndholmes ndholmes 8 Mar 1 18:52 temp_sensor_A -r--r--r-- 1 ndholmes ndholmes 13 Mar 1 18:52 temp_sensor_B -r--r--r-- 1 ndholmes ndholmes 13 Mar 1 18:52 temp_sensor_C -r--r--r-- 1 ndholmes ndholmes 13 Mar 1 18:52 temp_sensor_D -r--r--r-- 1 ndholmes ndholmes 8 Mar 1 18:52 temp_sensor_internal
What to do now should seem relatively obvious – standard shell commands will work just fine to interact with the node. I have only a single temperature sensor attached to channel A, but let’s just look through all the temperature sensor nodes just to see what each of them reads:
ndholmes@copernicus:~/mrbfs-build/mrbfs/mnt/bus0/0x81-workbench-temps$ cat temp_sensor_A 20.73 C ndholmes@copernicus:~/mrbfs-build/mrbfs/mnt/bus0/0x81-workbench-temps$ cat temp_sensor_B Open Circuit ndholmes@copernicus:~/mrbfs-build/mrbfs/mnt/bus0/0x81-workbench-temps$ cat temp_sensor_C Open Circuit ndholmes@copernicus:~/mrbfs-build/mrbfs/mnt/bus0/0x81-workbench-temps$ cat temp_sensor_D Open Circuit ndholmes@copernicus:~/mrbfs-build/mrbfs/mnt/bus0/0x81-workbench-temps$ cat temp_sensor_internal 21.16 C
As you can see, the sensor channels that don’t have anything attached just read “Open Circuit”. However, temp_sensor_A shows the temperature as measured in the units specified. By combining this with simple programs running on the computer, you can use this to log temperatures, display them on a webpage, or even trigger emails or text messages being sent as a result of things running out of range.
Most nodes will also have a “mrbus_voltage” node, so you can monitor bus power at any individual node. Battery-powered nodes will report their battery voltage. It’s quite handy for spotting power trouble before it actually causes issues.
Controlling Outputs
Now you’re thinking, “That’s great, but I don’t just want to measure the world around me, I want to control it from the filesystem!” Well, to that I have to say…
That’s why I added the MRB-ACSW to this example – it’s a simple and safe example of how to control things in the real world. The module itself has two high current relays built into the board, so you can drive all sorts of loads (up to 15A) with near-perfect isolation from your computer and your MRBus segment.
Warning: If you want to drive mains voltage loads, be sure you know what you’re doing and always place the ACSW and all other exposed power connections inside proper insulating enclosures. 120VAC/240VAC can kill you, your friends, your pets, your relatives, the whole darn town, and then go on to burn your house down. Once the fire department declares your house a complete loss, it’ll cause building inspectors and lawyers to descend upon you like a biblical plague. Seriously, be careful and know what you’re doing, because we’re not liable if any or all of the above calamities befall you.
Okay, now that I’ve got the necessary disclaimer out of the way, let’s look in the 0x8C-light-controls directory. You’ll see things like this:
ndholmes@copernicus:~/mrbfs-build/mrbfs/mnt/bus0/0x8C-light-controls$ ls -la total 0 dr-xr-xr-x 2 ndholmes ndholmes 0 Mar 1 18:51 . dr-xr-xr-x 2 ndholmes ndholmes 0 Mar 1 18:51 .. -rw-rw-r-- 1 ndholmes ndholmes 2 Mar 1 19:11 counterA -rw-rw-r-- 1 ndholmes ndholmes 2 Mar 1 19:11 counterB -r--r--r-- 1 ndholmes ndholmes 1 Mar 1 18:51 eepromNodeAddr -r--r--r-- 1 ndholmes ndholmes 4 Mar 1 19:11 in0 -r--r--r-- 1 ndholmes ndholmes 4 Mar 1 19:11 in1 -r--r--r-- 1 ndholmes ndholmes 4 Mar 1 19:11 in2 -r--r--r-- 1 ndholmes ndholmes 3 Mar 1 19:11 in3 -r--r--r-- 1 ndholmes ndholmes 8 Mar 1 19:11 mrbus_voltage -rw-rw-r-- 1 ndholmes ndholmes 4 Mar 1 19:11 out2 -rw-rw-r-- 1 ndholmes ndholmes 4 Mar 1 19:11 out3 -rw-rw-r-- 1 ndholmes ndholmes 1 Mar 1 19:11 rxCounter -r--r--r-- 1 ndholmes ndholmes 1800 Mar 1 19:11 rxPackets -rw-rw-r-- 1 ndholmes ndholmes 1 Mar 1 18:51 sendPing -rw-rw-r-- 1 ndholmes ndholmes 10 Mar 1 19:11 sw0 -rw-rw-r-- 1 ndholmes ndholmes 4 Mar 1 19:11 sw1
The most interesting things in here are sw0 and sw1, matching the two relays of the same name on the board. These are the ones that control the big, high current terminals.
Let’s turn on sw0:
echo "on" > sw0
Ta da! Relay sw0 turns on, and if you had a load connected, it would have as well. Similarly, you can read it back by catting the file, and it’ll tell you “ForceOn”, meaning you forced it to be on with your last command. You can read the four inputs on the ACSW by catting them as well. They’ll report “Off” unless you ground them, in which case they’ll show “On”.
Conclusion
That’s the MRBus Filesystem in a nutshell. Even though it’s still very much a work in progress, it’s very usable as a way for software running on a PC to interact with hardware in the real world. Additionally, it’s very easy to expand to have filesystems for your custom MRBus nodes.