Embedded Linux system for Raspberry Pi with Yocto Project


Yocto Project, and OpenEmbedded, have been making news lately as the toolchain to build custom embedded Linux systems. I decided to try and get a working Linux system up and running on a Raspberry Pi.

The procedure that follows uses the meta-raspberrypi BSP by David-John Willis, in tandem with Yocto Project poky.

Initialize the build by executing the following commands. I use an Ubuntu 14.04 VM with Parallels.

git clone http://git.yoctoproject.org/git/poky
git clone http://git.yoctoproject.org/git/meta-raspberrypi
git clone https://github.com/dv1/meta-gstreamer1.0.git
source poky/oe-init-build-env rpi-build
cd rpi-build

Append meta-raspberrypi and meta-gstreamer1.0 layers to BBLAYERS in conf/bblayers.conf. For example

BBLAYERS ?= " \
  /home/parallels/yocto/poky/meta \
  /home/parallels/yocto/poky/meta-yocto \
  /home/parallels/yocto/poky/meta-yocto-bsp \
  /home/parallels/yocto/poky/meta-gstreamer1.0 \
  /home/parallels/yocto/poky/meta-raspberrypi \
  "

Set MACHINE to raspberrypi in conf/local.conf. Complete the build by executing

bitbake rpi-hwup-image

That takes a while. After the build is complete, create an SD card with the following command. Remember to change /dev/sdb to the proper device name.

dd if=tmp/deploy/images/raspberrypi/rpi-hwup-image-raspberrypi.rpi-sdimg of=/dev/sdb

Boot Pi using the SD card. Command line output appears on the HDMI display, or the serial port on the expansion header. Login is root, with an empty password.

Posted in Raspberry Pi

Linux NAT router


This post documents the kernel modules and other packages required to build an embedded Linux NAT router using buildroot, for Raspberry Pi.

Modify Linux kernel configuration

Invoke make linux-menuconfig in buildroot folder to initiate kernel configuration utility. Select modules shown in the figures below.

Networking Options

Network packet filtering framework

Ensure that the following is enabled under Networking Options

  • Network packet filtering framework (Netfilter)

Core Netfilter Configuration

Ensure that the following are enabled under Core Netfilter Configuration

  • Netfilter connection tracking support
  • “conntrack” connection tracking match support
  • “state” match support

IP: Netfilter Configuration

Ensure following is enabled under Network packet filtering framework (Netfilter)

  • Advanced netfilter configuration

IPv4 packet filtering and NAT

Ensure the following modules are selected under IP: Netfilter Configuration

  • IPv4 connection tracking support (required for NAT)
  • IP tables support (required for filtering/masq/NAT)
  • Packet filtering
  • IPv4 NAT
    • MASQUERADE target support
    • NETMAP target support
    • REDIRECT target support

Enable other targets if you want to do sophisticated filtering.

Modify Buildroot Configuration

Include iptables utility package shown in the figure below, using the configuration utility invoked by executing make menuconfig. Include tcpdump if you want to sniff network data.

Buildroot iptables

Now, just execute make to build the system, and copy the kernel image and root file system to SD card.

Perform NAT routing

I use the following commands to bring up the network interfaces and setup NAT forwarding. Any packets received on interface eth0 are forwarded to usb0. Only packets for connections already established are forwarded back to eth0. usb0 is a USB CDC ethernet interface of the kind seen in modems.

ifconfig usb0 up
ifconfig eth0 up
dhcpcd
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -o usb0 -j MASQUERADE
iptables -A FORWARD -i usb0 -o eth0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -i eth0 -o usb0 -j ACCEPT

If you get any of the following error messages with iptables, you are probably missing one of the kernel modules mentioned above

  • iptables v1.4.21: can’t initialize iptables table `nat': Table does not exist (do you need to insmod?)
    Perhaps iptables or your kernel needs to be upgraded.
  • iptables v1.4.21: can’t initialize iptables table `filter': Table does not exist (do you need to insmod?)
    Perhaps iptables or your kernel needs to be upgraded.
  • iptables: No chain/target/match by that name.

If the NAT router is not the default gateway on downstream hosts, relaying packets to upstream networks may require you to setup routes. This can be done using commands such as

# On Mac OS X
sudo route -n add -net 10.0.0.0/8 192.168.0.101
# On Linux
route add -net 10.0.0.0/8 gw 192.168.10.1

Static route configuration may also be required in any downstream NAT routers.

Finally, a couple of useful commands to troubleshoot IP routing and forwarding

# List all rules in iptables
iptables -L -v
# A basic packet sniffer
tcpdump -A -i usb0
Posted in Linux, Raspberry Pi

