Beaglebone Black Board ($45)에서 HT11 ($0.5)를 간단하게 테스트 해 보았다.

HT11은 Aliexpress에서 10개 떨이로 무료 배송하는 것을 구입. 배송에 대략 2주가 걸렸다.


BBB 보드에 debian-7.2-console-armhf-2013-10-25.tar.xz 파일을 설치. emmc에 자동으로 플래싱을 해주는데, X windows 등을 사용하지 않아 원래 설치되어 있던 angstrom distribution보다 가벼워 선택. ubuntu를 설치할까 하다가 말았다. debian이 낫다. ssh는 기본적으로 설치되어 있고, develop 환경을 구축하기 위해 필요한 패키지를 설치하고 samba도 설치. 


samba만 셋업하여 PC와 연결. PC에서 소스를 에디팅.


HT11의 vcc는 P9.3번 핀, gnd는 P9.1번 핀에 각각 연결. 

HT11의 data pin은 cape의 P9 커넥터 16번 핀(GPIO1_19)에 연결. vcc와의 pull-up 저항 5Kohm을 연결.  사진의 중앙 상단의 파란색 4pin 짜리. 우하단은 조도센서로 다음에...



HT11의 온도 정밀도나 습도 정밀도가 낮으나 막 쓰기에는 좋아 보여 샀는데, measure 중에 갖가지 에러가 뜨더라. 정밀도는 정말로 의심스럽고. 데이터 전송 에러는 주로 bit 전송 중 중단되는 것과(대략 40% 가량), checksum error(대략 10% 미만), 합쳐서 50% 가량 나온다. 그래서 마지막에 읽은 값을 보전하고 있다가 재 사용하는 것이 바람직. 


문제점

  • 커널의 usleep() 함수는 정확한 usec 단위로 작동하지 않는다. 따라서 타이밍에 사용하기에는 부적합하다. 리눅스 커널보다는 아무래도 arduino 등의 MCU 보드가 차라리 낫겠다. 
  • 데이터 전송 에러의 원인은 아무래도 kernel의 switch latency 때문에 input data capture 타이밍을 놓쳐서 인 것 같다. 이 때문에 소스에서 1과 0을 판정하는 부분은 loop counter를 관찰해서 임의로 정했다.
  • HT11의 one-wire connection은 SPI나 I2C 인터페이스에 비해 다루기가 귀찮고 에러에서 자유롭지 않다. 
  • HT11의 습도 계측은 정전식인데, 이게 아무래도 미덥지가 않다. 마침 눈이 내리는 날에 측정을 했는데 이때의 RH(relative humidity)는 거의 100%에 가깝게 나와야 할 것 같다. 어쩌면 실내 환경이 눈 내리는 바깥보다는 건조한 탓인지도 모르겠다. 


kernel에서 export한 /sys/class/gpio는 간단한 IO질에는 괜찮아 뵈지만, 실용적으로 매우 속도가 느리고 거추장스러워서 AM3359 AP datasheet를 참조하고 kernel의 /dev/mem을 이용하여 gpio direct access를 구현했다(gpio.c 및 gpio.h).


실행 결과 (50회 계측)

bits=39: 21 32 3A -> 53  : 2: h=0% t=0 (bit error:1.00, chksum error:0.00)

bits=39: 22 32 3B -> 54  : 2: h=0% t=0 (bit error:1.00, chksum error:0.00)

bits=40: 22 19 3B -> 3B  : 1: h=34% t=25 (bit error:0.67, chksum error:0.00)

bits=40: 22 19 3B -> 3B  : 1: h=34% t=25 (bit error:0.50, chksum error:0.00)

bits=40: 91 8C 9D -> 1D  : 0: h=34% t=25 (bit error:0.40, chksum error:0.20)

bits=39: 22 32 3B -> 54  : 2: h=34% t=25 (bit error:0.50, chksum error:0.17)

bits=40: 21 09 3A -> 2A  : 0: h=34% t=25 (bit error:0.43, chksum error:0.29)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.38, chksum error:0.25)

bits=34: 40 00 02 -> 40  : 2: h=33% t=25 (bit error:0.44, chksum error:0.22)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.40, chksum error:0.20)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.36, chksum error:0.18)

bits=39: 42 32 3A -> 74  : 2: h=33% t=25 (bit error:0.42, chksum error:0.17)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.38, chksum error:0.15)

bits=28: 22 32 00 -> 54  : 2: h=33% t=25 (bit error:0.43, chksum error:0.14)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.40, chksum error:0.13)

bits=40: 21 19 2A -> 3A  : 0: h=33% t=25 (bit error:0.38, chksum error:0.19)

bits=40: 21 11 3A -> 32  : 0: h=33% t=25 (bit error:0.35, chksum error:0.24)

