Sunday, February 26, 2017

Simple OpenBSD File Server

These days, when people think of a file server, they assume that it must support Windows CIFS (a.k.a. SMB or Samba).  A few grizzled sysadmins know that NFS would be much, much simpler to set up and almost nobody would consider using FTP.

Well, that is too bad, since for many situations, anonymous FTP is best and it works purrfectly on my little OpenBSD netbook.

FTP is a very simple protocol, it only talks when it has to and is completely quiet otherwise.  It has none of the chattyness and incessant 'CACA' packets of CIFS.  It is extremely easy to set up and has native support in all operating systems.  Even Windows can do anonymous FTP transparently and can map a FTP server to a drive letter, thus enabling any program to connect to the server directly.

Some will speak up and say that FTP is insecure.  Well, yes, but so is NFS and CIFS.  The difference is that FTP doesn't even pretend to be secure.   BTW, don't use FTP with password authentication - the passwords are sent in the clear making it quite useless.

The Samba manual is about 2 inches thick, while the configuration file for a FTP server is only about a dozen lines.  Need I say more?

When you have a home or office with ten or twenty users, who just need a centralized place to store data that can be regularly backed up and you don't want to waste any time managing it, then an anonymous FTP server could be ideal, since you don't have to waste any time with accounts, passwords and access controls.

Set it and forget it - KISS.

Configuration Frustrations

I installed vsftpd using pkg_add:
# pkg_add vsftpd

Simple as that.

In Linux, the package manager is different for each distribution, otherwise, it is the same idea.

Example Configuration File:
$ cat /etc/vsftpd.conf
ftpd_banner=VsFTPd. Cool, eh?

The problem with setting up a FTP server is that the configuration for the server also depends on the local file system access restrictions.

In an attempt to frustrate a hacker I run the server as an unprivileged user _ftp and make the directory tree owned by a different unprivileged user called ftp.  Ensure that these two users do not have a login shell.  Use /usr/sbin/false instead of bash or ksh.  You can run the script adduser to make these accounts if required.

The chown_upload_mode=0664 is a relatively new parameter. All the other older guides on the web don't show this option and this is the main reason why I wrote this guide.

When you specify:

Then this does nothing, so I set it to 0000 as a reminder:

and you instead need:

That took me a long while to figure out.


Directories and Permissions

I made a tree /ftp/pub/data like this:
# mkdir -p /ftp/ftp  
# mkdir -p /ftp/pub/data
# chown root:ftp /ftp/pub/data
# chmod 1777 /ftp/pub/data

That makes the data directory owned by root:ftp and sticky so that new files will inherit that ownership.

Test, Test, Test

Run the server in one console and log in from another console and transfer little test files.  Then log in from another computer and repeat. 
# vsftpd

$ touch test1
$ touch test2
$ ftp localhost
Login: anonymous
Password: [Enter]
ftp> put test1
ftp> get test1

On BSD, see what is going on with tail - same idea, but a different log system on Linux though:
$ tail -f /var/log/vsftpd.log

It may take a while to work out the fat finger kinks.

Windows Mapping

Use the easy connect wizard in the Windows file browser (File, Map Network Drive, Map as Drive, Connect to a website..., Next, Choose...) to connect to the server using a URL like which will create a shortcut in the left pane which then works like any other network file share.

After installing a 128GB SD card for storage, I now have a little WiFi connected 'NAS' which is normally sleeping peacefully and when I need it, all I need to do is flip open its lid and wait about 5 seconds for it to wake up.

La voila!


Friday, February 3, 2017

OpenBSD on a Netbook

Recently, I got fed-up with the bloated Linux distributions and wanted to try something that is secure, small and efficient and downloaded OpenBSD 6.0 from Theo De Raadt's server in Calgary.  Since Calgary is actually my old home town - why not?

OpenBSD tries to be the most simple and secure UNIX system out of the box.  It is very much server oriented, but it can do anything and many architectures are supported just for fun. For example, Arm RPi and Beaglebone, Intel 32 and 64 bit and several more.  So OpenBSD is a good choice whether you want to build a server farm, a network router, or a robot.

I have an old little Lenovo S10e netbook that I threatened to toss away numerous times, but it doesn't want to break.  As I feel guilty about tossing something that works perfectly well in a bin, once in a while when I run short of resources, I end up using it again.  Last year, it was pressed into service as a Linux Mirror server to install a bunch of embedded computers.

So I dusted the Netbook off and readied it for a new Olde Skool UNIX experience...

Where to get OpenBSD

The last time I used OpenBSD was about 2004 - for web and mail servers in Calgary.  I certainly know Linux better, but my main machine is a Macbook Pro which runs a kind of BSD and the more things change, the more they stay the same.

Here you go:

When I bought the netbook, I wanted something small that I could carry around easily and although the single core 32 bit Intel processor is slow as molasses with the original Windows 7 OS, it always ran Linux with the XFCE desktop quite well, but I wanted to see whether I can make it fly.

From the above list of files, download the install60.fs file if you want to use a USB memory stick as the install medium.

I made the mistake of not reading the INSTALL.i386 instructions and downloaded the ISO file, then wondered why it would not boot.  So, do go and read the INSTALL file.  Pretty much everything you need to know is in there!

Write the install60.fs file to a memory schtick using dd:


The WiFi adaptor in this netbook never worked with Linux, so I didn't expect it to work with BSD.  I therefore plugged in a trusty little Edimax USB dongle (Ralink chip set) and hoped it would work.  OpenBSD recognized it and loaded the run driver, so the dongle showed up as run0 in ifconfig.

The OpenBSD installer is super simple and OpenBSD is even easier and quicker to install than Linux.  It just takes a few minutes.  So, plug the USB widget in and boot it, follow the very simple instructions and mostly just accept the defaults, till you get to the network configuration.  Be sure to type the correct information in for the WiFi adaptor if you are using one: You need to supply the SSID and password as a minimum.

Of course I fat fingered the password, so it could not connect.  The WiFi setup information is in a file called /etc/hostname.run0 and editing it later presented an interesting challenge, since I am severely vi impeded.  I had to read the vi man page to find out how to delete a character - really.

First boot

OpenBSD is a simple and clean system with no bells and whistles.  None.  Zilch.

When you boot up, you get a nice, self explanatory login prompt:
Login: herman

If you are freaked out by a $ prompt, then you either have to return your Geek Card, or read a UNIX book or three.

At that point, I had to go and fix the WiFi password first and then rebooted to see if it worked properly, but you can simply run startx to get a beautiful FVWM desktop, with a xterm and a clock on it - woohoo.

We have Country AND Western music!

The default install doesn't have much of anything for a laptop machine.  The vi editor, ssh and a ftp client are about it.  No web browser, not even links

Install a Package or Three

In order to make the netbook useful, I need a web browser and an editor that is more to my liking.

Packages are listed here:

You can install the dillo web browser like this:
# pkg_add

Dillo is quite a horrible little browser, but it sure is fast and much less clunky than links.  However, if you want something more full featured, install surf or luakit, which are both based on webkit and work with everything, including yootoob...

To preserve your sanity, add the path to the /root/.profile file:
# echo "export PKG_PATH= >> /root/.profile"

and also export it so it will work immediately:
# export "PKG_PATH="

After that you can simply run:
# pkg_add dillo
# pkg_add links 
# pkg_add surf
# pkg_add luakit
# pkg_add nano 
# pkg_add abiword
# pkg_add gnumeric
# pkg_add xournal
# pkg_add pdfshuffler
# pkg_add gimp
# pkg_add minicom
# pkg_add putty

You can make that all one line of course, but I prefer getting error messages for one thing at a time, to preserve my sanity.

Utilities like ftp, ssh, telnet, netcat, tcpdump and more are installed by default, so with the above additions, I can do pretty much anything I would ever want to do on a Netbook.

Stop the Mail Daemon

