Search

2013年12月24日 星期二

STM32 Toolchain Build For Mac OS X

  1.先裝xcode以及homebrew接著在terminal執行

brew install mpfr gmp libmpc texinfo libusb-compat libftdi wget automake libtool

2.編譯Toolchian

summon-arm-toolchain

3.編譯stlink tool

3-1.改.bashrc後source .bashrc

4.將STM32F4DISCOVERY與PC連接

5.執行st-util




6.執行arm-none-eabi-gdb並且連接

tar ext localhost:4242


7.下載examples

stm32f4-examples
or
STM32F4-workarea

8.隨便選一個project make

9.load elf

load YourPath/spi.elf



10.run


2013年12月21日 星期六

[memo] 仙人掌與多肉植物種植百科重點整理(未完)

  主要記錄最近讀多肉植物仙人掌種植活用百科這本書的重點,和一些網路上以及學校多肉社團吸收到的重點,目前接觸多肉約一個禮拜有誤請幫忙指正XD。

前言:

  多肉植物主要產於非洲、中亞、美洲,基本種類約三千種以上若含園藝雜交的就破萬以上。

  多肉植物葉子偏白或有白細毛以及刺葉可以減少吸熱以及紫外線,也可以禦寒或者凝聚水氣在順流到根部。

  仙人掌科原產於美洲大陸,種類約一千多種若含園藝雜交就超過五千種。

  仙人掌科的針刺是退化葉,除了減少水分蒸散也能保護自身,其它如:龍舌蘭屬的葉緣鋸齒、石頭玉(番杏科)則是用保護色。

  仙人掌有肉質莖,接著肉質莖突出一稜一稜的就是,稜上就有仙人掌的刺座,刺座等同於一般植物的,因此會長出刺、側芽子球、花苞,刺座下半部長刺上半部長花苞或是側芽子球。

  仙人掌的莖可分五大類,葉片形、長柱形、指形、團扇形、球形。
  葉片形:雨林生長,扁平狀,莖軟弱會長出氣根依附在其他東西上。
  長柱形:大型的仙人掌,可達數公尺之上,十幾公分大小還算是幼株離開花還要幾十年...。
  指形:約手指粗的柔軟細長莖,會隨著上長長度慢慢變成往地面成長,適合種植成吊盆。
  團扇形:莖為扁平或圓筒狀,刺座分佈在扁平莖上,除幾枝主刺還有細小的芒刺。
  球形:莖呈現球狀,稜呈縱向排列或者螺旋排列。

  仙人掌花為完全花(同時具備雄蕊與雌蕊),但大多還是要由異株授粉才會成功,開花前為了保護花苗會先在刺座長出綿毛,果實大多為漿果可以食用。

種植:

  多肉大約在十度到四十度左右都可種植,有些生長在岩縫或灌木叢比較不用太多日照,而理論上溫室栽培最好。

  澆水:


   每次澆水都要澆透(整盆土皆有水滲透到,且水從盆底流出),並且避免澆水澆到植物表面產生水漬甚至因此增加染病基層。

   夏季生長種:夏天一週一次,冬天兩週一次,澆水時機在清晨或者太陽快下山時,夏天澆水要等植物表面的水分乾了再關閉溫室這樣植物才不會因為水分升溫造成灼傷,冬季則要避免在夜晚澆水尤其寒流來這樣做更容易凍傷植物,如期間遇到颱風來襲則可延長一週半左右的時間再澆水。

冬季生長種:冬季時一到兩週給一次水即可,但夏季因為不是生長季不能給太多水而腐爛且又不能給太少水枯死,所以最好將植物放到陰涼通風的地方且避免雷陣雨還有颱風,澆水時可用噴霧或其他方法讓土壤表面保濕即可。

  介質:




  換盆:


   換盆


  繁殖:


   有性:



   無性:





害蟲與病害:




其他:





參考:

多肉植物仙人掌種植活用百科
台中教育大學仙人掌與多肉植物研究社
BATMAN的秘密花園

2013年12月19日 星期四

多肉植物是我看過最知性的植物了

又好久沒看這裡.......因為最近迷上多肉植物..................................而且還發現學校居然有這種社團...........所以現在都在研究園藝....對了我開始吃素...



2013年9月29日 星期日

ATmega328P-AU With UNO Bootloader

    最近想到拿ATmega328P-AU寫入Arduino UNO Bootloader來快速做個樣品給人看,結果今天Layout好了零件焊上完全無法用Arduino ISP的方式寫入Bootloader,一直朝更改Boards.txt與avrdude.conf的方向去走,結果弄了幾個小時想到把當作ISP的Digital 10直接接到ATmega328P-AU的Reset,不是走原本預留給Upload程式用的Auto Reset線路上有個104P電容,就可以寫入Bootloader了,不知道為什麼Digital 10多走一個104P電容就沒辦法,總之ATmega328P-AU寫入Arduino UNO Bootloader跟ATmega328P-PU一樣,接下來可以改作STM32F1的cost down版本。




=============2014/01/06===============


    如果發現Upload程式失敗可能是Upload的過程中被原先的程式佔用(?),可以Reset或者改接3.3v供電再試幾次就可以了。




=============2016/07/19===============
若在燒錄bootloader時發生錯誤可以在ArduinoISP board上的RESET與GND並聯一個10uF的電容。

2013年9月26日 星期四

三個月的近況...

  話說Blog荒廢三個月以上幾乎快忘記這邊的存在了,現在除了忙案子和開發公司的產品之外還有研究所要準備,以及大大小小的比賽和畢業專題...還是記錄一下這三個月在幹嘛好了

  大約在去年十二月左右強者我班上同學就想拉我去他開的公司(Doit 杜瓦行銷有限公司),不過一開始我因為案子很多還有我對當時想產品實在沒有興趣,所以我只有說等到有我有興趣的東西再去幫忙,五月左右聽到一個不錯的想法後就答應七月中處理完日檢還有帶女友回高雄老家玩後,就去桃園中壢公司住到開學全力開發產品雛形跟腦力激盪新產品,這兩個月都在弄指紋機、RFID和各種影像處理程式和奇怪的程式與公司內的Hackday XD。



  這兩個月其實很好玩,我覺得中壢生活比想像中的好,在我心中大約高雄 > 中壢 > 台中這樣吧,基本上能吃的都吃過了該去看的也都有,而且不知道為什麼總覺得中壢生態很好半夜都能聽到公司外的蟲叫,總之現在我們只是四人的新創公司,正在全力研發我們的新產品,因為人少的原因常常得一人Cover很多技術或公司內部等等的東西。



  開學到現在也要兩週了,都在忙比賽的文件和研究所甄試資料,這學期光比賽就被叫去參加四個以上,還有研究所推甄文件也要在十月五號前生出來才保險,畢業專題就等老師要看什麼就給什麼就好XD總之這學期真的很忙,還有女友、案子等等的事情...,雙手抓住太多東西真的怕什麼都會弄不好...XDZ