Linux Network Connection Bridging


This time I add network connection bridging to my custom embedded Linux system for Raspberry Pi. That should allow me to experiment with bridging network connections for internet sharing, software testing, and so on.

Modify Linux kernel configuration

Execute the following in the buildroot folder to get Linux Kernel configuration menu

make linux-menuconfig

Select the 802.1d Ethernet Bridging module shown in the following screenshot

Ethernet Bridging

Modify Buildroot configuration

Execute the following in the buildroot folder to get the configuration menu

make menuconfig

Select the bridge-utils package, shown in the screenshot below. This package contains the brctl utility required to configure bridging.

bridge-utils

Then, just execute make to build the system. Once that is done, copy over the new kernel image and root file system over to the SD card.

Perform bridging

These are the sequence of commands I typically use to bring up the bridge manually. I use a regular ethernet interface and a USB CDC ethernet interface for testing.

ifconfig eth0 0.0.0.0 promisc up
ifconfig usb0 0.0.0.0 promisc up
brctl addbr br0
brctl addif br0 eth0 usb0
ifconfig br0 up

If you need the bridge interface to have an IP address, you can assign one manually, or by invoking the DHCP client daemon as shown below. This is useful if you need to have access to your Pi over the network.

dhcpcd br0

To wrap it up, here’s how you can tear everything down.

ifconfig eth0 down
ifconfig usb0 down
ifconfig br0 down
brctl delbr br0
Posted in Linux, Raspberry Pi

Handling variable number of arguments in Lua


Here’s a quick example of how variable number of arguments can be handled in Lua. It has been tested with Lua 5.1 and 5.2.

