Thursday 18 April 2013

Digispark / RPi / HC-SR04 measurement - 2.

OK - now pretty much happy with the digispark / HC-SR04 interface.  Using a 'newping' library from here https://code.google.com/p/arduino-new-ping/ to control the interface which makes the digispark code simpler. - The code writes to the USB every time we get an input on the USB. - I will then use the Pi to send a 'request' to the digispark. - The digispark does the measurement (median / average of 20 pings), and returns the distance in cm.

Note that I had to comment out the 'timer' functions in the newping library to get it to compile for the digispark. - Think the Arduino has some functionallity which isn't available in the digispark.

HC-SR04 trigger connected to digispark pin 0.
HC-SR04 echo connected to digispark pin 2.

(Mine is a rev A digispark which means that the onboard LED is connected on Pin1, and I use this to flash when data is transmitted.)

Code for the digispark sketch.


#include <DigiUSB.h>
#include <NewPing.h>

//
// AJR - 17/4/13.  USing HC-SR04 to measure distance
// and output on the USB so it can be read on an RPi.
// Use with the receive python progs
// from the Digispark examples.
// DigisparkExamplePrograms-master.
//
// Utilising the NewPing library from https://code.google.com/p/arduino-new-ping/
// Note that this will only compile if several of the timer functions are removed
// from the library.  I think there are some bits missing on the digispark which
// would otherwise be present in arduino.  One thing it is missing is memory!
// I keep running out of memory in the code space, and if I try to do too much here,
// then also run out. - Would like to have returned version string etc, but not
// enough room.... Ho, hum.
//

#define trigPin 0
#define echoPin 2
#define maxDist 300
// Pretty self explanatory below, but maxDist is the distance beyond
// which a ping is considered invalid.
NewPing sonar(trigPin, echoPin, maxDist);

void setup() {
  DigiUSB.begin();
  pinMode(1,OUTPUT); //give us some idea it's workng by flashing led
  pinMode(trigPin, OUTPUT);
  digitalWrite(trigPin, LOW);
  pinMode(echoPin, INPUT);
}

void measure() {
  int lastRead;
    digitalWrite(1,HIGH);
    DigiUSB.delay(100);
    // - This is where we call the NewPing library.  Don't think digispark has enough memory to do 50
    // but it seems reliable with 20.
    unsigned int uS = sonar.ping_median(20);
    DigiUSB.println(uS / US_ROUNDTRIP_CM);
    digitalWrite(1,LOW);
    delay(50); //sleep for 0.5 seconds before sending again.
}

void get_input() {
  int lastRead;
  // when there are no characters to read, or the character isn't a newline
  while (true) { // loop forever
    if (DigiUSB.available()) {
      // something to read
      lastRead = DigiUSB.read();
          if (lastRead == '\n') {
              measure();
              break; // when we get a newline, break out of loop
          }
    }
    // refresh the usb port for 10 milliseconds
    DigiUSB.delay(10);
  }
}

void loop() {
  // print output
  // DigiUSB.println("Waiting for input...");
  digitalWrite(1, LOW);
  // Measure....
  get_input();
}


Wednesday 17 April 2013

Raspberry Pi / Digispark distance measurement 1.

I used the following sketch to be able to receive the distance measurement from an HC-SR04 ultrasound sensor connected to the Digispark on the Pi using the digispark example 'receive.py' script.

#include <DigiUSB.h>

//
// AJR - 17/4/13.  USing HC-SR04 to measure distance
// and output on the USB so it can be read on an RPi.
// Use with the receive python progs 
// from the Digispark examples.
// DigisparkExamplePrograms-master.
//

#define trigPin 0
#define echoPin 2
char data[] = "The quick brown fog jumped over the lazy dogx";
long distance = 42;
long duration;

void setup() {
  DigiUSB.begin();
  pinMode(1,OUTPUT); //give us some idea it's workng by flashing led
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
}

void measure() {
  int lastRead;
  while (true) { // loop forever
    digitalWrite(1,HIGH);
    DigiUSB.delay(100);
    // Try measure, and send response.
    digitalWrite(trigPin, LOW);  // Trig needs to go high on 10us pulse
    delayMicroseconds(2); // wait....
    digitalWrite(trigPin, HIGH);
    delayMicroseconds(10); // Trig pin pulse time.
    digitalWrite(trigPin, LOW);  // Now, we time to echo.
    duration = pulseIn(echoPin, HIGH);
    distance = (duration/2) / 29.1;
    ltoa(distance, data, 10);
    strcat(data, " cm");
    DigiUSB.println(data);
    digitalWrite(1,LOW);
    delay(500); //sleep for 0.5 seconds before sending again.
  }
}

void loop() {
  // print output
  DigiUSB.println("Waiting for input...");
  digitalWrite(1, LOW); 
  // Measure....
  measure();
}

Digispark / Rpi.


Talking with a relative about the arduino / Pi integration, he gave me a 'digispark' which he'd backed on kickstarter.  This device should be able to plug into the USB, and tidy up the wiring a bit from the arduino idea to measure distance........   So, how to make it work.

I don't seem able to program this on Linux - it isn't recognised on my PC - USB problems, however I can program it on Windows, then plug into the Pi, and it is recognised.  (There need to be various bits for the Pi recompiled to make the IDE work on Pi, and this is outside my needs (capabilities probably....).

On the pi...  sudo pip install PyUSB

From the digispark wiki, install the examples, load up the DigiUSB example onto the digispark, and run the recieve.py on the Pi:-


pi@raspberrypi ~/digispark/DigisparkExamplePrograms-master/Python/DigiUSB/source $ cat receive.py
#/usr/bin/python

#
# Written for PyUSB 1.0 (w/libusb 1.0.3)
#


import usb # 1.0 not 0.4


import sys
sys.path.append("..")

from arduino.usbdevice import ArduinoUsbDevice


if __name__ == "__main__":
    try:
        theDevice = ArduinoUsbDevice(idVendor=0x16c0, idProduct=0x05df)
    except:
        sys.exit("No DigiUSB Device Found")




    import sys
    import time

    while 1 == 1:
        try:
            lastChar = chr(theDevice.read())
            if(lastChar == "\n"):
                break
            sys.stdout.write(lastChar)
            sys.stdout.flush()


        except:
            # TODO: Check for exception properly
            time.sleep(0.1)


Cool. - Something was received!!  The foundation to rework the distance measurement here.

RPi / Arduino - interfacing.

Right. - So my issue is that I wrote a system on the Pi using Python with an HC-SR04 (ultrasonic distance) sensor. - This needs accurate measurement of time to give meaningful distance, and because the Pi is scheduled, multi-tasking OS, this is nigh on impossible to do, and the variance in distance measured was some 30cm over about 1.5M over a 24 hour period of monitoring.

Some tinkering with an Arduino seems to suggest that this is much more accurate in it's timing as you'd expect because it's not multi-tasking.....  So - plan is to implement distance measure on the Arduino, and use the Pi for the higher level bits. (tweet / mail / cosm etc).

http://neophob.com/2013/04/i2c-communication-between-a-rpi-and-a-arduino/

1. - sudo apt-get install arduino.
2. - Unblacklist i2c

pi@raspberrypi ~ $ cat /etc/modprobe.d/raspi-blacklist.conf
# blacklist spi and i2c by default (many users don't need them)

##blacklist spi-bcm2708
##blacklist i2c-bcm2708


pi@raspberrypi ~ $ sudo modprobe i2c-bcm2708


Add i2c-dev to /etc/modules

sudo modprobe i2c-dev.

sudo apt-get install i2c-tools

sudo adduser pi i2c

sudo i2cdetect -y 0

No errors, so this seems to be working.



sudo apt-get install python-smbus


#!/usr/bin/python

import smbus
import time
bus = smbus.SMBus(0)
address = 0x2a

while True:
        data = ""
        ch = ""
        while ch != 'x':
            try:
                ch = chr(bus.read_byte(address));
                print ch;
            except:
                print "no data..."
                time.sleep(1) # give it chance to wake up / reboot, whatever.
            if ch != 'x':
                    data += ch;
        print data
        ch = ""
        time.sleep(1);

Corresponding sketch on the arduino for distance with HC-SR04 sensor:-


#include <Wire.h>
#define SLAVE_ADDRESS 0x2A
#define trigPin 6
#define echoPin 7

int led = 13;
char data[] = "The quick brown fog jumped over the lazy dogx";
long distance = 42;
long duration;
int index = 0;

void setup() {
    // initialize i2c as slave
    Wire.begin(SLAVE_ADDRESS);
    Wire.onRequest(sendData);
    pinMode(led, OUTPUT);
    Serial.begin (9600);
    pinMode(trigPin, OUTPUT);
    pinMode(echoPin, INPUT);
}

void loop() {
  //Serial.println("Hello");
  digitalWrite(trigPin, LOW);  // Trig needs to go high on 10us pulse
  delayMicroseconds(2); // wait....
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10); // Trig pin pulse time.
  digitalWrite(trigPin, LOW);  // Now, we time to echo.
  duration = pulseIn(echoPin, HIGH);
  distance = (duration/2) / 29.1;
  ltoa(distance, data, 10);
  strcat(data, "x");
  //Serial.println(data);
  delay(100);
}


