Friday, November 5, 2021

Eduroam on Raspberry Pi

I bought a Raspberry Pi Zero 2 W last week and worked out how to do something I've been wanting to do for a while - get a Raspberry Pi to connect to Eduroam. These instructions will be specific to the University of Leicester but hopefully helpful to others.

1. Eduroam

On the Pi, either go to cat.eduroam.org and then go through the process to find the appropriate Eduroam installer, or download the Linux script from wireless.le.ac.uk/setup/linux.  The link says it's to download the certificate, but that isn't what's downloaded - what you get is a Python script.

2. Generating the certificate and wpa-supplicant file

Run the script (chmod +x first) and it asks for your username and password.  You end up with a directory .cat_installer in your home directory, and this contains ca.pem and cat_installer.conf. The latter file contains your password in plain text, which is not a good thing.

3. Hash your password

echo -n your-actual-password | iconv -t utf16le | openssl md4 > pw.txt

4. Edit wpa_supplicant.conf

Edit /etc/wpa_supplicant/wpa_supplicant.conf and add the contents of ~/.cat_installer/cat_installer.conf.  Replace the line 
password="your-actual-password
with the line
password=hash:1234567
Where 1234567 is the contents of the pw.txt file you created in step 3 (which will be a considerably longer hex number).  No quotes.  Now delete that file in ~/.cat_installer which has your plaintext password in it!
I moved the ca.pem file from step 2 to /etc/ssl/certs/leicester.pem, and edited the ca_cert line to reflect the new location and name of the certificate.  I also made some other changes based on this site, and a bit of experimentation to see what worked.  See below for the final version of the file.

5. Reboot

I now found that I was automatically connected to Eduroam, but DNS lookup wasn't working so I couldn't see websites.  I checked the Eduroam settings on my phone, found the IP addresses of the DHCP servers that was using, and entered them on the Pi through the network settings (click on the wifi symbol, change the settings for the Eduroam SSID).  The result was an /etc/resolv.conf file that looked like this:
# Generated by resolvconf
domain le.ac.uk
nameserver 143.210.12.158
nameserver 143.210.12.159

I also had to set priorities for the two networks in wpa_supplicant.conf, because the Pi was sometimes connecting to the Cloud (free wifi) rather than Eduroam.  I use the Cloud sometimes, but it's no good in headless mode because you have to finish the log in process using a browser (and lynx has stopped working for that).  The final file looks like this, and the Pi is now reliably connecting to Eduroam without any further intervention (I often use a Pi Zero in serial gadget mode so a web browser is out of the question).

ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
country=GB

network={
        ssid="_The Cloud"
        key_mgmt=NONE
        priority=10
}

network={
        ssid="eduroam"
        key_mgmt=WPA-EAP
        pairwise=CCMP
        eap=PEAP
        ca_cert="/etc/ssl/certs/leicester.pem"
        identity="nja@leicester.ac.uk"
        anonymous_identity="anonymous@le.ac.uk"
        password=hash:9911066b9816dc8dd0e82209ecc138a4
        altsubject_match="DNS:radius.le.ac.uk;DNS:radius.le.ac.uk"
        phase2="auth=MSCHAPV2"
        priority=20
}

Wednesday, May 1, 2019

A second image client

This one is quite different from the previous client. It sends the width and height of the image to the server, and reconstitutes the pickled Image object from the byte stream the server sends back. It then uses pygame to display the image in a window, and does that as fast as it can. The width and height default to 300x300, but can be altered from the command line.


A second image server