function SomeFunction(...)
    local list = {...}
    print(string.format("Received %d arguments:", #list))

    -- print all arguments
    print(...)

    -- print all arguments again using a for loop
    for i = 1, #list, 1 do
        print(list[i])
    end
end

SomeFunction("hello", "world")
Posted in Lua

Upgrade to Ubuntu 14.04 on Parallels Desktop 9


Ubuntu 13.10 is no longer officially supported so decided to bite the bullet and go ahead with the upgrade to Ubuntu 14.04, on Parallels Desktop 9. I’m running Parallels on an early 2013 MackBook Pro with Retina Display. The MAC OS X version is 10.10 (Yosemite) Public Beta. I installed the public beta after encountering problems running Parallels with Developer Preview beta 6.

Originally, I started with an Ubuntu 13.04 VM that Parallels officially supports, and at some point upgraded to 13.10. Ubuntu’s Unity interface does not work at all after that upgrade. I don’t get the taskbar or the menu bar at the top. I gave up on Unity, and followed instructions from Parallels to install and enable GNOME Flashback. Desktop works all right after that, even after upgrade to 14.04.

Posted in Miscellaneous

Integer division and timer resolution


I have this very specific need to send a list of pre-timed messages from an embedded system. Each message has a specific time when it needs to be sent out. The time is specified in milliseconds (ms) with respect to the previous message in the list. The first message being at time zero.

The algorithm to send messages out is very simple. I send out the first message, and arm a timer with the time of the second message. As soon as the timer expires, I send out the second message, and rearm the timer to send the third message, and so on. Thus, it is pretty clear that timer resolution is very important. Since the smallest integer time interval I require is 1 ms, a timer resolution of 1 ms or less would be ideal.

The embedded system I am dealing with, has a timer resolution of 2.5 ms i.e. 1 tick of the timer is 2.5 ms long. The timer routine does not accept a time of 0, 1 tick is the smallest integer value the routine expects. To convert time in milliseconds to ticks, I need a routine that can convert milliseconds to ticks. The conversion routine, putting it simply, would receive time in milliseconds, divide it by 2.5, and round the result to an integer value. I don’t have access to floating point math though, so the routine I’ve developed looks like this

typedef unsigned int uint32_t;
const unsigned short MULTIPLY_BY = 2;
const unsigned short DIVIDE_BY = 5; // never ever set to zero

uint32_t MillisToTicks(uint32_t timeInMillis, int *remainder)
{
    uint32_t timesTwo;
    uint32_t result;

    timesTwo = timeInMillis * MULTIPLY_BY;
    result =  timesTwo / DIVIDE_BY;
    *remainder += timesTwo % DIVIDE_BY;
    if (*remainder >= DIVIDE_BY)
    {
        result += 1;
        *remainder -= DIVIDE_BY;
    }
    else if(*remainder <= -1 * DIVIDE_BY && result > 1)
    {
        result -= 1;
        *remainder += DIVIDE_BY;
    }
    else if (result == 0)
    {
        result = 1;
        *remainder -= DIVIDE_BY - result;
    }
    return result;
}

MillisToTicks never returns zero. Dividing by 2.5 is the same as multiplying by 2 and dividing by 5. 2 is read from a constant called MULTIPLY_BY and 5 from another constant called DIVIDE_BY. Since the integer division will produce a remainder, MillisToTicks requires a pointer to integer where it can store the remainder of the integer division. That remainder is updated and checked during each call. If it exceeds DIVIDE_BY, the result is incremented by one, and remainder decremented by DIVIDE_BY.

If the result of integer division is zero, I force it to a value of 1, and decrement remainder by DIVIDE_BY. Next time MillisToTicks gets called, if remainder is less than or equal to -DIVIDE_BY, I decrement result by 1, and increment remainder by DIVIDE_BY. Putting it simply, there will be moments when I’ll stray away from timing the messages perfectly, but given enough messages, I’ll stray back on track. To test that, I have implemented the following code

int remainderMillisToTicks = 0;

uint32_t times1[] = {6, 6, 6, 2, 6, 6, 2, 2, 6, 6, 7, 8, 1, 1, 2, 3, 6, 20, 30, 9, 30, 100, 3000, 1, 1, 8000, 10000, 23, 1, 1, 19, 6, 5, 26, 201, 503, 901};

uint32_t times2[] = {6, 6, 6, 2, 6, 6, 2, 2, 6, 6, 7, 8, 503, 901, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 6, 20, 30, 9, 30, 100, 3000, 1, 1, 8000, 10000, 23, 1, 1, 19, 6, 5, 26, 201, 503, 901};

uint32_t times3[] = {6, 6, 6, 2, 6, 6, 2, 2, 6, 6, 7, 8, 1, 1, 2, 3, 6, 20, 30, 9, 30, 100, 3000, 8000, 10000, 23, 19, 6, 5, 26, 201, 503, 901, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};

void test(uint32_t *times, unsigned short size)
{
    int i;
    uint32_t totalMillis = 0;
    uint32_t ticks = 0;
    double ticksToMillis = 0;
    double totalTicksToMillis = 0;

    for (i = 0; i < size; i++)
    {
        totalMillis += times[i];
        ticks = MillisToTicks(times[i], &remainderMillisToTicks);
        ticksToMillis = ((double)ticks*DIVIDE_BY) / MULTIPLY_BY;
        totalTicksToMillis += ticksToMillis;
        //printf("%d\t%f\t%d\t%d\t%f\n", times[i], ticksToMillis, remainderMillisToTicks, totalMillis, totalTicksToMillis);
    }
    printf("Ideal total time required: %d, time achieved: %f\n", totalMillis, totalTicksToMillis);
}

int main()
{
    test(times1, sizeof(times1) / sizeof(uint32_t));
    remainderMillisToTicks = 0;
    test(times2, sizeof(times2) / sizeof(uint32_t));
    remainderMillisToTicks = 0;
    test(times3, sizeof(times3) / sizeof(uint32_t));
}

Here’s how the output of that test code looks like

Ideal total time required: 22953, time achieved: 22952.500000
Ideal total time required: 24363, time achieved: 24362.500000
Ideal total time required: 22965, time achieved: 22985.000000

Enable the commented printf statement in test and you’ll see a call by call log. At times, when the time value requested is too small, you’ll see the messages stray away from perfect timing, and stray back to a normal cadence later. The overall time is thus unaffected or affected only slightly. The last call to test represents a failure condition that can only be avoided by using a higher resolution timer.

Posted in C and C++, Software Design

Adding Node.js to Buildroot


I successfully built an embedded Linux system for Raspberry Pi using Buildroot recently, and decided it was time to add some useful packages to it. The first that came to mind is Node.js. Having that on the Linux system will allow me to serve an HTML5 UI to any computer with a browser, on the same network.

Invoke

make menuconfig 

in buildroot folder to obtain the configuration UI. Node.js requires IPv6 support so I enabled that in the Toolchain menu thus

buildroot_ipv6

Then I enabled Node.js and npm from the Target packages menu thus

buildroot_nodejs

buildroot_npm

I followed that up by issuing a full build thus

make clean
make

Buildroot stores all downloads in the dl folder, so it will only download whatever isn’t already there. It will build the host cross-compilation toolchain from scratch though, since that is available under output, and make clean removes everything there.

I’ve now got Node.js 0.10.12 working successfully on the Linux system.

Posted in Linux, Raspberry Pi
Follow

Get every new post delivered to your Inbox.

Join 64 other followers

%d bloggers like this: