ads1115 analog-to-digital 4-channel added
[i2c-telemetry.git] / am2321.c
1 /*
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.
6 *
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.
11 *
12 *
13 * Project Home - https://github.com/takagi/am2321
14 *
15 */
16
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <fcntl.h>              /* for O_RDWR */
20 #include <string.h>             /* for memcpy */
21 #include <linux/i2c-dev.h>      /* for I2C_SLAVE */
22
23 #include <unistd.h>             /* add by paulerr*/
24 #include <sys/time.h>           /* add by paulerr*/
25
26
27 /* I2C character device */
28 #define I2C_DEVICE "/dev/i2c-0"
29
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
34  */
35 #define AM2321_ADDR (0xB8 >> 1)
36
37
38 /*
39  *  udelay function
40  */
41
42 long timeval_to_usec( struct timeval tm ) {
43   return tm.tv_sec * 1000000 + tm.tv_usec;
44 }
45
46 void udelay( long us ) {
47   struct timeval current;
48   struct timeval start;
49   
50   gettimeofday( &start, NULL );
51   do {
52     gettimeofday( &current, NULL );
53   } while( timeval_to_usec( current ) - timeval_to_usec( start ) < us );
54 }
55
56
57 /*
58  *  CRC16
59  */
60
61 unsigned short crc16( unsigned char *ptr, unsigned char len ) {
62   unsigned short crc = 0xFFFF;
63   unsigned char i;
64
65   while( len-- )
66   {
67     crc ^= *ptr++;
68     for( i = 0; i < 8; i++ ) {
69       if( crc & 0x01 ) {
70         crc >>= 1;
71         crc ^= 0xA001;
72       } else {
73         crc >>= 1;
74       }
75     }
76   }
77
78   return crc;
79 }
80
81 unsigned char crc16_low( unsigned short crc ) {
82   return crc & 0xFF;
83 }
84
85 unsigned char crc16_high( unsigned short crc ) {
86   return crc >> 8;
87 }
88
89
90 /*
91  *  st_am2321 Structure
92  */
93
94 typedef struct {
95   unsigned char data[8];
96 } st_am2321;
97
98 void __check_crc16( st_am2321 measured ) {
99   unsigned short crc_m, crc_s;
100
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" );
105     exit( 1 );
106   }
107
108   return;
109 }
110
111 st_am2321 __st_am2321( unsigned char* data ) {
112   st_am2321 result;
113   memcpy( result.data, data, 8 );
114   __check_crc16( result );
115   return result;
116 }
117
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] );
124   return;
125 }
126
127 short __am2321_temperature( st_am2321 measured ) {
128   return (measured.data[4] << 8) + measured.data[5];
129 }
130
131 short am2321_temperature_integral( st_am2321 measured ) {
132   return __am2321_temperature( measured ) / 10;
133 }
134
135 short am2321_temperature_fraction( st_am2321 measured ) {
136   return __am2321_temperature( measured ) % 10;
137 }
138
139 short __am2321_humidity( st_am2321 measured ) {
140   return (measured.data[2] << 8) + measured.data[3];
141 }
142
143 short am2321_humidity_integral( st_am2321 measured ) {
144   return __am2321_humidity( measured ) / 10;
145 }
146
147 short am2321_humidity_fraction( st_am2321 measured ) {
148   return __am2321_humidity( measured ) % 10;
149 }
150
151
152 /*
153  *  Measurement function
154  */
155
156 st_am2321 am2321() {
157   int fd;
158   int ret;
159   int retry_cnt;
160   unsigned char data[8];
161
162   /* open I2C device */
163   fd = open( I2C_DEVICE, O_RDWR );
164   if ( fd < 0 ) {
165     perror( "am2321(1)" );
166     exit( 1 );
167   }
168
169   /* set address of I2C device in 7 bits */
170   ret = ioctl( fd, I2C_SLAVE, AM2321_ADDR );
171   if ( ret < 0 ) {
172     perror( "am2321(2)" );
173     exit( 2 );
174   }
175
176  retry_cnt = 0;
177  retry:
178
179   /* wake I2C device up */
180   write( fd, NULL, 0);
181
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" );
187     udelay( 1000 );
188     goto retry;
189   }
190   if ( ret < 0 ) {
191     perror( "am2321(3)" );
192     exit( 3 );
193   }
194
195   /* wait for having measured */
196   udelay( 1500 );
197
198   /* read measured result */
199   memset( data, 0x00, 8 );
200   ret = read( fd, data, 8 );
201   if ( ret < 0 ) {
202     perror( "am2321(4)" );
203     exit( 4 );
204   }
205
206   /* close I2C device */
207   close( fd );
208
209   return __st_am2321( data );
210 }
211
212 st_am2321 am2321_stub() {
213   unsigned char data[] = { 0x03, 0x04, 0x01, 0x41,
214                            0x00, 0xEA, 0x21, 0x8F };
215   return __st_am2321( data );
216 }
217
218
219 /*
220  *  Print functions
221  */
222
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 ) );
229   return;
230 }
231
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 ) );
239   return;
240 }
241
242 /*
243  *  Main
244  */
245
246 #define OPT_HUMAN_READABLE 0x1
247 #define OPT_STUB 0x2
248
249 int print_help() {
250   fprintf( stderr,
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" );
255   exit( 1 );
256 }
257
258 int parse_options( int argc, char* argv[]) {
259   int options = 0;
260   int flags = 0;
261
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;
266     default:
267       fprintf( stderr, "am2321: Unsupported option \"%s\"\n", argv[1+flags] );
268       print_help();
269       exit( 1 );
270     }
271     flags++;
272   }
273
274   return options;
275 }
276
277 int is_opt_human_readable( int options ) {
278   return options & OPT_HUMAN_READABLE;
279 }
280
281 int is_opt_stub( int options ) {
282   return options & OPT_STUB;
283 }
284
285 int main( int argc, char* argv[] ) {
286   int options;
287   st_am2321 measured;
288
289   /* parse the given options */
290   options = parse_options( argc, argv );
291
292   /* measure temperature and humidity */
293   measured = ! is_opt_stub( options ) ? am2321() : am2321_stub();
294
295   /* print measured temperature and humidity */
296   if ( ! is_opt_human_readable( options ) ) {
297     print_am2321( measured );
298   } else {
299     print_am2321_human_readable( measured );
300   }
301
302   return 0;
303 }