/* ** Ident: X-2 ** ******************************************************************************** ** ** ** © Copyright 1976, 2006 Hewlett-Packard Development Company, L.P. ** ** ** ** Confidential computer software. Valid license from HP and/or ** ** its subsidiaries required for possession, use, or copying. ** ** ** ** Consistent with FAR 12.211 and 12.212, Commercial Computer Software, ** ** Computer Software Documentation, and Technical Data for Commercial ** ** Items are licensed to the U.S. Government under vendor's standard ** ** commercial license. ** ** ** ** Neither HP nor any of its subsidiaries shall be liable for technical ** ** or editorial errors or omissions contained herein. The information ** ** in this document is provided "as is" without warranty of any kind and ** ** is subject to change without notice. The warranties for HP products ** ** are set forth in the express limited warranty statements accompanying ** ** such products. Nothing herein should be construed as constituting an ** ** additional warranty. ** ** ** ******************************************************************************** ** ** ** FACILITY: ** ** USB Example ** ** Abstract: ** ** This file has the definitions for the Lascar Electronics EL-USB-2 ** temperature and humidity logger. This logger is built around a ** Silicon Laboratories MCU. The MCU has a USB 2.0 controller, A to D ** converters a temperature sensor, flash memory and memory to program the ** controller. ** ** The EL-USB-2 is part of a family of device built by Lascar ** The EL-USB-1 which just does temperature, the EL-USB-2 temperature and ** humidity logger, and and EL-USB-3 temperature and power, EL-USB-4 ** temperature and current. There are some others as well that appear ** to use the same micro controller. They all should work in a similar ** fashion to the EL-USB-2. ** ** The EL-USB-2 has three basic commands it responsds to. They are: ** ** 1) Setup Logger Commnd ** Write to bulk out pipe 0x01 0x40 0x00 ** Write to bulk out pipe 64 byte data block ** ** 2) Read data from logger Command ** Write to bulk out pipe 0x03 0xff 0xff ** Read 3 bytes from bulk in pipe last 2 is amount of data to ** read ** Read all the data in 512 byte chunks have to ask for 513 on ** bulk in pipe ** ** 3) Read Logger Status Command ** Write to bulk out pipe 0x00 0xff 0xff ** Read 3 bytes from bulk in pipe last 2 is amount of data to ** read ** Read 128 bytes of status data you need to ask for 129 on bulk ** in pipe ** ** To get the device to respond to a command you first send it the ** following device request: ** ** device_request.ug$b_bmrequesttype = 0x40 ** ** The request type of 0x40 decodes to: ** ** Category Bit Position Description ** -------------- ------------- -------------- ** Xfer direction Bit 7 clear Host to Device ** Type Bit 6 set Vendor ** Recipient Bits 0-4 clear Device ** ** device_request.ug$b_brequest = 0x02 ** device_request.ug$w_wvalue = 0x0002 ** device_request.ug$w_windex = 0 ** device_request.ug$w_wlength = 0 ** ** Then one of the command sequences from the list of 3 above. Finally ** you need to send it the following device request: ** ** device_request.ug$b_bmrequesttype = 0x40 ** device_request.ug$b_brequest = 0x02 ** device_request.ug$w_wvalue = 0x0004 ** device_request.ug$w_windex = 0 ** device_request.ug$w_wlength = 0 ** ** Notes: ** ** 1) The device is not real tolerant of errors. If you send it request ** that returns data if you do not drain all the data it will return ** the vms status in the IOSB of SS$_OPINCOMPL. Then typically ** either USB$_DATAUNDERRUN or USB$_DATAOVERRUN in the USB status ** field in the IOSB. Other errors are possible but these are the ** most likley. If you manage to confuse the device the simplest ** thing is to unplug it and that will get it to reset itself in ** most cases. In rare cases you may need to remove the battery. ** ** 2) The device for the get status and read data commands it return ** the amount of data to read. In the case of the get status you ** need to read one more byte than it tells you. In the case of the ** read data command you need to read it back in 513 byte chunks. It ** will return 512 bytes for each request. Reading 512 will result in ** an error. ** ** 3) If you are not going to use the device as a pure logger it ** is better to just leave it run until the 16K sample buffer is ** almost full before resetting it. This save writing the same ** flash memory cells all the time. Flash memory has finite number ** or erase write cycles. This is typically around 1,000,000 cycles ** so minimizing writing to the same memory locations by stopping and ** starting it every time is highly desirable. ** ** 4) While it seems logical that the device should be able to run off ** or bus power that does not appear to be the case. There is also ** not obvious indicator of the health of the batter in the status ** returned by the device. So you need to remember to check and ** replace the battery at least once a year. ** ** The device claims the following limits and accuracy: ** ** Item Min Typical Max ** Temp C -35 +-1 +80 ** Temp F -31 +-2 +176 ** RH 0% +-3.5% 100% ** Sample 10 12 Hours ** Batt life 1 Year ** ** ** AUTHOR: Forrest A. Kenney 19-June-2006 ** ** REVISION HISTORY: ** ** X-2 FAK002 Forrest A. Kenney 21-August-2006 ** Rework prototypes to make it easier to build appilcations ** that use the logger. Fix problem with the month and the day ** being at the wrong offset. Add in delayed start field. There ** is still a low power indication somewhere that I have yet to ** find. ** ** X-1 FAK001 Forrest A. Kenney dd-mmmmmmmm-yyyy ** Initial check in. **/ #ifndef __EL_USB_2 #define __EL_USB_2 /* ** Define some constants */ #define MAX_HUMIDITY 200 /* 100% */ #define MIN_HUMIDITY 0 /* 0% */ #define MAX_FAHRENHEIT 216 /* 176 F */ #define MIN_FAHRENHEIT 5 /* -35 F */ #define MAX_CELSIUS 240 /* 80 C */ #define MIN_CELSIUS 10 /* -35 C */ #define MIN_SAMPLE_RATE 10 /* 10 seconds */ #define MAX_SAMPLE_RATE 43200 /* 12 hours */ #define MIN_YEAR 2000 /* Base year for device */ #define MAX_START_DELAY 1382400 /* Maxmumn number of seconds in the */ /* future the logger will start */ /* taking data */ /** ** The device return data in two byte pairs one for the temperature and ** one for the humidity. These are unsigned bytes that range from 0 to 255. ** This data has to be decoded to get the actualy values. Here is how the ** data is decoded. ** ** Humidity is read in 1/2% so reading from device are divided by 2 to ** get actual vaule. For example if the device returns 75 for humdity the ** actual relative humidity is 37.5 ** ** When the device is reading in Fahrenheit 40 is subtracted from the ** values it returns to get the actual temperature. For example if the device ** return 128 for a temperature the actual temperature is 88F. ** ** When the device is reading in Celsius the reading is divided by 2 and ** then 40 is subtracted out from the value it returns to get actual ** temperature. For example if the device returns 156 the actual temperature ** is 38C. ** */ /* ** The layout of recorded data in the logger is one byte of temperature and ** one byte for humdity. One extra byte is allocated in array as a pad for ** the last 513 byte read from the device. When stepping through the array ** of data when you encounter a temperature of 0xff the previouse temperature ** and humidity pair is the last valid data points. It is possible for there ** to be one or more valid data points than the count returned in the get ** status information. */ typedef struct __temp_data { unsigned char degrees; unsigned char humidity; } TEMP_DATA; typedef struct __logged_data { TEMP_DATA data[16385]; //16K entries plus a pad } LOGGED_DATA; /* ** Define the format of the status data returned by the device. */ typedef struct _status_block { unsigned short int header; // Device Type ? char name[16]; // Ascii name string unsigned char hour; unsigned char min; unsigned char second; unsigned char day; unsigned char month; unsigned char year; unsigned int start_delay; // Delay in seconds until sampling starts unsigned short int sample_rate; // Sample rate in seconds unsigned short int count; // Count of samples union { unsigned char limit_flags; // Define limits flag bits struct ___bits { unsigned char temp_high : 1, // High temp indicator temp_low : 1, // Low temp indicator temp_high_hold : 1, // Hold high temp indicator on temp_low_hold : 1, // Hold low temp indicator on rh_high : 1, // Relative Humidity high indicator rh_low : 1, // Relative Humidity low indicator rh_high_hold : 1, // Hold high RH indicator on rh_low_hold : 1; // Hold low RH indicator on } bits; } rh_temp_flags; union // Define Run stop flags { unsigned char run_stop; // struct __bits { unsigned char run : 1, // Run valid : 1, // Valid data bit cleared when logged data is read fill : 6; // Run and Valid clear device is stopped } bits; // } status; /* ** If the units flag is set to 1 for Celsius then each increment is .5 degree ** C. So 156 maps to 38c. The formula is ((156/2) - 40) = 38c. The reason ** reason is 0 maps -40 C which is the lowest valid temperature. ** ** If the units flag is 0 for Fahrenheit then each increment is 1 degree F. ** So 140 F maps to 100 F. The formular is (140 - 40) = 100. As with the ** conversion for Celsius the lowest value possible is -40 F and is represented ** as 0. ** */ unsigned char temp_high; unsigned char temp_low; unsigned char fill2[10]; // 10 mystery bytes unsigned char units; // 1 -> F 0 -> C unsigned char pad2; char version[4]; // unsigned int serial_num; // Serial Number unsigned char rh_high; // Relative humidity high limit unsigned char rh_low; // Relative humidity low limit // units are 0.5 percent. So 80% // is read as 160 // unsigned char fill3[72]; // One extra guard to make block 129 } STATUS_BLOCK; /* ** Define UG drivers IOSB layout it is non-standard. */ typedef struct _iosb { short int status; short int msg_len; int usb_status; } UG_IOSB; /* ** Define data structure to hold data controlling the device. */ #define MAX_PIPES 10 typedef struct _device_data { short int channels[MAX_PIPES]; int pipe_count; uint64 pipe_handles[MAX_PIPES]; int pipe_state[MAX_PIPES]; int pipe_type[MAX_PIPES]; int pipe_direction[MAX_PIPES]; int bulk_in_pipe_index; int bulk_out_pipe_index; struct dsc$descriptor_s device_name; UG_IOSB iosb; } DEVICE_DATA; /* ** Function prototypes */ int associate_channels (struct dsc$descriptor_s *devname, short int *channels, int pipe_count, uint64 *pipe_handles, int *pipe_direction, int *pipe_type); int check_status_block (STATUS_BLOCK *block, int *item_offset); int check_temp (unsigned char temp, unsigned char units); int dump_data (DEVICE_DATA *device, LOGGED_DATA *data); int get_channels (short int *channels, int *pipe_count, uint64 *pipe_handles); int get_status (DEVICE_DATA *device, STATUS_BLOCK *block); int map_pipes (DEVICE_DATA *device); int open_el_usb_2(DEVICE_DATA *device, char *device_name, void (*rtn)(), unsigned int ast_param); int send_to_device (DEVICE_DATA *device, unsigned char brequest, unsigned short int wval, unsigned short int windex, unsigned short int length, void *buffer); int setup_logger (DEVICE_DATA *device, STATUS_BLOCK *block, int *item_offset); #endif /* __EL_USB_2 */