It is currently Wed Jul 06, 2011 4:00 pm

All times are UTC - 8 hours




 Page 1 of 1 [ 1 post ] 
Author Message
 Post subject: Chumby hack
PostPosted: Sun Mar 13, 2011 3:35 pm 

Joined: Mon Feb 21, 2011 10:03 am
Posts: 26
Location: San Diego, CA
Here's the final version of the Chumby->inPulse hack, as discussed in this thread

inPulse code:


/**
*
* Image Viewer
*
* This app accepts simple bitmap drawing commands from another device
* and renders them on the display.  It supports blanking the display,
* filling a rectangle with a single color, and two commands for filling
* a rectangle with multiple colors - one for raw pixels, and one for
* for run-length encoded pixels.
*
* Copyright (C) 2011, Duane Maxwell [email protected]
*
* Permission to use, copy, modify, and/or distribute this software for 
* any purpose with or without fee is hereby granted, provided that the 
* above copyright notice and this permission notice appear in all copies. 

* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
* SOFTWARE.
**/

#include <pulse_os.h>
#include <pulse_types.h>
#include <stdint.h>

#define SLEEP_DELAY 30000
#define SNIFF_LEVEL 400

const color24_t const logoColors[16] = {
{0x00,0x00,0x00,0x00},
{0x01,0x01,0x20,0x00},
{0x01,0x03,0x04,0x00},
{0x03,0x06,0x0A,0x00},
{0x06,0x0A,0x12,0x00},
{0x0A,0x12,0x20,0x00},
{0x0F,0x1B,0x30,0x00},
{0x14,0x25,0x41,0x00},
{0x1F,0x36,0x5F,0x00},
{0x26,0x48,0x84,0x00},
{0x2B,0x51,0x93,0x00},
{0x30,0x5B,0xA7,0x00},
{0x39,0x67,0xB8,0x00},
{0x4D,0x78,0xC2,0x00},
{0x90,0xA8,0xD4,0x00},
{0xC2,0xD0,0xE8,0x00}
};

const char *logoMap =
"AAAAAAAAAAAAEFHIIJJKKJICAAAAA"
"AAAAAAAAAAFILMNNNNNNNNNIAAAAA"
"AAAAAAAAAINNNNMMMMMMMMMMFAAAA"
"AAAAAAAAHNNMMMMLLLLLMMMMIAAAA"
"AAAAAAAAKNMLNOMLLLMOMLMMIAAAA"
"AAAAAAADLMMNPOPLLLPOPLLMIAAAA"
"AAAAAAACKMLOOLPNKLPOPLLMIAAAA"
"AAAAAAAAJMLNPPOLKKMONLLLHAAAA"
"AAAAAAAAILLLMNLKKKKKKLLLGAAAA"
"AAAAAAAAHLLKKKKKKKKKKKLKFAAAA"
"AAAAAAEEILKKKJJJJJJKKKKKFAAAA"
"AACGIJKKLLKKJJJJJJJJKKKLJFAAA"
"AGKMMLLLLKKJJJJJJJJJJKKLLKGAA"
"ILLKKJJKKKJJJJJJJJJJJJIKLLKGA"
"HHGFEDGKKJJJJJJJJJJJJJEGJLLJD"
"AAAAAEKKKIHJJJIJJJIJKKGAEIKLH"
"AAAAAILKIEHJJIGJJIDIKKIAAAGII"
"AAAAEJKJDEKJJFHKJIAHKKJCAAACD"
"AAAAFKKFAIKKIAIKKHADJKJCAAAAA"
"AAAAFJGAAJKJEAJKKGAAGKJAAAAAA"
"AAAADFAAAJJFAAIKJEAAAHIAAAAAA"
"AAAAAAAAAIFAAAHKHAAAAACAAAAAA"
"AAAAAAAAAAAAAAEIDAAAAAAAAAAAA";

