Merge remote-tracking branch 'origin/emulator' into dev_aaron

This commit is contained in:
id101010
2015-06-01 18:51:27 +02:00
47 changed files with 2503 additions and 844 deletions

241
common/app/pixy_helper.c Normal file
View File

@@ -0,0 +1,241 @@
#include "pixy_helper.h"
#include "pixy.h"
#include "tft.h"
#include <stdlib.h>
static int renderBA81(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
static int saveBA81(FILE_HANDLE* handle, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
int pixy_render_full_frame(uint16_t x, uint16_t y) {
return pixy_render_cropped_frame(x,y,0,0,320,200);
}
int pixy_render_cropped_frame(uint16_t x, uint16_t y, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height) {
uint8_t* videodata;
int32_t response;
int32_t fourccc;
int8_t renderflags;
uint16_t xwidth;
uint16_t ywidth;
uint32_t size;
int return_value = pixy_command("cam_getFrame", // String id for remote procedure
INT8(0x21), // mode
INT16(xoffset), // xoffset
INT16(yoffset), // yoffset
INT16(width), // width
INT16(height), // height
END_OUT_ARGS, // separator
&response, // pointer to mem address for return value
&fourccc,
&renderflags,
&xwidth,
&ywidth,
&size,
&videodata, // pointer to mem address for returned frame
END_IN_ARGS);
if(return_value==0) {
return_value = renderBA81(x,y,xwidth,ywidth,size,videodata);
}
return return_value;
}
int pixy_save_full_frame(FILE_HANDLE* handle) {
return pixy_save_cropped_frame(handle,0,0,320,200);
}
int pixy_save_cropped_frame(FILE_HANDLE* handle, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height) {
uint8_t* videodata;
int32_t response;
int32_t fourccc;
int8_t renderflags;
uint16_t xwidth;
uint16_t ywidth;
uint32_t size;
int return_value = pixy_command("cam_getFrame", // String id for remote procedure
INT8(0x21), // mode
INT16(xoffset), // xoffset
INT16(yoffset), // yoffset
INT16(width), // width
INT16(height), // height
END_OUT_ARGS, // separator
&response, // pointer to mem address for return value
&fourccc,
&renderflags,
&xwidth,
&ywidth,
&size,
&videodata, // pointer to mem address for returned frame
END_IN_ARGS);
if(return_value==0) {
return_value = saveBA81(handle,xwidth,ywidth,size,videodata);
}
return return_value;
}
static void interpolateBayer(uint16_t width, uint16_t x, uint16_t y, uint8_t *pixel, uint8_t* r, uint8_t* g, uint8_t* b)
{
if (y&1)
{
if (x&1)
{
*r = *pixel;
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
*b = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
}
else
{
*r = (*(pixel-1)+*(pixel+1))>>1;
*g = *pixel;
*b = (*(pixel-width)+*(pixel+width))>>1;
}
}
else
{
if (x&1)
{
*r = (*(pixel-width)+*(pixel+width))>>1;
*g = *pixel;
*b = (*(pixel-1)+*(pixel+1))>>1;
}
else
{
*r = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
*b = *pixel;
}
}
}
static int renderBA81(uint16_t xpos, uint16_t ypos, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
{
uint16_t x, y;
uint8_t r, g, b;
// skip first line
frame += width;
// don't render top and bottom rows, and left and rightmost columns because of color
// interpolation
//uint32_t decodedimage[(width-2)*(height-2)];
uint16_t* decodedimage = malloc(sizeof(uint16_t)*(width-2)*(height-2));
if(decodedimage==NULL) { //not enough free space to decode image in memory
//decode & render image pixel by pixel
for (y=1; y<height-1; y++)
{
frame++;
for (x=1; x<width-1; x++, frame++)
{
interpolateBayer(width, x, y, frame, &r, &g, &b);
tft_draw_pixel(xpos+x-1,ypos+y-1,RGB(r,g,b));
}
frame++;
}
} else { //enough space
uint16_t* line = decodedimage;
for (y=1; y<height-1; y++)
{
//line = (unsigned int *)img.scanLine(y-1);
frame++;
for (x=1; x<width-1; x++, frame++)
{
interpolateBayer(width, x, y, frame, &r, &g, &b);
//*line++ = (0xff<<24) | (r<<16) | (g<<8) | (b<<0);
*line++ = RGB(r,g,b);
}
frame++;
}
tft_draw_bitmap_unscaled(xpos,ypos,width-2,height-2,decodedimage);
free(decodedimage);
}
return 0;
}
static int saveBA81(FILE_HANDLE* handle, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
{
uint16_t x, y;
uint8_t r, g, b;
uint32_t fpos = handle->fpos;
uint32_t row_size_padded = ((width-2)*3 + 3) & (~3); //row size aligned to 4 bytes
uint32_t fpos_end = fpos + row_size_padded* (height-2);
// skip first line
frame += width;
// don't render top and bottom rows, and left and rightmost columns because of color
// interpolation
for (y=1; y<height-1; y++)
{
frame++;
uint8_t rowbuf[row_size_padded];
//Bitmaps are saved "bottom-up". Seek to the right row.
if(filesystem_file_seek(handle,fpos_end-row_size_padded*y)!=F_OK) {
return -1;
}
for (x=1; x<width-1; x++, frame++)
{
interpolateBayer(width, x, y, frame, &r, &g, &b);
//bitmaps are saved in 24bit b,g,r format
rowbuf[(x-1)*3] = b;
rowbuf[(x-1)*3+1] = g;
rowbuf[(x-1)*3+2] = r;
}
if(filesystem_file_write(handle,rowbuf,row_size_padded)!=F_OK) {
return -1;
}
frame++;
}
return 0;
}
int pixy_cc_set_region(uint8_t signum, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height) {
int32_t response;
int return_value = pixy_command("cc_setSigRegion", // String id for remote procedure
INT32(0), // type = normal color code
INT8(signum),
INT16(xoffset), // xoffset
INT16(yoffset), // yoffset
INT16(width), // width
INT16(height), // height
END_OUT_ARGS, // separator
&response, // pointer to mem address for return value
END_IN_ARGS);
return return_value;
}

59
common/app/pixy_helper.h Normal file
View File

@@ -0,0 +1,59 @@
#ifndef PIXY_HELPER_H
#define PIXY_HELPER_H
#include <stdbool.h>
#include <stdint.h>
#include "filesystem.h"
/**
* Receives a fullsized frame from pixy and display's it on the display with the topleft corner at (x,y)
* @param x The x-Coordinate of the top left corner
* @param y The y-Coordinate of the top left corner
* @return 0 on success, otherwise the errorcode from pixy
*/
int pixy_render_full_frame(uint16_t x, uint16_t y);
/**
* Receives a cropped frame from pixy and display's it on the display with the topleft corner at (x,y)
* @param x The x-Coordinate of the top left corner to draw the image
* @param y The y-Coordinate of the top left corner to draw the image
* @param xoffset The x-Coordinate on the pixy image from where on you want the frame data
* @param yoffset The y-Coordinate on the pixy image from where on you want the frame data
* @param width The width of the image recorded from pixy
* @param height The height of the image recorded from pixy
* @return 0 on success, otherwise the errorcode from pixy
*/
int pixy_render_cropped_frame(uint16_t x, uint16_t y, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height);
/**
* Receives a fullsized frame from pixy and saves it to the given file in the 24bit (b,g,a) format.
* Use this method to write the bitmap-data part of a windows bitmap (.bmp).
* This method will neither open nor close the passed file.
* @param handle The file to write the data to. The file must be open and it should be seeked to the right position.
* @return 0 on success, otherwise the errorcode from pixy
*/
int pixy_save_full_frame(FILE_HANDLE* handle);
/**
* Receives a cropped frame from pixy and saves it to the given file in the 24bit (b,g,a) format.
* @param handle The file to write the data to. The file must be open and it should be seeked to the right position.
* @param xoffset The x-Coordinate on the pixy image from where on you want the frame data
* @param yoffset The y-Coordinate on the pixy image from where on you want the frame data
* @param width The width of the image recorded from pixy
* @param height The height of the image recorded from pixy
* @return 0 on success, otherwise the errorcode from pixy
*/
int pixy_save_cropped_frame(FILE_HANDLE* handle, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height);
/**
* Sets the color signature to the color in the selected region of the frame
* @param signum the color signature number (1..7)
* @param xoffset The x-Coordinate of the topleft point of the region
* @param yoffset The y-Coordinate of the topleft point of the region
* @param width The width of the region
* @param height The height of the region
* @return 0 on success, otherwise the errorcode from pixy
*/
int pixy_cc_set_region(uint8_t signum, uint16_t xoffset, uint16_t yoffset, uint16_t width, uint16_t height);
#endif /* PIXY_HELPER_H */

View File

@@ -70,6 +70,7 @@ static void enter(void* screen) {
y+=14;
}
filesystem_dir_close(dir);
y+=14;
@@ -129,46 +130,10 @@ SCREEN_STRUCT* get_screen_filetest() {
}
static void image_test() {
//Source: http://stackoverflow.com/a/17040962/2606757
FILE_HANDLE* file = filesystem_file_open("cpu.bmp");
if(file==NULL) {
if(!tft_draw_bitmap_file_unscaled(250,170,"cpu.bmp")) {
tft_print_line(10,180,BLUE,TRANSPARENT,0,"Could not open cpu.bmp");
return;
}
unsigned char info[54];
if(filesystem_file_read(file,info,54)!=F_OK) {
tft_print_line(10,180,BLUE,TRANSPARENT,0,"Could not read header of cpu.bmp");
filesystem_file_close(file);
return;
}
// extract image height and width from header
int width = *(int*)&info[18];
int height = *(int*)&info[22];
filesystem_file_seek(file,*(int*)&info[10]);
unsigned char data [width*4];
tft_draw_rectangle(100-1,160-1,100-1+width,160-1+height,BLACK);
for(int i = 0; i < height; i++)
{
filesystem_file_read(file,data,width*4);
for(int j = 0; j < width*4; j += 4)
{
unsigned char a = data[j];
unsigned char r = data[j+1];
unsigned char g = data[j+2];
unsigned char b = data[j+3];
if(a!=0) {
tft_draw_pixel(100+j/4,160+height-1-i,RGB(r,g,b));
}
}
}
filesystem_file_close(file);
tft_draw_rectangle(250-1,170-1,250-1+64,170-1+64,BLACK);
}

View File

@@ -6,7 +6,7 @@
/*@{*/
/**
* @defgroup filetest Filetest
* @defgroup filetest Filetest (Screen)
* The File-Test Screen tests the filesystem module. It read/writes from/to files and shows a bitmap
*/
/*@{*/
@@ -19,4 +19,5 @@
*/
SCREEN_STRUCT* get_screen_filetest();
/*@}@}*/
/*@}*/
/*@}*/

View File

@@ -7,7 +7,7 @@
/*@{*/
/**
* @defgroup guitest Guitest
* @defgroup guitest Guitest (Screen)
* The Gui-Test Screen tests the gui and the tft module.
*/
/*@{*/
@@ -20,4 +20,5 @@
*/
SCREEN_STRUCT* get_screen_guitest();
/*@}@}*/
/*@}*/
/*@}*/

View File

@@ -2,13 +2,35 @@
#include "screen_guitest.h"
#include "screen_pixytest.h"
#include "screen_filetest.h"
#include "screen_photomode.h"
#include "screen_tracking.h"
#include "button.h"
#include "tft.h"
#include "filesystem.h"
BUTTON_STRUCT b_guitest;
BUTTON_STRUCT b_pixytest;
BUTTON_STRUCT b_filetest;
BUTTON_STRUCT b_our_tracking;
BUTTON_STRUCT b_ref_tracking;
BUTTON_STRUCT b_photo_mode;
static void b_our_tracking_cb(void* button) {
tracking_set_mode(OUR_TRACKING);
gui_screen_navigate(get_screen_tracking());
}
static void b_ref_tracking_cb(void* button) {
tracking_set_mode(REFERENCE_TRACKING);
gui_screen_navigate(get_screen_tracking());
}
static void b_photo_mode_cb(void* button) {
gui_screen_navigate(get_screen_photomode());
}
static void b_guitest_cb(void* button) {
gui_screen_navigate(get_screen_guitest());
}
@@ -25,47 +47,108 @@ static void b_pixytest_cb(void* button) {
static void enter(void* screen) {
tft_clear(WHITE);
//button to reach guitest
b_guitest.base.x1=25; //Start X of Button
b_guitest.base.y1=45; //Start Y of Button
b_guitest.base.x2=AUTO; //b_guitest.base.x1+160; //Auto Calculate X2 with String Width
b_guitest.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_guitest.txtcolor=WHITE; //Set foreground color
b_guitest.bgcolor=HEX(0xDE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_guitest.font=0; //Select Font
b_guitest.text="Gui/Tft Test"; //Set Text (For formatted strings take sprintf)
b_guitest.callback=b_guitest_cb; //Call b_guitest_cb as Callback
gui_button_add(&b_guitest); //Register Button (and run the callback from now on)
//Heading
tft_print_line(10,10,BLUE,TRANSPARENT,1,"Discoverpixy");
tft_draw_line(0,40,319,40,BLACK);
//button to reach pixy test
b_pixytest.base.x1=150; //Start X of Button
b_pixytest.base.y1=45; //Start Y of Button
b_pixytest.base.x2=AUTO; //b_pixytest.base.x1+160; //Auto Calculate X2 with String Width
b_pixytest.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_pixytest.txtcolor=BLUE; //Set foreground color
b_pixytest.bgcolor=HEX(0x10DE10); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_pixytest.font=0; //Select Font
b_pixytest.text="Pixy Test"; //Set Text (For formatted strings take sprintf)
b_pixytest.callback=b_pixytest_cb; //Call b_pixytest_cb as Callback
gui_button_add(&b_pixytest); //Register Button (and run the callback from now on)
#define X_TAB 90
#define BUTTON_SPACING 7
//button to reach filesystem test
b_filetest.base.x1=240; //Start X of Button
b_filetest.base.y1=45; //Start Y of Button
b_filetest.base.x2=AUTO; //b_filetest.base.x1+160; //Auto Calculate X2 with String Width
b_filetest.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_filetest.txtcolor=WHITE; //Set foreground color
b_filetest.bgcolor=HEX(0x501EA0); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_filetest.font=0; //Select Font
b_filetest.text="File Test"; //Set Text (For formatted strings take sprintf)
b_filetest.callback=b_filetest_cb; //Call b_filetest_cb as Callback
gui_button_add(&b_filetest); //Register Button (and run the callback from now on)
//First line of buttons
#define Y_FIRST 60
tft_print_line(10,Y_FIRST,BLACK,TRANSPARENT,0,"Tracking:");
b_our_tracking.base.x1=X_TAB; //Start X of Button
b_our_tracking.base.y1=Y_FIRST-3; //Start Y of Button
b_our_tracking.base.x2=AUTO; //Auto Calculate X2 with String Width
b_our_tracking.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_our_tracking.txtcolor=WHITE; //Set foreground color
b_our_tracking.bgcolor=HEX(0xE30535); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_our_tracking.font=0; //Select Font
b_our_tracking.text="Our Tracking"; //Set Text (For formatted strings take sprintf)
b_our_tracking.callback=b_our_tracking_cb; //Call b_our_tracking when the button get's pressed
gui_button_add(&b_our_tracking); //Register Button (and run the callback from now on)
b_ref_tracking.base.x1=b_our_tracking.base.x2+BUTTON_SPACING;
b_ref_tracking.base.y1=Y_FIRST-3;
b_ref_tracking.base.x2=AUTO;
b_ref_tracking.base.y2=AUTO;
b_ref_tracking.txtcolor=WHITE;
b_ref_tracking.bgcolor=HEX(0xFF2151);
b_ref_tracking.font=0;
b_ref_tracking.text="Reference Tracking";
b_ref_tracking.callback=b_ref_tracking_cb;
gui_button_add(&b_ref_tracking);
//Second line of buttons
#define Y_SECOND Y_FIRST+25
tft_print_line(10,Y_SECOND,BLACK,TRANSPARENT,0,"Photo mode:");
b_photo_mode.base.x1=X_TAB;
b_photo_mode.base.y1=Y_SECOND-3;
b_photo_mode.base.x2=AUTO;
b_photo_mode.base.y2=AUTO;
b_photo_mode.txtcolor=WHITE;
b_photo_mode.bgcolor=HEX(0x21B1FF);
b_photo_mode.font=0;
b_photo_mode.text="Photo Mode";
b_photo_mode.callback=b_photo_mode_cb;
gui_button_add(&b_photo_mode);
//Third line of buttons
#define Y_THIRD Y_SECOND+25
tft_print_line(10,Y_THIRD,BLACK,TRANSPARENT,0,"Tests:");
b_guitest.base.x1=X_TAB;
b_guitest.base.y1=Y_THIRD-3;
b_guitest.base.x2=AUTO;
b_guitest.base.y2=AUTO;
b_guitest.txtcolor=BLACK;
b_guitest.bgcolor=HEX(0x00FA21);
b_guitest.font=0;
b_guitest.text="Gui Test";
b_guitest.callback=b_guitest_cb;
gui_button_add(&b_guitest);
b_pixytest.base.x1=b_guitest.base.x2+BUTTON_SPACING;
b_pixytest.base.y1=Y_THIRD-3;
b_pixytest.base.x2=AUTO;
b_pixytest.base.y2=AUTO;
b_pixytest.txtcolor=BLACK;
b_pixytest.bgcolor=HEX(0x00FA96);
b_pixytest.font=0;
b_pixytest.text="Pixy Test";
b_pixytest.callback=b_pixytest_cb;
gui_button_add(&b_pixytest);
b_filetest.base.x1=b_pixytest.base.x2+BUTTON_SPACING;
b_filetest.base.y1=Y_THIRD-3;
b_filetest.base.x2=AUTO;
b_filetest.base.y2=AUTO;
b_filetest.txtcolor=BLACK;
b_filetest.bgcolor=HEX(0x00FAC4);
b_filetest.font=0;
b_filetest.text="File Test";
b_filetest.callback=b_filetest_cb;
gui_button_add(&b_filetest);
//Bottom line
tft_draw_line(0,145,319,145,BLACK);
tft_print_line(10,150,BLUE,TRANSPARENT,0,"Powered by");
tft_draw_bitmap_file_unscaled(10,165,"pixy_small.bmp");
tft_draw_bitmap_file_unscaled(165,165,"stm_small.bmp");
}
static void leave(void* screen) {
gui_button_remove(&b_our_tracking);
gui_button_remove(&b_ref_tracking);
gui_button_remove(&b_photo_mode);
gui_button_remove(&b_guitest);
gui_button_remove(&b_pixytest);
gui_button_remove(&b_filetest);

View File

@@ -18,7 +18,7 @@
/*@{*/
/**
* @defgroup main Main
* @defgroup main Main (Screen)
* The Main Screen is the start-screen for the application
*/
/*@{*/
@@ -31,4 +31,5 @@
*/
SCREEN_STRUCT* get_screen_main();
/*@}@}*/
/*@}*/
/*@}*/

View File

@@ -0,0 +1,166 @@
#include "screen_photomode.h"
#include "screen_photomode_save.h"
#include "button.h"
#include "tft.h"
#include "touch.h"
#include "pixy.h"
#include "system.h"
#include "pixy_helper.h"
static bool pixy_connected = false; //Whether or not the pixy cam is currently connected
static BUTTON_STRUCT b_back; //Button to navigate back
static BUTTON_STRUCT b_save; //Button to save the current image
static TOUCH_AREA_STRUCT a_area; //Touch Area, where the frame is drawn. Used to drag the image around
static bool subMenu=false; //Whether or not we left the current screen for a submenu
//Callback for when the user presses the "back" button
static void b_back_cb(void* button) {
subMenu = false; //we're not entering a submenu
gui_screen_back(); //navigate back to the previous screen
}
//Callback for when the user presses the "save" button
static void b_save_cb(void* button) {
subMenu = true; //we're entering a submenu
gui_screen_navigate(get_screen_photomodesave()); //navigate to the save screen
}
static POINT_STRUCT pixy_pos; //The current position of pixy's servos
static POINT_STRUCT old_pos; //The last touch position on the screen
//Callback for when the user drags the image around
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
POINT_STRUCT p = touch_get_last_point(); //get the last touched point
switch(triggeredAction) {
case PEN_ENTER:
case PEN_DOWN:
old_pos = p; //If the user "newly" enters the touch area, we set the "last" position to the current
break;
case PEN_MOVE: //the user is moving around, he entered the screen a while ago (old_pos is set)
{
int16_t deltaX = p.x - old_pos.x; //Calculate x difference between last and current touch
int16_t deltaY = p.y - old_pos.y; //Calculate y difference between last and current touch
old_pos=p; //store the current touch point for the next time
//printf("%d %d\n",deltaX,deltaY);
if(pixy_connected) {
//Calculate new servo coordinates. 2 is just a proportional factor
int16_t new_x = pixy_pos.x+deltaX*2;
int16_t new_y = pixy_pos.y-deltaY*2;
//check limits
if(new_x<0) new_x=0;
if(new_x>1000) new_x=1000;
if(new_y<0) new_y=0;
if(new_y>1000) new_y=1000;
//set pixy_pos so that the main routine can send it to the servos
pixy_pos.x = new_x;
pixy_pos.y= new_y;
}
}
break;
case PEN_UP:
case PEN_LEAVE:
//printf("Leave/up\n");
break;
default: break;
}
}
//Callback for when the screen is entered/loaded
static void enter(void* screen) {
tft_clear(WHITE);
tft_print_line(5,5,BLACK,TRANSPARENT,0,"Drag the image around and ");
//Back button
b_back.base.x1=5; //Start X of Button
b_back.base.y1=19; //Start Y of Button
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_back.txtcolor=WHITE; //Set foreground color
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_back.font=0; //Select Font
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
b_back.callback=b_back_cb; //Call b_back_cb as Callback
gui_button_add(&b_back); //Register Button (and run the callback from now on)
//Save button
b_save.base.x1=190;
b_save.base.y1=3;
b_save.base.x2=AUTO;
b_save.base.y2=AUTO;
b_save.txtcolor=WHITE;
b_save.bgcolor=HEX(0x1010AE);
b_save.font=0;
b_save.text="Save it!";
b_save.callback=b_save_cb;
gui_button_add(&b_save);
//Frame Coordinates: topleft = (1,40); bottomright = (318,238)
//Leave a 10px border for the area
//Area to drag the image around
a_area.hookedActions = PEN_DOWN | PEN_MOVE | PEN_ENTER | PEN_UP | PEN_LEAVE;
a_area.x1 = 11;
a_area.y1 = 50;
a_area.x2 = 308;
a_area.y2 = 228;
a_area.callback = touchCB;
touch_register_area(&a_area);
//Pixy stuff
pixy_connected = (pixy_init()==0); //try to connect to pixy
if(pixy_connected && !subMenu) { //pixy is connected, but we are not coming from a submenu
pixy_pos.x=pixy_pos.y=500; //reset servo positions to center
}
}
//Callback for when the screen is left/unloaded
static void leave(void* screen) {
//remove buttons and touch area.
gui_button_remove(&b_back);
gui_button_remove(&b_save);
touch_unregister_area(&a_area);
}
//Callback for when the screen should be updated
//This is the main loop of the screen. This method will be called repeatedly
static void update(void* screen) {
//Note: The only way to detect that pixy has been disconnected is if a command fails. There's no pixy_is_connected method yet :'(
if(!pixy_connected) { //Pixy not connected
pixy_close(); //Ensure that all pixy resources are freed (failsafe)
if(pixy_init()==0) { //try to connect to pixy
pixy_connected=true;
if(!subMenu) { //we're not coming from a submenu
pixy_pos.x=pixy_pos.y=500; //reset servo positions to center
}
printf("pixy (re)initialized\n");
}
}
if(pixy_connected) { //If we are connected (now)
pixy_service(); //Handle pending pixy events (e.g. color info retrival)
pixy_render_full_frame(1,40); //render the pixy video at point (1,40)
//set the servo positions to the coordinates form the touch interrupt
pixy_rcs_set_position(0,pixy_pos.x);
pixy_rcs_set_position(1,pixy_pos.y);
}
}
//Declare screen callbacks
static SCREEN_STRUCT screen = {
enter,
leave,
update
};
SCREEN_STRUCT* get_screen_photomode() {
return &screen;
}

View File

@@ -0,0 +1,22 @@
#include "screen.h"
/**
* @addtogroup screens
*/
/*@{*/
/**
* @defgroup photomode Photo Mode (Screen)
* The Photo Mode Screen allows taking snapshots of the current pixy cam feed
*/
/*@{*/
/**
* Returns a pointer to the photomode screen
* \sa gui_screen_navigate
* @return
*/
SCREEN_STRUCT* get_screen_photomode();
/*@}*/
/*@}*/

View File

@@ -0,0 +1,301 @@
#include "screen_photomode_save.h"
#include "filesystem.h"
#include "button.h"
#include "tft.h"
#include "touch.h"
#include "pixy.h"
#include "pixy_helper.h"
#include <stdlib.h>
#include <string.h>
static BUTTON_STRUCT b_back; //Button to navigate back
static TOUCH_AREA_STRUCT a_area; //Touch area to select the save-file
//Callback for when the user presses the "back" button
static void b_back_cb(void* button) {
gui_screen_back();
}
static int num_files_ok; //number of files into which we can write the image (size, flags ok)
static enum {init, error, showlist, picking, saving, done} state; //Current state of the screen state machine
static int fontheight; //The space between one line of text to the next
static int liststart; //The y-Coordinate of the Start of the File-List
static const char* picked_file; //The filename picked by the user, to save the image to
//Linked list structure to save all files which are suitable for saving.
typedef struct FILE_LIST_ENTRY_S{
char* filename; //Name of the file
struct FILE_LIST_ENTRY_S* next; //Pointer to the next entry in the list or NULL
} FILE_LIST_ENTRY;
static FILE_LIST_ENTRY* files_ok; //Pointer to the head of the list
//Callback for when the user selects a file to save the image into
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
int y = touch_get_last_point().y-liststart; //Calculate the y-Coordinate of the touch point relative to the start of the file-list
int elem = y/fontheight; //Calculate the file index
if(elem<0 | elem>= num_files_ok) return; //Check if the file index is valid (0,1,..,num_files_ok-1)
//Search for the corresponding entry in the linked list
FILE_LIST_ENTRY* current_entry = files_ok; //Start walking through the list, starting by the head of the list
for(int i=0; i<elem; i++) { //Until we have reached the file (index)
current_entry= current_entry->next; //traverse to the next file
}
picked_file = current_entry->filename; //save the picked filename. It will be used by the statemachine in the main loop
touch_unregister_area(&a_area); //unregister the touch area, we no longer need it. No more interrupts will be fired.
state=saving; //Change the state of the statemachine
}
//Text-Lines to show if we have no matching files (num_files_ok==0)
static const char* nomatch_text [] = {
"Due to limitations of the filesystem",
"implementation you can only write to",
"existing files.",
"",
"The files need to have a .bmp",
"extension and must be at least",
"189410 bytes (185kb) large.",
"Unfortunately there were no such",
"files found in the root directory.",
"",
"Please create some files and come",
"back again.",
NULL
};
//Bitmap header for a 318x198x24bit windows bitmap. data starts at 0x7A (= after this header)
//This header has been taken from a white bitmap saved with gimp.
//Wikipedia has a pretty good description on the header: http://de.wikipedia.org/wiki/Windows_Bitmap
static unsigned char bmpheader_data[0x7A] = {
0x42, 0x4d, 0xe2, 0xe3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00,
0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x3e, 0x01, 0x00, 0x00, 0xc6, 0x00,
0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xe3,
0x02, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x47, 0x52, 0x73, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00
};
//Callback for when the screen is entered/loaded
static void enter(void* screen) {
tft_clear(WHITE);
#define X_OFS 5
//Back button
b_back.base.x1=X_OFS; //Start X of Button
b_back.base.y1=210; //Start Y of Button
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_back.txtcolor=WHITE; //Set foreground color
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_back.font=0; //Select Font
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
b_back.callback=b_back_cb; //Call b_back_cb as Callback
gui_button_add(&b_back); //Register Button (and run the callback from now on)
state =init; //Start with the init state
fontheight = tft_font_height(0)+2; //Save the height of the used font, for fast access
files_ok = NULL; //initialize the linked list with 0 elements
num_files_ok = 0; //we have zero! elements
}
//Callback for when the screen should be updated
//This is the main loop of the screen. This method will be called repeatedly
static void update(void* screen) {
switch(state) {
case init: //Init State: The user just entered the screen
{
DIRECTORY_STRUCT* dir = filesystem_dir_open("."); //open root directory
if(dir==NULL) { //error while opening root directory
tft_print_line(X_OFS,5,BLACK,TRANSPARENT,0,"Error accessing Filesystem");
state=error;
break;
}
bool nomatch= true; //whether or not we have zero files which are suitable for saving
for(int i=0; i<dir->num_files; i++) { //walk through all files in the directory
FILE_STRUCT* file = &(dir->files[i]); //Pointer to the current file/subdirectory
//Ignore directories, archives, hidden files, system files and files we cannot write to
if(file->fattrib&(F_SYS|F_HID|F_ARC|F_DIR|F_RDO)) continue;
//ignore files which are not large enough
if(file->fsize<189410) continue; //size taken from an example bitmap (318x198x24)
nomatch=false; //at least one file matches
break;
}
if(nomatch) { //not one file is suitable for writing
int y=5; //y-Coordinate where to start writing the error text
int i=0;
while(nomatch_text[i]!=NULL) { //for every line in the big error array
//Write the line's text and go to the next line
tft_print_line(X_OFS,y+i*fontheight,BLACK,TRANSPARENT,0,nomatch_text[i]);
i++;
}
state = error;
} else { //we have a least one suitable file
state = showlist;
}
filesystem_dir_close(dir); //free directory struct
}
break;
case showlist: //Show List State: Where we load and present the suitable file's to the user in a list
{
DIRECTORY_STRUCT* dir2 = filesystem_dir_open("."); //Open the directory again
if(dir2==NULL) return; //Error on opening? This should never happen, since it's handled in the previous state
int y = 5; //y-Coordinate where to start drawing/writing text/list-elements
tft_print_line(X_OFS,y,BLACK,TRANSPARENT,0,"Pick a file to save the image to");
y+=fontheight+5;
tft_print_line(X_OFS,y,BLUE,TRANSPARENT,0,"Name Modified Size");
y+=fontheight;
liststart = y; //store the y coordinate of the start of the list away (used in toucharea callback)
num_files_ok = 0; //we start with 0 matching files
FILE_LIST_ENTRY* current_entry = NULL; //We start with an empty list
for(int i=0; i<dir2->num_files && num_files_ok<10; i++) { //go through all the files of the directory, abort if we have 10 matches
FILE_STRUCT* file = &(dir2->files[i]);
//Ignore directories, archives, hidden files, system files and files we cannot write to
if(file->fattrib&(F_SYS|F_HID|F_ARC|F_DIR|F_RDO)) continue;
//ignore files which are not large enough
if(file->fsize<189410) continue; //size taken from an example bitmap (318x198x24)
//Print out filename, modified date,time and file size
tft_print_formatted(X_OFS,y,BLACK,
TRANSPARENT,0,"%-16s %02u.%02u.%02u %02u:%02u:%02u %u",
file->fname,
file->fdate.day,
file->fdate.month,
(file->fdate.year+1980)%100,
file->ftime.hour,
file->ftime.min,
file->ftime.sec*2,
file->fsize);
if(current_entry==NULL) { //The list is empty
current_entry = malloc(sizeof(FILE_LIST_ENTRY)); //create new entry
files_ok = current_entry; //assign it to the list head
} else { //there's a least one entry in the list
current_entry->next = malloc(sizeof(FILE_LIST_ENTRY)); //append entry to previous entry
current_entry = current_entry->next; //newly created entry is the current now.
}
current_entry->next = NULL; //we're at the end of the list (for now)
current_entry->filename = malloc(strlen(file->fname)+1); //allocate space for the filename + zero-termination
strcpy(current_entry->filename,file->fname); //copy filename (so that we can close the directory after scanning)
//since we have found a suitable file we need to increment the position in the list
num_files_ok++;
y+=fontheight;
}
//Touch area for file-selection (in the list)
a_area.hookedActions = PEN_UP; //we're only interested in PEN_UP events
a_area.x1 = X_OFS; //Left border
a_area.y1 = liststart; //Start where the list started
a_area.x2 = 320-X_OFS; //Right border
a_area.y2 = liststart+fontheight*num_files_ok; //stop at the end of the list
a_area.callback = touchCB; //execute our callback when PEN_UP occurs
touch_register_area(&a_area); //register the touch area and receive events from now on
filesystem_dir_close(dir2); //we no longer need the directory struct, since we have our own linked list now
state=picking;
}
break;
case picking: //Picking State: Where we wait on the users file choice
pixy_service(); //Handle pending pixy events
//do nothing and wait on user to pick a file
break;
case saving: //Saving State: Where we save the image to the selected file
{
FILE_HANDLE* file = filesystem_file_open(picked_file); //try to open the selected file
if(file==NULL) { //opening the file failed
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Could not open %s",picked_file);
state=error;
break;
}
filesystem_file_seek(file,0); //seek to the start of the file (optional?)
if(filesystem_file_write(file,bmpheader_data,0x7A)!=F_OK) { //Writing the header failed
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Error while writing to %s",picked_file);
filesystem_file_close(file);
state=error;
break;
}
if(pixy_save_full_frame(file)!=0) { //Writing the imagedata failed
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Error while writing to %s",picked_file);
filesystem_file_close(file);
state=error;
break;
}
//if we reach this point, we have written all data out successfully
filesystem_file_close(file); //close/finalize the file
tft_print_formatted(X_OFS,190,BLUE,TRANSPARENT,0,"Image saved to %s",picked_file);
state = done;
}
break;
case error: //Error State: Where we show an error message and leave the user no other choice than to click the backbutton
case done: //Done State: When saving the file was successful
pixy_service(); //Handle pending pixy events
//wait on user to click the back button
break;
}
}
//Callback for when the screen is left/unloaded
static void leave(void* screen) {
gui_button_remove(&b_back); //Remove/Free the back button
if(state==picking){ //The user left the screen in the "picking"-phase
touch_unregister_area(&a_area); //remove the touch area (for the list)
}
if(state==picking|| state==saving || state==done) { //the user left the screen after we created the linked list
//Iterate through the linked list and free all resources
FILE_LIST_ENTRY* current_entry = files_ok; //start with the list head
while(current_entry!=NULL) { //while we're not at the end
FILE_LIST_ENTRY* temp = current_entry->next; //save the next pointer because we free the current element on the next line
free((void*)(current_entry->filename)); //free filename
free(current_entry); //free element itself
current_entry= temp; //advance
}
}
}
//Declare screen callbacks
static SCREEN_STRUCT screen = {
enter,
leave,
update
};
SCREEN_STRUCT* get_screen_photomodesave() {
return &screen;
}

View File

@@ -0,0 +1,23 @@
#include "screen.h"
/**
* @addtogroup screens
*/
/*@{*/
/**
* @defgroup photomodesave Photo Mode Save (Screen)
* The Photo Mode Save Screen helps the user saving a file to the filesystem
*/
/*@{*/
/**
* Returns a pointer to the photomode save screen
* \sa gui_screen_navigate
* @return
*/
SCREEN_STRUCT* get_screen_photomodesave();
/*@}*/
/*@}*/

View File

@@ -1,173 +1,329 @@
#include "screen_pixytest.h"
#include "button.h"
#include "numupdown.h"
#include "tft.h"
#include "touch.h"
#include "pixy.h"
#include <stdlib.h>
#include "system.h"
#include "pixy_helper.h"
static volatile bool pixy_connected = false;
static volatile enum {detecting, idle,update_servos, update_ledcolor, update_ledcurrent} state; //Current state of the screen state machine
static BUTTON_STRUCT b_back;
static BUTTON_STRUCT b_runstop;
static TOUCH_AREA_STRUCT a_area;
static BUTTON_STRUCT b_servos_center;
static BUTTON_STRUCT b_servos_topleft;
static BUTTON_STRUCT b_servos_topright;
static BUTTON_STRUCT b_servos_bottomleft;
static BUTTON_STRUCT b_servos_bottomright;
static uint16_t servo_x;
static uint16_t servo_y;
static BUTTON_STRUCT b_led_off;
static BUTTON_STRUCT b_led_white;
static BUTTON_STRUCT b_led_red;
static BUTTON_STRUCT b_led_green;
static BUTTON_STRUCT b_led_blue;
static uint32_t led_color;
static uint32_t led_maxcurrent;
static NUMUPDOWN_STRUCT n_led_powerlimit;
static void b_back_cb(void* button) {
gui_screen_back();
}
static volatile bool pixy_running = false;
static bool old_pixy_running= false;
static void b_runstop_cb(void* button) {
pixy_running=!pixy_running;
}
static POINT_STRUCT pixy_pos;
static POINT_STRUCT old_pos;
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
POINT_STRUCT p = touch_get_last_point();
switch(triggeredAction) {
case PEN_ENTER:
case PEN_DOWN:
old_pos = p;
break;
case PEN_MOVE:
{
int16_t deltaX = p.x - old_pos.x;
int16_t deltaY = p.y - old_pos.y;
old_pos=p;
printf("%d %d\n",deltaX,deltaY);
if(pixy_connected) {
int16_t new_x = pixy_pos.x+deltaX*2;
int16_t new_y = pixy_pos.y-deltaY*2;
if(new_x<0) new_x=0;
if(new_x>1000) new_x=1000;
if(new_y<0) new_y=0;
if(new_y>1000) new_y=1000;
pixy_pos.x = new_x;
pixy_pos.y= new_y;
}
static void b_servos_center_cb(void* button) {
if(state==idle) {
servo_x=500;
servo_y=500;
state=update_servos;
}
break;
case PEN_UP:
case PEN_LEAVE:
printf("Leave/up\n");
default: break;
}
}
static void b_servos_topleft_cb(void* button) {
if(state==idle) {
servo_x=0;
servo_y=0;
state=update_servos;
}
}
static void b_servos_topright_cb(void* button) {
if(state==idle) {
servo_x=1000;
servo_y=0;
state=update_servos;
}
}
static void b_servos_bottomleft_cb(void* button) {
if(state==idle) {
servo_x=0;
servo_y=1000;
state=update_servos;
}
}
static void b_servos_bottomright_cb(void* button) {
if(state==idle) {
servo_x=1000;
servo_y=1000;
state=update_servos;
}
}
static void b_led_off_cb(void* button) {
if(state==idle) {
led_color=0x000000;
state=update_ledcolor;
}
}
static void b_led_white_cb(void* button) {
if(state==idle) {
led_color=0xFFFFFF;
state=update_ledcolor;
}
}
static void b_led_red_cb(void* button) {
if(state==idle) {
led_color=0xFF0000;
state=update_ledcolor;
}
}
static void b_led_green_cb(void* button) {
if(state==idle) {
led_color=0x00FF00;
state=update_ledcolor;
}
}
static void b_led_blue_cb(void* button) {
if(state==idle) {
led_color=0x0000FF;
state=update_ledcolor;
}
}
static void n_led_powerlimit_cb(void* numupdown, int16_t value) {
if(state==idle) {
led_maxcurrent=value;
state=update_ledcurrent;
}
}
static void enter(void* screen) {
tft_clear(WHITE);
//Back button
b_back.base.x1=10; //Start X of Button
b_back.base.y1=210; //Start Y of Button
b_back.base.x2=AUTO; //b_back.base.x1+160; //Auto Calculate X2 with String Width
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_back.txtcolor=WHITE; //Set foreground color
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_back.font=0; //Select Font
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
b_back.callback=b_back_cb; //Call b_back_cb as Callback
gui_button_add(&b_back); //Register Button (and run the callback from now on)
b_back.base.y1=210; //Start Y of Button
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_back.txtcolor=WHITE; //Set foreground color
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_back.font=0; //Select Font
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
b_back.callback=b_back_cb; //Call b_back_cb as Callback
gui_button_add(&b_back); //Register Button (and run the callback from now on)
//Back button
b_runstop.base.x1=60; //Start X of Button
b_runstop.base.y1=210; //Start Y of Button
b_runstop.base.x2=AUTO; //b_runstop.base.x1+160; //Auto Calculate X2 with String Width
b_runstop.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_runstop.txtcolor=WHITE; //Set foreground color
b_runstop.bgcolor=HEX(0xAE1010); //Set runstopground color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_runstop.font=0; //Select Font
b_runstop.text="Run/Stop"; //Set Text (For formatted strings take sprintf)
b_runstop.callback=b_runstop_cb; //Call b_runstop_cb as Callrunstop
gui_button_add(&b_runstop); //Register Button (and run the callrunstop from now on)
//Servo stuff
#define SERVO_BUTTON_Y 10
#define SERVO_BUTTON_SPACING 5
tft_print_line(5,SERVO_BUTTON_Y,BLACK,TRANSPARENT,0,"Servos:");
b_servos_center.base.x1=55;
b_servos_center.base.y1=SERVO_BUTTON_Y-3;
b_servos_center.base.x2=AUTO;
b_servos_center.base.y2=AUTO;
b_servos_center.txtcolor=WHITE;
b_servos_center.bgcolor=HEX(0xAE1010);
b_servos_center.font=0;
b_servos_center.text="Center";
b_servos_center.callback=b_servos_center_cb;
gui_button_add(&b_servos_center);
//Area test
a_area.hookedActions = PEN_DOWN | PEN_MOVE | PEN_ENTER | PEN_UP | PEN_LEAVE;
a_area.x1 = 0;
a_area.y1 = 0;
a_area.x2 = 317;
a_area.y2 = 197;
a_area.callback = touchCB;
touch_register_area(&a_area);
b_servos_topleft.base.x1=b_servos_center.base.x2+SERVO_BUTTON_SPACING;
b_servos_topleft.base.y1=SERVO_BUTTON_Y-3;
b_servos_topleft.base.x2=AUTO;
b_servos_topleft.base.y2=AUTO;
b_servos_topleft.txtcolor=WHITE;
b_servos_topleft.bgcolor=HEX(0xAE1010);
b_servos_topleft.font=0;
b_servos_topleft.text="ToLe";
b_servos_topleft.callback=b_servos_topleft_cb;
gui_button_add(&b_servos_topleft);
b_servos_topright.base.x1=b_servos_topleft.base.x2+SERVO_BUTTON_SPACING;
b_servos_topright.base.y1=SERVO_BUTTON_Y-3;
b_servos_topright.base.x2=AUTO;
b_servos_topright.base.y2=AUTO;
b_servos_topright.txtcolor=WHITE;
b_servos_topright.bgcolor=HEX(0xAE1010);
b_servos_topright.font=0;
b_servos_topright.text="ToRi";
b_servos_topright.callback=b_servos_topright_cb;
gui_button_add(&b_servos_topright);
b_servos_bottomleft.base.x1=b_servos_topright.base.x2+SERVO_BUTTON_SPACING;
b_servos_bottomleft.base.y1=SERVO_BUTTON_Y-3;
b_servos_bottomleft.base.x2=AUTO;
b_servos_bottomleft.base.y2=AUTO;
b_servos_bottomleft.txtcolor=WHITE;
b_servos_bottomleft.bgcolor=HEX(0xAE1010);
b_servos_bottomleft.font=0;
b_servos_bottomleft.text="BoLe";
b_servos_bottomleft.callback=b_servos_bottomleft_cb;
gui_button_add(&b_servos_bottomleft);
b_servos_bottomright.base.x1=b_servos_bottomleft.base.x2+SERVO_BUTTON_SPACING;
b_servos_bottomright.base.y1=SERVO_BUTTON_Y-3;
b_servos_bottomright.base.x2=AUTO;
b_servos_bottomright.base.y2=AUTO;
b_servos_bottomright.txtcolor=WHITE;
b_servos_bottomright.bgcolor=HEX(0xAE1010);
b_servos_bottomright.font=0;
b_servos_bottomright.text="BoRi";
b_servos_bottomright.callback=b_servos_bottomright_cb;
gui_button_add(&b_servos_bottomright);
//Led Color stuff
#define LED_COLOR_BUTTON_Y 35
#define LED_COLOR_BUTTON_SPACING 5
tft_print_line(5,LED_COLOR_BUTTON_Y,BLACK,TRANSPARENT,0,"Led Color:");
b_led_off.base.x1=85;
b_led_off.base.y1=LED_COLOR_BUTTON_Y-3;
b_led_off.base.x2=AUTO;
b_led_off.base.y2=AUTO;
b_led_off.txtcolor=WHITE;
b_led_off.bgcolor=BLACK;
b_led_off.font=0;
b_led_off.text="Off";
b_led_off.callback=b_led_off_cb;
gui_button_add(&b_led_off);
b_led_white.base.x1=b_led_off.base.x2+LED_COLOR_BUTTON_SPACING;
b_led_white.base.y1=LED_COLOR_BUTTON_Y-3;
b_led_white.base.x2=AUTO;
b_led_white.base.y2=AUTO;
b_led_white.txtcolor=BLACK;
b_led_white.bgcolor=HEX(0xEEEEEE);
b_led_white.font=0;
b_led_white.text="White";
b_led_white.callback=b_led_white_cb;
gui_button_add(&b_led_white);
b_led_red.base.x1=b_led_white.base.x2+LED_COLOR_BUTTON_SPACING;
b_led_red.base.y1=LED_COLOR_BUTTON_Y-3;
b_led_red.base.x2=AUTO;
b_led_red.base.y2=AUTO;
b_led_red.txtcolor=WHITE;
b_led_red.bgcolor=HEX(0xEE0000);
b_led_red.font=0;
b_led_red.text="Red";
b_led_red.callback=b_led_red_cb;
gui_button_add(&b_led_red);
b_led_green.base.x1=b_led_red.base.x2+LED_COLOR_BUTTON_SPACING;
b_led_green.base.y1=LED_COLOR_BUTTON_Y-3;
b_led_green.base.x2=AUTO;
b_led_green.base.y2=AUTO;
b_led_green.txtcolor=WHITE;
b_led_green.bgcolor=HEX(0x00EE00);
b_led_green.font=0;
b_led_green.text="Green";
b_led_green.callback=b_led_green_cb;
gui_button_add(&b_led_green);
b_led_blue.base.x1=b_led_green.base.x2+LED_COLOR_BUTTON_SPACING;
b_led_blue.base.y1=LED_COLOR_BUTTON_Y-3;
b_led_blue.base.x2=AUTO;
b_led_blue.base.y2=AUTO;
b_led_blue.txtcolor=WHITE;
b_led_blue.bgcolor=HEX(0x0000EE);
b_led_blue.font=0;
b_led_blue.text="Blue";
b_led_blue.callback=b_led_blue_cb;
gui_button_add(&b_led_blue);
//Led MaxPower stuff
#define LED_POWER_BUTTON_Y 70
tft_print_line(5,LED_POWER_BUTTON_Y,BLACK,TRANSPARENT,0,"Led Maximum Current:");
//Num up down test
n_led_powerlimit.x=160;
n_led_powerlimit.y=LED_POWER_BUTTON_Y-7;
n_led_powerlimit.fgcolor=WHITE;
n_led_powerlimit.value = 10;
n_led_powerlimit.max=40;
n_led_powerlimit.min =0;
n_led_powerlimit.callback=n_led_powerlimit_cb;
gui_numupdown_add(&n_led_powerlimit);
//Pixy stuff
pixy_connected = (pixy_init()==0); //try to connect to pixy
if(pixy_connected) {
pixy_pos.x=pixy_pos.y=500;
}
state=detecting;
}
static void leave(void* screen) {
gui_button_remove(&b_back);
gui_button_remove(&b_runstop);
touch_unregister_area(&a_area);
}
gui_button_remove(&b_servos_center);
gui_button_remove(&b_servos_topleft);
gui_button_remove(&b_servos_topright);
gui_button_remove(&b_servos_bottomleft);
gui_button_remove(&b_servos_bottomright);
gui_button_remove(&b_led_off);
gui_button_remove(&b_led_white);
gui_button_remove(&b_led_red);
gui_button_remove(&b_led_green);
gui_button_remove(&b_led_blue);
gui_numupdown_remove(&n_led_powerlimit);
int pixy_led_test();
int pixy_frame_test();
}
static void update(void* screen) {
//Note: The only way to detect that pixy has been disconnected is if a command fails. There's no pixy_is_connected method yet :'(
if(!pixy_connected) { //Pixy not connected
pixy_close(); //Ensure that all pixy resources are freed (failsafe)
if(pixy_init()==0) { //try to connect to pixy
pixy_connected=true;
pixy_pos.x=pixy_pos.y=500;
printf("pixy reinitialized\n");
}
}
if(pixy_connected) {
pixy_service(); //Send/receive event data from/to pixy failed
if(pixy_frame_test()!=0) {
pixy_connected=false;
}
/*if(pixy_led_test()!=0) {
pixy_connected=false;
}*/
if(!pixy_running) {
pixy_rcs_set_position(0,pixy_pos.x);
pixy_rcs_set_position(1,pixy_pos.y);
}
if(pixy_running!=old_pixy_running) {
old_pixy_running=pixy_running;
if(pixy_running) { //start tracking
int32_t response;
int return_value;
return_value = pixy_command("runprog", INT8(2), END_OUT_ARGS, &response, END_IN_ARGS);
} else { //stop tracking
switch(state) {
case detecting: //Detecting State: Where we try to connect to the pixy
if(pixy_init()==0) { //Pixy connection ok
int32_t response;
int return_value;
return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
pixy_led_set_max_current(10);
state = idle; //Go to next state
}
break;
case idle:
pixy_service();
break;
case update_servos:
pixy_rcs_set_position(0,servo_x);
pixy_rcs_set_position(1,servo_y);
state = idle;
break;
case update_ledcolor:
{
int32_t response;
int return_value;
return_value = pixy_command("led_set", INT32(led_color), END_OUT_ARGS, &response, END_IN_ARGS);
state = idle;
}
//system_delay(500);
}
break;
case update_ledcurrent:
pixy_led_set_max_current(led_maxcurrent);
state = idle;
break;
}
}
@@ -184,157 +340,3 @@ SCREEN_STRUCT* get_screen_pixytest() {
//-----------------------------------------------------------------------------------------------------------------
int colorind;
const uint32_t colors [] = {0xFF0000, 0x00FF00,0x0000FF,0xFFFF00,0x00FFFF,0xFF00FF,0xFFFFFF,0x000000};
const int num_colors = sizeof(colors)/sizeof(uint32_t);
int pixy_led_test() {
if(colorind==0) {
pixy_led_set_max_current(5);
}
int32_t response;
int return_value;
return_value = pixy_command("led_set", INT32(colors[colorind++]), END_OUT_ARGS, &response, END_IN_ARGS);
colorind%=num_colors;
if(return_value!=0) {
colorind=0; //reset color ind, to start at zero when plugging pixy in again
}
return return_value;
}
//----------------------------------------------------------------------------------------------------------------------------
int renderBA81(uint8_t renderFlags, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame);
int pixy_frame_test() {
uint8_t* videodata;
int32_t response;
int32_t fourccc;
int8_t renderflags;
uint16_t xwidth;
uint16_t ywidth;
uint32_t size;
int return_value = pixy_command("cam_getFrame", // String id for remote procedure
INT8(0x21), // mode
INT16(0), // xoffset
INT16(0), // yoffset
INT16(320), // width
INT16(200), // height
END_OUT_ARGS, // separator
&response, // pointer to mem address for return value
&fourccc,
&renderflags,
&xwidth,
&ywidth,
&size,
&videodata, // pointer to mem address for returned frame
END_IN_ARGS);
if(return_value==0) {
return_value = renderBA81(renderflags,xwidth,ywidth,size,videodata);
}
return return_value;
}
void interpolateBayer(uint16_t width, uint16_t x, uint16_t y, uint8_t *pixel, uint8_t* r, uint8_t* g, uint8_t* b)
{
if (y&1)
{
if (x&1)
{
*r = *pixel;
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
*b = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
}
else
{
*r = (*(pixel-1)+*(pixel+1))>>1;
*g = *pixel;
*b = (*(pixel-width)+*(pixel+width))>>1;
}
}
else
{
if (x&1)
{
*r = (*(pixel-width)+*(pixel+width))>>1;
*g = *pixel;
*b = (*(pixel-1)+*(pixel+1))>>1;
}
else
{
*r = (*(pixel-width-1)+*(pixel-width+1)+*(pixel+width-1)+*(pixel+width+1))>>2;
*g = (*(pixel-1)+*(pixel+1)+*(pixel+width)+*(pixel-width))>>2;
*b = *pixel;
}
}
}
int renderBA81(uint8_t renderFlags, uint16_t width, uint16_t height, uint32_t frameLen, uint8_t *frame)
{
uint16_t x, y;
uint8_t r, g, b;
// skip first line
frame += width;
// don't render top and bottom rows, and left and rightmost columns because of color
// interpolation
//uint32_t decodedimage[(width-2)*(height-2)];
uint16_t* decodedimage = malloc(sizeof(uint16_t)*(width-2)*(height-2));
if(decodedimage==NULL) { //not enough free space to decode image in memory
//decode & render image pixel by pixel
uint16_t* line = decodedimage;
for (y=1; y<height-1; y++)
{
frame++;
for (x=1; x<width-1; x++, frame++)
{
interpolateBayer(width, x, y, frame, &r, &g, &b);
tft_draw_pixel(x-1,y-1,RGB(r,g,b));
}
frame++;
}
} else { //enough space
uint16_t* line = decodedimage;
for (y=1; y<height-1; y++)
{
//line = (unsigned int *)img.scanLine(y-1);
frame++;
for (x=1; x<width-1; x++, frame++)
{
interpolateBayer(width, x, y, frame, &r, &g, &b);
//*line++ = (0xff<<24) | (r<<16) | (g<<8) | (b<<0);
*line++ = RGB(r,g,b);
}
frame++;
}
tft_draw_bitmap_unscaled(0,0,width-2,height-2,decodedimage);
free(decodedimage);
}
return 0;
}

View File

@@ -6,7 +6,7 @@
/*@{*/
/**
* @defgroup pixytest Pixytest
* @defgroup pixytest Pixytest (Screen)
* The Pixy-Test Screen tests the pixy module.
*/
/*@{*/
@@ -18,4 +18,5 @@
*/
SCREEN_STRUCT* get_screen_pixytest();
/*@}@}*/
/*@}*/
/*@}*/

View File

@@ -0,0 +1,349 @@
#include "screen_tracking.h"
#include "button.h"
#include "checkbox.h"
#include "tft.h"
#include "touch.h"
#include "pixy.h"
#include "system.h"
#include "pixy_helper.h"
static BUTTON_STRUCT b_back; //Button to navigate back
static BUTTON_STRUCT b_select; //Button to start the color region selection
static CHECKBOX_STRUCT c_frame_toggle; //Checkbox to toggle video data on/off
static TOUCH_AREA_STRUCT a_area; //Touch area for the color region selection
//Callback for when the user presses the "back" button
static void b_back_cb(void* button) {
gui_screen_back(); //navigate back to the previous screen
}
static volatile bool frame_visible = false; //Whether or not the video data should be displayed
static void c_frame_toggle_cb(void *checkbox, bool checked) {
frame_visible=checked; //Set the visibility of the frame to the checked state of the checkbox
//Frame will be drawn in the main loop below
}
static enum {detecting, init, tracking, preselecting, abortselecting, selecting, selected, error} state; //Current state of the screen state machine
static POINT_STRUCT point1; //First point of the rectangle selected by the user (color region selection)
static POINT_STRUCT point2; //End point of the rectangle selected by the user (color region selection)
static bool point1_valid; //Whether or not we have a valid first point
//Callback for when the user presses the "select color" button
static void b_select_cb(void* button) {
if(state==selecting) { //we're currently selecting a color region
state = abortselecting; //Abort selecting!!
} else if (state==tracking) { //we're currently watching the tracking
state = preselecting; //start selecting
}
}
//Video Region properties
//The camera records with 320*200px, but we need to keep a 1px border because of color interpolation (bayer format)
#define FRAME_START_X 1 //x-Coordinate of the top-left point of the frame rectangle on display
#define FRAME_START_Y 41 //y-Coordinate of the top-left point of the frame rectangle on display
#define FRAME_WIDTH 318 //Width of the video frame
#define FRAME_HEIGHT 198 //Height of the video frame
#define FRAME_END_X FRAME_START_X +FRAME_WIDTH-1 //x-Coordinate of the bottom-right point of the frame rectangle
#define FRAME_END_Y FRAME_START_Y +FRAME_HEIGHT-1 //y-Coordinate of the bottom-right point of the frame rectangle
//Callback for when the user touches the frame area to select a color region.
//Note: It doesn't matter in which direction the user draws the rectangle, we'll normalize the coordinates later
static void touchCB(void* touchArea, TOUCH_ACTION triggeredAction) {
POINT_STRUCT p = touch_get_last_point();
switch(triggeredAction) {
case PEN_DOWN: //The user just put down the pen
point1.x = p.x-FRAME_START_X; //Calculate x-Coordinate relative to frame start
point1.y = p.y-FRAME_START_Y; //Calculate y-Coordinate relative to frame start
point1_valid= true; //The point1 is now valid
break;
case PEN_UP: //The user took the pen away
if(point1_valid) { //only execute if point1 is valid
point2.x = p.x-FRAME_START_X; //Calculate x-Coordinate relative to frame start
point2.y = p.y-FRAME_START_Y; //Calculate y-Coordinate relative to frame start
state = selected;
}
break;
}
}
//Prototype for tracking start/stop methods
typedef void (*TRACKING_VOID_CALLBACK)(void* tracking_config);
//Prototype for tracking update method
typedef void (*TRACKING_BLOCK_CALLBACK)(void* tracking_config, struct Block* blocks, int num_blocks );
//Structure to save callbacks and settings of a tracking implementation
typedef struct {
TRACKING_VOID_CALLBACK start;
TRACKING_VOID_CALLBACK stop;
TRACKING_BLOCK_CALLBACK update;
} TRACKING_CONFIG_STRUCT;
//Methods for our tracking implementation ahead
//Method/Callback to start our tracking
void tracking_our_start(void* tracking_config) {
//Activate pixy's data send program
int32_t response;
int return_value;
return_value = pixy_command("runprog", INT8(0), END_OUT_ARGS, &response, END_IN_ARGS);
}
//Method/Callback to stop our tracking
void tracking_our_stop(void* tracking_config) {
//Stop pixy's data send programm
int32_t response;
int return_value;
return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
}
//Method/Callback to calculate one step of our tracking
void tracking_our_update(void* tracking_config, struct Block* blocks, int num_blocks) {
//TODO: Implement tracking!
//Calculate new servo pos and set the new servo pos
}
//Variable which stores all the callbacks and settings for our tracking implementation
static TRACKING_CONFIG_STRUCT tracking_our = {
tracking_our_start,
tracking_our_stop,
tracking_our_update
};
//Methods for reference tracking implementation ahead
//Method/Callback to start reference tracking
void tracking_reference_start(void* tracking_config) {
//Run reference tracking
int32_t response;
int return_value;
return_value = pixy_command("runprog", INT8(2), END_OUT_ARGS, &response, END_IN_ARGS);
}
//Method/Callback to stop reference tracking
void tracking_reference_stop(void* tracking_config) {
//Stop reference tracking
int32_t response;
int return_value;
return_value = pixy_command("stop", END_OUT_ARGS, &response, END_IN_ARGS);
}
//Method/Callback to calculate one step of the reference tracking
void tracking_reference_update(void* tracking_config, struct Block* blocks, int num_blocks) {
//Nothing to do here. Pixy does it all.
}
//Variable which stores all the callbacks and settings for the reference tracking implementation
static TRACKING_CONFIG_STRUCT tracking_reference = {
tracking_reference_start,
tracking_reference_stop,
tracking_reference_update
};
//Pointer to the currently active tracking implementation. See also tracking_set_mode
static TRACKING_CONFIG_STRUCT* tracking_current;
//Method to set the current tracking implementation. This function is exported and should be called before getting the screen
void tracking_set_mode(enum Tracking_Implementation impl) {
//Depending on the enum value let tracking_current point to a different setting/callback structure
switch(impl) {
case OUR_TRACKING:
tracking_current = &tracking_our;
break;
case REFERENCE_TRACKING:
tracking_current = &tracking_reference;
break;
default:
tracking_current=NULL;
break;
}
}
//Callback for when the screen is entered/loaded
static void enter(void* screen) {
tft_clear(WHITE);
//"Back" button
b_back.base.x1=5; //Start X of Button
b_back.base.y1=5; //Start Y of Button
b_back.base.x2=AUTO; //Auto Calculate X2 with String Width
b_back.base.y2=AUTO; //Auto Calculate Y2 with String Height
b_back.txtcolor=WHITE; //Set foreground color
b_back.bgcolor=HEX(0xAE1010); //Set background color (Don't take 255 or 0 on at least one channel, to make shadows possible)
b_back.font=0; //Select Font
b_back.text="Back"; //Set Text (For formatted strings take sprintf)
b_back.callback=b_back_cb; //Call b_back_cb as Callback
gui_button_add(&b_back); //Register Button (and run the callback from now on)
//"Select color" button
b_select.base.x1=150;
b_select.base.y1=5;
b_select.base.x2=AUTO;
b_select.base.y2=AUTO;
b_select.txtcolor=WHITE;
b_select.bgcolor=HEX(0xAE1010);
b_select.font=0;
b_select.text="Select Color";
b_select.callback=b_select_cb;
gui_button_add(&b_select);
//"Frame visible" checkbox
c_frame_toggle.base.x1 = 50;
c_frame_toggle.base.x2 = 50+16;
c_frame_toggle.base.y1 = 5;
c_frame_toggle.base.y2 = 5+16;
c_frame_toggle.checked = frame_visible;
c_frame_toggle.fgcolor = CHECKBOX_WIN_FG_COLOR;
c_frame_toggle.callback = c_frame_toggle_cb;
gui_checkbox_add(&c_frame_toggle);
tft_print_line(73,8,BLACK,TRANSPARENT,0,"Show Video");
//Area to select a "color region"
a_area.hookedActions = PEN_DOWN | PEN_UP;
a_area.x1 = FRAME_START_X;
a_area.y1 = FRAME_START_Y;
a_area.x2 = FRAME_END_X;
a_area.y2 = FRAME_END_Y;
a_area.callback = touchCB;
//Do not register it here, we do that later
if(tracking_current==NULL) {
state = error;
} else {
state = detecting; //Start with the detecting state
}
}
//Callback for when the screen is left/unloaded
static void leave(void* screen) {
//Remove buttons and checkbox
gui_button_remove(&b_back);
gui_button_remove(&b_select);
gui_checkbox_remove(&c_frame_toggle);
if(state==selecting) { //the user left the screen in the "selecting" phase
touch_unregister_area(&a_area); //remove the touch area
}
if(state==tracking) { //the user left the screen in the "tracking" phase
tracking_current->stop(tracking_current); //stop tracking
pixy_led_set_RGB(0,0,0);
}
}
//Callback for when the screen should be updated
//This is the main loop of the screen. This method will be called repeatedly
static void update(void* screen) {
switch(state) {
case detecting: //Detecting State: Where we try to connect to the pixy
if(pixy_init()==0) { //Pixy connection ok
state = init; //Go to next state
}
break;
case init: //Init State: Where we start the tracking
tracking_current->start(tracking_current);
state=tracking;
break;
case tracking: //Tracking state: Where we render the frame and the tracked objects
pixy_service(); //Receive events (e.g. block-data) from pixy
if(pixy_blocks_are_new()) { //There are new blocks available
if(frame_visible) { //If the user want's us to draw the video data
pixy_render_full_frame(FRAME_START_X,FRAME_START_Y);
} else { //the user want's a colored background
tft_fill_rectangle(FRAME_START_X,FRAME_START_Y,FRAME_END_X,FRAME_END_Y,RGB(200,200,200));
}
#define BLOCK_BUFFER_SIZE 5 //The maximum amount of blocks that we want to receive
struct Block blocks[BLOCK_BUFFER_SIZE]; //Storage to receive blocks from pixy
int blocks_received= pixy_get_blocks(BLOCK_BUFFER_SIZE,blocks); //Try to receive up to BLOCK_BUFFER_SIZE Blocks from pixy
if(blocks_received>=0) { //block receiving ok
tracking_current->update(tracking_current,blocks,blocks_received); //apply tracking
//Draw blocks
for(int i=0; i<blocks_received; i++) { //for each received block
struct Block* block = &(blocks[i]);
//block.x and block.y are the center coordinates of the object relative to the camera origin.
uint16_t x = block->x-1+FRAME_START_X -block->width/2; //Calculate x-Coordinate on the display
uint16_t y = block->y-1+FRAME_START_Y -block->height/2; //Calculate y-Coordinate on the display
tft_draw_rectangle(x,y,x+block->width-1, y+block->height-1,WHITE); //Draw a white rectangle
}
}
}
break;
case preselecting: //Pre-Selecting State: Where we set up the color region selection
{
tracking_current->stop(tracking_current); //Stop tracking
pixy_render_full_frame(FRAME_START_X,FRAME_START_Y); //Render one frame
touch_register_area(&a_area); //Register touch area and receive events from now on
point1_valid=false; //we start with an invalid point1
b_select.text="Abort"; //Change the button text to "Abort"
gui_button_redraw(&b_select); //redraw button
state = selecting; //The user can now select a region
}
break;
case selected: //Selected State: Where we send the users selection to pixy
{
//Ensure that (x1,y1) represent the top-left point and (x2,y2) the bottom-right.
unsigned int tmp;
if(point1.x > point2.x){
tmp = point1.x;
point1.x = point2.x;
point2.x = tmp;
}
if(point1.y > point2.y){
tmp = point1.y;
point1.y = point2.y;
point2.y = tmp;
}
//Send pixy the selected region
pixy_cc_set_region(1,point1.x,point1.y,point2.x-point1.x,point2.y-point1.y);
}
//no break here: We want the following code to be executed as well
case abortselecting: //Abort-Selecting State: Where we deinitialize the stuff we used for region selection
{
touch_unregister_area(&a_area); //Remove the touch area. We'll no longer receive touch events
b_select.text="Select Color"; //Change the button text back to "Select Color"
gui_button_redraw(&b_select); //redraw button
tracking_current->start(tracking_current); //Start tracking again
state=tracking;
}
break;
case selecting: //Selecting State: Where we wait on the user to select a color region
pixy_service(); //receive pixy events
//wait on user to select the image area
break;
case error: //Error State: Where we show an error message and leave the user no other choice than to click the backbutton
//wait on user to click the back button
break;
}
}
//Declare screen callbacks
static SCREEN_STRUCT screen = {
enter,
leave,
update
};
SCREEN_STRUCT* get_screen_tracking() {
return &screen;
}

View File

@@ -0,0 +1,37 @@
#include "screen.h"
/**
* @addtogroup screens
*/
/*@{*/
/**
* @defgroup tracking Tracking (Screen)
* The Tracking-Screen shows the object-tracking and allows some configuration
*/
/*@{*/
/**
* Enum which contains the available tracking implementations
*/
enum Tracking_Implementation {
OUR_TRACKING, //!< Our own tracking PID implementation
REFERENCE_TRACKING//!< Pixy's internal tracking implementation
};
/**
* Sets the current Mode/Tracking Implementation. Call this before using the screen obtained by get_screen_tracking()
* @param impl The new mode
*/
void tracking_set_mode(enum Tracking_Implementation impl);
/**
* Returns a pointer to the tracking screen
* \sa gui_screen_navigate
* @return
*/
SCREEN_STRUCT* get_screen_tracking();
/*@}*/
/*@}*/