Dual-booting Arch Linux on a Macbook with encryption

This was a tricky one to work out, but here’s how I got there with my MacbookPro 11,1. This may not be the best way, but it’s the only one I could get working.

Firstly, use Disk Utility on OS/X to reduce it’s partition size, allowing enough free space to install Arch. Download the Archboot ISO, put it on a USB key and turn the Macbook on while holding down Alt to boot from it.

Use the Archboot GUI to prepare the storage disk. Don’t let the script do it automatically; use the partitioning program to add the following:

  • EFI partition (128M, type af00)
  • Boot partition (128M, type 8300)
  • System partition (rest of space, type 8e00)

Exit the Archboot GUI, then follow these instructions up to the mkinitcpio part. This will create a LUKS container in the system partition and install LVM partitions. Once that’s done, reboot back into the Archboot GUI.

Set up your key mapping, date/time and network. In the storage disk preparation menu, mount the boot and encrypted system partitions. Use Archboot to select and install all of the base, dev and support packages and configure the system. Exit the GUI before you install the bootloader.

Use this command to log into your new system:

arch-chroot /install /bin/bash

Configure mkinitcpio by adding ‘encrypt’ and ‘lvm2’ to the HOOKS property in /etc/mkinitcpio.conf, and run the following commands:

mkinitcpio -p linux-lts
mkinitcpio -p linux

Edit /etc/default/grub and update the following lines:

GRUB_CMDLINE_LINUX_DEFAULT="quiet rootflags=data=writeback libata.force=noncq"
GRUB_CMDLINE_LINUX="cryptdevice=/dev/sdaX:MyStorage root=/dev/mapper/MyStorage-rootvol"

To generate an EFI bootloader, use the following commands (taken from here)

grub-mkconfig -o /boot/grub/grub.cfg
grub-mkstandalone -o boot.efi -d /usr/lib/grub/x86_64-efi -O x86_64-efi /boot/grub/grub.cfg

Insert a USB key, mount it and copy the boot.efi file.

mount /dev/sdX1 /mnt
cp boot.efi /mnt

In OS/X use Disk Utility to format (‘erase’) the 128MB EFI partition, move the boot.efi file into System/Library/CoreServices/ and create a file in the same directory called SystemVersion.plist, with the following contents:

<?xml version="1.0" encoding="utf-8"?>
  <plist version="1.0">
  <dict>
    <key>ProductBuildVersion</key>
    <string></string>
    <key>ProductName</key>
    <string>Linux</string>
    <key>ProductVersion</key>
    <string>Arch Linux</string>
  </dict>
</plist>

If you’re lucky, you should now be able to boot into Arch by holding down the Alt key while booting. If not, here are some resource which should help you out. Good hunting!

Advertisements

Beer monitoring with my Raspberry Pi

The secret to brewing great beer at home is making sure that you keep it at the right temperature. This can be tricky when your house doesn’t have a thermostat and you’re not in the house for most of the day. However, by using a cheap and cheerful sensor with a raspberry pi, you can record a log of the temperature and check it over the internet to make sure your beer is brewing nicely.

Hardware

The sensor I used is the DHT11 which, at the time of writing, you can order on eBay for £1.12 delivered. It has a digital interface, so you don’t need to do any calibration or digital conversion as you would with a thermistor. To connect the sensor to the RPi, all you need is a 10k resistor to pull-up the data signal and to make the following connections (see pic).

RPi VCC (pin 1) -> DHT11 pin 1
RPi GPIO4 (pin 7) -> DHT11 pin 2
RPi GND (pin 6) -> DHT11 pin 4

DHT11 wiring diagram

Interfacing

The DHT11 uses its own serial interface, which can be interrogated using the wiringPi C library. To install and compile the library, use the following commands:

sudo apt-get install git-core build-essential
git clone git://git.drogon.net/wiringPi
cd wiringPi
./build

This blog post details the code necessary to read the sensor data. The code below has been modified to return only the current temperature and to repeat the request if there is an error. Place the following into a file named dht11.c.

#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define MAX_TIME 85
#define DHT11PIN 7
#define ATTEMPTS 5
int dht11_val[5]={0,0,0,0,0};

int dht11_read_val()
{
  uint8_t lststate=HIGH;
  uint8_t counter=0;
  uint8_t j=0,i;
  for(i=0;i<5;i++)
     dht11_val[i]=0;
  pinMode(DHT11PIN,OUTPUT);
  digitalWrite(DHT11PIN,LOW);
  delay(18);
  digitalWrite(DHT11PIN,HIGH);
  delayMicroseconds(40);
  pinMode(DHT11PIN,INPUT);
  for(i=0;i<MAX_TIME;i++)
  {
    counter=0;
    while(digitalRead(DHT11PIN)==lststate){
      counter++;
      delayMicroseconds(1);
      if(counter==255)
        break;
    }
    lststate=digitalRead(DHT11PIN);
    if(counter==255)
       break;
    // top 3 transistions are ignored
    if((i>=4)&&(i%2==0)){
      dht11_val[j/8]<<=1;
      if(counter>16)
        dht11_val[j/8]|=1;
      j++;
    }
  }
  // verify checksum and print the verified data
  if((j>=40)&&(dht11_val[4]==((dht11_val[0]+dht11_val[1]+dht11_val[2]+dht11_val[3])& 0xFF)))
  {
    printf("%d.%d,%d.%d\n",dht11_val[0],dht11_val[1],dht11_val[2],dht11_val[3]);
    return 1;
  }
  else
    return 0;
}

int main(void)
{
  int attempts=ATTEMPTS;
  if(wiringPiSetup()==-1)
    exit(1);
  while(attempts)
  {
    int success = dht11_read_val();
    if (success) {
      break;
    }
    attempts--;
    delay(500);
  }
  return 0;
}

Compile and execute the source code with the following commands:

gcc -o dht11 dht11.c -L/usr/local/lib -lwiringPi -lpthread
sudo ./dht11

The program should return two numbers – one for relative humidity and the other for temperature.

Logging

The simplest method of creating a log of the temp/humidity is to use a cronjob. The program must be run as root, so use the following command to edit the cron config file:

sudo crontab -e

Add the following line to the end. It will save a timestamp and the temp/humidity every minute to temp.log.

* * * * * echo `date +\%Y\%m\%d\%H\%M\%S`,`/home/pi/wiringPi/dht11` >> /home/pi/temp.log

If that doesn’t seem to be working, the system cron may not be running. The following command will start it, and the line after will make sure it starts every time the RPi turns on:

sudo service cron start
sudo update-rc.d cron defaults

Display

Reading a log file isn’t the easiest way to check how the temp/humidity is changing, but we can use a graphing library to read the log file and plot it. For this, I used DyGraph which is a Javascript library for plotting time-based information. Here is the final .html file I used:

<html>
<head>
<script type="text/javascript" src="dygraph-combined.js"></script>
</head>
<body>
<div id="graphdiv" style="width:750px; height:400px;"></div>
<script type="text/javascript">

  function addZero(num)
  {
    var s=num+"";
    if (s.length < 2) s="0"+s;
    return s;
  }

  function dateFormat(indate)
  {
    var hh = addZero(indate.getHours());
    var MM = addZero(indate.getMinutes());
    //var ss = addZero(indate.getSeconds());
    var dd = addZero(indate.getDate());
    var mm = addZero(indate.getMonth()+1);
    var yyyy = addZero(indate.getFullYear());
    return dd+'/'+mm+' '+hh+':'+MM;
  }

  g = new Dygraph(
    document.getElementById("graphdiv"),
    "temp.log",
    {
      xValueParser: function(x) {
        var date = new Date(x.replace(
          /^(\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/,
          '$4:$5:$6 $2/$3/$1'
        ));
        return date.getTime();
      },
      axes: {
        x: {
          ticker: Dygraph.dateTicker,
          axisLabelFormatter: function(x) {
            return dateFormat(new Date(x));
          },
          valueFormatter: function(x) {
            return dateFormat(new Date(x));
          }
        }
      },
      labelsDivWidth: 310,
      rollPeriod: 30,
      strokeWidth: 2.0,
      labels: ['Date','Humidity (%)','Temp (&deg;C)']
    }
  );
</script>
</body>
</html>

You can set this up on the RPi by installing a web server like Apache, creating a symlink to the log file, downloading dygraph and saving the above html file as /var/www/temp.html.

sudo apt-get install apache2
ln -s /home/pi/temp.log /var/www/temp.log
wget -P /var/www http://dygraphs.com/dygraph-combined.js

Then use your browser to navigate to 192.168.1.2/temp.html, replacing the IP address with that of your RPi. To be able to access the page from the internet, you’ll have to set up NAT on your router.

Here’s what my final system looks like (click for an interactive demo):
Temp/humidity demo

Using aubio and alsaaudio with Python

The code used in this post no longer works. For an up-to-date example, please see demo_alsa.py.

Aubio is an audio analysis library which contains implementations of some useful algorithms, including pitch detection. It can be used with Python (through SWIG), but the documentation is very light and there doesn’t appear to be any Python-specific instructions.

Below is a small program that listens to the default audio input using alsaaudio, finds the pitch and energy of the signal using aubio, and prints the results to stdout. For the program to work, you will need the aubio and alsaaudio Python libraries installed, which can be done in Ubuntu/Debian with the following command:

sudo apt-get install python python-alsaaudio python-aubio

The smpl_t data type referred to in the code can be replaced by Python’s float type, but the fvec_t type must be populated one-by-one using the fvec_write_sample function.

import alsaaudio, struct
from aubio.task import *

# constants
CHANNELS	= 1
INFORMAT	= alsaaudio.PCM_FORMAT_FLOAT_LE
RATE		= 44100
FRAMESIZE	= 1024
PITCHALG	= aubio_pitch_yin
PITCHOUT	= aubio_pitchm_freq

# set up audio input
recorder=alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
recorder.setchannels(CHANNELS)
recorder.setrate(RATE)
recorder.setformat(INFORMAT)
recorder.setperiodsize(FRAMESIZE)

# set up pitch detect
detect = new_aubio_pitchdetection(FRAMESIZE,FRAMESIZE/2,CHANNELS,
                                  RATE,PITCHALG,PITCHOUT)
buf = new_fvec(FRAMESIZE,CHANNELS)

# main loop
runflag = 1
while runflag:

  # read data from audio input
  [length, data]=recorder.read()

  # convert to an array of floats
  floats = struct.unpack('f'*FRAMESIZE,data)

  # copy floats into structure
  for i in range(len(floats)):
    fvec_write_sample(buf, floats[i], 0, i)

  # find pitch of audio frame
  freq = aubio_pitchdetection(detect,buf)

  # find energy of audio frame
  energy = vec_local_energy(buf)

  print "{:10.4f} {:10.4f}".format(freq,energy)