// callback for sending data
void sendData() {
    digitalWrite(led, HIGH);
    Wire.write(data[index]);
    ++index;
    if (index >= strlen(data)) {
         index = 0;
         digitalWrite(led, LOW);
    }
 }


============================









Friday 12 April 2013

Remote access to Raspberry Pi with 3G USB Modem.

OK - so the scenario I have here is that I will deploy a Pi in a remote location some 3 hours from home. - This location doesn't have network access, so I've got a 3G USB modem installed on that Pi.  Periodically it brings up the link and will report monitoring data via email, twitter, and 'COSM' in time.

Here, my aim is to be able to connect to this remotely located Pi - I've pretty much followed the post here to do this using 'autossh'.  http://linuxaria.com/howto/permanent-ssh-tunnels-with-autossh?lang=en

My 'local' server is a also a RPi, and I've allowed access through my firewall to this pi on port 22 - let's call it 'localpi', and the remote pi 'remotepi'

I have tested that access can work in the following way.

1. - On 'remotepi', bring up the 3G link and then, ssh -R 2222:localhost:22 localpi.mydomain.com
This allows me to login to 'localpi' from my remote location.
2. - Now, the crux - can I login to 'remotepi' from 'localpi'? - ssh -p 2222 localhost - Yes, that works fine.
3. - Now, we know comms now work and can start automating the setup / link. On remotepi - sudo apt-get install autossh
4. - On localpi - sudo useradd -m -s /bin/false autossh
5. - On remotepi - sudo useradd -m -s /bin/false autossh
6. - On remotepi, run sudo su -s /bin/bash autossh
7. - On remotepi, run ssh-keygen.  Leave with empty passphrase and default location for the id.
8. - Transfer the id_pub key from 'remotepi' and enter into 'localpi' into ~autossh/.ssh/authorized_keys
9. - Do the same thing on localpi, and transfer the id_pub to ~autossh/.ssh/authorized_keys on remotepi - this way the connection for autossh will work both ways if necessary (and I'm not sure it is, but you never know...) - [ Note - It might be obvious, but you put the generated public key into the authorized_keys file on the host TO which you need to connect. - That way, you don't need a password when logging into that host. - Therefore, I actually did this backwards in the above.]
10. - On remotepi. - sudo su -s /bin/bash autossh
11. - On remotepi, bring up the link manually initially so you can accept the connection. - Future connections don't need to be interactive, though if 'localpi' changes it's IP address (for example if the broadband restart), then it might be necessary to do this step again.... - as user autossh on remotepi, run autossh -R 2222:localhost:22 localpi.mydomain.com
12. - Drop the ssh connection from step 11.
13. - Now try the 'whole' command on remotepi. - i.e. autossh -M 0 -q -f -N -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 2222:localhost:22
14. - On localpi, I should now be able to run ssh -p 2222 pi@localhost and get connected to 'remotepi', provided the link is up. - Great.  All that remains is to script it so it starts on boot, and test it.

Testing.....
1. - Put into /etc/rc.local:-


sudo su -s /bin/sh autossh -c 'autossh -M 0 -q -f -N -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 2222:localhost:22 autossh@localpi.mydomain.com'





- Reboot the remotepi.  Does this start? - Yes, it does. - Great.
- Now bring up the 3G link on the remotepi. - Does autossh connect to 'localpi' without intervention?  - Yes, it does! - Excellent. - So, now if I bring up the link periodically, then I should be able to make remote connection. - Cool.

- What I have found which isn't perfect is that the 'autossh' session which is established on 'localpi' doesn't ever seem to die when the link is dropped. - The solution here is to kill all 'autossh' sessions on 'localpi', then await the reconnection from 'remotepi' before trying to ssh to remotepi. - This seems to work reliably.
 - I've setup the script to bring the connection up for 15 minutes every hour, so this should give me time to connect if necessary.



RPi as Wireless Access Point.

A project I am working on will be deployed in a location without internet access.  The Pi will have a 3G modem connected, and I will detail that setup in another post.

What I want to be able to do here is to use the WI-FI dongle attached to the Pi to allow devices to connect, and potentially use the internet via the 3G dongle. - The first step in that is just getting the AP setup so that we can connect. - Taking this blog as a guide - http://www.rpiblog.com/2012/12/turn-raspberry-pi-into-wireless-access.html

Before I started, I plugged the Pi into a wired connection rather than it's more normal wireless connection, found the IP address and connected to that so I'm logged in over wired and won't lose connection when the hostapd kicks in.  I also ran an image backkup of the SD card so that I could at least revert back to previous configuration if necessary!


sudo apt-get install iw
sudo iw -list - shows that this supports AP mode correctly.


sudo apt-get install hostapd udhcpd

Give static IP to wlan0 - in /etc/network/interfaces:-


allow-hotplug wlan0
iface wlan0 inet static
address 192.168.3.1
netmask 255.255.255.0


/etc/udhcp.conf:-

pi@raspberrypi /etc/network $ grep -v "^#" /etc/udhcpd.conf |grep -v "^$"
start           192.168.3.20    #default: 192.168.0.20
end             192.168.3.254   #default: 192.168.0.254
interface       wlan0           #default: eth0
remaining       yes             #default: yes
opt     dns     8.8.8.8 4.2.2.2
option  subnet  255.255.255.0
opt     router  192.168.3.1
option  lease   864000          # 10 days of seconds

pi@raspberrypi ~ $ cat /etc/hostapd/hostapd.conf
interface=wlan0
driver=nl80211
ssid=MySSID
hw_mode=b
channel=4
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=mykey
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP


edit /etc/default/hostapd - change the following to point to the hostapd.conf file:-

DAEMON_CONF="/etc/hostapd/hostapd.conf"

edit /etc/sysctl.conf:-


# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1

sudo sysctl -p (to re-read the setting)

sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT

sudo service hostapd start
sudo service udhcpd start

At this point, we are working- hurray.  Now make the IP tables setup permanent.

sudo sh -c "iptables-save > /etc/iptables.ipv4.nat"

Edit /etc/network/interfaces:-

I didn't find these last commands necessary, since the install enabled the daemons, but this  is to enable the daemons on boot.

sudo update-rc.d hostapd enable
sudo update-rc.d udhcpd enable

Excellent - so, now I have an AP which will route traffic through eth0, and assign an IP address to my connected device.
DNS works fine.
I can also connect to the eth0 IP address when I'm on that network, so all appears to be working fine....

OK - this didn't work on reboot unless the ethernet cable was plugged in... Bizzare. - It appeared that the plugd thought that the wlan0 interface was already configured, so I commented out the line in interfaces:-

#allow-hotplug wlan0

And now, the Pi starts up, issues IP addresses, and seems to work fine even when the eth0 isn't plugged on power up. - Cool.


Now - to setup for 'ppp0' via the USB modem.
- Change eth0 to ppp0 in /etc/iptables.ipv4.nat
- This is my 'connect' script using sakis3g.


echo "Connect attempt at `date`" >>$LOGFILE
/home/pi/3G/sakis3g connect FORCE_APN="general.t-mobile.uk::t-mobile:.." MODEM="OTHER" OTHER="USBMODEM" USBMODEM="12d1:1003" DIAL="*99#" >>$LOGFILE 2>&1
/home/pi/3G/sakis3g status >> $LOGFILE 2>&1
echo "Restoring iptables" >> $LOGFILE 2>&1
sudo /sbin/iptables-restore < /etc/iptables.ipv4.nat >> $LOGFILE 2>&1
echo "Connect attempt complete at `date`" >> $LOGFILE


Note here that I need to re-apply the iptables rules - it doesn't seem to work applying these at boot time when the ppp0 interface does not exist.


Huawei E160 USB Modem

I need to be able to use a USB / 3G dongle to connect the Pi to network in remote location....  This post documents trying to get this working.

1. - Install usb_modeswitch as the dongle at first appears as a CD device.

sudo apt-get install usb-modeswitch

lsusb shows the modem:-

Bus 001 Device 074: ID 12d1:1003 Huawei Technologies Co., Ltd. E220 HSDPA Modem / E230/E270/E870 HSDPA/HSUPA Modem

So, now I've unpacked the driver file in /usr/share/usb_modeswitch - Unsure yet where this is used, but pretty sure it needs to be unpacked from the archive therein.


pi@raspberrypi /usr/share/usb_modeswitch $ sudo tar zxvf configPack.tar.gz "12d1:1003"

Added the following into /etc/usb_modeswitch.conf

# choose one of these:
#DetachStorageOnly=1

DefaultVendor=0x12d1
DefaultProduct=0x1003
#TargetClass=0xff
CheckSuccess=20
HuaweiMode=1

OK - So actually I don't think I needed the usb_modeswitch as I think it's built into 'sakis3g'.

wget http://www.sakis3g.org/versions/latest/armv4t/sakis3g.gz

(See http://shkspr.mobi/blog/2012/07/3g-internet-on-raspberry-pi-success/)
(Also http://shkspr.mobi/blog/2012/06/raspberry-pi-python-and-3g-dongles-oh-my/ for SMS).

sudo pip install pyserial
Then try sms.py from the above page.  ( This worked on T-Mobile SIM - hurray.)

sudo apt-get install ppp

 T-mobile works on network manager under Ubuntu with :-
*99#
general.t-mobile.uk
user web
pass web

I did get this working under sakis3g with apn=general.t-mobile.uk / user=t-mobile / pass=..

I did get connected on the Pi with the dongle with O2 using Sakis, but only once.... Will try powered USB hub.  Having re-installed raspian, it keeps dropping out the modem.



./sakis3g connect FORCE_APN="general.t-mobile.uk::t-mobile:.." MODEM="OTHER" OTHER="USBMODEM" USBMODEM="12d1:1003" DIAL="*99#" LOGFILE="/home/pi/3G/sakis.log"

3/4/13 - My powered hub arrived - yay.  Testing again with the above command, and it seems to work for a few minutes at least. - Managed to install lynx, and then browse using that for some time over the modem.


OK - so I've been running for some time. - I cron'd a script which brings up the connection every hour, sends a mail and drops the connection - pretty much what I need it to do remotely. - All good.









Thursday 11 April 2013

Email from Raspberry Pi.

OK - Next thing I want to be able to do is to send mail from the RPi.

Following some of the instruction from http://iqjar.com/jar/sending-emails-from-the-raspberry-pi/

1 - Create a suitable gmail account to send mail from.
2. - sudo apt-get update
3. - sudo apt-get install ssmtp
4. - Edit /etc/ssmtp/ssmtp.conf


pi@raspberrypi /etc/ssmtp $ grep -v "^#" ssmtp.conf |grep -v "^$"
root=postmaster
mailhub=smtp.gmail.com:587
hostname=raspberrypi
AuthUser=myuser@gmail.com
AuthPass=mypassword
UseSTARTTLS=YES

5. - Edit /etc/ssmtp/revaliases

root:root@my.domain:smtp.gmail.com:587
pi:pi@my.domain:smtp.gmail.com:587

6. - Test it..... create a file called message.txt which will contain the message. - e.g.

pi@raspberrypi ~/mail $ cat message.txt
From: my.address@gmail.com
Subject: test email

hello world!
It works!


Now run command - ssmtp destination.address@gmail.com < message.txt

Cool - it works!