Here is an example of how to use a TFT screen to control the pan and tilt of a Raspberry Pi camera.
Git repository here
The code can be pulled down to your Raspberry Pi with;
There are a number of elements in place to get this working;
- Detecting input events on the touchscreen.
- The use of double buffering for the framebuffer.
- Using fbcp(framebuffer copy) to copy camera image to back buffer. fbcp source has been integrated into the code above.
- Updating back buffer with text and buttons.
- Forking the actual process that starts recording.
- Create a unique file name fore each recording.
- Software PWM to control servers. (ServoBlaster)
The code has been well documented, so I will only cover imported snippets below.
Drawing buttons
The function below is used to draw the buttons and slider outlines to the display.
void drawButton(int x, int y, int w, int h, char *text, int backgroundColor, int foregroundColor);
x & y are the top left coordinates of the button.
w is width.
h is height.
The structure buttons holds all button data.
struct buttons { int coords[5]; int currentPosition[4]; int timer; int delta; int oldScaledData[2]; };
Coords
Looking at these defined values will help you understand what is stored in coords[5]
//used for button coordinates #define X 0 //Top left corner X position #define Y 1 //Top left corner Y position #define W 2 //Button width #define H 3 //Button height #define L 4 //Slider length
Using the values that are found in the above structure for the "PAN"button, we can easily draw a button using drawButton();
drawButton(button[0].coords[X],button[0].coords[Y],button[0].coords[W],button[0].coords[H],"PAN",GREY,BLUE);
The coordinate values int he structure are;
{ button[0].coords[X]=10, button[0].coords[Y]=239, button[0].coords[W]=80, button[0].coords[H]=80, button[0].coords[L]=200,
[wp_ad_camp_4]
We can also use drawButton() to draw the outline of the sliders. Just need to specify no text and have a black background. Below is what we can use to draw a slider outline around the "PAN" button;
drawButton(button[0].coords[X]-1, button[0].coords[Y]-1, button[0].coords[W]+button[0].coords[L]+2, button[0].coords[H]+2, "",RED,BLACK);
This is what the above is doing;
Line 1: One less from X and Y position so that the slider appears outside the PAN button.
Line 2: Button 0 is PAN, so we use the width of the button plus the length of the slide. Add an extra 2 to display it outside of the button.
Line 3: Add two to height so the outline appears outside the PAN button.
Line 4: No text, red outline and black fill.
Keep Track of Sliding Buttons
button[0].delta is used to keep track of the sliding buttons and is used to update the current position (button[0].currentPosition[X]).
Has the Button Been Pressed?
In each loop, we check to see if there are any touch events are happening within any of the button areas.
Below is used to detected touch within the "PAN" button area.
//Does the current X input fall between the current X position and the width of the button? //Does the current Y input fall between the current Y position and the height of the button? if((scaledX > button[0].currentPosition[X] && scaledX < ( button[0].currentPosition[X] + button[0].coords[W])) && (scaledY > button[0].coords[Y] && scaledY < (button[0].coords[Y]+button[0].coords[H])))
Pan and Tilt
Servoblaster is used to provide PWM to control the servos. The code for this can be found in servo.c.
The servo angles are controled by writing a percentage value to /dev/servoblaster
Which can be replicated from the command prompt with;
To get the corresponding values from the button location as a percentage, we just need to device the button delta by the button slide length and height or width.
float panValue = (float)button[0].delta/ (button[0].coords[L]+button[0].coords[W]); float tiltValue = (float)button[1].delta /(button[1].coords[L]+button[1].coords[H])
Capture Video
When we want to start capturing video, we spawn a new process. If we dont, our program will stop running while the video is being captured.
We also append the date and time to the file name so that each file is unqie and the last file recorded will not be overwritten.
Feel free to change the values in parmList[] to how you like the video to be recorded.
void videoCapture() { char str[100]; char timeAndDate[100]; time_t now = time(NULL); struct tm *t = localtime(&now); //Create a string with the current time and date. This will be used to append to the file name strftime(timeAndDate, sizeof(timeAndDate)-1, "%Y%m%d%H%M%S", t); sprintf(str, "video-%s.h264", timeAndDate); //Parameter list used in raspivid char *parmList[] = {"/opt/vc/bin/raspivid","-v","-t", "0","-rot","180","-h","720","-w","1280","-o", str, NULL}; //Create child process that does the recording if (pid == 0) if ((pid = fork()) == -1) perror("fork error"); else if (pid == 0) execv("/opt/vc/bin/raspivid", parmList); }
Compile the code
The code in the git repository can be complied with;
Further Reading
Programming for a Touchscreen on the Raspberry Pi
Controlling the GPIO on a Raspberry Pi with a Touchscreen
ServoBlaster
Pi-Pan, a Pan-Tilt Kit for Raspberry Pi Camera
[wp_ad_camp_1]
Hi There,
Do you have any suggestion for creating a simple machine using raspberry pi, to demonstrate errors in codes or hardware.
I look forward to hearing from you
Thank you very much
Tek1
I keep getting the error: “Error opening /dev/servoblaster” – any suggestions on what I can do to make it work?