void drawLogo() {
   int x = 33;
   int y = 52;
    pulse_set_draw_window(x,y,x+28,y+22);
    const char *map = logoMap;
    const color24_t *colors = logoColors;
    for (int i=0;i<29*23; i++) {
        pulse_draw_point24(colors[*map++-'A']);
    }
}

//
// this is the data structure corresponding to the Bluetooth packet
// sent by the external device.
//
#define MAGIC 'C'

enum {
   CLEAR = 0,
   FILL_RECT = 1,
   FILL_PIXELS = 2,
   FILL_PIXELS_RLE = 3
};

typedef struct {
   uint8_t magic; // should be MAGIC
   uint8_t command; // from the above enum
   union _u {
      struct { // data for FILL_RECT
         uint8_t left;
         uint8_t top;
         uint8_t right;
         uint8_t bottom;
         color24_t color;
      } fill;
      struct { // data for FILL_PIXELS
         uint8_t left;
         uint8_t top;
         uint8_t right;
         uint8_t bottom;
         color24_t colors[];
      } pixels;
      struct { // data for FILL_PIXELS_RLE
         uint8_t left;
         uint8_t top;
         uint8_t right;
         uint8_t bottom;
         uint8_t data[];
      } rle;
   } u;
} __attribute__((packed)) command;

//
// RLE pixels are stored as a sequence of runs
// each run starts with a count byte - if the high bit of the count is set,
// then it should emit a run of the rest of the byte with just the next color
// if the high bit is clear, then it's a run of different colors
//
void handle_received_bluetooth_data(const uint8_t *buffer) {
    pulse_oled_set_brightness(100);
    pulse_update_power_down_timer(SLEEP_DELAY);
   command *c = (command *)buffer;
   if (c->magic==MAGIC) {
      switch (c->command) {
         case CLEAR: {
            pulse_blank_canvas();
            }
            break;
         case FILL_RECT: {
             pulse_set_draw_window(c->u.fill.left, c->u.fill.top, c->u.fill.right, c->u.fill.bottom);
            int count = (c->u.fill.right-c->u.fill.left+1)*(c->u.fill.bottom-c->u.fill.top+1);
            color24_t color = c->u.fill.color;
            while (count--)
               pulse_draw_point24(color);
            }
            break;
         case FILL_PIXELS: {
             pulse_set_draw_window(c->u.pixels.left, c->u.pixels.top, c->u.pixels.right, c->u.pixels.bottom);
            int count = (c->u.pixels.right-c->u.pixels.left+1)*(c->u.pixels.bottom-c->u.pixels.top+1);
            color24_t *color = c->u.pixels.colors;
            while (count--)
               pulse_draw_point24(*color++);
            }
            break;
         case FILL_PIXELS_RLE: {
             pulse_set_draw_window(c->u.rle.left, c->u.rle.top, c->u.rle.right, c->u.rle.bottom);
            int count = (c->u.pixels.right-c->u.pixels.left+1)*(c->u.pixels.bottom-c->u.pixels.top+1);
            uint8_t *data = c->u.rle.data;
            while (count) {
               uint8_t pixelCount = *data++;
               if (pixelCount & 0x80) { // a run of pixelCount&0x7f pixels of the same color
                  pixelCount &= 0x7f;
                  count -= pixelCount;
                  color24_t *color = (color24_t *)data;
                  data += sizeof(color24_t);
                  while (pixelCount--) {
                     pulse_draw_point24(*color);
                  }
               } else { // run of pixelCount different colors
                  count -= pixelCount;
                  color24_t *color = (color24_t *) data;
                  while (pixelCount--) {
                     pulse_draw_point24(*color++);
                  }
                  data = (uint8_t *)color;
               }
            }
            }
            break;
         default:
            printf("BAD CMD %d\n",c->command);
            break;
      }
   } else {
      printf("BAD MAG %d\n",c->magic);
   }
}

void handle_button_causing_wakeup();

void main_app_init() {
   pulse_blank_canvas();
   drawLogo();
    pulse_oled_set_brightness(100);
    pulse_update_power_down_timer(SLEEP_DELAY);
    pulse_register_callback(ACTION_WOKE_FROM_BUTTON, &handle_button_causing_wakeup);
    pulse_register_callback(ACTION_HANDLE_NON_PULSE_PROTOCOL_BLUETOOTH_DATA, (PulseCallback)&handle_received_bluetooth_data);
   pulse_set_bluetooth_sniff(true, SNIFF_LEVEL);
}

void handle_button_causing_wakeup() {
   pulse_blank_canvas();
   drawLogo();
    pulse_oled_set_brightness(100);
    pulse_update_power_down_timer(SLEEP_DELAY);
}

void main_app_handle_button_down() {
    pulse_update_power_down_timer(SLEEP_DELAY);
}

void main_app_handle_button_up() {
}

void main_app_loop() {
}

void main_app_handle_doz() {
    for (int i = 100; i >= 0; i-=6) {
        pulse_oled_set_brightness(i);
        pulse_mdelay(60);
    }
}

void main_app_handle_hardware_update(enum PulseHardwareEvent event) {
}

This code is actually very generic and can be used to send pretty much any bitmap to the device.

Chumby code (requires BlueZ 2.x installed):

/**
*
* Name: ctoi
*
* Description:
*
* This app transfers a screen shot from a Falconwing (aka Chumby One)
* device to an Allerta inPulse Bluetooth watch.
*
* This app requires the device to have a USB Bluetooth dongle, the
* appropriate drivers (very likely already in the firmware), and a
* the BlueZ library - probably an old one (2.x) since the newer
* ones require D-Bus, which is not supported on the chumby
*
* Copyright (C) 2011, Duane Maxwell [email protected]
*
* Permission to use, copy, modify, and/or distribute this software for 
* any purpose with or without fee is hereby granted, provided that the 
* above copyright notice and this permission notice appear in all copies. 

* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES 
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 
* SOFTWARE.
**/

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>

//
// the chumby screen is 320x240 of RGB565LE pixels
//
#define LARGE_WIDTH 320
#define LARGE_HEIGHT 240
#define LARGE_PIXELS (LARGE_WIDTH*LARGE_HEIGHT)
#define LARGE_PIXEL_SIZE sizeof(uint16_t)
#define LARGE_SIZE (LARGE_PIXELS*LARGE_PIXEL_SIZE)

//
// this is the datatype for an inPulse pixel
//
typedef struct {
    uint8_t red;
    uint8_t green;
    uint8_t blue;
    uint8_t alpha;
} __attribute__((packed)) color24_t;

#define SMALL_WIDTH 80
#define SMALL_HEIGHT 60
#define SMALL_PIXELS (SMALL_WIDTH*SMALL_HEIGHT)
#define SMALL_PIXEL_SIZE sizeof(color24_t)
#define SMALL_SIZE (SMALL_PIXELS*SMALL_PIXEL_SIZE)

#define H_OFFSET 8
#define V_OFFSET 34

uint16_t *largePixels; // the chumby screen
uint16_t **large; // line starts
color24_t *smallPixels; // the thumbnail
color24_t **small; // line starts

//
// allocate buffers, set up line starts arrays
//
void  init() {
    int i;
    largePixels = (uint16_t *)malloc(LARGE_SIZE);
    large = (uint16_t **)malloc(LARGE_HEIGHT*sizeof(uint16_t *));
    for (i=0;i<LARGE_HEIGHT;i++) {
        large[i] = &largePixels[i*LARGE_WIDTH];
    }
    smallPixels = (color24_t *)malloc(SMALL_SIZE);
    small = (color24_t **)malloc(SMALL_HEIGHT*sizeof(color24_t *));
    for (i=0;i<SMALL_HEIGHT;i++) {
        small[i] = &smallPixels[i*SMALL_WIDTH];
    }
}

//
// all we do here is open the screen as a raw file and copy the entire thing into
// and array of RGB565 pixels
//
void copyScreen() {
    FILE *f = fopen("/dev/fb0","r");
    if (f==NULL) {
        fprintf(stderr,"Cannot open frame buffer\n");
        exit(1);
    }
    size_t count = fread(largePixels,1,LARGE_SIZE,f);
    fclose(f);
}

//
// This code is somewhat optimized based on the fact that the reduced image is exactly
// one quarter the screen size in each dimension, or 1/16th the area.  What it does
// is take each component of the source RGB565 pixel and add only the upper 4 bits into
// the final color - that way the 16 colors accumulate and the resulting color is already
// in the correct range.
//
void reduceScreen() {
    int x,y,lx,ly;
    for (y=0; y<SMALL_HEIGHT; y++) {
        color24_t *s = small[y];
        for (x=0; x<SMALL_WIDTH; x++) {
            s->red = s->green = s->blue = s->alpha = 0;
            for (ly=y*4;ly<(y+1)*4;ly++) {
                for (lx=x*4;lx<(x+1)*4;lx++) {
                    uint16_t lpixel = large[ly][lx];
                    int r = (lpixel >> 12) & 0xf;
                    int g = (lpixel >> 7) & 0xf;
                    int b = (lpixel >> 1) & 0xf;
                    s->red += r;
                    s->green += g;
                    s->blue += b;
                }
            }
            s++;
        }
    }
}

//
// this is the data structure for the Bluetooth packet we send to the watch
// we make sure it does not look like an inPulse notification
//
#define MAGIC 'C'
enum {
    CLEAR =0,
    FILL_RECT = 1,
    FILL_PIXELS = 2,
    FILL_PIXELS_RLE = 3
};

typedef struct {
   uint8_t magic; // should be MAGIC
   uint8_t command;
   union _u {
      struct {
         uint8_t left;
         uint8_t top;
         uint8_t right;
         uint8_t bottom;
         color24_t color;
      } fill;
      struct {
         uint8_t left;
         uint8_t top;
         uint8_t right;
         uint8_t bottom;
         color24_t colors[];
      } pixels;
      struct {
         uint8_t left;
         uint8_t top;
         uint8_t right;
         uint8_t bottom;
         uint8_t data[];
      } rle;
   } u;
} __attribute__((packed)) command;

//
// here we emit the thumbnail image as a set of 40x1 rectangles of raw pixels
// this is because the packet size is limited to 220 or so bytes
//
void emitScreen(char *bdaddr) {
    struct sockaddr_l2 addr = { 0 };
    int s, status;

    printf("Creating socket...");
    s = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

    addr.l2_family = AF_BLUETOOTH;
    addr.l2_psm = htobs(0x1001);
    str2ba( bdaddr, &addr.l2_bdaddr);
   
    printf("Connecting...");
    status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

   int size = 40*sizeof(color24_t)+6*sizeof(uint8_t); // 40 pixels worth of data
    command *c = (command *)malloc(size);
    c->magic = MAGIC;
    c->command = FILL_PIXELS;

    if (status==0) {
        int y;
        printf("Writing data...");
        for (y=0;y<SMALL_HEIGHT;y++) {
            c->u.pixels.left = H_OFFSET;
            c->u.pixels.right = H_OFFSET+39;
            c->u.pixels.top = c->u.pixels.bottom = y+V_OFFSET;
            memcpy(c->u.pixels.colors,small[y],40*sizeof(color24_t));
            status = write(s,c,size);
            c->u.pixels.left = H_OFFSET+40;
            c->u.pixels.right = H_OFFSET+79;
            memcpy(c->u.pixels.colors,&small[y][40],40*sizeof(color24_t));
            status = write(s,c,size);
        }
    }

    if (status < 0) perror("uh oh!");

    printf("Closing socket\n");
    close(s);
}

int main(int argc,char **argv) {
   if (argc<2) {
      printf("usage: ctoi <bt-addr>\n");
      exit(0);
   }
   init();
    for (;;) {
       copyScreen();
       reduceScreen();
       emitScreen(argv[1]);
       sleep(5);
    }
}



Offline
 Profile  
 
Display posts from previous:  Sort by  
 Page 1 of 1 [ 1 post ] 

All times are UTC - 8 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: