Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Sunday, February 1, 2015

CAN Bus to UART using an Adafruit Pro Trinket

I have been fighting with the onboard CAN controller on my BeagleBone Black trying to
get it to work properly for months. In the process I learned about pin muxing, bone-capemgr and compiling overlays. I was able to bring up the can0 interface and receive can messages with candump but the interface was unstable. It would randomly hang and stop receiving messages. If I tried to send anything with cansend I would get a kernel stack traces in dmesg and the can0 interface would hang.  I tried Angstrom, Ubuntu and the latest Debian images all with the same results. Maybe my BBB board has a hardware problem. It is one of the early Rev B boards. Or maybe I'm running into some sort of kernel driver bug. Whatever the issue I am done trying to figure out what the eff is wrong with it and I just want to move forward with my project. (and yes I was using a transceiver). I considered bypassing the onboard can controller and talking to a MCP2515 over SPI but that gets into compiling a custom kernel for the BBB and I don't want to use up a SPI port. I mention all of this because I know someone out there will say "why didn't you just use the onboard can controller". I tried and I am done fighting with it.

My attempt at using the CAN controller on
the BBB with an MCP2562 transceiver

What now?

Once I decided I wasn't going to get the onboard BBB can controller to work I started thinking about options. My first attempt at a workaround was an Arduino Uno with a Seeed CAN Bus shield connected to the BBB with a USB cable. I read the data from the serial port created over the USB connection. While this worked it was a bit bulky and fragile. The USB cable is much too big, the Uno with a shield is big and the Uno would reset when serial communication started (though there is a workaround for this).

I took a break from this project for a while and in the meantime I ordered a few Adafruit Trinkets and Pro Trinkets just to play around with. The Pro Trinket has the same microcontroller as the Uno but the board is much, much smaller.

Size comparison between Uno, Pro Trinket and Trinket.
(Photo credit: Adafruit)

Pro Trinket CAN to UART Converter

The Pro Trinket has SPI and UART plus a bunch of digital and analog pins. After playing around with the Trinket I realized I could build a CAN Bus to UART converter that would be able to push CAN data into the BBB over a UART. The BeagleBone is a 3.3volt device so I used the 3volt version of the Pro Trinket so I didn't have to use any level shifters. For the CAN Bus controller I used an MCP2515 and for the CAN transceiver I used an MCP2562. The MCP2515 connects to the Arduino using a SPI connection. Here is the circuit I came up with:



Here is a Fritzing breadboard diagram of the circuit:

The UART serial connection between the Arduino and the BeagleBone runs at 115,200 bps so you might drop a few packets on a very busy CAN bus running at 500kbps. My Digital Dashboard project is only going to have a few devices broadcasting CAN packets so the UART speed will be more than enough for my purposes. The current version only transmits CAN data to the BBB. It should be fairly trivial to connect the BBB UART1 TX to the RX on the Arduino. It would also need a bit of code to parse the message received on the UART and write it to the CAN bus. I'm planning on adding the functionality soon and I'll write up another post when I have that done. I'm planning on changing the Arduino code to use an External Interrupt so the Trinket can perform a few other functions instead of just polling for incoming messages.

The Arduino Code

The way this works is the MCP2515 has an INT (interrupt) pin that signals when a CAN message has been received. The INT pin drives Pin3 on the Arduino low and triggers it to read messages from the MCP2515 buffer. Once a message has been read from the MCP2515 buffer it is formatted into a NMEA-ish string that is written to the serial port and transmitted to the BBB on UART1.

Screenshot of CAN data coming in on UART1

 Here is the Arduino code:



Setting up the BeagleBone

The pins on the BeagleBone need to be configured to be used as a UART. I used Adafruit's python IO library to do this. When you install their library it will create the overlays needed to configure the pins. Follow the instructions on their learn site here: https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black. Here is the code to receive the CAN messages on BBB UART1:


Costs

The price for these parts wasn't bad at all