I don't need the mail daemon on my teenie little netbook.  The mail daemon isn't actually doing much, but I prefer it doing nothing and save every processor cycle and byte of RAM that I can.

Services are controlled via the /etc/rc.conf and /etc/rc.conf.local files, with a utility called rcctl:
# rcctl stop smtpd
# rcctl disable smtpd

The result is:
# cat /etc/rc.conf.local


XFCE Desktop Environment

The FVWM desktop is nice and fast, but it is really only good for masochists.  My favourite light weight desktop is XFCE and installing it is just as simple as any other package.

The package manager pkg_add is quite smart, so you can install XFCE for a better laptop experience by simply running:
# pkg_add xfce 

You can then press Ctrl-Alt-Del to quit X and restart it with:
$ startx /usr/local/bin/startxfce4

Or, you can put exec /usr/local/bin/startxfce4 in ~/.xinitrc and then just run startx as usual and with a nice wintry themed wall paper the little Netbook looks quite cool now:

See this for more details:

Minor Niggles

With OpenBSD, there is no drama and most everything works.  If I close the lid, it sleeps, if I open it, it resumes.  Well, almost - the WiFi dongle didn't come back after a resume, so I needed to figure out how to resuscitate the run device driver and hook it into the resume process somewhere.

The OpenBSD FAQ ( eventually revealed the netstart command, which successfully restores the WiFi connection after a resume:
# sh /etc/netstart

I just needed to figure out where to hook netstart so it would be invoked automatically upon resume.

Advanced Power Management

The problem with the WiFi widget was that apmd was not running.  The Advanced Power Management service controls suspend and resume, processor speed and a few more things.
# rcctl get apmd

Configure and start apmd with:
# rcctl enable apmd
# rcctl start apmd

The result is:
# cat /etc/rc.conf.local

According to the apmd man page the /etc/apm/resume program is run after resuming from standby, so that is the one where I need to put a call to /etc/netstart.

So I tried this:

# mkdir /etc/apm 

# nano /etc/apm/resume

and added the following:
#! /bin/sh

# chmod 755 /etc/apm/resume

Let's see if all is OK:
# rcctl stop apmd
# rcctl start apmd

Let's see if it works with the zzz command: 
# zzz
Suspending system...

and a few seconds later I type:
It resumes from its slumber.

However, it didn't seem to run the resume program.

Let's see what happened:
# tail /var/log/messages
apmd: failed to exec /etc/apm/resume: Exec format error

So, how now brown cow?

Eventually, I did two things to get it to work as explained below.  Don't ask me how I found these tricks, it is just years of experience with obstreperous embedded widgets coming to the rescue and a dogged determination to try various things till the hardware responds the way it should.

Make the netstart script executable, so I don't have to invoke a shell explicitly to run it:

# chmod +x /etc/netstart

Add a delay to the /etc/apm/resume script to give the USB widget time to load its firmware and let the magic fairy dust settle, before trying to configure it:
#! /bin/sh
sleep 1

Now I can make the netbook suspend and resume, the little green lights flash on the WiFi dongle and all is well, the whole universe shook, the BSD daemons sung and flowers fell down from the heavens...

Squash and Square the Web

British Prime Minister Harold Wilson said of the press:
If you can't squash them, square them 
and if you can't square them, squash them.

You can get a good /etc/hosts file to efficiently squash and square the rubbish on the wild wild web here:

My little netbook now zooms like a much faster model, with no Windows, SELinux, systemd, advertisements or spyware to slow it down!

La voila!


Monday, January 23, 2017

Linux Network Manager Manual Commands

I have a love/hate relationship with the Linux NetworkManager daemon.  It usually works and keeps your laptop PC network connections going smoothly when you move around, but it gets in the way when one does network tests and system integration using a laptop PC.

Each time you plug a cable in, or turn an embedded system off/on, NetworkManager restarts the connection and you can then lose your static IP address setting, which gets tiring really quickly in a lab setup.

Usually, I completely disable NetworkManager and assign a static IP address to my machine on a laboratory bench with a script in /usr/local/bin called static:

#! /bin/bash
echo Configure network for PDLU access

# Disable the Network Manager
systemctl disable NetworkManager.service
systemctl stop NetworkManager.service

# Set static IP address
ifconfig em1 netmask up

# Set multi casting route
route add -net netmask dev em1

# Open up the firewall
iptables -F

# Show setup

and when I get back to a desk, I set things back to normal with a script called dynamic:

#! /bin/bash
echo Configure network normally

# Disable the Network Manager
systemctl enable NetworkManager.service
systemctl restart NetworkManager.service
sleep 1

# Set multi casting route
route add -net netmask dev em1

# Show setup
sleep 1

Sometimes NetworkManager gets confused and the Aplet ends up spinning forever, trying to bring up a non-existent interface.

The way to fix these kind of issues is to invoke the command line program nmcli:

# nmcli connection show active
NAME                UUID                                  DEVICES      DEFAULT  VPN  MASTER-PATH
Wired connection 1  34111952-8271-4f64-a616-a6cd5899bae2  vboxnet0     no       no   --         
Wired connection 2  9dd23ba2-3378-4657-96bb-3b687cfe0180  enp0s29u1u2  yes      no   --       

Disable the errant interface:
# nmcli connection down id "Wired connection 1"

and finally delete it altogether:
# nmcli connection delete id "Wired connection 1"

This way I have a quiet GUI again without the irritating spinning widget in the corner.

La voila!


Saturday, January 21, 2017

Free and Open Systems - Free and Open Minds

The power of marketing, advertising and glossy brochures subtly warp people's minds to wherever advertising Dollars go.  This affects Free and Open embedded operating system software as well.  

Users visit a software company web site that showcases a new software feature and they don't realize that the "new feature" is 30 years old and exists in numerous other systems also

Some people do not understand that there is a large corpus of almost identical operating systems known as UNIX-like or POSIX compatible operating systems and that they can use any one of them and port their software between them, without shedding too many tears.

Embedded OS selection criteria should therefore not concentrate on perceived feature differences, but rather on availability:  Copyrights, Patents, Licensing and Export Permits.

This is especially important in small countries which are subject to the political whims of the big five, who can make you wait indefinitely for an export permit and delay your projects.


The Portable Operating System Interface (POSIX) is a family of standards specified by the IEEE Computer Society for maintaining compatibility between operating systems. 

This corpus of more or less POSIX compatible embedded operating systems include some big names:
Linux, BSD, Darwin, VxWorks, PikeOS, LynxOS, Integrity-178 and several lesser known ones.  

Each of the better known ones are also divided into several lesser known distributions.  For example, there are two types of VxWorks, three types of Apple Mac OS and four or more types of BSD.

Shared Packages

These POSIX operating systems share an enormous library of software.  About 25 000 shared packages are listed in the FreeBSD repositories. 

The actual differences between these operating systems are very subtle and are mostly a matter of who you go to for support, not technicalities, since they all share the same 25 000 packages, while the differences are only the OS kernels.
If any distribution would develop a new security feature or performance improvement, then that idea will get copied/included by everybody else in due course.  

Some features that are now in the news, such as ‘partitioning’ was first developed by BSD, decades ago - called Jails.  

Ditto for hypervisors such as Qemu, KVM, VMware and others which have been around for decades.

All the abovementioned versions of UNIX can do partitioning and hypervisors.  Partitioning isn’t unique to VxWorks or PikeOS - they are just marketing it.  

The PikeOS microkernel isn’t unique either - Apple OSX and Darwin also use the Mach kernel.  It makes these systems a little slower, so microkernels are not popular in general purpose computing. 


Another issue is certification by various authorities, for example DO-178B.  
It is clear that if for example VxWorks, LynxOS, PikeOS or Integrity-178 is certified to a standard, then by extension, BSD also benefits of the software reviews, since they share the same 25 000 base packages.

Regulation-Free Software

When building embedded systems, it is important to avoid using parts and equipment that are encumbered by overbearing regulations such as ITAR. 

VxWorks and PikeOS are both encumbered by licenses and permits that are hard to obtain. 

However, all versions of Linux and BSD are free of encumbrances and are known as Free and Open software. 


Hardware Support

Of all the above UNIX-like operating systems, Linux currently has the widest hardware support and Apple OSX the most limited hardware support. 

Style, is the ability to distinguish quality, 
without having to look at the price tag.

Windriver VxWorks became a popular embedded OS when they added the BSD network stack to VRTX and declared that “VRTX now Works".  

WindRiver also has their own embedded Linux distribution since about 2008. 

Escape From Export Permit Alcatrez

Therefore, it is entirely possible to install an embedded version of BSD or Linux on a CPU card and port software over from an encumbered embedded OS such as VxWorks, LynxOS or Integrity-178, thus avoiding all the licensing and permit issues.

Similarly one could port software from an encumbered version of PikeOS over to Apple's Darwin, which also has a microkernel. 

In Search of Excellence

One day when another OS version has a new feature that is in fashion, then the software can be ported over again.  That is the whole purpose of the POSIX standard.  

The user isn’t captive to any one system or provider and wasting time on obtaining licenses and permits from an unco-operative country isn’t at all necessary.

. -.-. .-. .- ... . --..  .-.. .  .. -. ..-. .- -- .


Sunday, December 11, 2016

Electronic Signatures - Snake Oil

Electronic signatures are commonly used in the more advanced enterprises to sign documents - PDF files mostly.  Unfortunately, the implementation is broken and it doesn't work.  The broken implementation reduces it to snake oil.

A typical IT system is set up and managed by one or two overworked computer geeks who clicked through a setup wizard to configure a key server on the company LAN.  The public and private keys are distributed on the company workstations and laptop PCs by Active Directory and GPG and once it looks like it is working and some users can sign a document, the whole universe shakes, angels and birds sing, flowers fall down from heaven and all is well... or is it?

I wanted to be able to sign documents on my engineering laptop PC which runs Linux, not just my office PC, which runs Windows (and which usually has some problem or another).  So I asked IT for a copy of my Private key.  After a few months, they emailed me my Public key.  So clearly, the IT geeks don't understand the basics of public/private key systems, yet they are entrusted with managing it...

Consequently, I spent a few minutes looking into the setup and as far as I can figure, the Private key resides somewhere on my Windows PC, but I don't have administrator rights to it, so I cannot recover it and I don't want to have admin rights on a Windows PC, since then I would be responsible for everything that goes wrong with it.

Thinking about it a bit more, I realized that it is my key, so only I should have access to it, but on a Windows PC, the IT administrators can do anything, so the key is not mine only, it is theirs too.  They can take my supposedly private key and do with it what they want.

The only thing that prevents the corporate IT administrators from misusing my private key and impersonating me, is their incompetence.

Therefore, I can sign a document on my office PC and everyone will then think that it was me, but it could have been someone else, because IT has access to the private keys of everybody and Windows machines are not exactly known for their security.

Furthermore, since the PDF reader can only verify signatures when the LAN and Key Server work properly, it frequently happens that one opens a document and gets a warning that the signatures cannot be verified - so all users are used to ignoring that.  The result is that anyone can subvert the keys and sign anything with any made up key and no-one will notice, or care.  Also, since the company key server is private, anyone outside the company, cannot verify the signatures at all, which considering, is probably a good thing.



Saturday, November 5, 2016

Grajcar Slovak Folk Band Does Metallica

Well, evidently good artists can play anything on any instrument.  Here is the Slovak folk dancing band Grajcar, playing Metallica on three violins, a double base, clarinet and cymbal.


I recorded a minute or so of Nothing Else Matters, at Sheik Maktoum's Majles at the Emirates Golf Club in Dubai, during a Czech and Slovak party. (The horrid video quality is due to Google, not me!).

Here is more of them in what looks like Bratislava: 

Saturday, September 24, 2016

Rover2: Serial Motor Controller

My new rover is supposed to be simpler than the first one and something that irked me with the first design was the motor controllers.  They worked, but they are ridiculously complicated.  So I bought a Sparkfun Monster Moto Controller and hooked it up - much simpler.

The only hassles with it is that if you would plug another board on top of it, then it could short to the tops of the capacitors and the VIN connector could short to the Arduino ICS pins underneath it.  I stuck a rectangle of clear plastic cut from some screws packaging between the boards and snipped the ICS pins off - done.

Serial Control

Controlling a DC motor is straight forward, using two pins to switch the H bridge direction (INA1, INB1) and one for speed PWM (PWM1).  There is also a current sense input (CS1) that you can set to turn the motors off if they get stuck and the current increases too much.  You'll have to set the sense level with trial and terror.

Here is an example for a serial motor control interface, using simple two character messages:
  • ms - stop
  • mf - forward
  • mb - backward
  • mr - turn right
  • ml - turn left
  • mu - speed up
  • md - slow down
This is also a good example of how to parse a serial data protocol efficiently with a switch-case statement in C.  A switch statement is easy to read and understand by humans and very efficient on computers.  Each case compiles to a check and a conditional jump.  It doesn't slog through all the code from top to bottom.

// Monster Moto Board
// Herman Oosthuysen, Sep 2016

// Monster motor controller with simple two character ASCII serial interface
// Controls two motors to make a rover run forward reverse and turn
// Turns are executed by speeding up the motor on one side and slowing down (or reversing) on other side
// A command starts with M and ends with Enter (CR or LF)
// MS - Stop, short the motors to ground
// MF - Forward, best to slow down and stop before going backward
// MB - Backward, best to slow down and stop before going forward
// ML - Turn Left, cancel a left turn with a right turn
// MR - Turn Right, cancel a right turn with a left turn
// MU - Speed Up, only three speed steps, stop, slow and fast
// MD - Slow Down, only three speed steps, stop, slow and fast

// Literals
#define BAUD  9600
#define RATE  200
#define CR    0x0D
#define LF    0x0A
#define MAX   2
#define MIN   -2
#define INC   0x20
#define MAXCS 0x80

// Pins
// Motor 1
#define INA1  7
#define INB1  8
#define PWM1  5
#define EN1   A0
#define CS1   A2

// Motor 2
#define INA2  4
#define INB2  9
#define PWM2  6
#define EN2   A1
#define CS2   A3

// Global Variables
int cs1 = 0;  // 0 to FF
int cs2 = 0;  // 0 to FF
int spd1 = 0; // -2 to 2
int spd2 = 0; // -2 to 2
int pwm1 = 0; // 0 to 1023
int pwm2 = 0; // 0 to 1023
char ch = 0;  // ASCII character received
char adr = 0; // ASCII M start of Motor message
char cmd = 0; // ASCII command

void setup()
  Serial.println("Monster Moto, eh.");
  // Stop the motors
  pinMode(INA1, OUTPUT);
  digitalWrite(INA1, LOW);
  pinMode(INA2, OUTPUT);
  digitalWrite(INA2, LOW);
  pinMode(INB1, OUTPUT);
  digitalWrite(INB1, LOW);
  pinMode(INB2, OUTPUT);
  digitalWrite(INB2, LOW);

  // PWM zero speed
  analogWrite(PWM1, 0);
  analogWrite(PWM2, 0);

void loop()
    ch =;

    // Message starts with M and ends with Enter
    // eg: ms[enter]
    // Ensure that the serial terminal sends the line ends
    if((ch == CR) | (ch == LF))
      adr = 0;
      cmd = 0;
    else if((ch == 'M') | (ch == 'm'))
      adr = 1;
      cmd = 'M';
    else if(adr)
      cmd = ch;
      Serial.print("cmd = ");

          case 'S':
          case 's':
            spd1 = 0;
            spd2 = 0;
            pwm1 = 0;
            pwm2 = 0;
          case 'F':
          case 'f':
            spd1 = 1;
            spd2 = 1;
            pwm1 = spd1 * INC;
            pwm2 = pwm1;
          case 'B':
          case 'b':
            spd1 = -1;
            spd2 = -1;
            pwm1 = abs(spd1) * INC;
            pwm2 = pwm1;
          case 'L':
          case 'l':
            if(spd1 < MIN)
              spd1 = MIN;
            if(spd2 > MAX)
              spd2 = MAX;
            pwm1 = abs(spd1) * INC;   
            pwm2 = abs(spd2) * INC;  
          case 'R':
          case 'r':
            if(spd1 > MAX)
              spd1 = MAX;
            if(spd2 < MIN)
              spd2 = MIN;
            pwm1 = abs(spd1) * INC;
            pwm2 = abs(spd2) * INC;        
          case 'U':
          case 'u':
            if(spd1 > MAX)
              spd1 = MAX;
            if(spd2 > MAX)
              spd2 = MAX;
            pwm1 = abs(spd1) * INC;
            pwm2 = abs(spd2) * INC;       
          case 'D':
          case 'd':
            if(spd1 < MIN)
              spd1 = MIN;
            if(spd2 < MIN)
              spd2 = MIN;
            pwm1 = abs(spd1) * INC; 
            pwm2 = abs(spd2) * INC;       
            Serial.print("Err = ");
        Serial.print("spd1 = ");
        Serial.print("spd2 = ");
        Serial.print("pwm1 = ");
        Serial.print("pwm2 = ");

  // Periodic Motor Control Update
  direction(spd1, spd2);
  speed(pwm1, pwm2);

// Left and right hand motors rotate in opposite directions
void direction(int spd1, int spd2)
  if(spd1 >= 0)
    digitalWrite(INA1, HIGH);  // CW - Forward
    digitalWrite(INB1, LOW);
    digitalWrite(INA1, LOW);  // CCW - Reverse
    digitalWrite(INB1, HIGH);         
  if(spd2 >= 0)
    digitalWrite(INA2, LOW);  // CCW - Forward
    digitalWrite(INB2, HIGH);
    digitalWrite(INA2, HIGH);  // CW - Reverse
    digitalWrite(INB2, LOW);         

void speed(int pwm1, int pwm2)
  analogWrite(PWM1, pwm1);
  analogWrite(PWM2, pwm2);

void sense(void)
  cs1 = analogRead(CS1);
  cs2 = analogRead(CS2);

  if((cs1 > MAXCS) | (cs2 > MAXCS))
    spd1 = 0;
    spd2 = 0;

PWM motor control is not linear.  The motors need a minimum amount of power to start turning and after that, the speed increases rapidly until a maximum is reached.  So the performance curve is S shaped and some experiments are required with your motors if you want to have meaningful speed steps for crawl, walk and run for example.

Seriously High Power

MOSFETs can be paralleled, so if you need to control a very big motor, then you can wire the two channels together, so this is a really nice controller board that could control a winch, a scooter or a wheel chair for example.  So it would be good if you need to build something to help a disabled friend - Sparkfun to the rescue!

A high powered motor controller should be close to the motor because of the high currents in the wires.  So in a big system, you may need one Arduino per motor controller.  If you use the same serial interface protocol on multiple Arduino powered actuators, then you can multi-drop the serial line from the main control computer to the various actuator computers.  For this example, the messages start with M and for something else, it could start with S or whatever else you like.

For a simple toy, error checks are a waste of time, so I just send a message and hope it works.  I don't bother with CRCs and Retries on toys, but if you want to control a winch or a wheel chair, then you should be more careful!

BTW, if you need to build something serious operating at 12V, then I recommend that you get Anti-Gravity batteries.   These are light weight and will ensure that one can still manhandle the thing - lead-acid batteries make it impossible to lift a wheel-chair into a car.

La Voila!