Skip navigation

This was an interesting exercise to try to get simple camera capability for the BeagleBone Black directly (without USB) for some low-resolution imaging ideal for some machine vision use-cases, robotics and movement detection. The lower resolution means there is less data to process, and the opportunity to connect multiple cameras  (e.g. for stereo vision).

Here is an example image taken with the BBB.


This image was 160x120 pixels, and has been slightly corrected - although still quite washed out - and resized to 640x480 using bilinear scaling on a desktop but which could be done by the BBB – see here.

This is the actual unaltered image from the camera – the software saves it in .png format:


So, (as expected) the quality is not great (and probably not even well focussed – I didn’t check), but adequate for some use-cases. In the photo, Bert is about 55cm away from the camera, the top of his hair is 18cm from the floor and the furthest end of the blue block is 45cm away from the camera. Also, the image is flipped left-to-right currently; this is an easy fix of course. Note that some work will need to be done to make this a usable camera system.

How does it work?

There is not much to the implementation – it is a low-cost camera module (OV9655) - £9.58 from ebay (or £2.56 or less with no PCB), connected up to a buffer and then connected to the BBB. In other words, in much the same procedure as for the ADC here. The same buffer board was used.

This is a photo of the entire layout:


This is a close-up of the camera board (oriented with the connector at the top gave an image the correct way up (but with left-right flipped as mentioned earlier):


As you can see in the photo, some slight modifications need to be done – these are detailed further below. Note that the camera is the same one that is available on the STM32F4DIS-CAM module which is more expensive, but which won’t need the modifications.

Some buffering is needed as mentioned earlier and this is achieved with a 74LVC244A device and MC74VHC1GT50 (see the ADC post for information).

The diagram here shows the overall system:


On startup, the application initializes the camera (using I2C) and then starts off the PRU software.

The PRU code is very similar to the ADC example in that it sits and waits for a command, and then it captures data from the camera and dumps to shared RAM, ready for the Linux hosted application (called cam_app in the diagram) to pick up. There is a slight complication here, in that there is not much RAM available; there is just 12kbyte shared between the PRU and the ARM core. This means that it’s not possible to transfer the entire image in one go; I had to grab the image in 4 portions. The cam_app code sends a number between 0 and 3 to select the desired portion of image. To fix this requires a driver to change the RAM allocation.

The code dumps the data into a cairo buffer, and writes a file in png format. Although it works, the code (attached) is extremely untidy  and is just a quick prototype.

Hardware details

As mentioned, a low cost camera module was used. It requires some decoupling capacitors, otherwise expect it not to work at all. Even with decoupling, the camera only just worked with the extreme wiring in the photo earlier (time to make a PCB).

The STM32F4DIS-CAM documentation was used as a reference. The cheap ebay board was missing 1nF capacitors. The two photos here show where they need to be soldered (0603 sized capacitors).

The rear of the camera board requires two 1nF capacitors, where the yellow outlines are shown in the photo here. This is an unmodified board photo. The 1nF capacitors are just piggy-backed on top of the existing capacitors.


Below is a photo of the front of the camera board (unmodified board photo). Another 1nF capacitor is required at the yellow outline again piggy-backed.

At the red outline (i.e. across the two pins of the header), a 10nF and 1uF capacitor were both piggy-backed. Something higher than 1uF will be preferable.


At the blue circle in the diagram above, a 32MHz clock needs to be fed in; a 3.3V oscillator in a 7x5mm package worked fine. It can be seen glued upside-down in one of the photos earlier.

Buffers are needed for all the data lines (there are 8, marked D2..9), and preferably for the VSYNC, HREF and PCLK pins too. The table here shows the wiring from the BBB point of view, and how it is assigned to the camera (through the buffers). All pins are inputs apart from the buffer enabling pin. HDMI is disabled.


There was a lot of high-frequency noise on the VSYNC line. I tried to reduce it with a resistor in series, but I didn’t measure it in any detail to have much effect. I don’t know if it is related to the PCB layout and (likely!) the wiring. After a while, I gave up trying to resolve it on such a poorly constructed layout (a PCB is required), so the solution for this particular layout was to perform some de-glitching in software which worked well.

The longer term plan is to connect up at least two of these cameras to the BBB for additional flexibility, such as stereo vision capability. It would be easy to do, since the buffers have an enable pin.

Camera Protocol

The camera is capable of 1Mpixel imaging but for now, I set the camera to the very low resolution QQVGA (160x120 pixels) mode which is still useful for some applications, and to reduce the need for a driver to allocate more RAM.

Once the camera has been configured by the I2C-like interface for QQVGA mode, PCLK runs at 2MHZ continuously. This I2C library was used.

The camera configuration is stored in camctrl.c, and is just an array of data (register and data pairs) that is not annotated. However it can be decoded using the camera datasheet.

The video is comprised of frames, and each frame contains 120 lines. Each line contains 160 pixels.

Each pixel is in RGB 565 format, which means that 16 bits (two bytes) are used per pixel. So to summarize each line contains 160*2 bytes (320 bytes) and two bytes are sent, one after the other, per pixel.

In order to capture images, it is important to understand how the camera signals the start and end of frame, and the format of data for each line within a frame. The detail is described here.

Start of frame

The start of each frame begins with VSYNC going low. The diagram here shows VSYNC going low, and then after about 2.8msec the first line of data appears (i.e. when HREF goes low).

The diagram shows all the interesting signals; PCLK, VSYNC, and HREF. Only some of the data signals (D7,8,9) are shown. Note that signals D2..D9 will be used for the eight bits (D0 and D1 are not used in all modes).


Line format

The diagram here shows the data per line. At 2MHz the PCLK period is of course 0.5usec. Once HREF goes low, the line data is read. 320 bytes are read as mentioned earlier, per line of 160 pixels. Each burst of line data is 160usec long (i.e. 320bytes x 0.5usec) followed by 640usec delay until the next line.


The diagram below shows a zoomed-in view of the beginning of each line. It can be seen that each byte needs to be read on the rising edge of PCLK.


End of frame

The diagram below shows the last few lines before end of the frame. It can be seen that after the last line in a frame (i.e. 120th line) there is approximately a 990usec delay until VSYNC goes high to indicate that the frame is complete.


Not shown on the diagram above, VSYNC will stay high for about 800usec and then go low for the next start of frame indication.


Building and Running the Software

The code can be built by issuing make clean followed by make (if you make a change to the C source, type make clean first, becuase my makefile is broken - I have to fix it someday!). (Note - make sure the i2c library has been compiled and installed first). This will build three things; the cam_app software, the PRU code and a .dtbo file which is used to configure the pins. Copy the built .dtbo file into the /lib/firmware folder.

HDMI will need to be disabled (or, modify the code to use different pins available to PRU#0 - the code currently uses PRU#1). HDMI disablement is mentioned on the ADC page.

To run the code, first type source which will execute some commands to configure the pins using the .dtbo file.

Then, just type ./cam_app

The code will capture an image and dump to a file called img.png

Next Steps

It would be great to integrate the camera and buffer into a single module so that multiple cameras could be connected up to the BBB (and perhaps also a serializer for wiring convenience and distance). Or, a board with two cameras spaced eye-distance apart for 3D vision processing.

Note April 2017: There are different revisions of the BeagleBone Black since this post was written in 2013, and without testing them all, it is the user's decision whether to try this solution or not. The revision changes are listed here and a possible option for at least some revision boards is shown in the photo here but I've not investigated this. The newer BeagleBone Blue already has a battery connection, so that is another option.


This posting is about implementing a rechargeable battery system for the BeagleBone Black. It is simple, safe and very low cost (less than 6 Euro).




First, some brief information about the power circuitry on the BBB.


The BBB has a built-in power management IC (PMIC) based on the TI TPS65217C device. This device contains multiple switch-mode regulators and LDO regulators to provide all voltage levels needed for the entire board. It handles wake-up using a push-button fitted on the BBB. Automatic power-down via the button requires some software to be implemented (to do the equivalent of 'shutdown now' from the command line). When the button is pressed, an interrupt is generated and the microprocessor is supposed to query the PMIC (via I2C) to learn that the button was pressed, and kick off the shutdown sequence. In the event of a failure here, the power can be switched off by holding the button down for 8 seconds.

The IC also contains built-in battery charging capability.

Apart from the USB requirement of 5V, there is no need to run the BBB from 5V; it can happily run from a lower supply. This means that a single 3.7V cell could be used to power the entire board. No need to step-up to 5V or to run dual cells and step-down to 5V, both of which could have been inefficient.

Why is this useful?

It makes it an excellent platform for outdoor/portable use.

For indoor use a battery is useful because it serves as a backup supply in case the main power (from a mains powered supply or from USB) gets disconnected. It could prevent filesystem corruption. If the main supply gets disconnected, the battery immediately takes over. In fact, I permanently leave the battery connected even when I'm running from the mains supply, in case I forget to safely shutdown the board.

What battery can be used?

Any small Lithium Ion (Li-Ion) or Lithium Polymer (Li-Po) single cell can be used, preferably as long as it has a built-in protection circuit. If it doesn't have an in-built circuit, it is highly advisable that one with a built-in thermistor is used. A cell in the range 700mAH to around 2AH is advisable. The one that I used in the photo above is from Olimex part code BATTERY-LIPO1400mAh. It should last around 3.5 hours (EDIT: Now been measured, it lasts 2 hours 50 minutes - see notes below, and comments below) on a full charge (not measured) and should fully charge in around 2 hours. This particular battery is just the right size to fit in between the two rows of headers and is flush so that a cape can still be plugged on top. So, the entire thing can fit inside any enclosure.

You will also need a connector (see next section) and one resistor.

Construction detail

The BBB has four holes that are suitable for connecting up the battery. They are detailed in the BBB system reference manual (SRM):


This is what they look like:


The Olimex Li-Po has a built-in protection circuit, so I soldered a 10k resistor to TS and GND to simulate the thermistor. (EDIT: You may or may not wish to do this, please study Li-Po and use your own judgement - see comments below) It is desirable to use a connector for the Li-Po.

The LiPo connector was convenient to solder to pins TP6 and TP8, and then  a zero-ohm link between TP5 and TP6 on the underside.

Here is how it was done step-by-step (there are just two steps):

1. Solder the 10k resistor, and a zero-ohm link (both are achieved on the underside of the board) as shown here in the yellow box. These are simple 0603 resistors; I used a 1% tolerance resistor, but 5% should be fine.


2. Solder in the connector.

This is straightforward, but complicated slightly by the fact that the connector has 2mm pin spacing, but the board has 0.1" spacing. It means that the pins on the connector need to be splayed or bent into the correct spacing. The connectors are available in vertical mount or right-angle (RA) mount. If you use a vertical mount connector, there may not be enough space to splay the pins. Instead, I used a right-angle connector and bent the pins into a vertical orientation, and then I had space to bend the pins and still have the connector flush with the board as shown here. You can see another view of the connector from the first photo.


I'm fairly sure that the desired connector is JST 'PH' series.


That's it; plugging in the battery, the board worked instantly. Note that the Li-Po charge method is usually to have a constant current supplied to the battery until it reaches a certain, very precise voltage. After that the charger switches to a constant-voltage mode. For the Li-Po battery that I used, the charger needs to be set to 4.2V, but the BBB by default sets it to 4.1V (It can be set to 4.2V via I2C control but today it doesn't). I'm not sure what the impact of this is (beyond storing less charge), but I believe it is safe. I have been using it daily for three months and the battery is always cool to touch.

Controlling the PMIC

The TPS65217C PMIC is very programmable; it has dozens of configuration settings specifically for charging and it has safety timer capability. The PMIC is configured upon startup via I2C. There are three I2C busses on the BBB, and one is dedicated to on-board peripherals. Control of the PMIC is not normally possibly by the user; it requires driver code or possibly there is access by the device tree infrastructure. Checking the .dts file in /boot did not reveal how to control the battery charger functionality. There are two current Google Summer of Code (GSoC) projects that touch on PMIC:

1. IIO, ADC, PMIC, LCD debug/patchwork (summary page, blog page) - Zubair Lutfullah

2. MINIX I2C drivers (summary page, blog page) - Thomas Cort

Hopefully the guys working on the projects (Zubair and Thomas) can offer some advice on how to set the level to 4.2V. Zubair's project also includes how to use the in-built ADC inside the AM3359 to monitor voltages.


There really should be some more detail including measurements to show how long the battery lasts and to observe the charging status (via I2C reads). Unfortunately I didn't have time to collect this information. But I've been using it for three months regularly and it functions well.

EDIT: The following simple test was conducted using the Olimex battery referred to above.

1. Power up the BBB using the DC power supply and let it charge the battery while powering the BBB

2. After about 4-5 hours, the DC supply was disconnected, and a script was run on the BBB to log the current date/time to a file, every minute. The script would sleep in-between. The Ethernet connection was left up, and the BBB was left alone until the battery died, and then the log file was examined.


while true; do
date >> log.txt
sleep 60


The result was that the log file showed that the BBB ran for 2 hours 30 minutes before it died. After this, the test was repeated. The second time, it ran for 2 hours 50 minutes. The discrepancy may be because this battery has never been fully charged followed by such a long discharge, and so perhaps it is related to that. No test has been run with the Ethernet disconnected, but the BBB should run for longer in that case of course.

In a third test, the battery again ran for  2 hours 50 minutes (to within 1 minute). Again, this was with the Ethernet interface up.

Laser Goodies is Proud to announce our newest case for the BeagleBone Black.

The case is crafted out of five wood layers with a transparent top and base. Each layer is laser cut from high quality bitch plywood and once stacked they securely contain a Beagle Bone Black while leaving the primary ports, accessible and safe. Weighing only 3oz the case is lightweight and ready to protect your Beagle Bone Black. Held together by Steel no to bolts and nuts no tools are required for assembly or disassembly.

  • Slim Profile Still keeping Board Safe
  • Clear top and base to leave Beagle Bone Black visible
  • Etched port markings Leaves all ports accessible
  • Protects your Beagle Bone Black High-quality Birch Wood and Acrylic


Go Check it out at

I'm co-founder of a non-profit named Project LEAD (Learn Everything Applying DIY), and last month I met the venerable Drew Fustini (AKA pdp7) when I spoke at an event held at Google's Chicago HQ. Since then I've gotten a ton of E14's leftovers from Maker-Faires, events, Etc. This includes a BBB and (more importantly) a BBB touchscreen cape. Needless to say I was very excited to get a hold of these as normally they aren't things I would actively go out an purchase.


Project LEAD focuses on producing digital content mainly on YouTube and Facebook. Videos were the obvious thing to do. At the advice of Drew I'm going to share some of those videos.


A quick intro of the first start that is reccomended on the little sheet of paper provided w/ the BBB:


The BBB with its 3.5" LCD Touchscreen Cape:


And finally the BBB booting from a Mobile Power Pack (making it completely mobile)



Hopefully you enjoy these videos, and if you have any questions about how anything performed feel free to ask. Also, if you have any suggestions for future Project LEAD videos let me know.


John Moosemiller

This was a fun yet initially challenging experiment, to find a convenient way to read in data at a reasonably high speed on a BeagleBone Black. This photo shows the results from a mobile, showing a couple of sampled waveforms (100kHz and 1MHz sinewaves in this case).


This was another capture of the same signals on a PC (this is an older picture with a x10 probe so the amplitude is a little low in the photo - it should fill the screen).


What does it do?

In its current state, it grabs analog data from an ADC, and dumps it into memory on the BBB, ready to be displayed or further processed. It could be used for gathering analog information from sensors, CCDs or other data acquisition use-cases. To be reasonably useful, the desire was for it to support 20Mbytes/sec of data or more. It does achieve this, but it is for further study to find higher speed methods.

How does it work?

A few different ways were considered. The initial approach was using an FTDI device (USB interface). However the method described here just feeds data directly into the on-chip 200MHz PRU that is part of the Beaglebone’s AM3359 chip. Other methods are possible too.

It was also desired to have an external clock, so that the data could be sampled at a determinable jitter, so that it could be useful for frequency analysis, or maybe Software Defined Radio (SDR).                                                            

The overall approach that was taken is outlined here.                                                                                                                                                                                   


The analog signal was amplified and fed into a high-speed ADC (A parallel bus ADC is needed in order to achieve high throughput).

A pre-built amp+ADC board was used from KNJN (note: in my opinion it is not a good choice, because it is closed source; there are no circuit diagrams for it so it is hard to modify it, the datasheet is sparse and also it is expensive; better to construct one up manually).

A linux application (called adc_app in the diagram) was used to kick off the PRU code which reads in data and dumps to shared memory. Once complete, the adc_app stores it in a .csv format file.

I wanted to try out Node.js ever since a recent blog post, so some very basic code is used to create a HTTP server.  The real-time comms between the browser and web server is achieved using Socket.IO which is a way of passing arbitrary data.

A photo of the overall system:


A bit more detail:


The underside was more untidy!



ADC Detail

As mentioned, a ready-built amplifier and ADC board was used. The on-board oscillator was disabled, so that an external one could be fed in. I needed a clock of 20MHz or less, but I only had a 32MHz oscillator at hand and didn't want to wait (and the local Maplin store doesn’t sell any 3.3V-compatible logic to divide by two!) and I’m not entirely sure how long the shared memory write takes, and I did experience lost samples with the 32MHz oscillator. I plan on trying frequencies in the range 14MHz-20MHz to find the upper limit for missing no samples; for further study!

Note that some ADCs will have specific requirements related to the clock and duty cycle.

The ADC on this particular board was ADC08200 but an ADC08100 or ADC08060 could have been used at lower cost).


These were extremely important for two reasons. One reason is that the pins to the BBB that I wished to use need to be isolated during power-up, because they are used for selecting the boot method. If there was any unusual level on the pins upon power-up then the BBB will not boot from the eMMC. So, a tri-state buffer is needed.

The other reason is that there is a fair bit of capacitance and it is highly likely that the ADC may not be able to directly drive the pins at high speed. I actually came across this problem while trying to connect a camera to the BBB. I struggled for days without realising that the camera could not support the load. So, the buffers are likely to be essential for most designs using the pins that were selected. I used a 74LVC244A device as a buffer.

Note that the clock also needs a buffer, unless significant jitter is acceptable. No tri-state is required here, so I used a MC74VHC1GT50.

PRU code

The PRU code uses shared memory for communication. I designated a single byte of shared memory to be used for commands. When run, the PRU sits in a loop waiting for the command to instruct it to begin the data capture.  PRU GPI mode is used which allows inputs to be read at the processor speed of 200MHz with no varying latency. Just a few instructions are needed to populate the data into shared memory. No attempt was made to pack the data, and 32-bits are used to store the 8-bit sample. This is not such a bad idea, because in future the ADC could be swapped out to a (say) 10-bit ADC with no code change on the PRU.

There are two PRUs in the AM3359. There are a total of 12 PRU GPI capable pins available that are connected to PRU 1 which are brought out to port 8 on the BBB. So, this means that realistically 10 or 11-bit is about the limit for high-speed parallel ADCs connected in this manner. Still, at (say) 16MHz clock, this would equate to 20Mbytes/sec of data for a 10-bit ADC.