Adafruit Pro Trinket 3v - $9.95
16Mhz Crystal - $1.12
Two 22pF ceramic capacitors - $0.66
MCP2515 CAN controller - $2.18
MCP2562 CAN transceiver - $1.12

Total: $15.03


Well that's it for now. Next I'm going to work on modifying the code to use External Interrupts and then see if I can get it setup to receive messages over the UART. I also need to get this thing moved from the breadboard and soldered on to some protoboard.




Resources
http://www.embedded-things.com/bbb/enable-canbus-on-the-beaglebone-black/
http://www.adafruit.com/product/2010
https://learn.adafruit.com/setting-up-io-python-library-on-beaglebone-black/uart
http://ww1.microchip.com/downloads/en/DeviceDoc/21801d.pdf
http://ww1.microchip.com/downloads/en/DeviceDoc/25167B.pdf

Friday, September 19, 2014

Trouble installing PyCrypto on Mac OSX 10.9


This afternoon I was trying to install the PyCrypto Python library on my Mac running OSX 10.9.4 with the built in Python. To install the library I was using the command 'sudo pip install pycrypto' but it kept failing when trying to compile parts of the library with the C compiler. This is the error I was getting:

checking for gcc... gcc

checking whether the C compiler works... no

configure: error: in `/private/tmp/pip_build_root/pycrypto':

configure: error: C compiler cannot create executables

See `config.log' for more details

Grrr... I searched around and read a bunch of stackoverflow questions but it seemed like I had everything in place that I needed to compile this library. Xcode was installed, I have the Command Line Tools installed, I have a recent version of pip installed. Why won't the C compiler work??

Well the answer turned out to be something really dumb. I recently updated Xcode but I had not launched the Xcode application since it was updated. Turns out the command line C compiler won't compile anything until you accept the license agreement for the updated Xcode. I opened the Xcode gui and clicked Accept. After that I was able to install PyCrypto. I also found you can accept the agreement from the command line using the command 'sudo xcodebuild -license' but you have to page through all the text and type agree.



Monday, August 25, 2014

Font issues with BeagleBone Black and ILI9341 TFT display

In my continuing quest to build a really cool digital speedometer for my car I have been experimenting with an Adafruit 2.2" color TFT display. This past weekend I loaded up Ubuntu 14.04 on my BeagleBone Black and wired up the TFT display to it. Adafruit has a python library that works on both the BeagleBone Black and a Raspberry Pi. After trying out the example code I decided I wanted to try using a nicer font than the default one in the example code. The first font I tried seemed to look fine but the second font I tried had the bottom third of the characters not displayed. To figure out which fonts were affected I wrote a python script that cycled through displaying a bunch of fonts on the screen. Here is a video of the results:


As you can see some fonts are affected more than others. A few have over half the line cut off. I started digging into the code that displays the text. I figured out the code is determining the height and width of the text and then turning the text into an image to be displayed on the screen. This is done so text can easily be rotated on the display.

Line number 17 in this snippet of code is where the height and width is determined before making the image.
The Adafruit library is using PIL (Python Image Library) to create an image from the text. Ubuntu 14.04 actually uses a fork of PIL called Pillow. I did some google searches and discovered that the textsize function has a bug that does not account for the font offsets which causes the clipping on some fonts. The Ubuntu 14.04 I installed on my BBB came with Pillow 2.3.0 which was broken. I updated it to latest available package which was Pillow 2.5.3 and it was still broken. I looked at the bug fix on the master branch of Pillow and it was just a small change to one file, PIL/ImageFont.py, so I decided to apply that change to my 2.5.3 install of Pillow.

Here is how I fixed it.

cd /usr/local/lib/python2.7/dist-packages/PIL
sudo vi ImageFont.py

At about line 142 look for the getsize function. Here is what it looked like before the change.




And here it is after the change.


Save the file and then you need to compile it into python byte-code.
sudo pycompile ImageFont.py

This creates an ImageFont.pyc file. Now to test it again.



All fixed! I'm sure that fix to getsize will be pushed out soon so this won't be a problem in the future but until then this will let me continue my experimentation.



Monday, October 7, 2013