Variation on the first.  This server expects the client to send the dimensions of the image to be returned - if the aspect ratio is different to the camera's aspect ratio, the camera image is cropped (centrally) to the correct aspect ratio, resized using thumbnail(), and sent.  Also, rather than sending a byte stream of the image data, I'm using pickle.dumps() to create a byte stream from an Image object, and sending that (since I'm already creating an Image object for the cropping and resizing).


Wednesday, April 17, 2019

A terrible security camera 3 - the client software

Notes:
  • Again, I'm using argparse.  The IP address now has to be the server's address, not the client's!
  • The client receives the image data, puts it into a PIL Image object, and then crops it to a square and resizes it to the dimensions of the Unicorn Hat (16 x 16).
  • If the --debug flag was used, the original and cropped/resized image are saved to files.
  • The client keeps sending requests to the server as fast as it can (which is not very fast given the other work it's doing).
If I run the client with the command below, the current webcam image is displayed on the Unicorn Hat (updating about twice a second) until I press CTRL-C.

pi@pibow:~/Python/camclient $ ./image-client.py -i 192.168.1.106 

 

 

The code


A terrible security camera 2 - the server software

A few notes on the server software:
  • I'm using the argparse module to allow me to change key information (IP address, port, debug information) from the command line.  The default is to use port 12345 on localhost.
  • The server simply takes a photo and sends it to the client.
  • The server runs forever until the process is stopped (ideally with CTRL-C).
An example of running the server: picam's IP address is 192.168.1.106 (I have set it up to have a static address on my home wifi). To run the server:

pi@picam:~/Python/camserver $ ./image-server.py -i 192.168.1.106 --debug
Server started on 192.168.1.106:12345


Because I've used the --debug flag, the server tells me it has started and the address and port number it's using.

 

The code


A terrible security camera 1 - the hardware

Raspberry Pi 3B in a Pibow case, with a Unicorn Hat HD attached and a diffuser on top of the case (machine name "pibow"). The Unicorn Hat HD comes with a dark diffuser, I prefer the look of the lighter diffuser which covers the whole top of the case.
Raspberry Pi Zero W in the official Pi case with a camera attached (machine name "picam").  The official case comes with a cable to attach the camera to the smaller connection on the Zero.

Raspberry Pi setup

A few notes on how I set up Raspberry Pi devices.

Static IP

My home router won't allocate addresses above 100 (I have set it up not to do so), so it is safe to use static IP addresses above that.  I keep a master list of allocated static addresses in /etc/hosts on my desktop (Linux) PC to avoid conflict with other addresses (doesn't do anything to avoid conflicts apart from having a master list in one place!). For example, a new Pi Zero W which I have called "pish" (it originally had a Pimoroni LED shim attached).

192.168.1.108 pish
 
Log in to pish and edit /etc/dhcpcd.conf to add the lines below, and reboot.


interface wlan0
static ip_address=192.168.1.108/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
 
Assuming ssh is enabled on pish, on the desktop machine to allow login without password:

ssh-copy-id pi@pish

Shell

Add this line to .bashrc
 
export PATH=~/bin:$PATH
Create a ~/bin folder, and add this script to it in a file called update - allows quick system updates just by typing update at the command prompt.
#!/usr/bin/env bash

sudo apt update
sudo apt dist-upgrade
sudo apt autoremove
Create .inputrc and add this line to it (allows tab completion of file names ignoring upper and lower case variants).
set completion-ignore-case on

Editing

gvim (my preferred editor) gives a warning unless these packages are installed:

sudo apt install libcanberra-gtk*-module

Wednesday, November 8, 2017

Ubuntu 17.10

After a brief hiatus...

I've been using Ubuntu Mate for a while because of its simplicity, but recently bought a reasonably high-end PC for home use (i7 processor, huge hard disk, smaller SSD which makes booting and running a Windows virtual machine blisteringly fast) and tried out standard Ubuntu 17.10 at the weekend.  Ubuntu is now using Gnome 3 as the standard desktop, which is what made me look again.  It was so successful (with a bit of tweaking) that I'm now installing it on my work Linux PC (which these days is my spare machine, used for odd occasions when I need to try out something the university's locked-down Windows installation won't let me do).


  1. Install Ubuntu 17.10.  Takes about half an hour.
  2. Replace the hideous purple and orange wallpaper.
  3. sudo apt-get update; sudo apt-get dist-upgrade
  4. sudo apt-get install gnome-tweak-tool
  5. Using the tweak tool, remove desktop icons (Icons / Show Icons)
  6. sudo apt-get install chromium-browser chromium-codecs-ffmpeg-extra chrome-gnome-shell
  7. Start Chromium, go to extensions.gnome.org.  Install the browser extension so you can install shell extensions.
  8. Install Dash to Dock, and Dynamic Panel Transparency.
  9. Back to the tweak tool, and in Extensions change the DtD settings to display on the bottom, panel mode, and change the autohide settings so "push to show" is disabled.  In the Launchers tab move the applications button to the beginning of the dock.  Change the DPT settings (in the Background tab) to have custom opacity with an unmaximized opacity of 100%.  The latter is because I really don't like the effect where the top bar is semi-transparent if no windows are touching it, or a solid colour if a window is touching it.  Doesn't take effect until you log out and then log on again.
  10. Add the Numix theme and icons:
    sudo add-apt-repository ppa:numix/ppa
    sudo apt-get update
    sudo apt-get install numix-gtk-theme numix-icon-theme-circle
  11. Back to the tweak tool again, select the Numix theme and numix circle icons. 
Addendum:
Icons from the dash/dock were appearing in the lockscreen and activities view - the solution was
apt-get purge gnome-shell-
extension-ubuntu-dock
which removes an alternative dock.

Tuesday, July 30, 2013

Twitter, again


Using the Arduino ethernet shield on the Mega 2560 Arduino, this displays tweets.  But not directly - Twitter's new API requires SSL, and the Arduino isn't up to doing that.  So I set up a CGI program on my desktop computer which checks a twitter feed, and returns a plain text version of the username and message.  Code for the CGI program:

#!/usr/bin/python

import twitter, sys, time, pickle

class Timeline:
    def __init__ (self):
        self.seen = set()
        self.cache = []

    def add_to_cache (self, statuses):
        for d in [s.AsDict() for s in statuses]:
            if d["id"] not in self.seen:
                self.seen.add(d["id"])
                self.cache.append(d)

    def __iter__ (self):
        return self

    def next (self):
        if len(self.cache) == 0:
            raise StopIteration
        else:
            return self.cache.pop()
 
oauth_file = open("access_token.txt", "r")
akey = oauth_file.readline().rstrip()
asec = oauth_file.readline().rstrip()

consumer_file = open("consumer_keys.txt", "r")
ckey = consumer_file.readline().rstrip()
csec = consumer_file.readline().rstrip()

try:
    api = twitter.Api(consumer_key = ckey, consumer_secret = csec,
        access_token_key = akey, access_token_secret = asec)
except Exception as e:
    print e
else:
    try:
        timeline = pickle.load(open("timeline.dat"))
    except:
        timeline = Timeline()

    timeline.add_to_cache(api.GetUserTimeline("LE17RH"))

    print "Content-type: text/plain\n"
    try:
        x = timeline.next()
        print x["user"]["screen_name"]
        print x["text"]
    except StopIteration:
        pass

    pickle.dump(timeline, open("timeline.dat", "w"))

The Timeline class is an iterator because in an earlier incarnation I was loading a lot of tweets and wanting to iterate over them, it isn't necessary for this program which just returns a plain text rendition of one username and message.

Code for the Arduino:

#include <SPI.h>
#include <Ethernet.h>
#include <LiquidCrystal.h>

byte mac[] = { 0x90, 0xA2, 0xDA, 0x00, 0x7F, 0xB6 };
const IPAddress ip(143,210,109,74);
const IPAddress dnsserver(143,210,12,154);
const IPAddress server(143,210,108,92);
const char get_header[] = "GET /cgi-bin/twittino.cgi";
const char server_header[] = "Host: 143.210.108.92 80";

EthernetClient client;
LiquidCrystal lcd(8, 9, 17, 16, 15, 14);

const int SDSELECT = 4;
const int PIN_G    = 21; // Pins on Mega board!
const int PIN_R    = 20;
const int PIN_B    = 19;

void setup() {
  pinMode(PIN_R, OUTPUT);
  pinMode(PIN_G, OUTPUT);
  pinMode(PIN_B, OUTPUT);
  pinMode(SDSELECT, OUTPUT);
  digitalWrite(PIN_R, HIGH);
  digitalWrite(PIN_G, LOW);
  digitalWrite(PIN_B, HIGH);
  digitalWrite(SDSELECT, LOW);
  Serial.begin(9600);  
  lcd.begin(16, 2);
  Ethernet.begin(mac, ip, dnsserver);
  delay(1000);
  digitalWrite(SDSELECT, HIGH);
  Serial.println(Ethernet.localIP());
  digitalWrite(PIN_G, HIGH);
}

void loop()
{
  static int counter = 1;
  char username[30];
  char tweet[141];
  
  Serial.println(counter++);
  
  if (client.connect(server, 80)) 
  {
    // Serial.println("Connected");
    // Make a HTTP request:
    client.println(get_header);
    client.println(server_header);
    client.println("Connection: close");
    client.println();
    int ulen = 0;
    int tlen = 0;
    boolean username_found = false;
    while (client.connected())
    {  
      if (client.available()) 
      {
        char c = client.read();
        if (!username_found)
        {
          if (c == '\n')
          {
            username_found = true;
            username[ulen] = '\0';
          }
          else
          {
            if (c != '\n') username[ulen++] = c;
          }
        }
        else
        {
          if (c == '\n') 
          {
            tweet[tlen++] = ' ';
          }
          else
          {
            tweet[tlen++] = c;
          }
          switch(c)
          {
            case 'R' : digitalWrite(PIN_R, LOW); break;
            case 'G' : digitalWrite(PIN_G, LOW); break;
            case 'B' : digitalWrite(PIN_B, LOW); break;
            case 'r' : digitalWrite(PIN_R, HIGH); break;
            case 'g' : digitalWrite(PIN_G, HIGH); break;
            case 'b' : digitalWrite(PIN_B, HIGH); break;
          }
        }
      }
    }
    client.stop();
    // Serial.println("Disconnected");
    tweet[tlen] = '\0';
    if (ulen != 0 && tlen != 0)
    {
      Serial.print("Username : ");
      Serial.println(username);
      Serial.print("Tweet    :\n");
      Serial.println(tweet);
      lcd.setCursor(0, 0);
      for (int i=0; i < ulen; i++)
      {
        lcd.print(username[i]);
      }
      for (int i=0; i <= tlen; i++)
      {
        lcd.setCursor(0, 1);
        for (int j=0; j < 16; j++)
        {
          if (j + i < tlen) lcd.print(tweet[j+i]);
        }
        lcd.print(' ');
        delay(500);
      }
    }
    else
    {
      Serial.println("Nothing.");
    }
  } 
  else 
  {
    Serial.println("Connection failed.");
  }
  delay(10000); // Check every 10 seconds
  lcd.clear();
}

Things to note: the SD card, if present, needs to be deactivated (by setting SD_SELECT, pin 4, to LOW) before setting up the ethernet connection.  In addition to the LCD panel, I have one of my RGB LED units connected, and if there is a capital R, G or B in the tweet, the appropriate colour is displayed (the lowercase equivalents turn the colour off).  The username is displayed on the top row of the LCD panel, and the tweet scrolls along the bottom row.

 

Friday, July 26, 2013

Shift Registers

I bought a "Nano 3.0 for Arduino" for eight quid - a neat little thing that reminds me of the Hexbug Nanos I bought my niece and nephew for Christmas a couple of years ago, but is rather more interesting for grownups.  It fits directly into a breadboard, and is powered and programmed through a USB lead (standard "mini A" connector).


The pins are:

Top row: digital pins D12 to D2 (D2 is just above the TX LED), ground, reset, RX and TX (RX and TX are D0 and D1 on an Arduino Uno).

Bottom row: digital 13, 3.3 V, REF (analogue reference, labelled AREF on bigger boards), analogue inputs A0 to A7 (two more than on the Uno, A7 is just below the LED), 5 V, reset, ground, and input voltage (VIN).

This is almost, but not quite, the same layout as the official Arduino Nano, and considerably cheaper.  I stuck it on a breadboard with a couple of shift register chips (74HC595), and used it to control some RGB LED units.

The RGB units have three LEDs with a common anode, so controlling four of them requires twelve bits.  I've used two shift registers, so that gives sixteen bits to play with.

A diagram is below.

Things to note: the board shown in the diagram doesn't have the same pin order as mine, and my breadboard (see below) has a very different layout but the circuit is the same.  The connections from the board are: 5V pin to the top power rail (which is then linked to the lower power rail in the middle, and the bottom power rail), ground pin to the top ground rail (similarly linked to the middle and bottom), and the SPI connection to the first shift register: blue wire links D2 to the data pin on the 74HC595, green wire links D4 to the latch pin, and yellow wire links D3 to the clock pin.

The two 74HC595 chips have the clock and latch pins linked (horizontal green and yellow wires), and the serial output from the first goes to the data pin on the second (horizontal blue wire below the yellow one).  This means that when two bytes are sent into the first chip, the first byte is displaced into the second chip (you can chain these things even further - so if you had four linked chips and sent four bytes into the first one, the fourth chip would contain the first byte sent, the third chip the second byte, the second chip the third byte and the first chip the fourth byte).

I have only shown the connections for one of the LED units - the horizontal red, blue and green wires connect from pins Q3, Q2 and Q1 of the first 74HC595 to the cathodes of one of the LED units.  Pins Q7-Q5 of the first 74HC595 go to the RGB cathodes of the second unit, and the third and fourth units are connected to the same pins on the second 74HC595.  On my breadboard (see below) there are no wires making these connections - I just bridged the pins of the 74HC595 chips to the LED units with resistors (270 Ω).

I'm not using pins Q0 or Q4 on either 74HC595 - they aren't needed (and Q0 is in a slightly inconvenient position, on the other side of the chip to the other seven output pins, next to the data pin).


Code:

const int latchPin = 4;
const int clockPin = 3;
const int dataPin  = 2;

const byte red     = 0x08;
const byte green   = 0x02;
const byte blue    = 0x04;
const byte yellow  = red   | green;
const byte magenta = red   | blue;
const byte cyan    = green | blue;
const byte white   = red   | green | blue;
const byte black   = 0x00;

const byte sequence[8] = {black, red, yellow, green, 
  cyan, blue, magenta, white};

void put_four_leds(byte leds[4])
{
    byte a = leds[0] | (leds[1] << 4);
    byte b = leds[2] | (leds[3] << 4);
    
    digitalWrite(latchPin, LOW);
    // Flip bits because low is on, high is off
    // Second pair goes first because that byte is shifted
    // through to the second shift register. 
    shiftOut(dataPin, clockPin, MSBFIRST, ~b);
    shiftOut(dataPin, clockPin, MSBFIRST, ~a);
    digitalWrite(latchPin, HIGH);
}

void setup() 
{
  //set pins to output so you can control the shift register
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() 
{
  byte led_array[4];
  int  i, j;
  
  for (i=0; i<8; i++)
  {
    for (j=0; j<4; j++)
    {
      led_array[j] = sequence[(i + j) % 8];
    }
    put_four_leds(led_array);
    delay(500);
  }
}
 
 

Monday, July 1, 2013

Open Day, Part II

The main things I learned from the open day:
  1. It is possible to mull over these things for a few weeks when you are too busy to do anything serious, and then put in a few hours' work late on a Friday afternoon and come up with something working.
  2. iPads are fun, and there are some great apps to do serious engineering, but you can't hack those apps, and you can't do much to hack the iPad.  You get to do what Apple, and Apple-approved developers, allow you to do.  Which is a great deal, but it's not the sort of activity that engineers find really satisfying (and despite the fact that I'm an ex-geochemist with a master's degree in philosophy, I still consider myself a kind of engineer).
  3. On Friday afternoon, I needed the python-twitter library to check the twitter feed from Python.  Downloading and installing it via apt-get from the Ubuntu repository took a few seconds. When I realised I needed a newer version to cope with changes in Twitter's API, it took a couple of minutes to find the newer version, download it and install it.  If I had been using the locked-down campus version of Windows, it would not have been that trivial. Perhaps not even possible. Linux makes it much, much easier to do this sort of thing.
  4. I enjoyed myself more late on Friday afternoon than I had done for weeks doing more "important" things.  It really fired my enthusiasm for using these things in next year's programmable electronics module (I have sixty kits sitting in a cupboard), and encouraging students to play around with them and see what they can do.
  5. I thought I had fried the GPIO port on my Raspberry Pi.  A morning sitting in a room with disappointingly few visitors meant I had time to do some troubleshooting with a multimeter, and it turned out to be a broken connection on the ground rail of one of my breadboards, which was a relief.  I love my multimeter.
  6. To some visitors, sending a tweet to turn on an LED was like magic.  To some, it was deeply unimpressive.  Only colleagues were able to grasp how much was really going on between pressing the send button on the iPad, and the red and green lights coming on.

Open Day, Part I

I was asked to demonstrate the use of an iPhone in Engineering at Saturday's alumni open day.  I demonstrated AutoDesk ForceEffect Motion (which is pretty good fun) on the iPad rather than iPhone, but also demonstrated using the iPad to send tweets to the department's Twitter account (@le_engineering), which was more fun for me to do.  I had a Python program running on my Linux netbook, which checked Twitter for mentions of @le_engineering, and then searched the rest of the text for the characters R, G, r, g, b and 1 or 0.  These characters were sent via USB to an Arduino, which interpreted them as commands to light (uppercase) or turn off (lowercase) a red and green LED, sound a buzzer, or move a servo to 90 or 0 degrees.

Wednesday, June 12, 2013

Raspberry Pi

I haven't done much with the Raspberry Pi since I bought it, but having semi-volunteered to give some talks at a forthcoming open day, I thought I would use it as a web server to interface with an Arduino and some hardware.  So I set it up again.

SD Card

I bought a new 16 gigabyte SD card for a tenner from the students' union shop. I think I paid about £130 for a 2 gigabyte hard disk back in the mid-nineties (I have a pile of them propping up a bookshelf in my office).  Downloaded the raspbian image on to my Linux netbook, popped the SD card into the slot, and created the image (the card was automounted as /dev/sdb1, hence the first command):
    umount /dev/sdb1
    dd if=2013-05-25-wheezy-raspbian.img of=/dev/sdb bs=4M

Network

The RPi was already registered for the campus network, so I haven't bothered with any of the static network address recipes - it picks up its address through DHCP.

Additional software

I installed (using apt-get) the following packages:
  • apache2
  • php5
  • libapache2-mod-php5
  • xfce4
  • tightvnc
  • arduino

VNC

I want to run the RPi headless most of the time, and don't want to have to muck about with monitor cables and spare keyboards when I do need to use the GUI.  Initially, I took the RPi up to one of the labs where I could pinch a monitor with DVI input, and work in peace.  These instructions worked to get VNC up and running.  I used vinagre and remmina to test the setup - VNC is now starting reliably at boot time.

Web server

I want the web server to use one of my subdirectories as root (documents in /home/pi/www/htdocs, CGI scripts in /home/pi/www/cgi-bin), so I edited /etc/apache2/sites-enabled/000-default to change the values of DocumentRoot, ScriptAlias, and the permissions on /home/pi/www/htdocs/.

Desktop

I use xfce as my standard desktop (I like Gnome 4 on netbooks, have never got on with Unity).  Selecting that as the default is simple (like most of the commands above, this has to be done using sudo to give yourself root privileges):
    update-alternatives --config x-session-manager
A menu appears, and I was able to choose /usr/bin/startxfce4 as my session manager.

Wednesday, April 17, 2013

Gmail

I've been using Python to email .MOBI files to the Kindle "Personal Documents" service, which adds them to your documents list on Amazon and allows them to be downloaded, synchronised between reading devices, etc.  I've been sending via Gmail's SMTP server rather than the University server (because my Gmail account is authorised to send to Kindle, and my University account is not).  That stopped working today, with the mail server reporting an incorrect password.

The answer: https://accounts.google.com/DisplayUnlockCaptcha

This doesn't actually display a Captcha, but it opens a ten minute window during which, if you use the application which is getting password rejections, the password will be accepted and the application added to a "whitelist".

Thursday, September 27, 2012

CTRL-ALT-DEL

A couple of things arose today in connection with the university's new Windows 7 service. Our lab machines, for reasons I won't go into because I can only vaguely remember them, are set up as staff PCs rather than student PCs. This means you can do CTRL-ALT-DEL and lock the screen, which isn't what you want in a student lab (I remember this being a major cause of disputes in Charles Wilson labs during the Windows 3.1 days, when there was fierce competition for machines and students would lock the screen and then go off for an hour to a lecture). There's a simple fix: run gpedit.msc as an administrator, and enable User Configuration / Administrative Templates / System / Ctrl+Alt+Del Options / Remove Lock Computer. This also has the effect of disabling the "Lock" option in the Windows start button (it is still there, it just doesn't do anything).

I was accidentally switched to the new service a couple of days ago, and found that access to the Z drive from both my Linux PC, and the Windows 7 virtual machine I run on it, no longer work (the X drive still works, but that seems to still be on older file servers). Pending the resolution of that problem, a new PC fell off the back of a lorry and I am back to having two computers on my desk, one running Linux and one Windows 7 (which I think we are officially supposed to call "UOL" because there are no other things associated with the University of Leicester which might use the initials "UOL" and it will not be at all confusing). Linking them using synergy:

Linux

From this page. I had to install a newer version of synergy (1.4, the current Ubuntu version is 1.3).

In the instructions below, EG-PC845 is the Linux box which will be running the server, and EG-PC1184 is the Windows machine.

As root, create /etc/synergy, and put this lot into /etc/synergy/synergy.conf:

section: screens
    EG-PC845:
    EG-PC1184:
end
section: links
    EG-PC845:
        left = EG-PC1184
    EG-PC1184:
        right = EG-PC845
end

Then put this lot into /etc/synergy/startsynergys, followed by chmod +x /etc/synergy/startsynergys to make the file executable:

#!/bin/bash

/usr/bin/synergys -c /etc/synergy/synergy.conf -n EG-PC845

And finally, append this to /etc/lightdm/lightdm.conf:

greeter-setup-script=/etc/synergy/startsynergys

The synergy server will now start when the machine boots.

Windows

Install synergy as normal, setting it up to start as a client when the machine does, and connect to the IP address of EG-PC845. The only remaining problem is to do CTRL-ALT-DEL to log on to Windows 7, because you are no longer using the keyboard attached to the PC. The solution is to use gpedit.msc again, as described here: enable Local Computer Policy / Computer Configuration / Administrative Templates / Windows Components / Windows Logon Options, with the option "Services and Ease of Access applications". CTRL-ALT-PAUSE then works in place of CTRL-ALT-DEL.

Thursday, July 26, 2012

Looking ahead

As the previous post suggests, I've been thinking about Twitter bots today, and in particular how to post lengthy text in 140-character chunks. A diversion was to think about how to "read ahead" - I wanted to be able to say "if the next chunk of text will take me over the 140-character limit, don't read it in". But I can't do that without reading it in (I could of course read the whole file into an array, then use pop() and append() to treat it as a stack, but I want to avoid having to read in the whole file if possible). A solution is below:
#!/usr/bin/python

from tempfile import mkstemp
import shutil
import os

ifpath = "a.txt"

fh, ofpath = mkstemp()
ofile = open(ofpath, "w")
ifile = open(ifpath)

lines = []
length = 0

while True:
    previous = ifile.tell() 
    line = ifile.readline()
    if length + len(line) < 140:
        lines.append(line)
        length += len(line)
    else:
        ifile.seek(previous)
        print lines
        break

for line in ifile:
    ofile.write(line)

ifile.close()
ofile.close()
os.remove(ifpath)
shutil.move(ofpath, ifpath)
This opens a.txt for reading (as ifile), and creates a temporary file (ofile). There's then an infinite loop - each time round, I store the current file location as previous, read in a new line, and check to see if that would take it over the limit. If not, I add the new line to the stored array. If it will go over the limit, I use ifile.seek() to go back to the file location before I read in the new line, and then print out the lines stored so far. All that remains is to write the unread lines (including the one I read in and decided not to use) to a temporary file, remove the original a.txt, and replace it with the temporary one.

Wednesday, July 25, 2012

Twitter Updating

Revised version of the code I originally wrote about here. The main difference is that I've included a counter so it doesn't post more than two tweets every time it is run (and since it only runs once an hour, this shouldn't make it too antisocial even when it hasn't run for a while and there are a lot of items in the RSS feed).
#!/usr/bin/python

import tweepy
import feedparser
import urllib
import urllib2

url = "http://www2.le.ac.uk/departments/engineering/news-and-events/blog/RSS"

oauth_file = open("access_token.txt", "r")
oauth_token = oauth_file.readline().rstrip()
oauth_token_secret = oauth_file.readline().rstrip()

consumer_file = open("consumer_keys.txt", "r")
consumer_key = consumer_file.readline().rstrip()
consumer_secret = consumer_file.readline().rstrip()

bitly_file = open("bitly.txt", "r")
bitly_username = bitly_file.readline().rstrip()
bitly_apikey = bitly_file.readline().rstrip()

bitly_base = "http://api.bit.ly/v3/shorten?"
bitly_data = {
    "login" : bitly_username,
    "apiKey" : bitly_apikey,
    "format" : "txt",
    "longUrl" : ""
    }

already_done = []
done_file = open("done.txt", "r")
for line in done_file:
    already_done.append(line.rstrip())
done_file.close()

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(oauth_token, oauth_token_secret)

api = tweepy.API(auth)
feed = feedparser.parse(url)

count = 0
for item in feed["items"]:
    url   = item["link"]
    title = item["title"]
    if url not in already_done and count < 2:
        bitly_data["longUrl"] = url
        to_shorten = bitly_base + urllib.urlencode(bitly_data)
        result = urllib2.urlopen(to_shorten).read()
        api.update_status(title + " : " + result)
        already_done.append(url)
        count = count + 1

done_file = open("done.txt", "w")
for url in already_done:
    done_file.write(url + "\n")
done_file.close()

Monday, June 4, 2012

Raspberry Pi

A Raspberry Pi arrived late last week, just before I came away for the bank holiday. So I bought a cheap USB keyboard, checked that an old iPod power supply coupled with a Kindle cable would provide enough oomph (just about), and took the lot to my mother's. Unfortunately the keyboard (PC World Essentials) turned out to be one which won't work. Also, I can't keep coming to my mother's house every time I want access to a TV with an HDMI socket. Here's how I got access to the board via my laptop, which runs both Windows and Ubuntu Linux 12.04 - instructions are for the latter, and while I think the instructions below could be adapted for Windows they would probably be more complex. In any case, if you're going to be doing things with an ARM board running Linux, why wouldn't you also be running Linux on your other machines?

  1. Enable SSH. I paid for an SD card pre-loaded with Debian. Stick the card in the laptop, rename boot_enable_ssh.rc to boot.rc, and put the card back into the Pi before powering up.
  2. Plug the Pi into one of the ports on the back of the wireless router. Using a web browser on the laptop, go to the router's management page and look up the list of attached devices. I found that the Pi was on 192.168.0.3.
  3. Open a command prompt on the laptop, and do "ssh pi@192.168.0.3". Password is "raspberry", and you should be logged on to the Pi.
  4. Update packages: sudo apt-get update followed by sudo apt-get upgrade.
  5. Install a VNC server: sudo apt-get install tightvncserver.
  6. Run the server: tightvncserver - you'll be asked to set a password.
  7. Start a VNC server with a resolution that will fit in your monitor - I used vncserver :1 -geometry 800x600 -depth 24
  8. On the laptop, run vinagre, a VNC client, and choose to connect to 192.168.0.3:1 via VNC - the password you will be asked for is the one you set in step 6, not the password from step 3.
  9. An LXDE desktop should appear:

Friday, January 6, 2012

Lectures

"Students expect lectures", I am told. I think lectures are a rotten way to try to teach programming - it is a practical skill, and you learn (at least at the elementary level) by doing it, not by reading about it. Via the philosophy/politics blog Crooked Timber:


I already subscribe to too many podcasts, but this looks good.

As an aside, this section on teaching physics echoes some of the material on "folk psychology" I studied a couple of years ago as part of an OU philosophy course - "eliminative materialists" think our traditional analysis of mental events ("folk psychology") can be replaced with a more scientific version, just as "folk physics" has been replaced by mathematical physics (and I think Hestenes and Halloun were cited in the paper I read - I'll have to dig it out). If you ask people who haven't studied physics how weights behave in gravity, what happens if you cut a pendulum string, and other similar questions, they'll give the wrong answer. But chuck a ball at them, and they'll catch it (that's "folk physics" - being able to predict how things behave, despite lacking an underlying theory).  What reminded me of the philosophy paper was the observation that even after a course of lectures, most of the students who had in theory been taught mathematical physics were still giving the wrong answers.

Friday, December 16, 2011

A stack on the Arduino



 I have put together a collection of Python scripts which allow me to build proper C++ programs for the Arduino Uno and Mega 2560 from the command line, and upload them all in one go. At the moment, these are only working in Linux but I think I know enough now that in principle I could get this going on Windows, possibly within Dev-C++ - a project for after Christmas, I think.