bits=37: 20 48 1A -> 68  : 2: h=33% t=25 (bit error:0.39, chksum error:0.22)

bits=37: 20 64 1A -> 84  : 2: h=33% t=25 (bit error:0.42, chksum error:0.21)

bits=38: 20 32 3A -> 52  : 2: h=33% t=25 (bit error:0.45, chksum error:0.20)

bits=39: 21 34 3B -> 55  : 2: h=33% t=25 (bit error:0.48, chksum error:0.19)

bits=39: 21 34 3B -> 55  : 2: h=33% t=25 (bit error:0.50, chksum error:0.18)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.48, chksum error:0.17)

bits=39: 21 12 3A -> 33  : 2: h=33% t=25 (bit error:0.50, chksum error:0.17)

bits=40: 21 09 3A -> 2A  : 0: h=33% t=25 (bit error:0.48, chksum error:0.20)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.46, chksum error:0.19)

bits=40: 21 19 3A -> 3A  : 1: h=33% t=25 (bit error:0.44, chksum error:0.19)

bits=39: 21 32 3A -> 53  : 2: h=33% t=25 (bit error:0.46, chksum error:0.18)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.45, chksum error:0.17)

bits=38: 22 34 3B -> 56  : 2: h=33% t=26 (bit error:0.47, chksum error:0.17)

bits=38: 22 32 3A -> 54  : 2: h=33% t=26 (bit error:0.48, chksum error:0.16)

bits=40: 20 1A 3B -> 3A  : 0: h=33% t=26 (bit error:0.47, chksum error:0.19)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.45, chksum error:0.18)

bits=39: 21 1C 3B -> 3D  : 2: h=33% t=26 (bit error:0.47, chksum error:0.18)

bits=39: 42 32 3A -> 74  : 2: h=33% t=26 (bit error:0.49, chksum error:0.17)

bits=40: 90 8D 9D -> 1D  : 0: h=33% t=26 (bit error:0.47, chksum error:0.19)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.46, chksum error:0.19)

bits=39: 21 34 3B -> 55  : 2: h=33% t=26 (bit error:0.47, chksum error:0.18)

bits=40: 90 8D 9D -> 1D  : 0: h=33% t=26 (bit error:0.46, chksum error:0.21)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.45, chksum error:0.20)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.44, chksum error:0.20)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.43, chksum error:0.19)

bits=37: 24 68 1B -> 8C  : 2: h=33% t=26 (bit error:0.44, chksum error:0.19)

bits=39: 22 34 3B -> 56  : 2: h=33% t=26 (bit error:0.45, chksum error:0.18)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.44, chksum error:0.18)

bits=40: 20 1A 3B -> 3A  : 0: h=33% t=26 (bit error:0.43, chksum error:0.20)

bits=39: 21 34 39 -> 55  : 2: h=33% t=26 (bit error:0.45, chksum error:0.19)

bits=40: 90 8D 9D -> 1D  : 0: h=33% t=26 (bit error:0.44, chksum error:0.21)

bits=39: 42 34 3B -> 76  : 2: h=33% t=26 (bit error:0.45, chksum error:0.20)

bits=40: 21 1A 3B -> 3B  : 1: h=33% t=26 (bit error:0.44, chksum error:0.20)



Makefile

.SILENT: 


CC = gcc

STRIP = strip

INCLUDES = 

LIBS = 

CFLAGS = -Wall -O2 

LFLAGS = 


COMPILE = $(CC) $(INCLUDES) $(CFLAGS)

LINK = $(CC) $(LFLAGS)


BIN =  ht11


all: $(BIN)


clean:

rm -fr *.o $(BIN)


.c.o:

@echo compiling $< ...

$(COMPILE) -c -o $@ $<


ht11: ht11.o gpio.o

@echo link $@ with $?

$(LINK) -o $@ $?

$(STRIP) $@

ht11.o: ht11.c gpio.h

gpio.o: gpio.c gpio.h


ht11.c

#include <stdio.h>

#include <unistd.h>

#include "gpio.h"


// #define HT11_DEBUG


#define HT11_PORT 1 // GPIO1_19 (P9.16)

#define HT11_BIT 19 // GPIO1_19 (P9.16)


#define BIT0 82 // experimental value


#define HT11_INPUT() GPIO_SET_DIR_IN(HT11_PORT, HT11_BIT)

#define HT11_OUTPUT() GPIO_SET_DIR_OUT(HT11_PORT, HT11_BIT)

#define HT11_SET() GPIO_SET(HT11_PORT, HT11_BIT)

#define HT11_CLEAR() GPIO_CLEAR(HT11_PORT, HT11_BIT)

#define HT11_GET() GPIO_GET(HT11_PORT, HT11_BIT)


int dht_measure(int* temp, int* humi)

{

unsigned char data[8] = { 0, };

int counter, bit, chksum;

#ifdef HT11_DEBUG

int i, n = 0, ca[100] = { 0, };

#endif


// start measure. condition: force high 250ms, and then low ~20ms


HT11_OUTPUT();

HT11_SET();

usleep(250*1000);

HT11_CLEAR();

usleep(10*1000);

HT11_SET();

usleep(1); // small sleep for transition

// wait for beginning of HT11 response


HT11_INPUT();

for (counter = 0; HT11_GET() == 1 && counter < 10000; counter++) ;

// wait 80us for start condition low from HT11

for (counter = 0; HT11_GET() == 0 && counter < 10000; counter++) ;

// wait 80us for start condition high from HT11

for (counter = 0; HT11_GET() == 1 && counter < 10000; counter++) ;


// read 40bits

for (bit = 0; bit < 40; bit++) {

// check start transfer condition

for (counter = 0; HT11_GET() == 0 && counter < 10000; counter++) ;

if (counter >= 10000)

break;

// measure high or low

for (counter = 0; HT11_GET() == 1 && counter < 10000; counter++) ;

#ifdef HT11_DEBUG

ca[n++] = counter;

#endif

if (counter >= 10000)

break;

data[bit/8] <<= 1;

if (counter > BIT0)

data[bit/8] |= 1;

}


chksum = ((data[0] + data[2]) & 0xff);

printf("bits=%02d: %02X %02X %02X -> %02X  : ", bit, data[0], data[2], data[4], chksum);

#ifdef HT11_DEBUG

for (i = 0; i < n; i++) 

printf("\t%02d : %d %d\n", i, ca[i] > BIT0 ? 1 : 0, ca[i]);

#endif

if (bit == 40) {

if (data[4] == chksum) {

*temp = data[2];

*humi = data[0];

return 1;

}

return 0;

}

else

return 2;

return 0;

}


int main(int argc, char*argv[])

{

int temp = 0, humi = 0;

int e;

int tries = 0;

int ebit = 0;

int echk = 0;

gpio_init();

while (1) {

switch ((e = dht_measure(&temp, &humi))) {

case 0: echk++; break;

case 2: ebit++; break;

default: break;

}

tries++;

printf("%d: h=%d%% t=%d (bit error:%.2f, chksum error:%.2f)\n", e, humi, temp, (double)ebit/tries, (double)echk/tries);

if (tries == 50)

break;

}

return 0;

}


gpio.h

#ifndef __GPIO_H__

#define __GPIO_H__


extern volatile unsigned int* gpio_base[];

extern int gpio_init();


#define GPIO_REG_OE 0x134 // output enable 0=output, 1=input

#define GPIO_REG_IN 0x138 // input

#define GPIO_REG_SET 0x194 // write '1' set

#define GPIO_REG_CLEAR 0x190 // write '1' clear


#define GPIO_PTR(port, ofs) ((volatile unsigned int*)(((unsigned char*)gpio_base[port])+ofs))


#define GPIO_SET_DIR_IN(port, bit) (*GPIO_PTR(port, GPIO_REG_OE) |= (1<<bit))

#define GPIO_SET_DIR_OUT(port, bit) (*GPIO_PTR(port, GPIO_REG_OE) &= ~(1<<bit))

#define GPIO_SET(port, bit) (*GPIO_PTR(port, GPIO_REG_SET) = (1<<bit))

#define GPIO_CLEAR(port, bit) (*GPIO_PTR(port, GPIO_REG_CLEAR) = (1<<bit))

#define GPIO_GET(port, bit) ((*GPIO_PTR(port, GPIO_REG_IN) >> bit) & 1)



#endif


gpio.c

#include <stdio.h>

#include <fcntl.h> 

#include <unistd.h>

#include <sys/mman.h>


#define MAX_PORT 4


#define GPIO0_BASE 0x44E07000

#define GPIO1_BASE 0x4804C000

#define GPIO2_BASE 0x481AC000

#define GPIO3_BASE 0x481AE000

#define GPIO_IO_SIZE (4096) // 4KB


static int gpio_fd = -1;

static int gpios[MAX_PORT] = { GPIO0_BASE, GPIO1_BASE, GPIO2_BASE, GPIO3_BASE };

volatile unsigned int* gpio_base[MAX_PORT];


int gpio_init() 

{

int i;

if ((gpio_fd = open("/dev/mem", O_RDWR)) == -1) {

printf("can't open /dev/mem\n");

return 0;

}

for (i = 0; i < MAX_PORT; i++) {

gpio_base[i] = mmap(0, GPIO_IO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, gpio_fd, gpios[i]);

if (gpio_base[i] == MAP_FAILED) {

printf("mmap gpio%d failed\n", i);

return 0;

}

}

return 1;

}



,