Nagios monitoring for Amazon SQS queue depth

I have found that a bunch of messages stacking up in my SQS queue's can be the first sign of something breaking. Several things can cause messages to stack up in the queue. I have seen malformed messages, slow servers and dead processes all cause this at different times. So to monitor the queue depth I wrote this Nagios check / plug-in. The check simply queries the SQS api and finds out the count of messages in each queue. Then it compares the count to the warning and critical levels.

This check is written in python and uses the boto library. It includes perfdata output so you can graph the number of messages in the queue. The the AWS API for SQS does wildcard matching of queue names so you can monitor a bunch of queues with one check if they have some sort of common prefix to the name. The way I use this is I have several individual checks using the complete explicit name of the queue and then a catchall using a wildcard set to a higher number that will catch any queues that have been added. Make sure you have a .boto file for the user that will be running this nagios check. It only requires read permissions.

Some queues may be more time sensitive than others. That is the case for my setup. For queues that are time sensitive I set the warning and critical counts to low values. Less time sensitive queues are set to higher count values. This screenshot is an example of that:


Config


Here is the command definition I use for Naigos:

# 'check_sqs_depth' command definition
define command{
        command_name    check_sqs_depth
        command_line    /usr/lib/nagios/plugins/check_sqs_depth.py --name '$ARG1$' --region '$ARG2$' --warn '$ARG3$' --crit '$ARG4$'
        }

and here is the service definition I'm using

define service{
        use                                 generic-service   
        host_name                      sqs.us-east-1
        service_description          example_name SQS Queue
        contact_groups                admins,admins-page,sqs-alerts
        check_command             check_sqs_depth!example_name!us-east-1!150!300!
        }


Code


The code is available on my github nagios-checks repository here: https://github.com/matt448/nagios-checks and I have posted it as a gist below. My git repository will have the most up-to-date version


Saturday, May 25, 2013

Monitor S3 file ages with Nagios


I have started using Amazon S3 storage for a for a couple different things like static image hosting and storing backups. My backup scripts tar and gzip files and then upload the tarball to S3. Since I don't have a central backup system to alert me of failed backups or to delete old backups I needed to handle those tasks manually. S3 has built in lifecycle settings which I do utilize but as with everything AWS it doesn't always work perfectly. As for alerting on failed backups I decided to handle that by watching the age of the files stored in S3 bucket. I ended up writing a Nagios plugin that can monitor both the minimum and maximum age of files stored in S3. In addition to monitoring the age of backup files I think this could also be useful in monitoring the age of files if you use an S3 bucket as a temporary storage area for batch processing. In this case old files would indicate a missed file or possibly a damaged file that couldn't be processed.

I wrote this my favorite new language Python and used the boto library to access S3. The check looks through every file stored in a bucket and checks the file's last_modified property against the supplied min and/or max. The check can be used for either min age, max age or both. You will need to create a .boto file in the home directory of the user executing the Nagios check with credentials that have at least read access to the S3 bucket.

The check_s3_file_age.py file is available on my github nagios-checks repository here: https://github.com/matt448/nagios-checks.

To use this with NRPE add an entry something like this:

command[check_s3_file_age]=/usr/lib/nagios/plugins/check_s3_file_age.py --bucketname myimportantdata --minfileage 24 --maxfileage 720

Here is output from --help:

./check_s3_file_age.py --help

usage: check_s3_file_age.py [-h] --bucketname BUCKETNAME
                            [--minfileage MINFILEAGE]
                            [--maxfileage MAXFILEAGE] [--listfiles] [--debug]

This script is a Nagios check that monitors the age of files that have been
backed up to an S3 bucket.

optional arguments:
  -h, --help            show this help message and exit
  --bucketname BUCKETNAME
                        Name of S3 bucket
  --minfileage MINFILEAGE
                        Minimum age for files in an S3 bucket in hours.
                        Default is 0 hours (disabled).
  --maxfileage MAXFILEAGE
                        Maximum age for files in an S3 bucket in hours.
                        Default is 0 hours (disabled).
  --listfiles           Enables listing of all files in bucket to stdout. Use
                        with caution!
  --debug               Enables debug output.