EDIT: See comments section - on PRU0, all 16 pins are in theory available).

The pins used are shared with the HDMI interface; it proved necessary to disable the HDMI interface by recompiling the device tree file in the /boot folder (EDIT: See Brian's comment below for a better method to disable HDMI). Since I wished to display the data using a web browser, I have no issue with losing the HDMI.

Once the data has been captured (2000 samples in this example), the command byte is acknowledged, so that the Linux hosted application can know that the PRU has completed. The PRU now sits and waits for a new instruction from the Linux hosted application.

These were the pins used. They were used as D[0..7], CLK and a *EN signal for controlling the octal tri-state buffer.


Linux hosted application

The adc_app program is very simple (C code); it downloads the assembled code into PRU1 and executes it. The resultant data in shared memory is dumped to a text file and then the program exits.

Node.js application

The Node.js application creates a HTTP server (no need for apache!) and a Socket.IO connection. This sits and waits for a connection from any web browser. Once it receives a connection, it will send a handshake and then wait for a ‘capture’ command from the web browser. It then calls the adc_app program. Once complete, it opens the file of captured data and transmits it over the Socket.IO connection line-by-line. This is very inefficient, but it is proof-of-concept code that could really be optimised.

Web Page

The web page served up contains some small bits of code to handle the Socket.IO connection and send a ‘capture’ request when a button is clicked and to display the received data with a canvas element and pixel manipulation.


In conclusion, it is possible to read analog data with low jitter at fairly high speeds without any external FIFO or logic (beyond a simple buffer IC), while continuing to run Linux applications such as a web server. It is also nice that a web-based UI can be rapidly created using Node.js.


Note: It is still for further study how long captures can be sustained and read off by the Linux application without any data loss. If it can be sustained, then it could be useful for SDR applications just about, although a higher speed (and better ADC) would be preferable.


Note2: The waveforms used to test out the system were generated by the same BBB using a low-cost ‘direct digital synthesis’ (DDS) board. That’s a subject for a later date.


Using the code

Disable the HDMI as mentioned in the comments.

You may need to install Socket.IO. Type this to install it:

npm install


Create a development folder and then the attached code can be unzipped to (say) /home/root/development/adc.


make clean
cp BB-BONE-HSADC-00A0.dtbo /lib/firmware/
node index.js

Then, navigate to http://xx.xx.xx.xx:8081/index.html


If you want to make subsequent changes, there is a bug in the makefile, and you will need to issue 'make clean' before typing 'make' whenever any change is made in the C code.

If you just want to reassemble the PRU code, type 'make pru' (no need for 'make clean').