You are viewing outdated content for BUG. If you have a BUG Y.T. edition or 2.0 series device, please visit our updated wiki: http://wiki.buglabs.net
Develop with Python
From BUG Wiki
Contents |
Introduction
Python is an excellent language to use on the bug, as it is extremely easy to learn, easy to work with, and is able to access many of the lower level system calls and hardware functions that are unreachable from java. Even better, python is an interpreted language, so you don't need any compilers or cross-compilers, and you dont need to move binaries on and off the bug each time you want to test things out. You can just pull up a python shell and start throwing commands at the bug - only a USB cable and an ssh client necessary.
The scope of python development on the bug would be somewhere between the low level c code that makes up the linux kernel and provides hardware drivers, and the high level java interface capable of gui programming and complex user interaction. Python is excellent for testing out a new hacked-up sensor you want to play around with, or running in the background, polling things for data and logging or transmitting information. As such, this document will focus on the meat of python development on the bug - interfacing with modules and other hardware resources available on the bug base. It will evolve as more python libraries are written and more is discovered about how to use python with the bug.
Hardware communication
Overview of Architecture
Before getting started developing with python, it is important to gain an understanding of how to communicate with bug modules and hardware.
The bug rootfs and kernel include several kernel drivers which can be used by users to communicate directly with attached bug hardware. These drivers have been specially written for each module, and are automatically loaded and unloaded when a given module is inserted. If everything is working correctly, the drivers can be accessed via block devices in the /dev directory. For example, my VonHippel module appears as /dev/bmi_vh_control_m1 (note, the block device name / access may change with subsequent rootfs releases).
The block device is what allows users to access specific driver functions and direct control of the hardware through system calls. The nice thing about these system calls is that they are somewhat language specific - you don't necessarily need to include the source code for the kernel in order to use them. You may need to get information from it, but you don't need to include it. All you need is a language that can use standard system calls like open() and ioctl(), and information about those system calls (like the ioctl group, type and data structure).
Getting Started
Requirements
the great thing about python is that development can be done right on the bug - no cross-compiler or special software needed. All you need is some kind of terminal connection to the bug. Assuming you have followed the start instructions, you can use an ssh application like putty(windows) to ssh into root@10.10.10.10 with no password. Simply type python into the terminal to launch the python interpreter in interactive mode - ready to accept lines of code and commands.
Another resource you will probably need to consult would be the kernel driver header files. You can check our svn server. The newest testing version of this code can be found under /bug/trunk/bug-linux-2.6.27.2/include/linux/bmi/. I will reference several files from this directory.
Introduction to ioctl calls in python
Many features of bug modules are accessed by ioctl calls. You may want to look at the reference for fnctl.ioctl(). The arguments to this function seem simple, but can be tricky. The first argument is a file descriptor to the device you want to work with - fd. You will need to preform an open() command on the device, and supply the ioctl syscall with the file descriptor returned from the open() syscall.
fd = open('/dev/bmi_vh_control_m1','r')
This is an example with a vonhippel module pluged into slot 1. You will need to edit this depending on what device you are trying to work with.
The second and third arguments are where the magic happens. The second argument is a 32-bit integer that is packed to contain several pieces of information - the type of operation, the group of the ioctl (specific to the type of module), the ioctl type (specific to a given command), and the size of the argument data passed between the ioctl() call and the driver. When working in c, this argument is usually generated by an _IO(), an _IOR(), or an _IOW() macro. _IOR() is used to read data from the device, IOW() is used to write data to the device and _IO() is used to send a command with no additional data. Another macro for _IORW() exists as well, but will not be discussed here. Sadly, no standard python modules replicate these macros, but their function is not terribly hard to replicate.
Equivalent macro code
Through experimentation with the macros in c, I was able to determine the equivalent code in python. The following code works for _IOW() and _IOR() macros:
op = direction << (4*7) | size << (4*4) | group << (4*2) | type
The follow code works for the _IO() macro:
op = group << (4*2) | type
- direction is 0x4 for an _iow() macro, and 0x8 for an _ior() macro.
- group is an 8-bit unsigned character representing the type of ioctl interface being used. In the bug, each module has it's own group, defined in bmi_ioctl.h.
- type is an 8-bit unsigned character representing the command to use. Each module has several ioctl types defined, and can be found in each header file like bmi_vh.h
- size is defined as the size of the data passed from the user application back to the kernel. The kernel determines the number of bytes to transfer by passing the value in this argument to the sizeof operator. This argument is specified in bytes and requires some delicacy on two points:
- Python does not use the same datatypes as c or c++, so you cannot use them to transmit and recieve data. You must use the struct.pack() and struct.unpack() commands.
- Looking carefully though supplied c sourcecode, data is almost always passed to and from the ioctl() syscall using pointers or integers. On a c system, these would be 4 bytes. With python we don't use pointers to transmit or recieve data, and instead need to trick the ioctl() syscall into thinking we are. So, size is almost always 4.
You will be greeted by an 'Inappropriate ioctl for device' exception if you get any of these values wrong.
The third argument is used to get data to and from the driver interface. Because of point 1 above, this cannot be a standard python variable but must instead by a plain string packed using the struct.pack() method. If you are reading from the driver, you must pack this argument full of blank space (using the 'x' format) to the correct size. If you pack this too small, you may be missing data. If you pack it too large, you will get extra zeros or will be unable to unpack it correctly. If you are writing to the driver, you will need to pack this argument with the specific contents of the data you want to write.
A word on struct.pack()
You may be wondering what format to use for struct.pack(). The short answer is that you need to check the kernel module header file (like bmi_vh.h). Here is an example from a module that is currently under testing:
struct sensor_acc_rw { // see the datasheets unsigned char address; unsigned int count; // number of bytes to read (1 or 2) unsigned char data[2]; }; #define BMI_SENSOR_ACCWR _IOW(BMI_SENSOR_IOCTL, 0x1c, struct sensor_acc_rw *) // write Accelerometer
In this instance, the struct sensor_acc_rw is being passed to the _IOW macro as a pointer. Preforming a sizeof() command on a struct sensor_acc_rw * will always yield 4, as a standard pointer is 4 bytes on most c systems. So the size used in generating the 2nd argument would be 4. However, the struct itself is not 4 bytes in length. It is actually 10. You can experiment by pulling up a python console (by executing python on the bug), and executing the following code:
import struct pack = struct.pack('x') struct.unpack('BI2s')
Here, we are packing a dummy variable (pack) with a single blank character. Then we are asking python to try and unpack that variable with a specific format. The format was determined by looking at the struct from the c code. We will need one unsigned char 'B', one unsigned integer 'I', and a string of length 2 '2s'. Python will throw an error stating unpack requires a string argument of length 10, since it needs 10 bytes to pack or unpack that particular format. Now you know how much to pack into the variable if trying to read, and how to unpack the data when trying to write and read.
To recap the third argument to ioctl():
- if you are preforming a write command, simply pack a variable with data using the struct.pack() method, and the format from the c header file.
- if preforming a read command, pack a dummy variable with the precise number of bytes required for the struct.unpack() command. Then preform the ioctl() call, and unpack the result by using the struct.unpack() method on the returned data.
- if preforming a non-data write (using the _IO() macro) you dont need to use the third argument at all.
Available Code
- VonHippel(python) - A python library written by TheTerg for using the VonHippel module on the bug. You can use this module with any number of attached VonHippel modules to access many of their features. You can read and write to GPIO and IOX pins, Read from the ADC, control the adjustable voltage output, and write to the SPI bus.
Examples
do you need a change in mood when it comes to going to another place? are you tired of watching those the same old beach, scenery, and places? try this one and you will never regret it. and you will come over and over again. this may help you in your needs, try to see this link!!
Reading Base Button Values
To read the status of the buttons on the bugbase, you essentially need to poll '/proc/bugnav' for updates. /proc/bugnav is a system proc interface that reads out the status of all of the buttons on the base. Here are a few examples:
A basic example that waits for the M1 base button to be pressed:
from time import sleep #Some code that waits for the M1 button to be pressed... nav = open('/proc/bugnav','r') ret = nav.readlines() while ret[1][3] == '0': sleep(0.150) nav.seek(0,0) ret = nav.readlines() print "Button has been pressed!"
If you have the VonHippel python module copied onto your bug, this will turn on the iox pin and LED when the corresponding button on the left of the base is held down.
#********************************* from VonHippel import * from time import sleep v = VonHippel('/dev/bmi_vh_control_m1') f = open('/proc/bugnav','r') while(1): f.seek(0,0) lines = f.readlines() for line in lines: if line[0] == 'M' v.set_iox(int(line[1]) - 1,int(line[3])) sleep(0.150)
GUI
Using 2 screens
- here's a howto that can make use of the 2 screens in pygtk
Categories: Development | Software | Python | Tutorials