Arduino - RFID NXP MFRC522

  MFRC522是飛利浦的13.56Mhz的RFID Solution,悠遊卡或者高雄一卡通用的,本來想說要自己Layout洗板,不過因為沒熱風槍不知道怎麼黏MFRC522,總之只要按照Document由SPI傳操作暫存器跟指令就可以用了,Mifare One卡的Block 0前面4byte就是UID,第5byte是校驗和由UID的4byte依序XOR沒錯誤就會得到和第5byte相同的值,另外就是一開始很那悶為什麼得到的UID和iBon不同,原來要轉換成8H10D的格式,將Block 0前面4byte十六進位值反向然後直接視為一個4byte的十進位值就是8H10D了,例如得到:0x12 0x34 0x56 0x78 -> 8H10D:0x78563412 = 2018915346,如果不滿十位數就在開頭補零,最後在網路上看到很多Library不過都很亂所以我重寫整理一下和加入一個8H10D格式的example。


                 Arduino                     MFRC522
            Digital 10            ---->     SDA
            Digital 11(MOSI)  ----> MOSI(DI)
            Digital 12(MISO)  ----> MISO(DO)
            Digital 13(SCK) ----> SCLK(CK)
            Digital 5              ---->      RST
                VCC 3.3V  ---->      VCC
                    GND    ---->      GND

MFRC522.h

#ifndef __MFRC522__
#define __MFRC522__


#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#include <SPI.h>

//data array maxium length
const unsigned int MAX_LEN = 16;

//MFRC522 command bits
const unsigned char PCD_IDLE = 0x00; //NO action; cancel current commands
const unsigned char PCD_AUTHENT = 0x0E; //verify password key
const unsigned char PCD_RECEIVE = 0x08; //receive data
const unsigned char PCD_TRANSMIT = 0x04; //send data
const unsigned char PCD_TRANSCEIVE = 0x0C; //send and receive data
const unsigned char PCD_RESETPHASE = 0x0F; //reset
const unsigned char PCD_CALCCRC = 0x03; //CRC check and caculation

//Mifare_One card command bits
const unsigned char PICC_REQIDL = 0x26; //Search the cards that not into sleep mode in the antenna area
const unsigned char PICC_REQALL = 0x52; //Search all the cards in the antenna area
const unsigned char PICC_ANTICOLL = 0x93; //prevent conflict
const unsigned char PICC_SElECTTAG = 0x93; //select card
const unsigned char PICC_AUTHENT1A = 0x60; //verify A password key
const unsigned char PICC_AUTHENT1B = 0x61; //verify B password key
const unsigned char PICC_READ = 0x30; //read
const unsigned char PICC_WRITE = 0xA0; //write
const unsigned char PICC_DECREMENT = 0xC0; //deduct value
const unsigned char PICC_INCREMENT = 0xC1; //charge up value
const unsigned char PICC_RESTORE = 0xC2; //Restore data into buffer
const unsigned char PICC_TRANSFER = 0xB0; //Save data into buffer
const unsigned char PICC_HALT = 0x50; //sleep mode


//THe mistake code that return when communicate with MFRC522
const unsigned char MI_OK = 0;
const unsigned char MI_NOTAGERR = 1;
const unsigned char MI_ERR = 2;


//------------------MFRC522 register ---------------
//Page 0:Command and Status
const unsigned char Reserved00 = 0x00;
const unsigned char CommandReg = 0x01;
const unsigned char CommIEnReg = 0x02;
const unsigned char DivlEnReg = 0x03;
const unsigned char CommIrqReg = 0x04;
const unsigned char DivIrqReg = 0x05;
const unsigned char ErrorReg = 0x06;
const unsigned char Status1Reg = 0x07;
const unsigned char Status2Reg = 0x08;
const unsigned char FIFODataReg = 0x09;
const unsigned char FIFOLevelReg = 0x0A;
const unsigned char WaterLevelReg = 0x0B;
const unsigned char ControlReg = 0x0C;
const unsigned char BitFramingReg = 0x0D;
const unsigned char CollReg = 0x0E;
const unsigned char Reserved01 = 0x0F;
//Page 1:Command
const unsigned char Reserved10 = 0x10;
const unsigned char ModeReg = 0x11;
const unsigned char TxModeReg = 0x12;
const unsigned char RxModeReg = 0x13;
const unsigned char TxControlReg = 0x14;
const unsigned char TxAutoReg = 0x15;
const unsigned char TxSelReg = 0x16;
const unsigned char RxSelReg = 0x17;
const unsigned char RxThresholdReg = 0x18;
const unsigned char DemodReg = 0x19;
const unsigned char Reserved11 = 0x1A;
const unsigned char Reserved12 = 0x1B;
const unsigned char MifareReg = 0x1C;
const unsigned char Reserved13 = 0x1D;
const unsigned char Reserved14 = 0x1E;
const unsigned char SerialSpeedReg = 0x1F;
//Page 2:CFG
const unsigned char Reserved20 = 0x20;
const unsigned char CRCResultRegM = 0x21;
const unsigned char CRCResultRegL = 0x22;
const unsigned char Reserved21 = 0x23;
const unsigned char ModWidthReg = 0x24;
const unsigned char Reserved22 = 0x25;
const unsigned char RFCfgReg = 0x26;
const unsigned char GsNReg = 0x27;
const unsigned char CWGsPReg = 0x28;
const unsigned char ModGsPReg = 0x29;
const unsigned char TModeReg = 0x2A;
const unsigned char TPrescalerReg = 0x2B;
const unsigned char TReloadRegH = 0x2C;
const unsigned char TReloadRegL = 0x2D;
const unsigned char TCounterValueRegH = 0x2E;
const unsigned char TCounterValueRegL = 0x2F;
//Page 3:TestRegister
const unsigned char Reserved30 = 0x30;
const unsigned char TestSel1Reg = 0x31;
const unsigned char TestSel2Reg = 0x32;
const unsigned char TestPinEnReg = 0x33;
const unsigned char TestPinValueReg = 0x34;
const unsigned char TestBusReg = 0x35;
const unsigned char AutoTestReg = 0x36;
const unsigned char VersionReg = 0x37;
const unsigned char AnalogTestReg = 0x38;
const unsigned char TestDAC1Reg = 0x39;
const unsigned char TestDAC2Reg = 0x3A;
const unsigned char TestADCReg = 0x3B;
const unsigned char Reserved31 = 0x3C;
const unsigned char Reserved32 = 0x3D;
const unsigned char Reserved33 = 0x3E;
const unsigned char Reserved34 = 0x3F;
//-----------------------------------------------

