Monthly Archives: February 2020

VoCore2: Temperature and Humidity Sensor(I2C)

i2c-tools is not that easy to use in some situation. Recently I need to make sure my room is wet enough to kill virus, so I develop a simple tool based on SHT20 — a temperature and humidity sensor, but I find I can not use i2c-tools to test SHT20 I2C bus, it missed some key features.

I have to spend couple of hours write another one.

usage: i2ctest [dev path] [address] [r/w] [length/data]


example:

show device on i2c-0:
	i2ctest /dev/i2c-0

read 3 byte from i2c-0, address 0x40:
	i2ctest /dev/i2c-0 0x40 r 3

write 2 byte(0xe2 0x88) to i2c-0, address 0x21:
	i2ctest /dev/i2c-0 0x21 w '0xe2 0x88'

For my usage, query i2c devices:

root@OpenWrt:/tmp# i2ctest /dev/i2c-0 
0x10: BUSY
0x40: READY

0x10 is BUSY because the ES8388 driver already taken the address.

0x40 is the SHT20 address. From SHT20 datasheet, write 0xF3 first then read 3 bytes from it, we will get temperature.

root@OpenWrt:/tmp# i2ctest /dev/i2c-0 0x40 w 0xf3
write: 1 byte(s)
root@OpenWrt:/tmp# i2ctest /dev/i2c-0 0x40 r 3
00000000: 64 98 56                                            d.V

Calculate from the data: T = 0x6498 * 175.72 / 65536 – 46.85 = 22.198C

Here is the source code for i2ctest.c

#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>

#include <linux/i2c.h>
#include <linux/i2c-dev.h>


#define min(a, b)	((a) < (b) ? (a) : (b))

void hex_print(const unsigned char* s, int size, FILE *fp)
{
	int i = 0, n = 0, c = 0, p = 0;

	// default print to stdout.
	if (fp == NULL)
		fp = stdout;

	while(1) {
		n = size > 0x10 ? 0x10 : size;
		size -= n;

		p += fprintf(fp, "%08X: ", c);
		for(i = 0; i < n; i++) {
			if(i == 8) {
				p += fprintf(fp, " ");
			}
			p += fprintf(fp, "%02X ", *(s + c + i));
		}
		for(i = n; i < 0x10; i++) {
			if(i == 8) {
				p += fprintf(fp, " ");
			}
			p += fprintf(fp, "   ");
		}
		p += fprintf(fp, "   ");
		for(i = 0; i < n; i++) {
			if(*(s + c + i) >= 0x20 && *(s + c + i) <= 0x7E) {
				p += fprintf(fp, "%c", *(s + c + i));
			} else {
				p += fprintf(fp, ".");
			}
		}
		p += fprintf(fp, "\n");
		if(size <= 0) {
			break;
		}
		c += n;
	}
}

// convert string to unsigned int.
// hex: 0x, oct: 0, bin: ...b, dec: normal.
unsigned int atox(char* s)
{
	char *p = s, *e = s;
	unsigned int r = 0;

	while(*e != '\0')
		e++;

	if(*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X')) {
		// hex: skip first 0x or 0X header.
		p += 2;

		while(p != e) {
			r <<= 4;
			r += ((*p >= '0' && *p <= '9') ? (*p - '0') : ((*p >= 'A' && *p <= 'F') ?
			    *p - 'A' + 0xA : ((*p >= 'a' && *p <= 'f') ? *p - 'a' + 0xA : 0)));
			p++;
		}
	} else if((e - p) >= 2 && *(e - 1) == 'b') {
		// bin: ignore last b.
		e--;

		while(p != e) {
			r <<= 1;
			r += (*p == '0' ? 0 : 1);
			p++;
		}
	} else if(*p == '0') {
		// oct: skip first 0 header.
		p += 1;

		while(p != e) {
			r <<= 3;
			r += ((*p >= '0' && *p <= '7') ? (*p - '0') : 0);
			p++;
		}
	} else {
		// dec: we do not need to deal the number.
		while(p != e) {
			r *= 10;
			r += ((*p >= '0' && *p <= '9') ? (*p - '0') : 0);
			p++;
		}
	}

	return r;
}

int decode_data(unsigned char *out, int max, const char *in)
{
	int used = 0;
	const char *p = in;
	char num[5] = {0};

	while (*p) {
		if (*p == ' ') {
			p++;
			continue;
		}

		if (*p != '0')
			return 0;

		if (used >= max)
			return used;

		memcpy(num, p, 4);
		out[used++] = atox(num);

		p += 4;
	}

	return used;
}

int main(int argc, char *argv[])
{
	unsigned char buf[0x100] = {0};
	int i2c, code, size;

	if (argc != 2 && argc != 5) {
		printf("usage: i2ctest [dev path] [address] [r/w] [length/data]\n");
		printf("\n\nexample:\n");
		printf("\nshow device on i2c-0:\n"
		       "\ti2ctest /dev/i2c-0\n");
		printf("\nread 3 byte from i2c-0, address 0x40:\n"
		       "\ti2ctest /dev/i2c-0 0x40 r 3\n");
		printf("\nwrite 2 byte(0xe2 0x88) to i2c-0, address 0x21:\n"
		       "\ti2ctest /dev/i2c-0 0x21 w '0xe2 0x88'\n\n");
		return 0;
	}

	i2c = open(argv[1], O_RDWR);
	if (i2c < 0) {
		printf("error: can not open device %s\n", argv[1]);
		return -1;
	}

	if (argc == 2) {
		for (code = 0; code < 0x7f; code++) {
			ioctl(i2c, I2C_SLAVE, (unsigned char)code);
			if (errno == EBUSY)
				printf("0x%02x: BUSY\n", code);
			size = write(i2c, buf + code, 1);
			if (size > 0)
				printf("0x%02x: READY\n", code);
		}
		return 0;
	}

	code = ioctl(i2c, I2C_SLAVE, (unsigned char)atox(argv[2]));
	if (code < 0) {
		printf("error: can not set address to %s\n", argv[2]);
		return -1;
	}

	switch (argv[3][0]) {
	case 'r': {
		size = min(sizeof(buf), atox(argv[4]));
		size = read(i2c, buf, size);
		hex_print(buf, size, stdout);
		break; }

	case 'w': {
		size = decode_data(buf, sizeof(buf), argv[4]);
		size = write(i2c, buf, size);
		printf("write: %d byte(s)\n", size);
		break; }

	default: {
		printf("error: unsupport mode %c.\n", argv[3][0]);
		return -1; }
	}

	close(i2c);
	return 0;
}

compile it: mipsel-openwrt-linux-gcc i2ctest.c -o i2ctest

Notify: Shipping Delayed until Feb.10

Recently The well known Corona Virus from Wuhan is spreading very fast…Our China government extended Chinese New Year holiday to Feb.9 in order to slow down the virus spreading. From Jan.20 to Feb.10, we can not ship out any package.

Sorry about the delay, hope the disaster will pass soon…