2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
13 * Project Home - https://github.com/takagi/am2321
19 #include <fcntl.h> /* for O_RDWR */
20 #include <string.h> /* for memcpy */
21 #include <linux/i2c-dev.h> /* for I2C_SLAVE */
23 #include <unistd.h> /* add by paulerr*/
24 #include <sys/time.h> /* add by paulerr*/
27 /* I2C character device */
28 #define I2C_DEVICE "/dev/i2c-0"
30 /* I2C address of AM2321 sensor in 7 bits
31 * - the first 7 bits should be passed to ioctl system call
32 * because the least 1 bit of the address represents read/write
33 * and the i2c driver takes care of it
35 #define AM2321_ADDR (0xB8 >> 1)
42 long timeval_to_usec( struct timeval tm ) {
43 return tm.tv_sec * 1000000 + tm.tv_usec;
46 void udelay( long us ) {
47 struct timeval current;
50 gettimeofday( &start, NULL );
52 gettimeofday( ¤t, NULL );
53 } while( timeval_to_usec( current ) - timeval_to_usec( start ) < us );
61 unsigned short crc16( unsigned char *ptr, unsigned char len ) {
62 unsigned short crc = 0xFFFF;
68 for( i = 0; i < 8; i++ ) {
81 unsigned char crc16_low( unsigned short crc ) {
85 unsigned char crc16_high( unsigned short crc ) {
95 unsigned char data[8];
98 void __check_crc16( st_am2321 measured ) {
99 unsigned short crc_m, crc_s;
101 crc_m = crc16( measured.data, 6 );
102 crc_s = (measured.data[7] << 8) + measured.data[6];
103 if ( crc_m != crc_s ) {
104 fprintf( stderr, "am2321: CRC16 does not match\n" );
111 st_am2321 __st_am2321( unsigned char* data ) {
113 memcpy( result.data, data, 8 );
114 __check_crc16( result );
118 void am2321_dump( st_am2321 measured ) {
119 printf( "[ 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x ]\n",
120 measured.data[0], measured.data[1],
121 measured.data[2], measured.data[3],
122 measured.data[4], measured.data[5],
123 measured.data[6], measured.data[7] );
127 short __am2321_temperature( st_am2321 measured ) {
128 return (measured.data[4] << 8) + measured.data[5];
131 short am2321_temperature_integral( st_am2321 measured ) {
132 return __am2321_temperature( measured ) / 10;
135 short am2321_temperature_fraction( st_am2321 measured ) {
136 return __am2321_temperature( measured ) % 10;
139 short __am2321_humidity( st_am2321 measured ) {
140 return (measured.data[2] << 8) + measured.data[3];
143 short am2321_humidity_integral( st_am2321 measured ) {
144 return __am2321_humidity( measured ) / 10;
147 short am2321_humidity_fraction( st_am2321 measured ) {
148 return __am2321_humidity( measured ) % 10;
153 * Measurement function
160 unsigned char data[8];
162 /* open I2C device */
163 fd = open( I2C_DEVICE, O_RDWR );
165 perror( "am2321(1)" );
169 /* set address of I2C device in 7 bits */
170 ret = ioctl( fd, I2C_SLAVE, AM2321_ADDR );
172 perror( "am2321(2)" );
179 /* wake I2C device up */
182 /* write measurement request */
183 data[0] = 0x03; data[1] = 0x00; data[2] = 0x04;
184 ret = write( fd, data, 3 );
185 if ( ret < 0 && retry_cnt++ < 5 ) {
186 fprintf( stderr, "am2321: retry\n" );
191 perror( "am2321(3)" );
195 /* wait for having measured */
198 /* read measured result */
199 memset( data, 0x00, 8 );
200 ret = read( fd, data, 8 );
202 perror( "am2321(4)" );
206 /* close I2C device */
209 return __st_am2321( data );
212 st_am2321 am2321_stub() {
213 unsigned char data[] = { 0x03, 0x04, 0x01, 0x41,
214 0x00, 0xEA, 0x21, 0x8F };
215 return __st_am2321( data );
223 void print_am2321( st_am2321 measured ) {
224 printf( "%d.%d %d.%d\n",
225 am2321_temperature_integral( measured ),
226 am2321_temperature_fraction( measured ),
227 am2321_humidity_integral( measured ),
228 am2321_humidity_fraction( measured ) );
232 void print_am2321_human_readable( st_am2321 measured ) {
233 printf( "Temperature (C)\t\t%d.%d\n",
234 am2321_temperature_integral( measured ),
235 am2321_temperature_fraction( measured ) );
236 printf( "Humidity (%%)\t\t%d.%d\n",
237 am2321_humidity_integral( measured ),
238 am2321_humidity_fraction( measured ) );
246 #define OPT_HUMAN_READABLE 0x1
251 "Usage: am2321 [-r] [-s]\n"
252 "Get temperature and humidity measured with Aosong's AM2321 sensor.\n"
253 " -r human readable output\n"
254 " -s not measure with AM2321, instead stub of 23.4[C] and 32.1[%]\n" );
258 int parse_options( int argc, char* argv[]) {
262 while( 1+flags < argc && argv[1+flags][0] == '-' ) {
263 switch( argv[1+flags][1] ) {
264 case 'r': options |= OPT_HUMAN_READABLE; break;
265 case 's': options |= OPT_STUB; break;
267 fprintf( stderr, "am2321: Unsupported option \"%s\"\n", argv[1+flags] );
277 int is_opt_human_readable( int options ) {
278 return options & OPT_HUMAN_READABLE;
281 int is_opt_stub( int options ) {
282 return options & OPT_STUB;
285 int main( int argc, char* argv[] ) {
289 /* parse the given options */
290 options = parse_options( argc, argv );
292 /* measure temperature and humidity */
293 measured = ! is_opt_stub( options ) ? am2321() : am2321_stub();
295 /* print measured temperature and humidity */
296 if ( ! is_opt_human_readable( options ) ) {
297 print_am2321( measured );
299 print_am2321_human_readable( measured );