class MFRC522
{
public:
MFRC522();

void Write(const unsigned char,const unsigned char);
unsigned char Read(const unsigned char);
void SetBitMask(const unsigned char,const unsigned char);
void ClearBitMask(const unsigned char,const unsigned char);
void AntennaOn();
void AntennaOff();
void Reset();
void Init();

unsigned char MFRC522ToCard(const unsigned char,
const unsigned char*,
const unsigned char,
unsigned char*,
unsigned int*);
unsigned char WriteBlock(unsigned char,unsigned char*);
unsigned char Request(unsigned char,unsigned char*);
unsigned char Anticoll(unsigned char*);
void CalulateCRC(unsigned char*,unsigned char,unsigned char*);
unsigned char SelectTag(unsigned char*);
void Halt();
private:
const static int chip_select_pin = 10;
const static int NRSTPD = 5;
};

#endif

MFRC522.cpp

#include <MFRC522.h>

#if ARDUINO >= 100
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

MFRC522::MFRC522()
{
pinMode(chip_select_pin,OUTPUT);
digitalWrite(chip_select_pin,LOW);

    pinMode(NRSTPD,OUTPUT);
digitalWrite(NRSTPD,HIGH);
}

void MFRC522::Write(const unsigned char address,const unsigned char value)
{
digitalWrite(chip_select_pin,LOW);
SPI.transfer((address << 1) & 0x7E);
    SPI.transfer(value);
digitalWrite(chip_select_pin,HIGH);
}

unsigned char MFRC522::Read(const unsigned char address)
{
unsigned char value;

digitalWrite(chip_select_pin,LOW);
    SPI.transfer(((address << 1) & 0x7E) | 0x80);
    value = SPI.transfer(0x00);
digitalWrite(chip_select_pin,HIGH);

return value;
}

void MFRC522::SetBitMask(const unsigned char reg,const unsigned char mask)
{
    unsigned char temp;
    temp = Read(reg);
    Write(reg,temp | mask);
}

void MFRC522::ClearBitMask(const unsigned char reg,const unsigned char mask)
{
    unsigned char temp;
    temp = Read(reg);
    Write(reg,temp & (~mask)); // clear bit mask
}

void MFRC522::AntennaOn()
{
    unsigned char temp;

    temp = Read(TxControlReg);
    if(!(temp & 0x03)){
SetBitMask(TxControlReg,0x03);
}
}

void MFRC522::AntennaOff()
{
ClearBitMask(TxControlReg,0x03);
}

void MFRC522::Reset()
{
Write(CommandReg,PCD_RESETPHASE);
}

void MFRC522::Init()
{
Reset();

    Write(TModeReg,0x8D);
    Write(TPrescalerReg,0x3E);
    Write(TReloadRegL,30);
    Write(TReloadRegH,0);
   
    Write(TxAutoReg,0x40);
    Write(ModeReg,0x3D);

AntennaOn();
}

unsigned char MFRC522::MFRC522ToCard(const unsigned char command,const unsigned char *send_data,const unsigned char send_len,unsigned char *back_data,unsigned int *back_len)
{
    unsigned char status = MI_ERR;
    unsigned char irqEn = 0x00;
    unsigned char waitIRq = 0x00;
    unsigned char lastBits;
    unsigned char n;
    unsigned int i;

    switch(command)
    {
        case PCD_AUTHENT: //verify card password
        {
            irqEn = 0x12;
            waitIRq = 0x10;
            break;
        }
        case PCD_TRANSCEIVE: //send data in the FIFO
        {
            irqEn = 0x77;
            waitIRq = 0x30;
            break;
        }
        default:
            break;
    }
 
    Write(CommIEnReg,irqEn | 0x80); //Allow interruption
    ClearBitMask(CommIrqReg,0x80); //Clear all the interrupt bits
    SetBitMask(FIFOLevelReg,0x80); //FlushBuffer=1, FIFO initilizate
   
    Write(CommandReg,PCD_IDLE); //NO action;cancel current command ???

    //write data into FIFO
    for (i = 0; i < send_len;++i){
        Write(FIFODataReg,send_data[i]);
    }

    //procceed it
    Write(CommandReg,command);
    if (command == PCD_TRANSCEIVE){
SetBitMask(BitFramingReg,0x80); //StartSend=1,transmission of data starts
    }
   
    //waite receive data is finished
    i = 2000; //i should adjust according the clock, the maxium the waiting time should be 25 ms???
    do {
        //CommIrqReg[7..0]
        //Set1 TxIRq RxIRq IdleIRq HiAlerIRq LoAlertIRq ErrIRq TimerIRq
        n = Read(CommIrqReg);
        --i;
    }

    while ((i != 0) && !(n & 0x01) && !(n & waitIRq));

    ClearBitMask(BitFramingReg,0x80); //StartSend=0
   
    if (i != 0){
if(!(Read(ErrorReg) & 0x1B)){ //BufferOvfl Collerr CRCErr ProtecolErr
            status = MI_OK;
            if(n & irqEn & 0x01){
                status = MI_NOTAGERR; //??
            }
           
            if(command == PCD_TRANSCEIVE){
                n = Read(FIFOLevelReg);
                lastBits = Read(ControlReg) & 0x07;
                if (lastBits){
                    *back_len = (n - 1) * 8 + lastBits;
                }
                else{
                    *back_len = n*8;
                }
               
                if(n == 0){
                    n = 1;
                }
                if (n > MAX_LEN){
                    n = MAX_LEN;
                }
               
                //read the data from FIFO
                for (i=0; i < n;++i){
                    back_data[i] = Read(FIFODataReg);
                }
            }
        }
        else{
            status = MI_ERR;
        }
       
    }
   
    //SetBitMask(ControlReg,0x80); //timer stops
    //Write(CommandReg, PCD_IDLE);

    return status;
}

unsigned char MFRC522::WriteBlock(unsigned char block_address,unsigned char  *write_data)
{
    unsigned char status;
    unsigned int recvBits;
    unsigned char i;
    unsigned char buff[18];
   
    buff[0] = PICC_WRITE;
    buff[1] = block_address;
    CalulateCRC(buff,2,&buff[2]);
    status = MFRC522ToCard(PCD_TRANSCEIVE,buff,4,buff,&recvBits);

    if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A)){
        status = MI_ERR;
    }
       
    if (status == MI_OK){
for (i=0;i < 16;++i){ //Write 16 bytes data into FIFO
            buff[i] = *(write_data+i);
        }
        CalulateCRC(buff,16,&buff[16]);
        status = MFRC522ToCard(PCD_TRANSCEIVE,buff,18,buff,&recvBits);
       
        if ((status != MI_OK) || (recvBits != 4) || ((buff[0] & 0x0F) != 0x0A)){
            status = MI_ERR;
        }
    }
   
    return status;
}

unsigned char MFRC522::Request(unsigned char mode,unsigned char *tag_type)
{
    unsigned char status;
    unsigned int backBits; //the data bits that received

    Write(BitFramingReg,0x07); //TxLastBists = BitFramingReg[2..0] ???
   
    tag_type[0] = mode;
    status = MFRC522ToCard(PCD_TRANSCEIVE,tag_type,1,tag_type,&backBits);

    if ((status != MI_OK) || (backBits != 0x10)){
        status = MI_ERR;
    }
 
    return status;
}

unsigned char MFRC522::Anticoll(unsigned char *uid)
{
    unsigned char status;
    unsigned char i;
    unsigned char serNumCheck=0;
    unsigned int unLen;
   
    //ClearBitMask(Status2Reg, 0x08); //strSensclear
    //ClearBitMask(CollReg,0x80); //ValuesAfterColl
    Write(BitFramingReg,0x00); //TxLastBists = BitFramingReg[2..0]

    uid[0] = PICC_ANTICOLL;
    uid[1] = 0x20;
    status = MFRC522ToCard(PCD_TRANSCEIVE,uid,2,uid,&unLen);

    if (status == MI_OK){
        //Verify card UID
        for (i=0;i < 4;++i){
            serNumCheck ^= uid[i];
        }
        if (serNumCheck != uid[i]){
            status = MI_ERR;
        }
    }

    //SetBitMask(CollReg, 0x80); //ValuesAfterColl=1

    return status;
}

void MFRC522::CalulateCRC(unsigned char *input,unsigned char len,unsigned char *output)
{
    unsigned char i,n;

    ClearBitMask(DivIrqReg,0x04); //CRCIrq = 0
    SetBitMask(FIFOLevelReg,0x80); //Clear FIFO pointer
    //Write(CommandReg, PCD_IDLE);

    //Write data into FIFO
    for (i=0;i < len;i++){
        Write(FIFODataReg,*(input + i));
    }
    Write(CommandReg,PCD_CALCCRC);

    //waite CRC caculation to finish
    i = 0xFF;
    do {
        n = Read(DivIrqReg);
        --i;
    }
    while ((i != 0) && !(n & 0x04)); //CRCIrq = 1

    //read CRC caculation result
    output[0] = Read(CRCResultRegL);
    output[1] = Read(CRCResultRegM);
}

unsigned char MFRC522::SelectTag(unsigned char *uid)
{
unsigned char i;
unsigned char status;
unsigned char size;
unsigned int recvBits;
unsigned char buffer[9];

//ClearBitMask(Status2Reg, 0x08);                        //MFCrypto1On=0

buffer[0] = PICC_SElECTTAG;
buffer[1] = 0x70;

for (i=0; i<5; i++){
    buffer[i + 2] = *(uid + i);
}

  CalulateCRC(buffer,7,&buffer[7]);

  status = MFRC522ToCard(PCD_TRANSCEIVE,buffer,9,buffer,&recvBits);
  if ((status == MI_OK) && (recvBits == 0x18)){
    size = buffer[0];
  }
  else{
    size = 0;
  }

  return size;
}

void MFRC522::Halt()
{
    unsigned char status;
    unsigned int unLen;
    unsigned char buff[4];

    buff[0] = PICC_HALT;
    buff[1] = 0;
    CalulateCRC(buff,2,&buff[2]);

    status = MFRC522ToCard(PCD_TRANSCEIVE,buff,4,buff,&unLen);
}


8H10D.ino

#include <MFRC522.h>
#include <SPI.h>

/* 16^0 ~ 16^7 */
unsigned long table[8] = {1,16,256,4096,65536,1048576,16777216,268435456};

MFRC522 mfrc522;

unsigned char uid[5];

void setup()
{
  Serial.begin(9600);
  SPI.begin();
  mfrc522.Init();
}

void loop()
{
  unsigned char status;
  unsigned char str[MAX_LEN];

  status = mfrc522.Request(PICC_REQIDL,str);
  if (status != MI_OK){
    return;
  }

  status = mfrc522.Anticoll(str);
  if (status == MI_OK){
    memcpy(uid,str,5);
    mfrc522.SelectTag(uid); //防止同張卡片持續感應與讀取
 
    Serial.print("Original UID : ");
    unsigned long sum = 0; //將UID每位byte加總(4byte uid & 1byte check)
    for(int i = 3;i >= 0;--i){
      unsigned long temp_h = (uid[i] >> 4) & 0x0000000F;
      Serial.print(temp_h,HEX);
      sum += (temp_h * table[(i*2)+1]); //加上UID每byte的高4bit
      unsigned long temp_l = (uid[i] & 0x0000000F);
      Serial.print(temp_l,HEX);
      sum += (temp_l * table[i*2]); //加上UID每byte的低4bit
    }
    Serial.println("");
 
    Serial.print("8H10D UID : ");
    String _8H10D = String(sum);
    if(_8H10D.length() < 10){
      for(int i = 0;i < (10 - _8H10D.length());++i){
        Serial.print("0"); //8H10D格式未滿十碼開頭補零
      }
    }
    for(int i = 0;i < _8H10D.length();++i){
      Serial.print(_8H10D.charAt(i));
    }
  }
  Serial.println("");

  mfrc522.Halt();
  delay(100);
}

參考

MFRC522 Datasheet

2013年6月9日 星期日

Arduino - PIR Sensor(ZEPIR0AAS01SBCG )

  學校作業做物聯網相關,所以就借一大堆Sensor來玩,人體紅外線跟一般三隻腳的不同,借到的ZEPIR0AAS01SBCG有很多隻腳,可以選擇兩個Mode,一個是跟三隻腳的用法一樣,另一個是Serial的方式,可以看下面參考的Datasheet。

VCC         --->         3.3V
GND         --->         GND
MD/RST   --->         GPIO
LG            --->         3.3V


const int PIR_PIN = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(PIR_PIN,INPUT);
}

void loop()
{
  boolean pir = digitalRead(PIR_PIN);

  Serial.println(pir);

  delay(10);
}

參考:

2013年4月13日 星期六

STM32F10xC DIY

  今天趁有空稍微確認了幾點,首先是電源電路用3.3v並聯三個0.1u陶瓷電容與一個1u無極性電解電容,接著reset電路可以參考這裡,然後8 or 12MHz Crystal配上22p陶瓷電容(PD0 OSC_IN、PD1 OSC_OUT),32.768KHz Crtstal配10p陶瓷電容(PC14-OSC32_IN、PC15-OSC32_OUT),VBAT與所有VDD都接3.3v然後所有VSS都接地,最後將USB to Serial的RX&TX接到STM32F10xC(我在STM32F100C8T6B測試)的PA9&PA10。

Reset Circuit





  接著toolchain因為自己DIY沒有STLINK所以必須用UART的方式去將binary upload到flash,在windows下可以用Keil ARM編譯code接著就可以用MCUISP將binary upload,而linux可以找ARM-Toolchain編譯code然後用stm32flash將binary upload,要注意的是upload前要先將BOOT 0接上3.3v然後最好再按下reset(因為像是MCUISP上一次燒錄完程式會自動reset但stm32flash不會),接著再將BOOT 0 接地並且按下reset按鈕就可以運作了。




  話說先畫成轉接板接到breadboard再搭電路對我來說比較保險,而且我不太會焊電子零件這樣小小一個我也要弄30分鐘(看以前學電子的學長不用一下就好了)...下次有空應該可以Layout出完整的版子了:Q

2013年4月1日 星期一

wxKinect - Hand Detect

  前幾天看到一個日本人寫的這篇檢測手的方法,流程:Frame轉成HSV(因為要檢測顏色) =>  濾波(去除雜訊) => 將膚色作為閾值做二值化 => 接著就是標準的找輪廓(Find Contours)、找尋凸包(Convex Hull)、找尋凸缺陷(Convexity Defects),就可以找到手的幾個辨識關鍵,實作這個方法後感覺還可以但是就沒有發揮到Kinect的功能,不過昨天在GitHub看到這篇,有個我覺得很高明的地方也利用到Kinect的特點,就是直接把手的深度範圍當作閾值獨立出手的部分算是蠻準的,其餘的處理就都是一樣了,玩Kinect一個禮拜感覺上可以發揮的點很多。






wxKinect.h


#ifndef __WX_KINECT__
#define  __WX_KINECT__

#if _MSC_VER < 1600
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#else
#include <stdint.h>
#endif

#include <wx/wx.h>

#include <cv.h>
#include <highgui.h>

#include <NiTE.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HIGHT = 480;
/*
const int ROI_MAX_SIZE_X = 540;
const int ROI_MAX_SIZE_Y = 380;
*/
const float DEPTH_SACLE = 255.0f / 4096.0f;
const int HAND_ROI_SIZE = 100;

const int LIKELY_THE_HAND_AREA = 2000;
const float HAND_IS_GRASPING = 0.8f;

const int DEPTH_RANGE = 7;

class App:public wxApp
{
public:
bool OnInit();
};

class Frame:public wxFrame
{
public:
Frame(const wxString&);
~Frame();

void InitKinect();
void CreateUI();
void Display();

void OnExit(wxCommandEvent&);
private:
friend class Thread;
Thread *thread;

nite::HandTracker hand_tracker;
nite::HandTrackerFrameRef hand_tracker_frame;

wxPanel *depth_screen;
wxPanel *hand_screen;

DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(Frame,wxFrame)
EVT_MENU(wxID_EXIT,Frame::OnExit)
END_EVENT_TABLE()

class Thread:public wxThread
{
public:
Thread(Frame*);

void* Entry();
private:
Frame *frame;
};

#endif



wxKinect.cpp


#include "wxKinect.h"

DECLARE_APP(App)
IMPLEMENT_APP(App)

bool App::OnInit()
{
Frame *frame = new Frame(wxT("wxKinect - Hand Detect"));

frame->Show(true);

return true;
}

Frame::Frame(const wxString &title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(700,800),wxMINIMIZE_BOX | wxCLOSE_BOX | wxCAPTION | wxSYSTEM_MENU)
{
InitKinect();
CreateUI();

thread = new Thread(this);
thread->Create();
thread->Run();
}

void Frame::CreateUI()
{
wxMenu *file = new wxMenu;
file->Append(wxID_EXIT,wxT("E&xit\tAlt-q"),wxT("exit"));

wxMenuBar *bar = new wxMenuBar;
bar->Append(file,wxT("file"));
SetMenuBar(bar);

wxBoxSizer *top = new wxBoxSizer(wxVERTICAL);
this->SetSizer(top);

wxBoxSizer *screen_box = new wxBoxSizer(wxVERTICAL);
top->Add(screen_box,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);

depth_screen = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(SCREEN_WIDTH,SCREEN_HIGHT));
screen_box->Add(depth_screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

hand_screen = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(HAND_ROI_SIZE * 2,HAND_ROI_SIZE * 2));
screen_box->Add(hand_screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

CreateStatusBar(2);
SetStatusText(wxDateTime::Now().Format());
}

void Frame::InitKinect()
{
nite::NiTE::initialize();

hand_tracker.create();
hand_tracker.startGestureDetection(nite::GESTURE_CLICK);
hand_tracker.startGestureDetection(nite::GESTURE_WAVE);
hand_tracker.startGestureDetection(nite::GESTURE_HAND_RAISE);
}

Frame::~Frame()
{
thread->Delete();

//hand_tracker.destroy();
nite::NiTE::shutdown();
}

void Frame::Display()
{
hand_tracker.readFrame(&hand_tracker_frame);

cv::Mat depth_mat(hand_tracker_frame.getDepthFrame().getHeight(),hand_tracker_frame.getDepthFrame().getWidth(),CV_16UC1,(void*)hand_tracker_frame.getDepthFrame().getData());
depth_mat.convertTo(depth_mat,CV_8UC1,DEPTH_SACLE);

const nite::Array<nite::GestureData> &gestures = hand_tracker_frame.getGestures();
CvPoint2D32f position;

for(int i = 0;i < gestures.getSize();++i){

if(gestures[i].isComplete()){

const nite::Point3f &pos = gestures[i].getCurrentPosition();
nite::HandId hand_id;
hand_tracker.startHandTracking(pos,&hand_id);
}
}

const nite::Array<nite::HandData> &hands = hand_tracker_frame.getHands();

for(int i = 0;i < hands.getSize();++i){

const nite::HandData hand = hands[i];

if(hand.isTracking()){ //如果跟蹤到手的運動

const nite::Point3f &pos = hand.getPosition();
hand_tracker.convertHandCoordinatesToDepth(pos.x,pos.y,pos.z,&position.x,&position.y);
float hand_depth = pos.z * DEPTH_SACLE;

/*
* 將ROI設定在手的大小左右,
* 而且ROI範圍不可以超出擷取的影像否則會丟出Exception。
*/
cv::Rect hand_roi;
hand_roi.width = HAND_ROI_SIZE * 2;
hand_roi.height = HAND_ROI_SIZE * 2;
hand_roi.x = position.x - HAND_ROI_SIZE;
hand_roi.y = position.y - HAND_ROI_SIZE;
int ROI_MAX_SIZE_X = SCREEN_WIDTH - (HAND_ROI_SIZE * 2);
int ROI_MAX_SIZE_Y = SCREEN_HIGHT - (HAND_ROI_SIZE * 2);
if(hand_roi.x < 0){hand_roi.x = 0;}
if(hand_roi.x > ROI_MAX_SIZE_X){hand_roi.x = ROI_MAX_SIZE_X;}
if(hand_roi.y < 0){hand_roi.y = 0;}
if(hand_roi.y > ROI_MAX_SIZE_Y){hand_roi.y = ROI_MAX_SIZE_Y;}

cv::Mat hand_roi_mat(cv::Mat(depth_mat,hand_roi).clone());
hand_roi_mat = (hand_roi_mat > (hand_depth - DEPTH_RANGE)) & (hand_roi_mat < (hand_depth + DEPTH_RANGE)); //這裡是關鍵,二值化的閾值取決於手的深度範圍。

cv::medianBlur(hand_roi_mat,hand_roi_mat,5); //做中值濾波使邊緣明顯
cv::Mat hand_roi_debug;
hand_roi_debug = hand_roi_mat.clone();
cvtColor(hand_roi_debug,hand_roi_debug,CV_GRAY2RGB);

std::vector<std::vector<cv::Point> > contours;
cv::findContours(hand_roi_mat,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE); //找尋所有可能的多邊形邊緣

if(contours.size()){ //如果有找到

for (int i = 0;i < contours.size();i++){ //則迭代每個多邊形邊緣

std::vector<cv::Point> contour = contours[i];
cv::Mat contour_mat = cv::Mat(contour);
double contour_area = cv::contourArea(contour_mat); //計算該多邊形面積

if(contour_area > LIKELY_THE_HAND_AREA){ //如果大於這個值則可能是手的面積

cv::Scalar center = mean(contour_mat);
cv::Point center_point = cv::Point(center.val[0],center.val[1]);

std::vector<cv::Point> approx_curve;
cv::approxPolyDP(contour_mat,approx_curve,10,true); //逼近該多邊形的邊緣

std::vector<std::vector<cv::Point> > contour_vector;
contour_vector.push_back(approx_curve);
cv::drawContours(hand_roi_debug,contour_vector,0,CV_RGB(255,0,0),3); //畫出該多邊形的邊緣

/*
* 找尋凸包(Convex Hull)並畫出點。
*/
std::vector<int> hull;
cv::convexHull(cv::Mat(approx_curve),hull,false,false);
for(int j = 0;j < hull.size();j++){
int index = hull[j];
cv::circle(hand_roi_debug,approx_curve[index],3,CV_RGB(0,255,0),2);
}

/*
* 找尋凸缺陷(Convexity Defects)並畫出點。
*/
std::vector<CvConvexityDefect> convex_defects;
CvSeq* contour_points;
CvSeq* defects;
CvMemStorage* storage;
CvMemStorage* str_defects;
CvMemStorage* contour_str;
CvConvexityDefect *defect_array = 0;
str_defects = cvCreateMemStorage();
defects = cvCreateSeq(CV_SEQ_KIND_GENERIC|CV_32SC2, sizeof(CvSeq),sizeof(CvPoint),str_defects);
contour_str = cvCreateMemStorage();
contour_points = cvCreateSeq(CV_SEQ_KIND_GENERIC | CV_32SC2,sizeof(CvSeq),sizeof(CvPoint),contour_str);
for(int j = 0; j < (int)approx_curve.size(); j++) {
CvPoint cp = {approx_curve[j].x,approx_curve[j].y};
cvSeqPush(contour_points, &cp);
}
int count = (int)hull.size();
int *convert_hull = (int*)malloc(count * sizeof(int));
for(int j = 0;j < count;j++){
convert_hull[j] = hull.at(j);
}
CvMat hull_mat = cvMat(1,count,CV_32SC1,convert_hull);
storage = cvCreateMemStorage(0);
defects = cvConvexityDefects(contour_points, &hull_mat,storage);
defect_array = (CvConvexityDefect*)malloc(sizeof(CvConvexityDefect)*defects->total);
cvCvtSeqToArray(defects,defect_array,CV_WHOLE_SEQ);
for(int j = 0;j < defects->total;j++){
CvConvexityDefect def;
def.start       = defect_array[j].start;
def.end         = defect_array[j].end;
def.depth_point = defect_array[j].depth_point;
def.depth       = defect_array[j].depth;
convex_defects.push_back(def);
}
for(int j = 0;j < convex_defects.size();j++){
cv::circle(hand_roi_debug,cv::Point(convex_defects[j].depth_point->x,convex_defects[j].depth_point->y),3,CV_RGB(0,0,255),2);
}
cvReleaseMemStorage(&contour_str);
cvReleaseMemStorage(&str_defects);
cvReleaseMemStorage(&storage);
free(defect_array);

/*
* 這裡也算關鍵,直接把逼近的面積除以凸包的面積得到的值來決定手是張開還是合閉。
*/
std::vector<cv::Point> hull_points;
for(int j = 0;j < hull.size();j++){
int curve_index = hull[j];
cv::Point p = approx_curve[curve_index];
hull_points.push_back(p);
}
double hull_area  = cv::contourArea(cv::Mat(hull_points));
double curve_area = cv::contourArea(cv::Mat(approx_curve));
double hand_ratio = curve_area / hull_area;
if(hand_ratio > HAND_IS_GRASPING){
cv::circle(hand_roi_debug,center_point,5,CV_RGB(255,0,255),5); //張手就在手中心畫出淺綠的點
}
else{
cv::circle(hand_roi_debug,center_point,5,CV_RGB(100,220,80),5); //閉手就在手中心畫出粉紅的點
}

IplImage hand_image(hand_roi_debug);
wxClientDC hand_dc(hand_screen);
cvConvertImage(&hand_image,&hand_image,CV_CVTIMG_SWAP_RB);
unsigned char *data;
cvGetRawData(&hand_image,&data);
wxImage *image = new wxImage(hand_image.width,hand_image.height,data,true);
wxBitmap *bitmap = new wxBitmap(*image);
int x,y,width,height;
hand_dc.GetClippingBox(&x,&y,&width,&height);
hand_dc.DrawBitmap(*bitmap,x,y);
delete image;
delete bitmap;
}
}
}
//imwrite("hand.jpg",hand_roi_image);
}
}

IplImage depth_image(depth_mat);
IplImage *convert_image = cvCreateImage(cvGetSize(&depth_image),IPL_DEPTH_8U,3);
cvCvtColor(&depth_image,convert_image,CV_GRAY2BGR);

wxClientDC depth_dc(depth_screen);
cvConvertImage(convert_image,convert_image,CV_CVTIMG_SWAP_RB);
unsigned char *data;
cvGetRawData(convert_image,&data);
wxImage *image = new wxImage(convert_image->width,convert_image->height,data,true);
wxBitmap *bitmap = new wxBitmap(*image);
int x,y,width,height;
depth_dc.GetClippingBox(&x,&y,&width,&height);
depth_dc.DrawBitmap(*bitmap,x,y);

delete image;
delete bitmap;
cvReleaseImage(&convert_image);
}

void Frame::OnExit(wxCommandEvent &event)
{
Close();
}

Thread::Thread(Frame *parent):wxThread(wxTHREAD_DETACHED)
{
frame = parent;
}

void* Thread::Entry()
{
while(!TestDestroy()){
frame->Display();
}

return NULL;
}









參考:

2013年3月25日 星期一

wxKinect

  話說老師的Lab東西很多昨天借了Kinect來玩一下,寫一個Demo試試SDK可以很簡單讀取骨架也可以選擇顯示Color & Depth & Canny...等等的影像處理,感覺OpenNI很好用,OpenNI 2在Windows下已經不使用這個Driver了而改用MS Kinect SDK的Driver,整體來說影像格式轉換比較浪費時間 OpenNI & NiTE => OpenCV => wxWidgets 不過應付FPS只有30的Kinect已經足夠,使用OpenNI或其他Middleware如果在VS2008含以下必須判斷一下然後做一些Define,收到的深度影像是CV_16UC1且數值是0 ~ 4096(10000),要轉換成CV_8UC1要把數值從0 ~ 4096(10000)映射到0 ~ 255畫出來顏色才不會太黑,如果要轉換到wxImage直接灰階轉彩色就好(CV_GRAY2BGR),然後如果要Release Binarry就要把OpenNI含Middleware的SDK安裝目錄下的Redist目錄內的動態連結檔案與Redist底下還有一個資料夾放一起就可以了。





wxKinect.h


#ifndef __WX_KINECT__
#define __WX_KINECT__

#if _MSC_VER < 1600
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#else
#include <stdint.h>
#endif


#include <wx/wx.h>

#include <highgui.h>
#include <cv.h>

#include <OpenNI.h>
#include <NiTE.h>

const int SCREEN_WIDTH = 640;
const int SCREEN_HIGHT = 480;

const int COLOR_MODE = 1;
const int DEPTH_MODE = 2;
const int CANNY_MODE = 3;

enum{
ID_COLOR_FRAME = 100,
ID_DEPTH_FRAME,
ID_CANNY_FRAME
};

class App:public wxApp
{
public:
bool OnInit();
};

class Frame:public wxFrame
{
public:
Frame(const wxString&);
~Frame();

void CreateUI();
void InitKinect();

void Display();

void OnExit(wxCommandEvent&);
void OnColor(wxCommandEvent&);
void OnDepth(wxCommandEvent&);
void OnCanny(wxCommandEvent&);
private:
friend class Thread;
Thread *thread;

openni::Device device;
nite::UserTracker user_tracker;
openni::VideoMode color_mode;
openni::VideoStream color_stream;
openni::VideoMode depth_mode;
openni::VideoStream depth_stream;
openni::VideoFrameRef color_frame;
openni::VideoFrameRef depth_frame;
nite::UserTrackerFrameRef user_tracker_frame;
int max_depth;

int select_mode;

wxPanel *screen;

DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(Frame,wxFrame)
EVT_MENU(wxID_EXIT,Frame::OnExit)
EVT_MENU(ID_COLOR_FRAME,Frame::OnColor)
EVT_MENU(ID_DEPTH_FRAME,Frame::OnDepth)
EVT_MENU(ID_CANNY_FRAME,Frame::OnCanny)
END_EVENT_TABLE()

class Thread:public wxThread
{
public:
Thread(Frame*);

void* Entry();
private:
Frame *frame;
};

#endif


wxKinect.cpp


#include "wxKinect.h"

DECLARE_APP(App)
IMPLEMENT_APP(App)

bool App::OnInit()
{
Frame *frame = new Frame(wxT("wxKinect"));

frame->Show(true);

return true;
}

Frame::Frame(const wxString &title):wxFrame(NULL,wxID_ANY,title,wxDefaultPosition,wxSize(800,600),wxMINIMIZE_BOX | wxCLOSE_BOX | wxCAPTION | wxSYSTEM_MENU)
{
InitKinect();
CreateUI();

select_mode = COLOR_MODE;

thread = new Thread(this);
thread->Create();
thread->Run();
}

Frame::~Frame()
{
thread->Delete();

color_stream.destroy();
depth_stream.destroy();
device.close();
openni::OpenNI::shutdown();
nite::NiTE::shutdown();
}

void Frame::CreateUI()
{
wxMenu *file = new wxMenu;
file->Append(wxID_EXIT,wxT("E&xit\tAlt-q"),wxT("exit"));

wxMenu *select = new wxMenu;
select->AppendRadioItem(ID_COLOR_FRAME,wxT("c&olor\tCtrl-c"),wxT("color"));
select->AppendRadioItem(ID_DEPTH_FRAME,wxT("d&epth\tCtrl-d"),wxT("depth"));
select->AppendRadioItem(ID_CANNY_FRAME,wxT("c&anny\tCtrl-x"),wxT("canny"));

wxMenuBar *bar = new wxMenuBar;
bar->Append(file,wxT("file"));
bar->Append(select,wxT("select"));
SetMenuBar(bar);

wxBoxSizer *top = new wxBoxSizer(wxVERTICAL);
this->SetSizer(top);

wxBoxSizer *screen_box = new wxBoxSizer(wxHORIZONTAL);
top->Add(screen_box,0,wxALIGN_CENTER_HORIZONTAL | wxALL,5);

screen = new wxPanel(this,wxID_ANY,wxDefaultPosition,wxSize(SCREEN_WIDTH,SCREEN_HIGHT));
screen_box->Add(screen,0,wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL,5);

CreateStatusBar(2);
SetStatusText(wxDateTime::Now().Format());
}

void Frame::InitKinect()
{
openni::OpenNI::initialize();
nite::NiTE::initialize();

device.open(openni::ANY_DEVICE);
device.setImageRegistrationMode(openni::IMAGE_REGISTRATION_DEPTH_TO_COLOR);

user_tracker.create();
user_tracker.setSkeletonSmoothingFactor(0.1f);

color_mode.setFps(30);
color_mode.setResolution(SCREEN_WIDTH,SCREEN_HIGHT);
color_mode.setPixelFormat(openni::PIXEL_FORMAT_RGB888);

color_stream.setVideoMode(color_mode);
color_stream.create(device,openni::SENSOR_COLOR);
color_stream.start();

depth_mode.setFps(30);
depth_mode.setResolution(SCREEN_WIDTH,SCREEN_HIGHT);
depth_mode.setPixelFormat(openni::PIXEL_FORMAT_DEPTH_100_UM);

depth_stream.setVideoMode(depth_mode);
depth_stream.create(device,openni::SENSOR_DEPTH);
depth_stream.start();
max_depth = depth_stream.getMaxPixelValue();
}

void Frame::Display()
{
color_stream.readFrame(&color_frame);
depth_stream.readFrame(&depth_frame);
user_tracker.readFrame(&user_tracker_frame);

cv::Mat RGBMat(color_frame.getHeight(),color_frame.getWidth(),CV_8UC3,(void*)color_frame.getData());
IplImage color_image(RGBMat);
cvCvtColor(&color_image,&color_image,CV_RGB2BGR);

cv::Mat DepthMat(depth_frame.getHeight(),depth_frame.getWidth(),CV_16UC1,(void*)depth_frame.getData());
DepthMat.convertTo(DepthMat,CV_8UC1,255.0f / max_depth);
IplImage depth_image(DepthMat);

IplImage *select_image = cvCreateImage(cvGetSize(&color_image),IPL_DEPTH_8U,3);

if(select_mode == COLOR_MODE){
cvCopyImage(&color_image,select_image);
}
else if(select_mode == DEPTH_MODE){
cvCvtColor(&depth_image,select_image,CV_GRAY2BGR);
}
else if(select_mode == CANNY_MODE){
cvCanny(&depth_image,&depth_image,50,200);
cvCvtColor(&depth_image,select_image,CV_GRAY2BGR);
}

const nite::Array<nite::UserData> &users = user_tracker_frame.getUsers();
for(int i = 0;i < users.getSize();++i){
const nite::UserData &user = users[i];

if(user.isNew()){
user_tracker.startSkeletonTracking(user.getId());
}
else if(user.isLost()){
}

if(user.isVisible()){
const nite::Skeleton &skeleton = user.getSkeleton();
nite::SkeletonJoint joints[15];

if(skeleton.getState() == nite::SKELETON_TRACKED){
joints[0] = skeleton.getJoint(nite::JOINT_HEAD);
joints[1] = skeleton.getJoint(nite::JOINT_NECK);
joints[2] = skeleton.getJoint(nite::JOINT_LEFT_SHOULDER);
joints[3] = skeleton.getJoint(nite::JOINT_RIGHT_SHOULDER);
joints[4] = skeleton.getJoint(nite::JOINT_LEFT_ELBOW);
joints[5] = skeleton.getJoint(nite::JOINT_RIGHT_ELBOW);
joints[6] = skeleton.getJoint(nite::JOINT_LEFT_HAND);
joints[7] = skeleton.getJoint(nite::JOINT_RIGHT_HAND);
joints[8] = skeleton.getJoint(nite::JOINT_TORSO);
joints[9] = skeleton.getJoint(nite::JOINT_LEFT_HIP);
joints[10] = skeleton.getJoint(nite::JOINT_RIGHT_HIP);
joints[11] = skeleton.getJoint(nite::JOINT_LEFT_KNEE);
joints[12] = skeleton.getJoint(nite::JOINT_RIGHT_KNEE);
joints[13] = skeleton.getJoint(nite::JOINT_LEFT_FOOT);
joints[14] = skeleton.getJoint(nite::JOINT_RIGHT_FOOT);
}

CvPoint2D32f point[15];
for(int i = 0;i < 15;++i){
const nite::Point3f &pos = joints[i].getPosition();
user_tracker.convertJointCoordinatesToDepth(pos.x,pos.y,pos.z,&(point[i].x),&(point[i].y));
}

cvLine(select_image,cvPoint(point[0].x,point[0].y),cvPoint(point[1].x,point[1].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[1].x,point[1].y),cvPoint(point[2].x,point[2].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[1].x,point[1].y),cvPoint(point[3].x,point[3].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[2].x,point[2].y),cvPoint(point[4].x,point[4].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[3].x,point[3].y),cvPoint(point[5].x,point[5].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[4].x,point[4].y),cvPoint(point[6].x,point[6].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[5].x,point[5].y),cvPoint(point[7].x,point[7].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[1].x,point[1].y),cvPoint(point[8].x,point[8].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[8].x,point[8].y),cvPoint(point[9].x,point[9].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[8].x,point[8].y),cvPoint(point[10].x,point[10].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[9].x,point[9].y),cvPoint(point[11].x,point[11].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[10].x,point[10].y),cvPoint(point[12].x,point[12].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[11].x,point[11].y),cvPoint(point[13].x,point[13].y),CV_RGB(255,0,0),3);
cvLine(select_image,cvPoint(point[12].x,point[12].y),cvPoint(point[14].x,point[14].y),CV_RGB(255,0,0),3);

for(int i = 0;i < 15;++i){
if(joints[i].getPositionConfidence() > 0.5f){
cvCircle(select_image,cvPoint(point[i].x,point[i].y),3,CV_RGB(0,255,0),2);
}
else{
cvCircle(select_image,cvPoint(point[i].x,point[i].y),3,CV_RGB(0,0,255),2);
}
}
}
}

wxClientDC dc(screen);
cvConvertImage(select_image,select_image,CV_CVTIMG_SWAP_RB);
unsigned char *data;
cvGetRawData(select_image,&data);
wxImage *image = new wxImage(select_image->width,select_image->height,data,true);
wxBitmap *bitmap = new wxBitmap(*image);
int x,y,width,height;
dc.GetClippingBox(&x,&y,&width,&height);
dc.DrawBitmap(*bitmap,x,y);
delete image;
delete bitmap;

cvReleaseImage(&select_image);
}

void Frame::OnColor(wxCommandEvent &event)
{
select_mode = COLOR_MODE;
}

void Frame::OnDepth(wxCommandEvent &event)
{
select_mode = DEPTH_MODE;
}

void Frame::OnCanny(wxCommandEvent &event)
{
select_mode = CANNY_MODE;
}

void Frame::OnExit(wxCommandEvent &event)
{
Close();
}

Thread::Thread(Frame *parent):wxThread(wxTHREAD_DETACHED)
{
frame = parent;
}

void* Thread::Entry()
{
while(!TestDestroy()){
frame->Display();
}

return NULL;
}