I am a better sys admin than I am a programmer so please let me know if you find bugs or see ways to improve the code. The best way to do this is to submit an issue on github.

Here is sample output in Nagios

Monday, March 18, 2013

Template Nagios check for a JSON web service

I wrote two different custom Nagios checks for work last week and realized I could make a useful template out of them. After writing the first check I was able to reuse most of the code for the second check. The only changes I had to make had to do with the data returned. So I decided to make this into a generic template that I can reuse in the future. The check first verifies that the web service is responding correctly and then checks various data returned in JSON format.  While writing this template I found this really cool service (www.jsontest.com) that let me code against a service available to anyone who wants to try out this Nagios check before customizing it. This is the first time I have used Python's argparse function and I have to say it is fantastic. It makes adding command line arguments very easy and the result is professional looking.

My github repo can be found here: https://github.com/matt448/nagios-checks

Here is the code in a gist:

Friday, July 6, 2012

pyMCU and DS18B20 temperature sensors

As part of my Garage Monitor project I am using a Maxim DS18B20 digital temperature sensor with a pyMCU microcontroller. I found lots of pages and sample code for using the DS18B20 with Arduino boards and various other microcontrollers but nothing for the pyMCU. The pyMCU uses a Python library published by the board manufacturer to control it. The DS18B20 uses the 1-Wire communication bus and the pyMCU Python library includes functions for 1-Wire communication called owWrite and owRead. The tricky part is figuring out the command sequence to coax a temperature reading out of the sensor. The communication with the sensor involves writing several hex values to it and then reading a hex temperature value. That hex value is then converted to a decimal value.

It took me the better part of three evenings to figure out the sequence of writes and reads. The other thing that was a bit difficult was figuring when to send reset pulses.

The pyMCU 1-Wire function uses this format:

owWrite(pinNum, mode, writeData)

The mode value determines if a reset pulse is sent before or after the data. Here is how I wired it:


And is the code that I wrote:

#!/usr/bin/python
#
# Written by Matthew McMillan
# matthew.mcmillan at gmail dot com
#
# This code reads a temperature value from
# a DS18B20 sensor using a pyMCU board. It only
# reads from a single sensor.
#
import pymcu           # Import the pymcu module

# Function to read value from DS18B20 temperature sensor
# Need to pass digital pin number and flag for F or C units
#
# 0x33  READ ROM. Read single device address.
# 0xCC  SKIP ROM. Address all devices on the bus
# 0x44  CONVERT T. Initiate temperature conversion
# 0xBE  READ SCRATCHPAD. Initiate read temp stored in the scratchpad.

def readtemp(pin,ForC):
    mb.owWrite(pin,1,0x33)
    ReadVal = mb.owRead(pin,0,8)
    mb.owWrite(pin,1,0xCC)
    mb.owWrite(pin,0,0x44)
    mb.owWrite(pin,1,0xCC)
    mb.owWrite(pin,0,0xBE)
    ReadVal = mb.owRead(pin,0,12)
    HexVal1 = hex(ReadVal[1])
    HexVal2 = hex(ReadVal[0])
    HexVal1 = HexVal1[2:4]
    HexVal2 = HexVal2[2:4]
    HexVal = '0x'+HexVal1+HexVal2
    DecVal = int(HexVal, 0)
    TempC = (DecVal * 0.0625)
    if ForC:
        TempF = ((TempC*9)/5)+32
        TempFR = round(TempF, 1)
        return TempFR
    else:
        TempCR = round(TempC, 1)
        return TempCR

################
# Main program
################

# Initialize mb (My Board) with mcuModule Class Object
mb = pymcu.mcuModule() 

#  Need to pass digital pin number and flag for ForC
tempout = readtemp(7,1)

print 'Temp: ' + str(tempout)


Please leave a comment if you found this helpful. I found this page on hackaday.com very helpful in figuring this out.

Saturday, March 17, 2012