From dd995683ea1f233b79724d524a9a3dbcdc171d30 Mon Sep 17 00:00:00 2001 From: Dean Camera Date: Sun, 24 Jan 2010 11:13:23 +0000 Subject: [PATCH] Enhance TemperatureDatalogger project -- add RTC capabilities so that data is logged along with the current time and date. Make logging interval configurable, set by a C# PC host application. --- LUFA.pnproj | 2 +- LUFA/ManPages/FutureChanges.txt | 3 + LUFA/ManPages/VIDAndPIDValues.txt | 2 +- Projects/AVRISP-MKII/makefile | 1 + Projects/TemperatureDataLogger/Descriptors.c | 87 ++++++++- Projects/TemperatureDataLogger/Descriptors.h | 48 ++--- Projects/TemperatureDataLogger/Lib/DS1307.c | 129 +++++++++++++ Projects/TemperatureDataLogger/Lib/DS1307.h | 111 +++++++++++ .../TemperatureDataLogger/Lib/FATFs/diskio.c | 14 +- .../TemperatureDataLogger/Lib/FATFs/ffconf.h | 2 +- .../TemperatureDataLogger/TempDataLogger.c | 165 +++++++++++++--- .../TemperatureDataLogger/TempDataLogger.h | 25 ++- .../TempLoggerHostApp/TempLoggerHostApp.sln | 20 ++ .../TempLoggerHostApp/TempLoggerHostApp.suo | Bin 0 -> 17408 bytes .../DataLoggerSettings.Designer.cs | 181 ++++++++++++++++++ .../TempLoggerHostApp/DataLoggerSettings.cs | 172 +++++++++++++++++ .../TempLoggerHostApp/DataLoggerSettings.resx | 120 ++++++++++++ .../TempLoggerHostApp/Hid.Linux.dll | Bin 0 -> 9216 bytes .../TempLoggerHostApp/Hid.Net.dll | Bin 0 -> 24576 bytes .../TempLoggerHostApp/Hid.Win32.dll | Bin 0 -> 94208 bytes .../TempLoggerHostApp/Program.cs | 21 ++ .../TempLoggerHostApp.csproj | 95 +++++++++ .../TemperatureDataLogger.txt | 29 ++- Projects/TemperatureDataLogger/makefile | 3 + 24 files changed, 1147 insertions(+), 83 deletions(-) create mode 100644 Projects/TemperatureDataLogger/Lib/DS1307.c create mode 100644 Projects/TemperatureDataLogger/Lib/DS1307.h create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.sln create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.suo create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.Designer.cs create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.cs create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.resx create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/Hid.Linux.dll create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/Hid.Net.dll create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/Hid.Win32.dll create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/Program.cs create mode 100644 Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/TempLoggerHostApp.csproj diff --git a/LUFA.pnproj b/LUFA.pnproj index 54dce2e1ef..f50eba3a45 100644 --- a/LUFA.pnproj +++ b/LUFA.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/LUFA/ManPages/FutureChanges.txt b/LUFA/ManPages/FutureChanges.txt index 38cbf0ccf3..9006040d3d 100644 --- a/LUFA/ManPages/FutureChanges.txt +++ b/LUFA/ManPages/FutureChanges.txt @@ -17,6 +17,8 @@ * -# Add ability to get number of bytes not written with pipe/endpoint write routines after an error * -# Add standardized descriptor names to class driver structures * -# Correct mishandling of error cases in Mass Storage demos + * -# FIX BROKEN RNDIS HOST CLASS DRIVER (BLOCKING) + * -# TEST AND CORRECT TPI PROGRAMMING SUPPORT IN THE AVRISP-MKII PROJECT (BLOCKING) * - Documentation/Support * -# Remake AVRStudio project files * -# Add detailed overviews of how each demo works @@ -32,6 +34,7 @@ * -# Finish MIDI class Bootloader * -# Finish SideShow demo * -# Finish StandaloneProgrammer project + * -# Finish Webserver project * - Ports * -# AVR32 UC3B series microcontrollers * -# Atmel ARM7 series microcontrollers diff --git a/LUFA/ManPages/VIDAndPIDValues.txt b/LUFA/ManPages/VIDAndPIDValues.txt index 1a101a8933..339fd47b23 100644 --- a/LUFA/ManPages/VIDAndPIDValues.txt +++ b/LUFA/ManPages/VIDAndPIDValues.txt @@ -265,7 +265,7 @@ * 0x2063 * * - * Currently Unallocated + * Mass Storage/HID Interface Datalogger Project * * * diff --git a/Projects/AVRISP-MKII/makefile b/Projects/AVRISP-MKII/makefile index 58d2a46d09..5c0fe0f64f 100644 --- a/Projects/AVRISP-MKII/makefile +++ b/Projects/AVRISP-MKII/makefile @@ -197,6 +197,7 @@ CDEFS += -DAUX_LINE_MASK="(1 << 4)" CDEFS += -DVTARGET_ADC_CHANNEL=2 CDEFS += -DENABLE_ISP_PROTOCOL CDEFS += -DENABLE_XPROG_PROTOCOL +#CDEFS += -DXPROG_VIA_HARDWARE_USART # Place -D or -U options here for ASM sources diff --git a/Projects/TemperatureDataLogger/Descriptors.c b/Projects/TemperatureDataLogger/Descriptors.c index 40ce620a64..e4f5666e5f 100644 --- a/Projects/TemperatureDataLogger/Descriptors.c +++ b/Projects/TemperatureDataLogger/Descriptors.c @@ -49,6 +49,32 @@ #warning USE_INTERNAL_SERIAL is not available on this AVR - please manually construct a device serial descriptor. #endif +/** HID class report descriptor. This is a special descriptor constructed with values from the + * USBIF HID class specification to describe the reports and capabilities of the HID device. This + * descriptor is parsed by the host and its contents used to determine what data (and in what encoding) + * the device will send, and what it may be sent back from the host. Refer to the HID specification for + * more details on HID report descriptors. + */ +USB_Descriptor_HIDReport_Datatype_t PROGMEM GenericReport[] = +{ + 0x06, 0x9c, 0xff, /* Usage Page (Vendor Defined) */ + 0x09, 0x01, /* Usage (Vendor Defined) */ + 0xa1, 0x01, /* Collection (Vendor Defined) */ + 0x09, 0x02, /* Usage (Vendor Defined) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, GENERIC_REPORT_SIZE, /* Report Count (GENERIC_REPORT_SIZE) */ + 0x15, 0x80, /* Logical Minimum (-128) */ + 0x25, 0x7F, /* Logical Maximum (127) */ + 0x81, 0x02, /* Input (Data, Variable, Absolute) */ + 0x09, 0x03, /* Usage (Vendor Defined) */ + 0x75, 0x08, /* Report Size (8) */ + 0x95, GENERIC_REPORT_SIZE, /* Report Count (GENERIC_REPORT_SIZE) */ + 0x15, 0x00, /* Logical Minimum (0) */ + 0x25, 0xff, /* Logical Maximum (255) */ + 0x91, 0x02, /* Output (Data, Variable, Absolute) */ + 0xc0 /* End Collection */ +}; + /** Device descriptor structure. This descriptor, located in FLASH memory, describes the overall * device characteristics, including the supported USB version, control endpoint size and the * number of device configurations. The descriptor is read out by the USB host when the enumeration @@ -66,7 +92,7 @@ USB_Descriptor_Device_t PROGMEM DeviceDescriptor = .Endpoint0Size = FIXED_CONTROL_ENDPOINT_SIZE, .VendorID = 0x03EB, - .ProductID = 0x2045, + .ProductID = 0x2063, .ReleaseNumber = 0x0000, .ManufacturerStrIndex = 0x01, @@ -88,7 +114,7 @@ USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .Header = {.Size = sizeof(USB_Descriptor_Configuration_Header_t), .Type = DTYPE_Configuration}, .TotalConfigurationSize = sizeof(USB_Descriptor_Configuration_t), - .TotalInterfaces = 1, + .TotalInterfaces = 2, .ConfigurationNumber = 1, .ConfigurationStrIndex = NO_DESCRIPTOR, @@ -98,7 +124,7 @@ USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .MaxPowerConsumption = USB_CONFIG_POWER_MA(100) }, - .Interface = + .MSInterface = { .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, @@ -114,7 +140,7 @@ USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .InterfaceStrIndex = NO_DESCRIPTOR }, - .DataInEndpoint = + .MSDataInEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, @@ -124,7 +150,7 @@ USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .PollingIntervalMS = 0x00 }, - .DataOutEndpoint = + .MSDataOutEndpoint = { .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, @@ -132,7 +158,44 @@ USB_Descriptor_Configuration_t PROGMEM ConfigurationDescriptor = .Attributes = (EP_TYPE_BULK | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), .EndpointSize = MASS_STORAGE_IO_EPSIZE, .PollingIntervalMS = 0x00 - } + }, + + .HIDInterface = + { + .Header = {.Size = sizeof(USB_Descriptor_Interface_t), .Type = DTYPE_Interface}, + + .InterfaceNumber = 1, + .AlternateSetting = 0, + + .TotalEndpoints = 1, + + .Class = 0x03, + .SubClass = 0x00, + .Protocol = HID_NON_BOOT_PROTOCOL, + + .InterfaceStrIndex = NO_DESCRIPTOR + }, + + .HIDInfo = + { + .Header = {.Size = sizeof(USB_HID_Descriptor_t), .Type = DTYPE_HID}, + + .HIDSpec = VERSION_BCD(01.11), + .CountryCode = 0x00, + .TotalReportDescriptors = 1, + .HIDReportType = DTYPE_Report, + .HIDReportLength = sizeof(GenericReport) + }, + + .HIDDataInEndpoint = + { + .Header = {.Size = sizeof(USB_Descriptor_Endpoint_t), .Type = DTYPE_Endpoint}, + + .EndpointAddress = (ENDPOINT_DESCRIPTOR_DIR_IN | GENERIC_IN_EPNUM), + .Attributes = (EP_TYPE_INTERRUPT | ENDPOINT_ATTR_NO_SYNC | ENDPOINT_USAGE_DATA), + .EndpointSize = GENERIC_EPSIZE, + .PollingIntervalMS = 0x0A + }, }; /** Language descriptor structure. This descriptor, located in FLASH memory, is returned when the host requests @@ -163,9 +226,9 @@ USB_Descriptor_String_t PROGMEM ManufacturerString = */ USB_Descriptor_String_t PROGMEM ProductString = { - .Header = {.Size = USB_STRING_LEN(22), .Type = DTYPE_String}, + .Header = {.Size = USB_STRING_LEN(10), .Type = DTYPE_String}, - .UnicodeString = L"LUFA Mass Storage Demo" + .UnicodeString = L"Datalogger" }; /** This function is called by the library when in device mode, and must be overridden (see library "USB Descriptors" @@ -209,6 +272,14 @@ uint16_t CALLBACK_USB_GetDescriptor(const uint16_t wValue, const uint8_t wIndex, break; } + break; + case DTYPE_HID: + Address = (void*)&ConfigurationDescriptor.HIDInfo; + Size = sizeof(USB_HID_Descriptor_t); + break; + case DTYPE_Report: + Address = (void*)&GenericReport; + Size = sizeof(GenericReport); break; } diff --git a/Projects/TemperatureDataLogger/Descriptors.h b/Projects/TemperatureDataLogger/Descriptors.h index dcaaddaf7d..a0f03c38d9 100644 --- a/Projects/TemperatureDataLogger/Descriptors.h +++ b/Projects/TemperatureDataLogger/Descriptors.h @@ -5,33 +5,6 @@ dean [at] fourwalledcubicle [dot] com www.fourwalledcubicle.com */ - -/* - Copyright 2010 Dean Camera (dean [at] fourwalledcubicle [dot] com) - - Permission to use, copy, modify, distribute, and sell this - software and its documentation for any purpose is hereby granted - without fee, provided that the above copyright notice appear in - all copies and that both that the copyright notice and this - permission notice and warranty disclaimer appear in supporting - documentation, and that the name of the author not be used in - advertising or publicity pertaining to distribution of the - software without specific, written prior permission. - - The author disclaim all warranties with regard to this - software, including all implied warranties of merchantability - and fitness. In no event shall the author be liable for any - special, indirect or consequential damages or any damages - whatsoever resulting from loss of use, data or profits, whether - in an action of contract, negligence or other tortious action, - arising out of or in connection with the use or performance of - this software. -*/ - -/** \file - * - * Header file for Descriptors.c. - */ #ifndef _DESCRIPTORS_H_ #define _DESCRIPTORS_H_ @@ -41,6 +14,9 @@ #include #include + #include + + #include "TempDataLogger.h" /* Macros: */ /** Endpoint number of the Mass Storage device-to-host data IN endpoint. */ @@ -51,6 +27,15 @@ /** Size in bytes of the Mass Storage data endpoints. */ #define MASS_STORAGE_IO_EPSIZE 64 + + /** Endpoint number of the Generic HID reporting IN endpoint. */ + #define GENERIC_IN_EPNUM 1 + + /** Size in bytes of the Generic HID reporting endpoint. */ + #define GENERIC_EPSIZE 16 + + /** Size in bytes of the Generic HID reports (including report ID byte). */ + #define GENERIC_REPORT_SIZE sizeof(Device_Report_t) /* Type Defines: */ /** Type define for the device configuration descriptor structure. This must be defined in the @@ -60,9 +45,12 @@ typedef struct { USB_Descriptor_Configuration_Header_t Config; - USB_Descriptor_Interface_t Interface; - USB_Descriptor_Endpoint_t DataInEndpoint; - USB_Descriptor_Endpoint_t DataOutEndpoint; + USB_Descriptor_Interface_t MSInterface; + USB_Descriptor_Endpoint_t MSDataInEndpoint; + USB_Descriptor_Endpoint_t MSDataOutEndpoint; + USB_Descriptor_Interface_t HIDInterface; + USB_HID_Descriptor_t HIDInfo; + USB_Descriptor_Endpoint_t HIDDataInEndpoint; } USB_Descriptor_Configuration_t; /* Function Prototypes: */ diff --git a/Projects/TemperatureDataLogger/Lib/DS1307.c b/Projects/TemperatureDataLogger/Lib/DS1307.c new file mode 100644 index 0000000000..37817e97f6 --- /dev/null +++ b/Projects/TemperatureDataLogger/Lib/DS1307.c @@ -0,0 +1,129 @@ +/* + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +#include "DS1307.h" + +void DS1307_Init(void) +{ + // Nothing to initialize +} + +void DS1307_SetDate(uint8_t Day, uint8_t Month, uint8_t Year) +{ +#if defined(DUMMY_RTC) + return; +#endif + + DS1307_DateRegs_t CurrentRTCDate; + CurrentRTCDate.Byte1.TenDay = (Day / 10); + CurrentRTCDate.Byte1.Day = (Day % 10); + CurrentRTCDate.Byte2.TenMonth = (Month / 10); + CurrentRTCDate.Byte2.Month = (Month % 10); + CurrentRTCDate.Byte3.TenYear = (Year / 10); + CurrentRTCDate.Byte3.Year = (Year % 10); + + if (TWI_StartTransmission(DS1307_ADDRESS_WRITE)) + { + TWI_SendByte(DS1307_DATEREG_START); + TWI_SendByte(CurrentRTCDate.Byte1.IntVal); + TWI_SendByte(CurrentRTCDate.Byte2.IntVal); + TWI_SendByte(CurrentRTCDate.Byte3.IntVal); + + TWI_StopTransmission(); + } +} + +void DS1307_SetTime(uint8_t Hour, uint8_t Minute, uint8_t Second) +{ +#if defined(DUMMY_RTC) + return; +#endif + + DS1307_TimeRegs_t CurrentRTCTime; + CurrentRTCTime.Byte1.TenSec = (Second / 10); + CurrentRTCTime.Byte1.Sec = (Second % 10); + CurrentRTCTime.Byte1.CH = false; + CurrentRTCTime.Byte2.TenMin = (Minute / 10); + CurrentRTCTime.Byte2.Min = (Minute % 10); + CurrentRTCTime.Byte3.TenHour = (Hour / 10); + CurrentRTCTime.Byte3.Hour = (Hour % 10); + CurrentRTCTime.Byte3.TwelveHourMode = false; + + if (TWI_StartTransmission(DS1307_ADDRESS_WRITE)) + { + TWI_SendByte(DS1307_TIMEREG_START); + TWI_SendByte(CurrentRTCTime.Byte1.IntVal); + TWI_SendByte(CurrentRTCTime.Byte2.IntVal); + TWI_SendByte(CurrentRTCTime.Byte3.IntVal); + + TWI_StopTransmission(); + } +} + +void DS1307_GetDate(uint8_t* Day, uint8_t* Month, uint8_t* Year) +{ +#if defined(DUMMY_RTC) + *Day = 1; + *Month = 1; + *Year = 1; + return; +#endif + + if (TWI_StartTransmission(DS1307_ADDRESS_WRITE)) + { + TWI_SendByte(DS1307_DATEREG_START); + + TWI_StopTransmission(); + } + + DS1307_DateRegs_t CurrentRTCDate; + + if (TWI_StartTransmission(DS1307_ADDRESS_READ)) + { + TWI_ReceiveByte(&CurrentRTCDate.Byte1.IntVal, false); + TWI_ReceiveByte(&CurrentRTCDate.Byte2.IntVal, false); + TWI_ReceiveByte(&CurrentRTCDate.Byte3.IntVal, true); + + TWI_StopTransmission(); + } + + *Day = (CurrentRTCDate.Byte1.TenDay * 10) + CurrentRTCDate.Byte1.Day; + *Month = (CurrentRTCDate.Byte2.TenMonth * 10) + CurrentRTCDate.Byte2.Month; + *Year = (CurrentRTCDate.Byte3.TenYear * 10) + CurrentRTCDate.Byte3.Year; +} + +void DS1307_GetTime(uint8_t* Hour, uint8_t* Minute, uint8_t* Second) +{ +#if defined(DUMMY_RTC) + *Hour = 1; + *Minute = 1; + *Second = 1; + return; +#endif + + if (TWI_StartTransmission(DS1307_ADDRESS_WRITE)) + { + TWI_SendByte(DS1307_TIMEREG_START); + + TWI_StopTransmission(); + } + + DS1307_TimeRegs_t CurrentRTCTime; + + if (TWI_StartTransmission(DS1307_ADDRESS_READ)) + { + TWI_ReceiveByte(&CurrentRTCTime.Byte1.IntVal, false); + TWI_ReceiveByte(&CurrentRTCTime.Byte2.IntVal, false); + TWI_ReceiveByte(&CurrentRTCTime.Byte3.IntVal, true); + + TWI_StopTransmission(); + } + + *Second = (CurrentRTCTime.Byte1.TenSec * 10) + CurrentRTCTime.Byte1.Sec; + *Minute = (CurrentRTCTime.Byte2.TenMin * 10) + CurrentRTCTime.Byte2.Min; + *Hour = (CurrentRTCTime.Byte3.TenHour * 10) + CurrentRTCTime.Byte3.Hour; +} diff --git a/Projects/TemperatureDataLogger/Lib/DS1307.h b/Projects/TemperatureDataLogger/Lib/DS1307.h new file mode 100644 index 0000000000..4918360795 --- /dev/null +++ b/Projects/TemperatureDataLogger/Lib/DS1307.h @@ -0,0 +1,111 @@ +/* + Copyright (C) Dean Camera, 2010. + + dean [at] fourwalledcubicle [dot] com + www.fourwalledcubicle.com +*/ + +#ifndef _DS1307_H_ +#define _DS1307_H_ + + /* Includes: */ + #include + + #include + + /* Type Defines: */ + typedef struct + { + union + { + struct + { + unsigned int Sec : 4; + unsigned int TenSec : 3; + unsigned int CH : 1; + }; + + uint8_t IntVal; + } Byte1; + + union + { + struct + { + unsigned int Min : 4; + unsigned int TenMin : 3; + unsigned int _RESERVED : 1; + }; + + uint8_t IntVal; + } Byte2; + + union + { + struct + { + unsigned int Hour : 4; + unsigned int TenHour : 2; + unsigned int TwelveHourMode : 1; + unsigned int _RESERVED : 1; + }; + + uint8_t IntVal; + } Byte3; + } DS1307_TimeRegs_t; + + typedef struct + { + union + { + struct + { + unsigned int Day : 4; + unsigned int TenDay : 2; + unsigned int _RESERVED : 2; + }; + + uint8_t IntVal; + } Byte1; + + union + { + struct + { + unsigned int Month : 4; + unsigned int TenMonth : 1; + unsigned int _RESERVED : 3; + }; + + uint8_t IntVal; + } Byte2; + + union + { + struct + { + unsigned int Year : 4; + unsigned int TenYear : 4; + }; + + uint8_t IntVal; + } Byte3; + } DS1307_DateRegs_t; + + /* Macros: */ + #define DS1307_TIMEREG_START 0x00 + #define DS1307_DATEREG_START 0x04 + + #define DS1307_ADDRESS_READ 0b11010001 + #define DS1307_ADDRESS_WRITE 0b11010000 + + /* Function Prototypes: */ + void DS1307_Init(void); + + void DS1307_SetDate(uint8_t Day, uint8_t Month, uint8_t Year); + void DS1307_SetTime(uint8_t Hour, uint8_t Minute, uint8_t Second); + + void DS1307_GetDate(uint8_t* Day, uint8_t* Month, uint8_t* Year); + void DS1307_GetTime(uint8_t* Hour, uint8_t* Minute, uint8_t* Second); + +#endif diff --git a/Projects/TemperatureDataLogger/Lib/FATFs/diskio.c b/Projects/TemperatureDataLogger/Lib/FATFs/diskio.c index e6be687343..0c0083bacc 100644 --- a/Projects/TemperatureDataLogger/Lib/FATFs/diskio.c +++ b/Projects/TemperatureDataLogger/Lib/FATFs/diskio.c @@ -83,5 +83,17 @@ DRESULT disk_ioctl ( DWORD get_fattime (void) { - return (1UL << 25) | (1UL << 21) | (1UL << 16) | (1UL << 11) | (1UL << 5) | (1UL << 0); + uint8_t Day, Month, Year; + uint8_t Hour, Minute, Second; + + DS1307_GetDate(&Day, &Month, &Year); + DS1307_GetTime(&Hour, &Minute, &Second); + + + return ((DWORD)(20 + Year) << 25) | + ((DWORD)Month << 21) | + ((DWORD)Day << 16) | + ((DWORD)Hour << 11) | + ((DWORD)Minute << 5) | + (((DWORD)Second >> 1) << 0); } diff --git a/Projects/TemperatureDataLogger/Lib/FATFs/ffconf.h b/Projects/TemperatureDataLogger/Lib/FATFs/ffconf.h index 18510157d6..9b414e979f 100644 --- a/Projects/TemperatureDataLogger/Lib/FATFs/ffconf.h +++ b/Projects/TemperatureDataLogger/Lib/FATFs/ffconf.h @@ -36,7 +36,7 @@ / 3: f_lseek is removed in addition to level 2. */ -#define _USE_STRFUNC 1 /* 0, 1 or 2 */ +#define _USE_STRFUNC 0 /* 0, 1 or 2 */ /* To enable string functions, set _USE_STRFUNC to 1 or 2. */ diff --git a/Projects/TemperatureDataLogger/TempDataLogger.c b/Projects/TemperatureDataLogger/TempDataLogger.c index b181ec2f83..538a6a5e81 100644 --- a/Projects/TemperatureDataLogger/TempDataLogger.c +++ b/Projects/TemperatureDataLogger/TempDataLogger.c @@ -58,52 +58,93 @@ USB_ClassInfo_MS_Device_t Disk_MS_Interface = }, }; +/** Buffer to hold the previously generated HID report, for comparison purposes inside the HID class driver. */ +uint8_t PrevHIDReportBuffer[GENERIC_REPORT_SIZE]; + +/** LUFA HID Class driver interface configuration and state information. This structure is + * passed to all HID Class driver functions, so that multiple instances of the same class + * within a device can be differentiated from one another. + */ +USB_ClassInfo_HID_Device_t Generic_HID_Interface = + { + .Config = + { + .InterfaceNumber = 1, + + .ReportINEndpointNumber = GENERIC_IN_EPNUM, + .ReportINEndpointSize = GENERIC_EPSIZE, + .ReportINEndpointDoubleBank = false, + + .PrevReportINBuffer = PrevHIDReportBuffer, + .PrevReportINBufferSize = sizeof(PrevHIDReportBuffer), + }, + }; + +/** Non-volatile Logging Interval value in EEPROM, stored as a number of 500ms ticks */ +uint8_t EEMEM LoggingInterval500MS_EEPROM; + +/** SRAM Logging Interval value fetched from EEPROM, stored as a number of 500ms ticks */ +uint8_t LoggingInterval500MS_SRAM; + +/** Total number of 500ms logging ticks elapsed since the last log value was recorded */ +uint16_t CurrentLoggingTicks; + /** FAT Fs structure to hold the internal state of the FAT driver for the dataflash contents. */ FATFS DiskFATState; /** FAT Fs structure to hold a FAT file handle for the log data write destination. */ FIL TempLogFile; -/** Counter to count the number of 10 millisecond ticks that has elapsed since the last sample */ -uint16_t CurrentLogTick; - ISR(TIMER1_COMPA_vect, ISR_BLOCK) { - if (CurrentLogTick++ != LOG_INTERVAL_10MS) - return; - uint8_t LEDMask = LEDs_GetLEDs(); + /* Check to see if the logging interval has expired */ + if (CurrentLoggingTicks++ < LoggingInterval500MS_SRAM) + return; + LEDs_SetAllLEDs(LEDMASK_USB_BUSY); - CurrentLogTick = 0; - + /* Reset log tick counter to prepare for next logging interval */ + CurrentLoggingTicks = 0; + if (USB_DeviceState == DEVICE_STATE_Unattached) { - f_printf(&TempLogFile, "%d Degrees\r\n", Temperature_GetTemperature()); + uint8_t Day, Month, Year; + uint8_t Hour, Minute, Second; + + DS1307_GetDate(&Day, &Month, &Year); + DS1307_GetTime(&Hour, &Minute, &Second); + + char LineBuffer[100]; + uint16_t BytesWritten; + + BytesWritten = sprintf(LineBuffer, "%02d/%02d/20%04d, %02d:%02d:%02d, %d Degrees\r\n", + Day, Month, Year, Hour, Minute, Second, Temperature_GetTemperature()); + + f_write(&TempLogFile, LineBuffer, BytesWritten, &BytesWritten); f_sync(&TempLogFile); } LEDs_SetAllLEDs(LEDMask); } - /** Main program entry point. This routine contains the overall program flow, including initial * setup of all components and the main program loop. */ int main(void) { - SetupHardware(); + /* Fetch logging interval from EEPROM */ + LoggingInterval500MS_SRAM = eeprom_read_byte(&LoggingInterval500MS_EEPROM); LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); + SetupHardware(); + /* Mount and open the log file on the dataflash FAT partition */ - f_mount(0, &DiskFATState); - f_open(&TempLogFile, LOG_FILENAME, FA_OPEN_ALWAYS | FA_WRITE); - f_lseek(&TempLogFile, TempLogFile.fsize); - f_printf(&TempLogFile, "===========================\r\n"); - + OpenLogFile(); + /* Discard the first sample from the temperature sensor, as it is generally incorrect */ uint8_t Dummy = Temperature_GetTemperature(); (void)Dummy; @@ -111,10 +152,33 @@ int main(void) for (;;) { MS_Device_USBTask(&Disk_MS_Interface); + HID_Device_USBTask(&Generic_HID_Interface); USB_USBTask(); } } +void OpenLogFile(void) +{ + char LogFileName[12]; + + /* Get the current date for the filename as "DDMMYY.csv" */ + uint8_t Day, Month, Year; + DS1307_GetDate(&Day, &Month, &Year); + sprintf(LogFileName, "%02d%02d%02d.csv", Day, Month, Year); + + /* Mount the storage device, open the file */ + f_mount(0, &DiskFATState); + f_open(&TempLogFile, LogFileName, FA_OPEN_ALWAYS | FA_WRITE); + f_lseek(&TempLogFile, TempLogFile.fsize); +} + +void CloseLogFile(void) +{ + /* Sync any data waiting to be written, unmount the storage device */ + f_sync(&TempLogFile); + f_close(&TempLogFile); +} + /** Configures the board hardware and chip peripherals for the demo's functionality. */ void SetupHardware(void) { @@ -133,9 +197,9 @@ void SetupHardware(void) Dataflash_Init(); USB_Init(); - /* 10ms interval timer configuration */ - OCR1A = (((F_CPU / 1024) / 100) - 1); - TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10); // CTC mode, Fcpu/1024 speed + /* 500ms logging interval timer configuration */ + OCR1A = ((F_CPU / 1024) / 2); + TCCR1B = (1 << WGM12) | (1 << CS12) | (1 << CS10); TIMSK1 = (1 << OCIE1A); /* Clear Dataflash sector protections, if enabled */ @@ -148,7 +212,7 @@ void EVENT_USB_Device_Connect(void) LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); /* Close the log file so that the host has exclusive filesystem access */ - f_close(&TempLogFile); + CloseLogFile(); } /** Event handler for the library USB Disconnection event. */ @@ -156,11 +220,8 @@ void EVENT_USB_Device_Disconnect(void) { LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); - /* When disconnected from the host, re-open log file so we can resume logging */ - f_mount(0, &DiskFATState); - f_open(&TempLogFile, LOG_FILENAME, FA_OPEN_ALWAYS | FA_WRITE); - f_lseek(&TempLogFile, TempLogFile.fsize); - f_printf(&TempLogFile, "===========================\r\n"); + /* Mount and open the log file on the dataflash FAT partition */ + OpenLogFile(); } /** Event handler for the library USB Configuration Changed event. */ @@ -170,12 +231,16 @@ void EVENT_USB_Device_ConfigurationChanged(void) if (!(MS_Device_ConfigureEndpoints(&Disk_MS_Interface))) LEDs_SetAllLEDs(LEDMASK_USB_ERROR); + + if (!(HID_Device_ConfigureEndpoints(&Generic_HID_Interface))) + LEDs_SetAllLEDs(LEDMASK_USB_ERROR); } /** Event handler for the library USB Unhandled Control Request event. */ void EVENT_USB_Device_UnhandledControlRequest(void) { MS_Device_ProcessControlRequest(&Disk_MS_Interface); + HID_Device_ProcessControlRequest(&Generic_HID_Interface); } /** Mass Storage class driver callback function the reception of SCSI commands from the host, which must be processed. @@ -192,3 +257,53 @@ bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* MSInterfa return CommandSuccess; } + +/** HID class driver callback function for the creation of HID reports to the host. + * + * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced + * \param[in,out] ReportID Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID + * \param[in] ReportType Type of the report to create, either REPORT_ITEM_TYPE_In or REPORT_ITEM_TYPE_Feature + * \param[out] ReportData Pointer to a buffer where the created report should be stored + * \param[out] ReportSize Number of bytes written in the report (or zero if no report is to be sent + * + * \return Boolean true to force the sending of the report, false to let the library determine if it needs to be sent + */ +bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, uint8_t* const ReportID, + const uint8_t ReportType, void* ReportData, uint16_t* ReportSize) +{ + Device_Report_t* ReportParams = (Device_Report_t*)ReportData; + + DS1307_GetDate(&ReportParams->Day, &ReportParams->Month, &ReportParams->Year); + DS1307_GetTime(&ReportParams->Hour, &ReportParams->Minute, &ReportParams->Second); + + ReportParams->LogInterval500MS = LoggingInterval500MS_SRAM; + + *ReportSize = sizeof(Device_Report_t); + return true; +} + +/** HID class driver callback function for the processing of HID reports from the host. + * + * \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced + * \param[in] ReportID Report ID of the received report from the host + * \param[in] ReportData Pointer to a buffer where the created report has been stored + * \param[in] ReportSize Size in bytes of the received HID report + */ +void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, const uint8_t ReportID, + const void* ReportData, const uint16_t ReportSize) +{ + Device_Report_t* ReportParams = (Device_Report_t*)ReportData; + + GPIOR0 = ReportParams->Day; + GPIOR1 = ReportParams->Month; + GPIOR2 = ReportParams->Year; + + DS1307_SetDate(ReportParams->Day, ReportParams->Month, ReportParams->Year); + DS1307_SetTime(ReportParams->Hour, ReportParams->Minute, ReportParams->Second); + + if (LoggingInterval500MS_SRAM != ReportParams->LogInterval500MS) + { + LoggingInterval500MS_SRAM = ReportParams->LogInterval500MS; + eeprom_write_byte(&LoggingInterval500MS_EEPROM, LoggingInterval500MS_SRAM); + } +} diff --git a/Projects/TemperatureDataLogger/TempDataLogger.h b/Projects/TemperatureDataLogger/TempDataLogger.h index b9d1b9114d..ee10a09713 100644 --- a/Projects/TemperatureDataLogger/TempDataLogger.h +++ b/Projects/TemperatureDataLogger/TempDataLogger.h @@ -40,12 +40,14 @@ #include #include #include - + #include + #include "Descriptors.h" #include "Lib/SCSI.h" #include "Lib/DataflashManager.h" #include "Lib/FATFs/ff.h" + #include "Lib/DS1307.h" #include #include @@ -53,6 +55,7 @@ #include #include #include + #include /* Macros: */ /** LED mask for the library LED driver, to indicate that the USB interface is not ready. */ @@ -76,8 +79,24 @@ /** Data log interval between samples, in tens of milliseconds */ #define LOG_INTERVAL_10MS 1000 + /* Type Defines: */ + typedef struct + { + uint8_t Day; + uint8_t Month; + uint8_t Year; + + uint8_t Hour; + uint8_t Minute; + uint8_t Second; + + uint8_t LogInterval500MS; + } Device_Report_t; + /* Function Prototypes: */ void SetupHardware(void); + void OpenLogFile(void); + void CloseLogFile(void); void EVENT_USB_Device_Connect(void); void EVENT_USB_Device_Disconnect(void); @@ -85,5 +104,9 @@ void EVENT_USB_Device_UnhandledControlRequest(void); bool CALLBACK_MS_Device_SCSICommandReceived(USB_ClassInfo_MS_Device_t* MSInterfaceInfo); + bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, uint8_t* const ReportID, + const uint8_t ReportType, void* ReportData, uint16_t* ReportSize); + void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo, const uint8_t ReportID, + const void* ReportData, const uint16_t ReportSize); #endif diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.sln b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.sln new file mode 100644 index 0000000000..f3fbdb2cf6 --- /dev/null +++ b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TempLoggerHostApp", "TempLoggerHostApp\TempLoggerHostApp.csproj", "{A2D66069-8CF9-4104-828C-49A73D7DB5D1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2D66069-8CF9-4104-828C-49A73D7DB5D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A2D66069-8CF9-4104-828C-49A73D7DB5D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2D66069-8CF9-4104-828C-49A73D7DB5D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A2D66069-8CF9-4104-828C-49A73D7DB5D1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.suo b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp.suo new file mode 100644 index 0000000000000000000000000000000000000000..647c7e68a7d5f2071f9ec436be891da64d469e33 GIT binary patch literal 17408 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 E05OX^VgLXD literal 0 HcmV?d00001 diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.Designer.cs b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.Designer.cs new file mode 100644 index 0000000000..c6b106f98f --- /dev/null +++ b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.Designer.cs @@ -0,0 +1,181 @@ +namespace Project1HostApp +{ + partial class frmDataloggerSettings + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.btnSetValues = new System.Windows.Forms.Button(); + this.dtpTime = new System.Windows.Forms.DateTimePicker(); + this.lblTime = new System.Windows.Forms.Label(); + this.lblLoggingInterval = new System.Windows.Forms.Label(); + this.nudLogInterval = new System.Windows.Forms.NumericUpDown(); + this.lblSeconds = new System.Windows.Forms.Label(); + this.btnGetValues = new System.Windows.Forms.Button(); + this.lblDate = new System.Windows.Forms.Label(); + this.dtpDate = new System.Windows.Forms.DateTimePicker(); + ((System.ComponentModel.ISupportInitialize)(this.nudLogInterval)).BeginInit(); + this.SuspendLayout(); + // + // btnSetValues + // + this.btnSetValues.Location = new System.Drawing.Point(168, 136); + this.btnSetValues.Name = "btnSetValues"; + this.btnSetValues.Size = new System.Drawing.Size(90, 35); + this.btnSetValues.TabIndex = 0; + this.btnSetValues.Text = "Set Values"; + this.btnSetValues.UseVisualStyleBackColor = true; + this.btnSetValues.Click += new System.EventHandler(this.btnSetValues_Click); + // + // dtpTime + // + this.dtpTime.CustomFormat = ""; + this.dtpTime.Format = System.Windows.Forms.DateTimePickerFormat.Time; + this.dtpTime.Location = new System.Drawing.Point(148, 61); + this.dtpTime.Name = "dtpTime"; + this.dtpTime.ShowUpDown = true; + this.dtpTime.Size = new System.Drawing.Size(110, 20); + this.dtpTime.TabIndex = 1; + // + // lblTime + // + this.lblTime.AutoSize = true; + this.lblTime.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblTime.Location = new System.Drawing.Point(51, 67); + this.lblTime.Name = "lblTime"; + this.lblTime.Size = new System.Drawing.Size(82, 13); + this.lblTime.TabIndex = 2; + this.lblTime.Text = "Device Time:"; + // + // lblLoggingInterval + // + this.lblLoggingInterval.AutoSize = true; + this.lblLoggingInterval.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblLoggingInterval.Location = new System.Drawing.Point(30, 101); + this.lblLoggingInterval.Name = "lblLoggingInterval"; + this.lblLoggingInterval.Size = new System.Drawing.Size(103, 13); + this.lblLoggingInterval.TabIndex = 3; + this.lblLoggingInterval.Text = "Logging Interval:"; + // + // nudLogInterval + // + this.nudLogInterval.Location = new System.Drawing.Point(148, 94); + this.nudLogInterval.Maximum = new decimal(new int[] { + 60, + 0, + 0, + 0}); + this.nudLogInterval.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.nudLogInterval.Name = "nudLogInterval"; + this.nudLogInterval.Size = new System.Drawing.Size(51, 20); + this.nudLogInterval.TabIndex = 5; + this.nudLogInterval.Value = new decimal(new int[] { + 5, + 0, + 0, + 0}); + // + // lblSeconds + // + this.lblSeconds.AutoSize = true; + this.lblSeconds.Location = new System.Drawing.Point(209, 101); + this.lblSeconds.Name = "lblSeconds"; + this.lblSeconds.Size = new System.Drawing.Size(49, 13); + this.lblSeconds.TabIndex = 6; + this.lblSeconds.Text = "Seconds"; + // + // btnGetValues + // + this.btnGetValues.Location = new System.Drawing.Point(30, 136); + this.btnGetValues.Name = "btnGetValues"; + this.btnGetValues.Size = new System.Drawing.Size(90, 35); + this.btnGetValues.TabIndex = 7; + this.btnGetValues.Text = "Get Values"; + this.btnGetValues.UseVisualStyleBackColor = true; + this.btnGetValues.Click += new System.EventHandler(this.btnGetValues_Click); + // + // lblDate + // + this.lblDate.AutoSize = true; + this.lblDate.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.lblDate.Location = new System.Drawing.Point(51, 33); + this.lblDate.Name = "lblDate"; + this.lblDate.Size = new System.Drawing.Size(82, 13); + this.lblDate.TabIndex = 8; + this.lblDate.Text = "Device Date:"; + // + // dtpDate + // + this.dtpDate.CustomFormat = "dd/MM/yyyy"; + this.dtpDate.Format = System.Windows.Forms.DateTimePickerFormat.Custom; + this.dtpDate.Location = new System.Drawing.Point(148, 27); + this.dtpDate.Name = "dtpDate"; + this.dtpDate.Size = new System.Drawing.Size(110, 20); + this.dtpDate.TabIndex = 9; + // + // frmDataloggerSettings + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(300, 197); + this.Controls.Add(this.dtpDate); + this.Controls.Add(this.lblDate); + this.Controls.Add(this.btnGetValues); + this.Controls.Add(this.lblSeconds); + this.Controls.Add(this.nudLogInterval); + this.Controls.Add(this.lblLoggingInterval); + this.Controls.Add(this.lblTime); + this.Controls.Add(this.dtpTime); + this.Controls.Add(this.btnSetValues); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "frmDataloggerSettings"; + this.Text = "DataLogger"; + this.Load += new System.EventHandler(this.frmDataloggerSettings_Load); + ((System.ComponentModel.ISupportInitialize)(this.nudLogInterval)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button btnSetValues; + private System.Windows.Forms.DateTimePicker dtpTime; + private System.Windows.Forms.Label lblTime; + private System.Windows.Forms.Label lblLoggingInterval; + private System.Windows.Forms.NumericUpDown nudLogInterval; + private System.Windows.Forms.Label lblSeconds; + private System.Windows.Forms.Button btnGetValues; + private System.Windows.Forms.Label lblDate; + private System.Windows.Forms.DateTimePicker dtpDate; + } +} + diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.cs b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.cs new file mode 100644 index 0000000000..370dfbd69f --- /dev/null +++ b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.cs @@ -0,0 +1,172 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using Hid; + +namespace Project1HostApp +{ + public partial class frmDataloggerSettings : Form + { + private const int DEVICE_VID = 0x03EB; + private const int DEVICE_PID = 0xFAFA; + + private struct Device_Report_t + { + public Byte Day; + public Byte Month; + public Byte Year; + + public Byte Hour; + public Byte Minute; + public Byte Second; + + public Byte LogInterval500MS; + + public Byte[] ToReport() + { + Byte[] Report = new Byte[7]; + + Report[0] = this.Day; + Report[1] = this.Month; + Report[2] = this.Year; + Report[3] = this.Hour; + Report[4] = this.Minute; + Report[5] = this.Second; + Report[6] = this.LogInterval500MS; + + return Report; + } + + public void FromReport(Byte[] Report) + { + this.Day = Report[0]; + this.Month = Report[1]; + this.Year = Report[2]; + this.Hour = Report[3]; + this.Minute = Report[4]; + this.Second = Report[5]; + this.LogInterval500MS = Report[6]; + } + }; + + private IDevice GetDeviceConnection() + { + IDevice[] ConnectedDevices = DeviceFactory.Enumerate(DEVICE_VID, DEVICE_PID); + IDevice ConnectionHandle = null; + + if (ConnectedDevices.Count() > 0) + ConnectionHandle = ConnectedDevices[0]; + else + return null; + + // Fix report handle under Windows + if (ConnectionHandle is Hid.Win32.Win32DeviceSet) + { + ((Hid.Win32.Win32DeviceSet)ConnectionHandle).AddDevice(0x00, + ((Hid.Win32.Win32DeviceSet)ConnectionHandle).UnallocatedDevices[0]); + } + + return ConnectionHandle; + } + + public frmDataloggerSettings() + { + InitializeComponent(); + } + + private void btnSetValues_Click(object sender, EventArgs e) + { + IDevice ConnectionHandle = GetDeviceConnection(); + + if (ConnectionHandle == null) + { + MessageBox.Show("Error: Cannot connect to DataLogger device."); + return; + } + + Device_Report_t DeviceReport = new Device_Report_t(); + DeviceReport.Day = (byte)dtpDate.Value.Day; + DeviceReport.Month = (byte)dtpDate.Value.Month; + DeviceReport.Year = (byte)((dtpDate.Value.Year < 2000) ? 0 : (dtpDate.Value.Year - 2000)); + DeviceReport.Hour = (byte)dtpTime.Value.Hour; + DeviceReport.Minute = (byte)dtpTime.Value.Minute; + DeviceReport.Second = (byte)dtpTime.Value.Second; + DeviceReport.LogInterval500MS = (byte)(nudLogInterval.Value * 2); + + try + { + ConnectionHandle.Write(0x00, DeviceReport.ToReport()); + MessageBox.Show("Device parameters updated sucessfully."); + } + catch (Exception ex) + { + MessageBox.Show("Error: " + ex.Message); + } + } + + private void btnGetValues_Click(object sender, EventArgs e) + { + IDevice ConnectionHandle = GetDeviceConnection(); + + if (ConnectionHandle == null) + { + MessageBox.Show("Error: Cannot connect to DataLogger device."); + return; + } + + Device_Report_t DeviceReport = new Device_Report_t(); + + try + { + Byte[] Report = new Byte[7]; + + ConnectionHandle.Read(0x00, Report); + DeviceReport.FromReport(Report); + + try + { + dtpDate.Value = new DateTime( + (2000 + DeviceReport.Year), + DeviceReport.Month, + DeviceReport.Day); + + dtpTime.Value = new DateTime( + DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, + DeviceReport.Hour, + DeviceReport.Minute, + DeviceReport.Second); + } + catch (Exception ex) + { + dtpDate.Value = DateTime.Now; + dtpTime.Value = DateTime.Now; + } + + try + { + nudLogInterval.Value = (DeviceReport.LogInterval500MS / 2); + } + catch (Exception ex) + { + nudLogInterval.Value = nudLogInterval.Minimum; + } + + MessageBox.Show("Device parameters retrieved sucessfully."); + } + catch (Exception ex) + { + MessageBox.Show("Error: " + ex.Message); + } + } + + private void frmDataloggerSettings_Load(object sender, EventArgs e) + { + + } + } +} diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.resx b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.resx new file mode 100644 index 0000000000..ff31a6db56 --- /dev/null +++ b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/DataLoggerSettings.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/Hid.Linux.dll b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/Hid.Linux.dll new file mode 100644 index 0000000000000000000000000000000000000000..4c19edd212c44e0c785bcd621e3d290e79159d83 GIT binary patch literal 9216 zcmeZ`n!v!!z`(%5z`*eTKLf)K1_*F~PMVckf2voT$IcJbsNa6%)r191*JjaehLf>Apd~am^j`!zsMyg$2T!Ek0G}> zIlm}1RWBtchXH01#NQz22&ghJY!PK(V1yb1;zOBqp@aC@TX46I&06)L>*_;F1?eN@!34DPRF9VEiY_ z&cMLL-UA{L3RFP~SV3l~fmrMw3=Tp}|3o<$7??PEKqNw`I!FNrNC7iM0VkRQ4Uhs( z4~7FmED!~PENCh;K`OXFDp>!CaxyS539|NpD1;?iAQjvo6>R@Rxj-t|dO#FHg*He9 z56BtpP!;TGDs(_9ctK`xKvi&{sn7+f;6n*iJ&+K;2a7|4K8Pg%vVaq+ofA#F0Z4@) z$O5i^qHG{Xa`k{Hgd+_>Duh8Qxc`ZAgH&+$fGC6tBajLakS#n=6+CDvj2ReMv>5ah z*gO~-m_V_qrKcbSXA2v!GuT72j$ly@BSS$kF9QQt(F8_@f_y#(1}-kPzRX@01_nlM zF&+zk1_lOp4mOTi`TPtFT#GowBX~I2`ZDcb{Qv)-U5i23fQ12+{g@dT1ewG%m>3wi zRJp`#1Q{3@iWnIf3YavQ85p>Vg%}vvs@Sv`!0H1a#hoCN1`7iNm#Bj?0|SEwE7)Qw zYfye0`2se;t!EO;^WRn+2zQ`t*tni0}B&NZ8C^rtxW+(6T_yW1&j;@Ow}OC9)1P} zMy6_z0#F8EWU2-!0y9A_#?T5T%G6fY(XAv7TdH>PTk94KEi zY6vqhfHccN#I&4(y_^`KB^0RKFEi9L)H5+QG6I)StPBMV3=AzC3=GP33=Cg57#IvV z85op89b}%w9D2E0!+!1DAI00fi<>V(ZFfc3;1=m6h%I=|_E+EBF z3@VbsZ5N4hf(l6s20>8S4Jso+RRSowQyCZ-M3JfqP~FK1Nv90#P#RQqfiOrPNDM@S zj04dO46Te1nvH>rL5P8i!I{CAfr}xI!Iy!JA)BFyfs3IHEYbrOS-?=lz{ao?Os--O zVqjy~3?^@aMc5fd8QBn-W;xDa#;^)Z?g5jh!Q@Ra`3g+_0F!L2Ae9ne zQWH#?fk|gD82~0@z+?uPtOt`*!Q^7r;|y$U#cUuqRI}wXhBY*T$qu%BMmC07Y{wbc z80N7ZVPIoe0%orPlbgZhE--n3t(cLG;W(H)4<@gJ#b1EQcVO}l$QFjrVD<};X#(@u z${5)geu8abWe3^94~k33fNB5+ zL5Yp5rx+L+zJMwhhG`5tSYLxmD`ikoGL7K`>uXTq!N9=Kz%WhW3hM`G8yLd-$-o9u z29;xC1eGB$mE4S=(hkNGVFXogFrEyf5RyI>Mo|VHhgBqJPqXa_- zlxN1~!YIk`1j=(|^JA1^FaQ-@4AU5b*`gSw87jcMHVJvg35>D~bHKb~44I6{3*wHU~vVoN)@oU215q}2ZIhnF9Roo0mCE)P6iW(=?t6<77TM3 zI2mjh7BX-$I4~?^;9zh8o9w}`jDdr}2h0utn-;>bnt_8M0&H3g*t7(O4GbI%DGXZ~ zI2bY*b}?`;*+WK)P)?nMn-3iABX3i8&0xmBl5gxq3mRc_o>-sd}DyC8Jyvzzczr>QvvQ&^+ z$%#2R4EY7Ac?^)25=g8hHJ8CHzbH4cgdsU6zc`h_wWugBpCPy;1>_L-)DoY>;u6=Q zqWmI;kjjEo2B*rBR0elvkHox`90UU_<(LEN`Z}hh6#1tGWaj0iruZjir6!j!I2IKp zRx+fgmc;wy=cW6k=B1ZpFchUGrZ5DQ6oupmmlTyImy{NzGPo6`rZTu>7NsVaOQV6gGcPbtkwwL=JjJI`QAkIWPX4^Nl) zAlCr@ppf{G$N*P{jLejj)Ux>0vedj17$+sQEED8ps9bV>PEKkv$kupd;bL$jH9jS^ zIJqdZpd`NtroAY&Ait;tMQvJUYEBA@NNI6mdTM-8Y8p%zTp&KTG^ZpJYKqGFI= z!H!3G9U2+&p8n49e*S*03}9AxkY@;#8RY8d0ul5Ja`FrbhOj~2Meu?>qg)X@7f&RG zenG(=o^BxsT}WJzHYBc#CsZv`uzI@0hx&Q@`G@-<2ezk6yqjlGa0r^1pKAoVkgsE~ zH<~J6#|UH*kWb=$JyFbuh(Xo4dAjA%jL9QlhL58|o9{86O(#=>lPC6zu5_4nR+TXUBj5A5Uk;5QK=k zi))ytpPN5OGe|webx@wCpJxcdY>+exFBpaA4&fmi2KEKA0N966!-J7I?xD{9F5sX9 z*#hz;F)P1_!8bE6zX%jx$r%i(;L0>NwHQ>CgKI@__3Dc*;8&WPlv)I; zkMoOQl3lA_F{(h_hz4{kH0ra@~T2ADz@XqA(hpNCKm zlXT9{OUq0zEds|8HtF1g#JoyOi2z7!V@f#Z7gQEyre~C3N`(|9rljU37G+~fxuoVK zRt9ILV{xH(Y9*+`N9bW_SRBfb$B@X7#E`>~%Amkd!jR7ZYUCC$q%!0&C@`cjq%xE- zWHKa!Wvdws7-|{x7!(*H8S)uQ859_D84?*P859`u7*ZKh8B)Nke1;MR1%?cUM20f3 zt|Eq1hD5MjB11CRq+*6*uq{Ol3JjTG)i86Cz&bL)c9${~Go&);F)(O4LG`CGWJ28u za#t}!GD8tVCPM+()kPqc;+_n73?&Sy3`Gn@45eThkgFJCLB0g(DPqWBsAK?{pAL3G z5=v)*3+r7EV`E_A;1lFzWnkjtlVM?C%BD)MQp*kYH9|;9_H7=HigxW@BKIkr0w)R$yRcQD9``V^d(16*A>xR$$;` zXJFyulaY}D&D1ckfEwZ~ppiHh(5My*AIKR3AObXr!}9vdUz$nAcD<~o;Bd7p|DuNnNC?TjLsKdg* z$jQOMz|6tHDIh2UV<^BF8Zd?oJ6Me@D})w+>rr80V36hGU}u&UGL_}y0EG%4Gdm-Q z!N|eF&Ik@3K?4p}b|wx%15jvi2+4A=v&gavs!GVPa4^X7F|!HsFtM>Qv&phD%QZ4E zvk7W2v&nKW%e62tv9U9;v9c*JGqSU?f=n}DW)l=;R$$;@=3wAs=3wC9V`Sh&w2~QE zLBYrX3TY+=2A#xPpA#9PycQgv;kv-;6lj^md20| zmRA8~6owFnB8EhUJceS19EL=O5@-Y*RKt1~ah9ZW1h7^WUa6uRbE`z#xMxPKd3*z`%i|hC_jY z!2^k}SCU#$!oa}Lq6hUC$gju_g9s<+6%`jHGeF%2@;gWkTI4t|FgWNHrRL-(L)^*$ ziWrc(03-_;7#JKF7@SZ=MtP$lFd71*Aut*OqaiRF0;3@?8UmvsFd71*Aut*OBP9e9 zK>cb4NZ*u+ftA5SkAcBMo`Hdp%m7%&q#?k-z{SK|%gn&Q!o*U`0-}lq85p=~8No~; z5R-|4fko>QbG|YI0~=E{NU1R&bG{0g2hyR(UBt-1P*5z)z`#|-R1MO}oG-$_z@^2& zz`$(44DZOYpcuh|-w2j`Q3eJsHKuBi^~@~!Vju(MAogp?F;#47yTwHAV1`G^r_n6i0F$ghfD1zec$vr+Hrhj703=GowN(>BK zcOVV~B{MEi>f++n78YXGP-S4?;$}zE&a9yZ)6V=)j1{h3i-CdB1eBS?7&zn@m}MCl z7?~J&7!-6E_&}lz4517hu`qFO28C>hI4F(BLKHB-vXq88DC@9-a#0N%0|U!HQC5&? z)gX~N4NwXK3u|bC6FHN(h9Cn2TP-_CJ(C24!vW?7#J8>85jhM zwLsw_#Q0BCoq>T-h>^Kgn}LB#h*1GlBGl?IFmPE+Wnf?sVyxB$MW-GpJlTX8t3fiC zIP&!w7`ViEwIhUB{)w`I3}6vrtOl7-YXC9^!~?0UH3aiGYmGpzhIqgjETmxqGC`la zsGfm{{wK@Ush8RKpdd zP@lVKA>7aG;t|~Jc`SXISw;tin4potBms#GCT6gAg_y)^L6HFUwj0PSCWyE7xr=tg z%>w72A_fKqb_NDPgxkrwQ%2wWA&XFx3&e$y$-j6h5dmLLp8`_ zm*yZj{Qyf}Cb&FhW?07n9uE~1=YpE$2{FqH>h+Isvmo*pKmo7e&A`B=;lsedwG_nV zW~v4yW+BFEP%K=6ggPTqsQWT7a6JGQ13Y59+7?2LoZxVWm{hh z3=9lh3=D$E%GjBT1weY4ij^1`*tpn0K#>VA5e4NE=H&7gz*DI<$Zi6}WMb4=UJ&xwM6ZxWFa5FsmSo zAS>8qLR^r7{pmeEAui;Codetq$d3Y9#jPzT#H|qxVzLRc2(f9zfRZPhMl2}6KsB)t zw?-VOe7|{*Pl)@U7#F;J7w6UH5#rQ{XJFv6a{mAS|2;k-&VOQ@aCuH$b~cXu1O^5! zK@RZD!@$5M%pxYtsvQdQ@GNdNb`4ojiy#@4Agp;H4Gm6ah60ZK6b1$^ zj_Onf2Cg1c1_s9bG>{SrUWgKXW`+Vzuo7@vx+$hh$zy`{X5*(bfxY_d= zK{?2nosAtdT>&Z7Ir=h<9zm=CrC*R4cG%5OL^eZ!J)enzfsGl{u$yqhqz%}>We*=H z|3RCk8hW65ksDMM*6@PsQEqTOiV)Gr09BzHnG6hEI^0E`%nSv^S)i(jsfGuvo(ryC zh(RM8q)LJfBBzl9(s_?lh(RM4#JU4Bq!nREHOM0I;yh4ZXJVIxXspg>VBiujE&%0q zK1jt3%EwF$u?+k<44k0sZo~w3Zgn9j)xbDK$edy*2johyfm%jT(@Q`ypthj_3xgxb zFc7;h6G>K2q39?xLjjUVk0olejj0;sFlI64zRdhmyyk)YqNf0E147LMiQqG@3~C-I z?qO|jCT0y#!WlovIcL0kp~22i~MvI3Mmm~{jo(VgD{DkeZJWF~OB)o2AJYb{XP zWME)s;1z(>F;2l=PK=;7BBXp`C^OPC&@(kKH!=sA#>$Yxz`!8F&A^~s$H36Q&A^br z#lWB(TvC*omtG7~@j#S;;R6c;gK}sv!v!hGRIRdmsHY191H%I;28IlfoKsGI5=b$m zzr`3XzK9c?F@7-`Gl+o;A_iMX+YMY`g6eH`D8Cg_o`Av>)XoLhpP)JvG`9=t>q6#_ zxu8B^W2j~*VPIou2a|6YE;6t&urXd_;9`hmbZ6jVCrFJTaZvX8RwWM^Y| z2NwSYCM7sZ7=*#*urXM3Tx8&5DBv(;xXe%wsw^2+a)5j)$@!Oqjlqr+6 z3}KAX42%pJjByMq46Tew3@Qv0!Q?!~BnC!?HH`TnHB5&XK)L8B0}Deh(@6#vhP6y* zp(*beZyy6JoiH#Q2Tel^498g4Gl+s#A%JxrW8K0a!@$ZA0M24<5(bPm z4D1Xw5FUdWqZ0!s!xlKtgMo{INfD&+87Ig-9tJKb4`d$?g9w;6g~5T*pMi(L0nB^G z2(q1z;S7`qvR!~-i4xcpMv(1740oVBKgMtd5r!GcAUTj*iWo!~Sip-i8W~PAmNSSl zJOlF}wu>=*f$~7Mi!m^%g7tA+W#nQIV-NuIAhwG!Jc05+wo5RisDV{7JY;NVkYZ2( zWk-f*V7EvyB!GEs9Pb!=7-Sf>fO*dtK`xVFcmn1fWB3l1djsZ8VSA24qsgBH_#23dwRnh-e$rWFiw3<03#1j9522c~rlatu@8ysZrK3=hCO zh~E_%)@XzDwQ+#LT8UvNln3&y62mbl59C`VhRaai4yKC?N(>L6yc1yF2MCX0CBsz) zB?br3panx4!+M6>49W}>pu8;%4;fS#_CR%6kEpn*!!d zVfe}PfI^)fwImEj82uPOF@OS$ks*K;)Uu3Z1?4OV zI}V8r5#I+cO^<`~TPl()G8>{2VgfQ7(=-!Mub&|jOcJl&fCJ=0kV_aDKqM0b#61(i z;(44P*+wu4VMD}0t#C#LCoa&qB{KsD12Y3F0}BHO0}BHy0~-S?Lnea(V-Z6kn5+bo zjSL2ipBOs9?1^AjSXF`PjVO!9z9 z5it1@tdfVJj8&A8hoOp9no)$Ij8&2GCqoabCZh<$L{@1=9)`KBmW+&yJ6Y`+IT;VL zx-#-GoMpYpAi^-0)tgb0@d;}fBM-wj)>K9r1{St*Mi~YHwi-rG22hR=VbEl2U{qoF z#yXWzhk=D{6{8MV){?=J?I@!qgA3a!MoWfZwzG^TVE0%uma{!#v}9}si8J=Hono{B zyWa%tDnka4tRVwP){tQaNDadhFd5ADmeG+B6mB+PzgRLHfr@`))MU8ECdH)5aFNlSu*IbpJEJT z3}QEAvSf$=i>HFgU^ZK(NX8C!S0+n_S?qpHh73!<;%nIxm_U9^Wh!Jm$DYen$asUj zn8}ji5xXH%B_kI{BU2-zGDka;B?G9O@C1`C;LvaZhpG=aR7DuRu`Xr`VPIj~!4$*b z#Zk@}!w|%=iZKKno<3mG1?+bhh8T{sOetU!QotfF8B#cU7#1>ia_}*OVo;bl1+2!C zVH$@u^G3#v9L~%O!J+5LaG1lL*^}WWhaa;i!!wRB=ADcNj4{lC3>uu442cW|j7iLq z4Eq=?86p{ugGojk6*~;w6FrBlL`6eSM?SXtf zk=c{s5N9W|C&LZSPUeS#ucryIwoX-4^5u8dHxwsZFOESuEtznjARN>ml{F2d- zYdiBrMjx(l22X|{u070p4BQL{ne`Z?8O|~5F(@L7;eIp^o(q$ZbS=I0gbxu@o(7G)+g z_+%EBBp5P;<|XFjSVq^3X|Tg>2+309g|RGDA|^+7R1d>PCo@da>a=z;vj;Fto{ z3SxPtgyaXM7UUO|FhG2i%8-`Akdc{^l3Es@l3JFTmzEDT)Dy&n%A{qc=A^`fB|Y;B zN=w{8{9*?G(h>y6Ej6*Ev?vuS6`z-wn+gebkIWPX4^Nl)AlCr@ppf{G$N*Oc_f(h} zkYFq-&dkqaaLUO~&d$tB2Zt9(!VhFfNIs+w#SoSXQ3e(TNkf7pAh9H)n8CRyHL)ZW zBEsODp9c!FJh(`3YDqDJYhGz?YEfcID%6J%jUI`4DLJW-*oQhbAl}(AAeg~9u^=%i zGbghoGqo7x1DANmkdPoxr_d1BU zNrW;%P6Y>>PikIzNd`EvK!OfQ0Fpf5UJFhw%1q4hE6q(xEdqrW#A8smh7^^8{TG#5 zlpm5>l$#0iXbLoxkZnS-1<3{!)1j#_C^Ze53K<-Wi&Jxxaw-sd}DyC8=jRpY=cF=( zq*j!G3&J9>oNG~0ei5WN^vTRi1&cZ7m*$l)c;=ND8!FH7|UEin&E%qdMpl%im>!D7V>pghjtUyzyy$v~iN?CJ09?&2Ee z>F4IpkeQ!ck^>EhkjjEoSW#F6E+Zi&9+X`Il7_M~QyB6}bK}9qVle}liqFhTNv(jH z@8;?1;{vt;T+<*z)5FsRWIv=V1S^1)+zilS8|Hx0;>7gS_@dM_hQxw`oXli!3^PDP zlk-zjp-hOSWg!1Utqu+ic64`zaUk}`0iQevZDbaO**F<(ikA4|W2m zRCEk>_Vff5Z_wCx&dD!^mEo>=Wtm0!dAT4{{R=?lc4l5WBs+m4*gqIr0x<;SB$lM* z7v;k0VJMdYRG%fMf{T9d)JkxI2uREU@#Jd%xrlP9x%}g%JFV0UZfwXA!f)mqHA)#N)0Ahp6Y6zbJ zTmwKdHz>zKD;bai$K>SH;$m1u3aworl>~RE9E!Oon8JR0aixM20+u6b5~Ue1;+h1%?2I zB8Gg16oyiUWQGz31qM$B7lvX61%?!cd53D4&!t6ftBnB!QC+C`sxuFxV5LH<=-kp@1QgA&DWAAqSjA zGGU=;>jKV!i43U8kjOYN4Q0p!n+u9RP!0y=ZJ7JZz{whv-$CU8NL?;NDkvZ0)rFi{ zK;=Xt15}qKKD$6=1So708FIj-ODRJx11Rkjf$Vcd4Wndm;Q$IxQ04?>X;8U^>;qIc zKx2tv&P-p%J?rNfLW|U z5+FgrhM7=75Q`O}g;A89MMy$O0&0pVOp1Y#gO7=glY@bogM(8*P?Q_&5lG-Nvk5AK zBTrBgCNIeeRoF0BlARSvtzg4kh$c=@RI$O8E#+iq7Hn9`DcCTVg@KWggN>aD#ASyV z!@0B>IKYWsgk=_&Cgvyvmz1Vt=7XGS8WT{I4;rE?j&Ux^ zFD~}TObJOXE{XBTOo_=)%8Cg}%}GrxP6hG7gNOwwNem1=&^6*9kK}>a;8{u#4;91K zlB0^oJLea<#0D*T4r2&rh-V06aAgQ$2xb7a-uxKi5p6aG z28O*X@BcwYwLvKvq!~ingP4%D+n@!$P#%N=tqEad2m^Ny+`z4zR0dCmJccxee6Ru) z@M#7i3`O9URxv{kxYd&hZm5G5Fl=D4U;^11%m8U+fjStNCW(O67%;$~2}2V2NCFe^ zjK4EOK0_|JrIp7}$>7HTX}f^T_XYQoKs};jhJ1!J2G9xz0|pibMh2K2ppHy21E_71 z$xy&h!hmp_2SX-93TVGS0|%1&FmTJc7)hNWgC2tc95XOjFz_%ifN$ zt03m;G5CSon|cf>3_0L6IWY{<42%pu45?y3>jdzV~Qy-tYlDN z!1SjAgAq8iEI`4)-~e`$Kh!Kx{{UnIEDT_6M9|(7ke@N#rU4E!)V?LcM=*U13?|^P zLRE(zVwm9t3SUQtVg^ua64ZXoVW?zKU?8Lq?6FZ|Gz3ONU^E0qLtr!nMnhmU1V%$( zGz3ONU^E1VVhA{Z_meR+fHqtWMfZ=oc{Bt@Ltr!nMnhmU1V%$(Gz3ONU^E0qLtr!n PMnhmU1V%%EUuM5z0WJ=7hu@WsZixXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1J zpb&Tf>Q^&B`ld__tPDAd3=B&w85vM}n8XK&v;iB#Wd;V2e!*g41_mxJAw~^$1_m}E zMv*!Z1_my9Aq_?b1~wic=6|Bh3=B*{%$z+ST0@k9fvc8td}O0|SeeACraxx)=ikgHy1V6D(vwV+v(PdIox?2IfZQpzvj7$YEe$I1|ml zpj^kmU=hQ>U~r6qK{>djC^Ij;7^EUYiIG8qfq_9eG?<}5i4i=^q3j;&>B7LkFhPlt zK}4K^LD?xMKMACm0pv~wf$+VHWI34_7#Myr8Z(H2;{`mV0vemJWoKXzWME*>f{ZmV zD6%s!@Pfu9pcs?}kTGa@1jOfq#4jjx8Cbz~ae>(k3~UU`85S_GF{}fVJHX^2Fv-9O z5 zjhTl%53@0Jg30BFL1x|olkdS~>k*Jh&e6c5Yz)OmL2jr&3KE$PX3qhWpTQ!W$Nn7z zc?RS|P&hF%G%^Y>NH8ceurh#Rjf+u$L7Rbrfs=uSL4X0o1BE*$0|%6s0+!=~^76nu zDFy)sP(0N!C^GOe$T0{oFflSP^fG|LU7kURA*|sz0~bSB!&NZ(nn9d_jlqwJi-CC7 znL+BMz@#CV^Z=7_V6qHM_JPTzVA78X2`0CL z$rE7mKA8LrCfV3Q=7@qxbueiKCjG!<5|}InlO14k4wzgICJ%ziYhdyXm}KGrnI;D& z4Z)-fn2ZFIxnQyhOilxnE5YP$FnJbC-T{+ez$7;($TTG|X$2+&z+?)TtOJv?!Q>h+ zc>+wn0F!^gq!1U#1Qjr84kkUpWHgv81e1+mavqr61tu?o$+uvVg&SmoJeV{Dlipx5 z4ont-$qq0%2TX1RlPAFBLooRVObYOT%uxlCR$$T#Oh$vr957i2CMSZ)Wngj_m^=q2 z?}Nz?V3L^^WSTgb)CQAwVA2mvrhv(EFgX!St_G7w!Q?G4`3_7n^MT9}29qja(gsWh zfXQSqSpg;|fXPK*axIuV0w$k=NhW@fITBz}7fiZ?$!IWH1tuqg$yH$T7?`{dCcl75 z5dn}1`e4!@Os0X!YA`tyOzr@a*TLi$Fv%?l(xm_yfk`tk=>;Yez+?%S z>;aQYz~mM%c@j+C0h3?Bq_7CcG+i+11STWFWImW|1(Wl^N-@qilG)R{kn6w3xpo(7Yzz$BY2$OJ_&X#*ysz+@Si>;sbv!Q=rjc>_$o2a`N< zAibJk(hf|9fXQ?)*$5^lg2}~TatD|^4kmAd$!}niPabRznDhXXX<)JuOfCbHN5SMB zF!>!!vMYdeiGfKiFzEm$qrhY?nCu0U>%iniF!>2gaw~%Ls)0!-Fc}OcbHHQ|m|O}b zcYw)DVDbr=`~fC;l|Uw_f=LH38385>!DKg>TnHw2g2^jj@*SAuPzLGM0+W7VG8s&k zfXNmxIU7uF0h8yza3MOsAWH^}21CyO#asim!1}4vg$=hJ^2bdI91({$B zCcVLA3Ye?`lT*Ot3NX1HOkM<&&%xv`Fe#}9GQki`dV$GAFj)g8=Yh#}VDbo;sdl!Q?S8`4CM00h5v%Aia8EG8jx&fysU_xgJcO1Cw9C zB)29=y*!vS0h9hU$zU*<4kl~CAOFv+P4GC>VYI)KSAFj)X5yTRm2FnIz@J_C~sdLUiWVA23g z`hdwSFxd(w7l6rKVDdbed<-W4gGm{EkO_uh(hE$cfyp{BITK8-2a{*P)$3LAk;Fawj`U@{I&7J|ucFu4Lu9srX!!Q@LY$z=@Es|F?=z+?=VEC-Vl!Q?71 zc@RwA1(UzPq?8FruPK-e0FxPDvIa~}1CtxU6jlloxN1574@$to~81x)S)lNZ6{Gcd_$4$>Bj{soi5Rv^8aVA38;hJwjlFxdho=Yz@ZVDc20dNzriHG14yqLm^25Io?tQ>OqPMkUNE@~Ozr`b zx4`6kFv;i$GC>?n>VQcPFc}Xf3&CV7n4AeFH-pKGVDdSbWN-qRAPFXo!K5dcOazlP zU~(Fm+yo}ifXNqNlEoRMR{~7xfk|gD84V^2!DKg>TnHxjgUOp<@(Y;casip32`25p zWH6Y_1Cz~Qaw?cy2_|=g$#Y=x0hs&-CPiF9<`{!XA268;ChNiEd@#8UOkM$#@4+Oq z8%UQdm^1^E0bnv4Om=|D)nIZzn7jriKY~eicaUC9Fc|gUK6U@*|jJ z@dD{p0+VK7(hE!`fXNat*#RaOfXVG(@&cHA1tuB2K_*CnNpmpi2`1yfWFeUB1d|KF z8%$<`$qq2N08H)ylQ+TSTQJEL z4ALb7CUwE2J(vswlR03r157Rill#ErO)&WuOtOT4Oi%-p4q!3{Ojd%)UNE^BOzs7f zH^Af@Fv%1O(kligb-<)6m`nhZC1A1(Oilxn%fRG5FnJA3z5$a=VIXrP!K4A0bODpG zV6p;CP5_guz~oUdc^gcA1CyNLAQM!ca>ao3Du78# zFc}IaGr?pRm|O@Zw}Q!=VDc-NCZ~eQ9bob@nEU`Hc~U^?wZLQ$m@EgA^T6Z*FnJG5GNyu5YJkZg zFj)pBr-R9DVDc)MWJm+4Q~;B9U@{L(c7Vw(VDb!@d;%taf=QutkS=*JsShTdz+^O- zEC!PuU~(at+y^FafXUBbQXm6lf-;zN1d|D1vH?sk0+ZXpD;Or8akZ@?sD2}qY1nA8K4ZeTJFOcsO5E-<+qOdbN0*TLiuFez3F zGQkK;dV|R;a!!DI)RTm&W$fyoD8lBoiuULH)EfJt94nGPnK!Q@gfxfe`c0h1rWBv&O!uO^su z29uFsvK&lK0h6o2fJ4kq)!WH*>x3MLPL$?IV9GnnM92I*A; zlkQ+L2~0MC$pv6?E0{b7CZB^z<{FSLDKKdYCIi7_KA7wVlPkgGAu#z6OfuGjbSZ*K zYcLrLCL6)z5-_Y(Dck_kr41%s!DI@UYz33^!Q^%@c^yoC29w;4AYE!; z(gjQ=gUJ>!IUh{!0h71E6ihw>lZ-7O^(tV}5==&b$t*Bg2PP+h$(3O8D42WC728b zlf_`N4@|BFlZV0NZ7}%_ObWJv^y+{~PcWGRCL6%y0x-D;OkM_)Z^0yUJ4lx%m<#}u zC17$Ym|O)WPk_m1V3M^1q+SI~x`4@iFxdkpSAfYwVDdVc`~@b3J3+ei!K5FUOaqgR zU~(~-+yf>bfXUxrQnm}E%K=O#gUJpsxe-iW1e0IDq;NM#r7f6@1CtG4axR$M2PU6@ zNwFS~N&_$%3?@s!=2n zjlpCPn5+bo3&7+)F!=yXGEM=hR05MuU@{3zHh{^QVDc!Kd=4fVrh?RKfJrwnnFS^r z!Q^Z(xeH7_1e5&JK$H8A-V zOp49`srLbs$zZY?Oil-rTfyX6F!=#Ya?Av&HvyB;V6q%cE(Vi_!Q?YA$vF$8QUy%< zg2@6f*#{<9gUM51@+p{Poefei3ntCLq#u|}1Cw=NavGRi3nq_%$va^31DIr;12RDp zOlpEjXD}HBCd)$O3wwEV-6<$z+^F)oCPL#gUP#K@;8_i zng>#E3?{?CWI33e0Vdah$+KYcHJId`4^poQCIi4^E|{DECRc;W<6!a$m}Fc4Qm+Cg z-N0lbm}~`;OTpw(F!>ZrGAsnCR{)b1U@{y`mV(I%U~)B>JOCzdfXPo_QgjhWuPK=H z1CzO6vKvgU1e3?WKa7nqb-0y4oE zOxl1+S1{=hCL_UQGMLN-lQm$n8%$0IlZ(ORW-z%QOr8akcfjO(Fv+wOWQzcpQ~;9( zVA2ju`h&@6Fqs7=%fVzjn4AhG7l6stU~)T{JOU=KfXRno@*|l11tz(cfov29lj>m7 z1WY=BNiQ%N0w&|YWG0xb0F&KdaxR!$0VX$t$%A0>8kl?qCK;E5Y!L>N8eq~2OnQLH z2r!ufCdskelYnMOmeLTsaFM)&R{YQOtyi^HDK}znEVPRCD(w|JAlbNFgX)U9srY1 z!6eUGkQ!|;=>;ZJz+?lMTmmM~g30G#@;{iAS_jf)0wzPiWC@s@1SU6w$tz&;6POfO z4^poNCLO_KGMH=ylS{zl9x!Gz`4UWWYzL`V0h4xMG6_u9gUR_|avzv{ z0w(`}N%0*ZT_#{M3{2L5$+=*12bjDACV6*)RN8^b95A^EOx^&Kg1bPndSEgbOy+{g zRxmjqOzr}ccfjONFe$bhq{|pg27<{VFxdwt*MZ41VDbf+;#kR z!Q^Ey`4LQt?ggng1(Ts*vJyow@*M!FHw2U6V6q%cP6d;@z~mD!$$b!{QU^?i zfytJGb3luG85$U-DX=oiL)R;Vc#@2o&{fPJo*APa0~=gr5Ti8%H=I|*=mlOc%>Xu~ zmQfSD8XLmvWDI4HgsYs%7{ee1=dEWH?8lR*|PcbTz}K_1R~##qUq25l28f z1j>(qR$Wgz!l1y&umNns4zLLaz-mr|O}GYD^YjP<13SYg2_TKSqUZ^!DJ_xoXGH;nN@5d!*gbCv6T$ZnU^wb1dH!vc+M;!b`mVY114`W zR2}VSc*xLxR8H(BL*G$lv6l?%j;f12WH@kCPwXbc$)l!X4;d~V48tP^c7_WKPZ`)5t}wh{U}w0&@S1_0;SR$)26l!A3?CWT8J;kFVPI!?!SJ1d zo#73`F9vpo4-9`9*crYsFfg(+{9s^aWM}xpz{beVz`)4K$j-pR$iv9az`@AR$j-pS zD8$IlAiyZf$j%_bD8b0iAi*fj$j%_cD96aopunie$j+d`sKUt3puwol$j+d{sKv<6 zV8E!$$j)HGXu!zMV8Lk2$j)HHXvWCS;J|3f$j;!xXv4_P;K69m$j;!y=)}m*5Wwil z$j%VL=)uU&5W(op$j%VM=*P&;kiZzo$j*?$7{bWTkii(v$j*?%7{$oWP{0_=$j(s0 zn83)+P{EkY$j(s1n8wJ?(7>3<$j;Ehn8V1<(7~9`$j;EiSj5QAFoCg@k)2@*V+A8S z!wkl1Ms|idjCG9c3=0?=8QB?@Ft#wVGpt~2XJlts!`Q{h&ai>8myw-e3*!Vvc7`2{ zlNs3=_ApLkWM??QIFpf`;Rxd#Ms|i1jPn`U8O|^+Vq|Byz_^r=o#6`O3PyH@8;q+N z*%|IIu480pc)+-kk)7cQ;}%ACh8K+68QB@$Fz#YxXZXOlmyw;}3*!M$_%j{`g+Jpl zQ1~;Q1cg7-8Bq8$od<hLjsc^6FWl+lL!+#Lk5#L6FWl=lN1v>LjjX46FWl*lL8YvLj{vE z6FWllMxd;!vrQ%CU%A?OcqS+3^SOlnb;ZT zFxfG&Gb~_oWMXGn!sNol&ai^Xor#@c4U-oWJHrMhUnX{jEldGS>6Phw(c5MZ9l#LghXJcEgyL4tWU6FY+p^E@VY1_kDYOzaFQ z%uATq88n!eGqE%1Ft1`_XE0!1%f!xL!n}croxy^6GZQ<54f8f8b_NILolNWuF3fwF z*cm*S_cO6G_%I&=g+KFAQ1~;S0EIvEX;Anxp96(I^F>hjGhYFPKl61^_%q)Eg+KFM zQ1~-H0EIvEV^H`rKLdq7^Gi_pGrs|aKl6J~_%nY3g+KFGQ1~VHlbM}?g~f-N zoq>bJpP8M3hb4%aok4&ll$o7Dge8KRok4;nnwgzJh9!=fok4*mk(r%Ag(ZcVok4>o zotd3Mhb4=doxy-5mzkZxgr$I)oxy^on3nl9`>sg{6j>oxy{po|&D& zhoy;`ogsjwm6@F(gr$R-ogspyo0**CEg5IV`i7 z*%=C0<}$N0l&~ydW@o5iS!XPCmWgPEOS2Fq?{c7{1D`>WdEbI(C ztU@g83<9j8EbI&-tP(8j3=*u;EbI(2ta2>u3<|7@EbI&_tST(*3>vKJEbI(AtXeGW z3tOhLX3>K`$EbI(6tY$3i3=XW8EbI&}tTrs{3?8iZEbI(EtWGTK3<0dJ zEbI&+tR5`v3=yo}EbI(1tbQ!)3<<1(EbI&^tRXDy3>mE9EbI(9tWhlN3B=&EbI(5tZ6Ll3=OQAEbI&|tT`;~3>~cbEbI(DtVJyB3=>#OS=bq-uvW0J zGt6MEW?^TT!&=9}&ai;Bk%gUM32O@rJHrarb{2MqHLP7M>pB*8h6k)0S=bq#ux?>tXL!N7orRs@4eKrzc7_kEds)~SzOWtug+J?IQ24VR z1BE}^Nl^H+odJbE+j&sSiva&O@u<5X}Gjy=&v$8Yvuo} zD?0-Vdk-r+0|$FQD?0-Z`y^I&1_Ab|tn3UT>@!%|86?U@H?y)c*syP7WoK|;-^t3(;KII# zm7T$ZeLpKZgAe;5Q24VS1%*HR2~ha6p9Y0L`#DhfvtI;-Kl>F>__JRJg+Kc(Q24Xo z1%*HR15o(0KL&+A`!i7Zv%ds|Kl>X{__MzUg+KczQ24Wd1%*HR4^a5C{|1FW`#(_l zvoo@>Gb~|eVPj`l!OqUc&aj4^i;bOO13NDpJHr-s0XBAq9qhtv>TsUgj*cm)H>e<*C zd^noe*ck#iTG`kcLO43u*cl=?y4lznVmSKP*clQyCbF?Jq;O1OV`s?Vn9jz|ki#*H zjh&%@V=fyzLkY(MHg<*zj>T;33^g3f*w`5wI99T;GqiB5VPj|L;8@Sb&d|fLiH)6M z0>@T1c7`b&JJ{G6W^n9gV`rGdv5$?NVFAZMQ228k0fj%uaZvbkoC1YE$5~MLb6fz0 zKgVTI_;XwXg+IqlQ22A)0fj%ueNgywJOYJ3$5T-FbG!hBKgVlO_;b7ig+IqfQ229v z0fj%ucTo6q`~rnP$6rwRb1<;8GyLFSW@l&k!@Ps2 z&S1c)%g)YV!fC+H&S1f5%+AhW!)eCO&fvgl$XJ`1pxtE=t;S1*hQ228m28BQ8 zF;Mt(odktH*BMawbDal;Ki4Ht_;Xzag+JE~Q22A*28BP@Jy7^_Jp_e6*Ar0qb3F%z zKi4Zz_;bAlg+JE^Q229w28BP@H&FO<{RD+S*B?;$bNvT}KNk}RJ3{~$D+fD62p0zj zJ3|B)HwQaI3>O~r13zrTDJ3|MTJ_kEP50?=KJHrGnQx0~9DO?sD>o}@PezHgPq|GR}}|4!w0Tf4t9nwTn!xT3_rM zaKQBe4Ep8$nF_i0f0bDsl+KlepY_;X(Yg+KRoQ22A-0);>K zT~PRQKLCY4_hV4_b3X%xKle*e_;bGjg+KRuQ229y0);>KS5WwK{{V$Q_is@6bN>T{ zKQ|*MJHrxg7EX4C72NEc>vjW!sE%w z&cMOr!^zIT!{g7%&LF@O#L3Pe!V}8L&LF`P!O6}b!xPQP&Y-{($H~s1!js6!&Y;1Q z!pY8{!;{X*&S1ck#mUZK!jsF%&S1e)z{$>F!&A)3&fvgP#>vj$!c)n~&fvjQ!^zIz z!&A@6&Je)U#L3PO!qdvh&Je-V!O6}L!_&>l&XB;<$H~r+!ZVSRogsr~3MV^54$pK> zc7_6;S)A+)B|LLE*%>N$7I3mN)bK3kWM^pLS;ooE(89BllbxZ1XALJiLl4h-PIiU~ zJexS#8K&@Tmr6#hI%K;h4G92EXMr#RUew(y(< zg+I>)Q26s)28BP*HBk8T+ysR`&mB=Q26t_28BP*J5c!Z zd<2C*&lgbm^Lz(|KhG~v`1AY)g+C7i7dyir9%e4k(GqN2>Be3cPY$>Rjv$ z2E1Ba>>$d6xY!xC@b+@CGwk4< zz{Sq6hj%g;JHr9qXaE5n27dyiR-bGyO3|DxUa1KxF9>Ac=vL#GyLE^01AKJ z!=UizI|d4WzLTKv=Q{%mf4=je@aMY(3V*(31 zc7_9dVchHtNBAPS*%?mo#c;DToZ*Y-W@os-m&DD^aD^|Go1NhXUj{cj!yUeCZgz$T ze0kjL3{Ut9x!DJHsEoW^Q%{2L3i~ zb_N#yPHuJv4*ni)b_O2)er|RK0scwc>&*o-lP~e}(&Ca00 zzmS`qL4$t@H#>t4|8j121_S<8-0Tb{{A;<{87%lWaI-Vm@Ned3XK>)(#?8*)!oQQ7 zoxy{D4>vo55C48{c7_1{L!j{IKMD$e{u7|^=RXY!fBtiz@aMk>3V;49pz!Cv4hnz% zTcGggzY7X~{s*A&=YI?efBt8n@aKOC3V;4Lpz!B^4+?+&PoVJU{|X9!{vV+5=l=}~ zfBt`<@aJdbVP{yu&%(pbu!f(Vhn-;qKNk->!xnyC9(INu`~p1e414&6dDs~a@Qd-V zGaTWU`0>2UuJHr)zRUUSR8~hqP>y!85jiIc-R?O1Uz}z z88`%dc-R?u1pIl}83Y7^c-R?41VVY(86*TEc-R?a1fqG^859KKc-R?K1QL1J88ie^ zc-R?q1k!of84Lunc-R?C1af)U87u?}c-R?i1d4gs85{)4c-R?S1S)yh89W4Pc-R?y z1nPO%83F{Fc-R?21X_9686pHac-R?Y1iE?H84?8gc-R?I1SayZGh_%%;bCXU5tz=y z&QKsQi-(<|L|`rtJ41!Q0v>jT8iB<;>9MKv0O6ok2uUl$V`BLQsO2ok2!WnwOnHK~RpDok2xVk(Zr8Lr{g6ok2%XotK@# zKv0X9oxwy9sf?yFZJHr&gQeJk3 z8G;qO>|O$3WpPbP^Q)LT5nXFLWLh{z8{P;V*O*6#hatK;bWR8x;OR_dww<^bi#OLQg>9 zFZ3J~{z9)n;V<+S6#haVK;bX+85I6P-$3Cn^b-{RLVrNvFZ3T2{z6QA>newqS%n-8RV`rEn zWX;FUut3O;kDXzOkRu;E!wMl6K6ZvRLhgL*3>$>J_}Ce?2>J4{Gwcuw;A3alBNWWX z&Tv2|jE|k+h)^USJHrW~7(RA}GeYrv>_u`}Ed%HU&XxFeL!$IkFT zD36bw;fYWoA3MVfp%OlJhBrdxeC!M#gsS-18NLYB^071g5NhCKXZR!3%*W2aAl$~s z&cGtv$;ZyXA>6~q&cGww&&SRnAUuhWok2u+Djz$8gzyYLb_N;Y*?jB_3c~aF*cnuW z7xJ+)Xb3OiV`tD2Ue3qPU?9ATkDb9pcr710gN5)0K6VBh;mv&P3=YEE_}Cd-gm?0> zGk6H^;bUj;5#GJ?{=zRo;V=9K6#l~RLE$g_2^9XqUqRt7`~wvJ!oNY` zFZ>S_{=$s>>%~KRbhrNHjk?gMvsLKRbhpNFqNwgN8^7 zKRbhtNIE||gMmmEKRbhoNG?A+gM~-|KRbhsNHIS)hJtFJ**%>B? zY~p8Um?E;3pPgZb$PRvXhB+d;`Pms3i0tEMXILU~5ET9*M?m2(avT)?BBwy%FLD+X z{vsDZ;V*I-6#gRDK;bWP6BPa;cR=AUavv1_B9B1fFY*)={vt0x;V<$U6#gRbK;bX) z5fuI+U-;P>-iUk$g}=xzQ22}d1%ZgPKeGCU}rcZI$waD;ezNQ0d|HfqDuwX8E%NK z5MXDxBf46Eo#BD#IstZuC!!k#*co1kZV_N-cq6)9fSuuk=q>?vhA*Of1=tyWh#mli zzvy95_=_C_g}>NIQ22|T0foQVc~JO^T>^!_*i}&Yi`@W)zu0Y1_>0{Gg}>NCQ22{I z0foQVb5Qt;y#j^5*jrHei+uovzu0F`_=|l5g}>NOQ22}e0foQVe^B^~F$uCWgov>U zvNJ@8aR{gsq=*R$vNL3ei3qYY$j&fB%tDZz zVUC!!AUneXF*`wah9zQ-g6s?{#9Rc~8PFZke%U12Kg}?X%Q22{K28F-)Gf?=8zXXN9_#068i@yhjzxXFm_=|r9g}?X@Q22}g z28F-)KT!CKGYYXYtPp1rVrN(*&Mw5xutA(lh@D}JIIj>p!wzu)A$Ep6;=)4g3q}LhK9{5(Pr+3^o$QLhK9<5@kZ{3@#FtLhKA45;a2X z3_cR|LhK9y5=}zv3?UM&LhK9?5*{u9h@D}I#8x48h8Yq&gxDG8NbDA3XILPyPl%miiNrxr_)8oCg}=mcQ20xn0)@ZC zSy1>(TmXf?#AP9Nh657UK;bWO6BPaucR=AUaUT@^5|2RPFYy!<{t_=h;Vs z)rHv^3?#LL*%?eEb%og(EF=wt*%@pkjfL4693;(z*%@3UErr<`JS1&|*%^E!?Sj!4cFW@k7dIY*eC;f&;bVRnWKl8c1d8Lmh!6=r9+ zA-O`Bo#Bq;YGHPU2a@ZA*%_WlZWLx`cplbqo~#QYS&-FLeeK{!-^b;V*Rw6#i0ILE$fT0~G#Jw?W}Abq^H&QV&7l zFZBcz{!-6D;V<*EI!p_hlr76PB&>^KG!p_hmr7yzHFhR;lgq>lEl&J_i!we}4 z5q5?-Qr05u3=5>}MA#XYNI8nIGpvwu5n*RoBjql_&agqsON5E$Bq35R0*%|IgYm2foJdoBC zWoLLIZ79ml@Iu-|l%3&?w7Dod!v|?AQFewe(zc@P3_qkDMA;esNIQ$NGcd@wiLx`W z$aspfGjPcGh_W;A$oPx0GYH58iLx_@$b^crGf2oph_W-t$V7{>GbqT!iLx`O$Rvug zGib=9h_W;2$fS$1GZ@HZiLx`8$mEK$Gg!zJh_W--$P|mRGdReUiLx`e$W)55GkD0< zh_W;I$kdCnGX%&qiLx_<$h3;GGepRAh_W-p$aIUcGbG6LiLx`K$V?PvXULG5BFfH? zBQsr;ouNQxmMA+ziOgJ4c7_U>1)}T>H8P7u*%=ySmWi@6w8*R!WoPJ+StH8M&?B>6 zl$~LM%qCHGhAA>zMcEl<$m|ehXP6_iTa=w)fy_Qpc7`P~2SMR4a|9IrGRHyTFLMeM z{xWAl;V*Lm6#g=oLE$fR4HW(|H$mYqa|aauGWS8@FY^c#{xVNN;V<(76#g==LE$g+ z4ix?}A3@^%%Gr(!}%_q#26E^ca*G=8EYt7=uY`hQ(rf z49*Nk#Pk@v87_HeDhOEb5iXXJTg=C!ZY)Xjr3A-au~oYkIWQ=Fhg)ayo+m?r?YFk zr(cL`kej2kYrKnNh$D6h*APcfABen%r%OP*vtvLogNLV!OT1%9NRX#fXozbt#1_|z z+REFH*Z`1 zO?joc3@)DT&TjDmL9W5Bej!jE#IbOErIJ(FChq(s%I0gi`x-htxW~ML% zr3|hmLx({xfP|RVh9I-G&?626hqXwr5ca86YxXOPoqeO7io- zA%-LhPGRVRprH56OUVR9FGMZGK^R6rgwf4_h+-N7bv}kEP;qo)pu))Jgr(-C;YE@vc}cVKh)hL-q+RFKPVEa z7G{8xkH51wRLsfICEnF9%rnT}&lgkP_3b@LDMbqs+jgK34h3r!GQqCqtJhlcpO zVYSpH$TJM)Xy?$NAXmSTco)wgSLYCf13?ZCcJy_H7Mf6N{QTp6{exUl!Y4e)GsHDM zAjm(&)fwhSkfTHWJYigrVL`5rE->GNq@DeJeI4O(;vD1*wE`sP7HKu(HB7H|hey`!g3 zXb>!Tf;}9AJpJ6`!#w?c9FYgobd8<4~k z@9OUcbsC>;G< zLqLfgW{zt}xPOp$yi;f}EFxT7!@$P4Mj*_A=LBb87kD0UaSVv}a0!oh4T1$H)cp7$ z*WduC1)+Yf5drajt|4g3K#p~EafB%g2=ep`2}Jc_d~krjzfXK{fTJ_a17KzG&W?W0 zu0B3+kNdfXfIJ!R;_Bmy2o67}*C2TUHLrpk3i4-2WB|-)kPyh3Ad{eOhx-~N2J<#t zFc?wNz*0njV`wnkRY9(S;BW-l5A$Y_tBYq)Jc=?y@yxy=pK z@bEM;fw~Ktkb^^lLY+fGgIr-69eqHlH!|KaIM~zO5AG0H;Sk^$931W+gkD|+I0iZT zx`x2>1Sk+N^M#*3C_6jH2mAPk!0bZ`3SY+n4}XM9TqB%);)7j%<6%zn4@bBMB<>j; zAM6TCrO4*Q2e}4@x(37TfH~eV1ky$e3JnN}5AlR2X^>$c!-L{o5JfRaFf`aTDBj1@ z7hd3ivyiigqn|r0SixmpJSf}3!^$bh-`mwMKEM;6E+A#UqbDM{L31vs$>rwb9}bNa zu<Uzl*1Ymlc~WPEVA zXNWVbI0U6#uxEneeO$v_VTFXVqmK_b33~bl__%`V54b-;Ap{CV|4>*Yc=`p0y198e zdxA=Sh!@cu1j+?yLddrH`NxO(`-J+s#``!r!QJ5N?*euLN@W}5?~EZB?&uj3@8=)y z?BVI-666Z2t-)NdVrPGlFGApv2dYfBS3NO8ibx1K=oEU zO0x!>km5l)ef<60p9HgKs4H_^YF<-|ZZ&#Ois4}PoC^|rrD9Ob$#5Kq<#MK4d zM~Akog5%9hvZ0!wM#TGq!o$bkIoLA_UYbPu`FQ$y$GZjj`@*b>2etBGTKxkcMX9Tw zqZ6VkfSDNV>h2N%58}`ee^8q{*wr12BS3~4fuwvxgG1t-T;qNH!$3wtEedk=@pq1Q z_Hgv{i;r-1bph3r?mnKrP-#y;Hy?lhfOt>8cqEljNmswHFvlQpN{9VAcCb(S`@8s#{2<>*oV^I_WZnJ~ZqN@wC+Msw) zvO{VcfSAa|q@O=H=|MdQmIb-m+27C4)frKNfV)ee6arNk<{S?ytI$-z!qe3exe4#; z7!NKdJ^h^h{enG%LqNSkP0DV|)lG*&;XDkz5RE?!eVzFTOxw18%!P)xi@$fIp%E z7yv3{9DU-ET z(Ms~5cwbM<#!IlXe}F64Zsa@$vlAo>N4L7pKVzOEsj&foz$q}C!hZJ?(MuplUQ zUHuRhimM~6je|1U;2!`g>pg@0C=NaYd0&OWm?FW@b zutFCm6zby>>>T9k>KE_p?*c1;{QToXK<#s=A}2=|7bgVU+ZC3qogMu`K<##@9Ec05 z<&n7H4i1zb&F`=$I%(N;Rw^~;u#DoaA4h6XU`z#P#?#jc$jycVX+U_;O`14cU+N1GGLO>vJjLH z0z%@QLxNzX1WW-aGEwR(NaHOYCJzlXAJ-sI_<_oIXh?uYbOJ!bLZHeMsv0T<4;&YK zP74ljb@p`gbOw!>!1H84kUwZ>Gafa!LyboY18|CjS_hN$@pt$41502;2)GRg>OEn! z-XKW_9F1tkK^tUl{-Cf2jkuu6gM%BE>(OF7-p3ne6U=pp_(RJiPX7KO@Bsu!L}9q! z&o#_72-H?V8V7R;4e;><^`7Bz1#0L(9qHl;8g@g<9H7z_sha{#7cQ=#kxNLID+nBP zi19yEB`6UCwZzXKR+dD9Morv<9AWJ@n5RLh%hM0k_<(B(i4S&ka|L@EstPLQ>gNI) zxAb*&@pOa=feIH;G{i&P2@e)0S9ecvPDj@e>>7eiJ+#4sZVb521t(!pqDS}~R&anv zsN+#aWFb>i;JN^=8}6)7aHAR2rv+Es$UZ?xh5GsUJG#UpOM=;;#;~6|+$^V1P}d4E zcm!(bfIHex9JN38;hW7mPAg>*C`R@9F0m0*+a@(ZL>}Auj&mejuX) zg8bcs5H&fd4UJpY(;szQA=ov@6VwDo6ZQ-WL~bpDn}Ohp2$osE#hz;rT9NDK;~x+Z z8Sm*54~{}m(m`%|K&8XMjaKJKA5T9Qcpnfd7wYHj2l5wq5&)KjAf|#^w2(Rr(IE#n zl!9EH9eq5VKpiU7Py)*$njfGP0uMp39B9fCEo5E&ebHM$AaA%rWlw zpmqxqAC`AqkjC&qBenj{p2(93E-tR%;D%ZWN~oZaLXL1yiU&=&fJZnX4Q-fnppCQm z;7C8`cz-`1c$MfM8szL6?+O_Oj7J_3ashX*z)pb6BMoDE`uMoIJNm$-P#d_uj_!_8 zpzasQjj*|Da4Lgli1;8^P(Jf?^zn>>*x`pf-2p8%KurzLAlKk{XCGHbq=pJq+Qr{L z2s{S|3lzwlJ;+YQ_ViDxQ9j zngvlr!4!b}hthBf@CS7gK?M@1+CZ81gz0hh1GRr4M#DS>Dj6UV0CfR0tU-N_P(Q~o zM^9Lj38D&KRR_8Hx`M``;NJK12TuU`L;KH;@M%1d6#+s1VV;N*3tG8?TJInaf?N!> z9;z5g1msgtGtAM`4_@NAMufQfxwyKZG@f92&ppULGythF022rGUP41$gRn?}2QsmU zyZHM$dir6JfF>3!GSF!%sCQs?gXWH0v8aa4)?kr>=5pLJpgaw=1~f|riZSOPR~J{m z5Kt;`_V;u1@r2LIf|6_qXskRKRMbJHOu(Z;NYj#_c|;5!L!vd@KL|GO3K_;kjE;c| zC}@F=6m3Z92z9{LJ;>47)h*NqX*2}t3uvhWPhyV2!QgTep2T1ZKuH2a63Iens6&lK zl0eBZu&F~(m(|hP1Jvz+>hSOn4vEJUhK)2}lkyM3rUO*uxdw-zwdau?gwm}Kat(nF zSi!p+;8+E{gb)b*GJ7_|~(-%|-1&0KAA}@V_NQQd{hd6@fXwhUr;|4*X8GrCt zIHF*IH{rqkWTgHvG^v9deW-**H? z>*2%ff>#>w7>3n)kirI5;epy^q0S!h!JdeTA#lzFb;>}UT2TKCr9%R$72`eKK=PnY z8{B-*a7U1*3utHuGD!%t0#xpRQ*uC%KT;|Nc?Lcn51LsDM$AUI27!AexEqY%WCUtz zLSxO(-w)Ogi4TTXDL7k&u(8wL6haB$|86V*9;|cE( z2f4b(`#FYy{2UzW3+|jkbvg$-gA1T|e>XQDPd`{R1bBlY0NfBkwElyA9fLwZGb-RI zEXdrC8_LQEkdNGh99=-IHcQiQ*NG!$sw2+WMCee z7$D<*khx!Qb3G^&HVq43BoH4M>JOd;Kv}lp;_4LY?(Q0dKE4miB%n!Xv=s@#{=T4m z77`Ds9H7?w`9lm322BFOvIA7k$KTn}Cmx&)VKo|JxX&-t$0y#$Hy&1HLiNCUJwdKv zo}j7?Dh%VI&lbR<2zf*rCKeFn>FXF284rqiSOeeB9~7IeLBalhkQ9nMGzl7V1XW|8 zsspr!%*7F99TjNw&)+W|RDdG7U9hqYGF$`oCe+a|xqu+gFi#&>`1mWF4=#i~Tzz0= zEzIT6u{7i<1aK&#l!-9YK{8Ma!9GP@mjY7-mVzpSwDdq7W-Owh*!T292{a!^P@U)E z>j_I(kp2`}%z66wfYN}ks|$LZ!W3euz97K{DF|TY7|i!UuIN=EEPNokJp4m3rlUa( zo8S;|)IvQAw;Z)Zfmwn!K@ecy$OZd7|P(py@Am}hC)G&w`v`BQiS73}I9;=UT*!ifI`-Q|i2l)pF$GiGD2StJ#spw5BSa3js02)TH=tmKRJ0cj#c3)yFKq?NQ z7JwT4uuKFM0*`Z|&I*FYn?XwsVTm5mJn`{#cJ&K(g*0x^Gb*J1gDk5;uB3fKLqb7` zEi}Xf+?<2g5+R_jU9h(+Y~3Mzu{NY&0!1pQz(uT*fQ3)Er=N>|IB2d1u{<3n=j-Yh zie1JzG$`0V2)i7pNc6!j<>Bw|ja|kiJOEW9#3wvH+%v=@K0Fw_*bf@S&X85GZk~QF z@!@_hC?z=5EB?H0mA!bvj(k+20SD)K{ufyjXx`t+KLRw&4~j$7{&ak} zr;iJ`>*eg>806?2f@n25`*;R8`8x(dRw{t%LZ~aC;Q%rWBMcyZ0B1soQ=oc4SvJVu z2b&{co^tm0b@B%}6gmn8)rn*xOa)X1rqR{KGbG-_)zjSrwj|I6QrjSV6{Z-Re%w5L z5X+KbG7v$~*dT0o09Y+(SUEV}!`C0yRB`w5cXIRr4}!;oR6tz=8}S4QAuT}$b3sAn z=@%U0h_tpGX04A?JgSF-T|>arHK49Pxb}jrMDTF}Cuh(i?GQ(&;1K@+SXu!M%LVyI zLK_#LP9T=#3(flpusbM9fIT{>0on%wcR&baaRStFP+^!o?&0yEY7?=21116K zV#dQ8q<;Q>@xCse$o_;H72qEb8UW9kj>rqeK)!bl^7rv^a>Qt;2g4*#RE$5 zge)`~LG3I=(-2g&lA-4L$MD$G7c)mz#c=b4B>L%rZaev0o0Xn4M=lL zaMci#z)SRD*%+E1!Cht0MorZI9ZVL~(sGS}FIj^LV^19cE=lpr9;-z5}2 zC=L^Kb9F>ojtUb6%@_FlfmYsu8$R(c@qi%EyeLNZ3=(W8?MzUK0UC$*bPo*z_fVj& z0oMrej!wY{!(pC*Bvb5i(8e^{d?nONpm}h#QVnE%V5n<|bGvg1(*yT!zNOTm`C>P#ZBY{V)~Kga!`;i2hJdur5Rs9HtGNh)_lz z;WCbX?xBwEuJNIMprJgJm2r?TfUE`ptsBN+ILu8!uAnX;+{>_Cgy38W8ZX3@0VOxM zDAY@kRgzGTf!66|jp2yUP)`>$8CO3S$gDTWkB%W|vY-uMp!qbYu%CZCl6ugV zSyXFaRR%;9GH?RUB#^2LA`CAm5OYWn32=76DGQSTHI5;RVj;R9yU{{|T?0Xjfk4YQ z!P8PO$q?`wX=Dws657{22$I7fN@2pDZV1mpqaYX|0u5f62)Isz=1o}0Kox@;eg1Bs z!AWSF0%8qxXb3uM4$iMA5}@8HcsmG06c)9xX-I!?XBcKZXpaBJnPMu22VpyaHMh4V@l`XhzDikkS!UD?l}%$boiCLbSk~7~<#-RR|Mt0`IDZ zr~)S)(8>bvR5e5lrVbU8r)9QBH^qEXY89iT4Cgu}~+%W^N$^G>BvjTHNUa>I?mi%tiPE)ISCdX@OV2Amw9lvl=m?3Na+gHON2S z#S^r;1!@AQRCNW7aDh%MfQq>VfYii;6eC1JL2LNH+XWy-xdnjdKq{? zAZ^=V#AF)C!4Rt;12~}lt)Tn{8wi2OgKY_Nb&ChrL#{zk1HmiV!M)XZjC>Ul?-Uv0 z8V~7ogEA>1??!+(<^+d?Iw8;3!eSQGIf#eMYC>BGpamT8tq9;nrcep+sxQ#KET3S< zc*w9Px=gU6FWL|=bjlh!U=J>Z5lvQ@WRNRl0cx;^X8<-Oa4}H$1jmCz40DZ_pKAzY z3>B2f!QCo&dlNPbg*rC{-f->;I>Z578i7*;G|HeJ18=E!4T3FohSj*xrV!kCSm^|s zzJ}_A3xW3T!%e^@2`VdHgM;CV*?+f=LDhc_PmuAqj)d>VXtJ z5FIctz}yZk6d;o5`ar#I%s~oR%N*)@m?q?-Hy{xME6ku}JYv)U5-;F_06H-Pj(W7T z;^z-h2O21V_PpcqsRLCJpaD+Mst7crP;3FM-GOfhgLoXO6;ugfiFVkq1~|hYHP;|| z;j-Z787Q$qieNNZaM6lW*2mG$9m8ahYUr|G$XEzGSiwODI)VvOm_hRZVoe!DAvD~P zH#tEhK=F!}iI5ufD#5a@nFP`TUcfV?OjCdc7tVQ)TO9!&qGTZP|ktP<-sTA!F!}2 zOShm_LvskI?g9-RqIwrp1;cU_D0)GAj6ow`{-8xHkcmP62u~LVWIievvASd^LV+>>?X#NeQ_XJ2rZcZ~p56mZD^$kI8ml%or1OFqQFfFQScS0BjgYh2=>zBXv& z38KS2C;+PtWO0}dNLuywh1G4~C3El^4U{ZEZ8_8mGr%N-MV z2x=DC@rcE{;8_&tHUg+M$O@4wB$%U|{eAp{z^hE48a$mLMSqNN?0*PKsw&V75VTfP#OTGByjG8*a(Uy z=rUn=41?=;Q2fK1BcQ4#4AfQzJHpf74<-Q}%|%&cfYOeOhmQ3_-3c#`K}U^%N=8uT zfXYFRX98IZjydEA2?n2*02=WQ3UTp=P1Hd}9bH_!{9&_YU<<&rdEf>jykm#n{)DK5 zIu55QSahJMhB_BT476emaj*ec$`v%r4LbY9DL6hb)HM_yx@g0V(76=Y1O-AC+#W|? zvH&w06m7^M1QUnk@c3}YAjAPlXmY5>bW#ltxO zHZB8NV~o7JAl%n8-W@V@gSx>6Bms_OKWE5VP~^}7ONO{&+kX#|0@Y8B?v98>gub5f zE&&*+5Tq2;)eH9b2?HKRca)q6)22ufQh@h4cAQ`Z`K~*o>$qXPlXzLnzJOL6! zAW0WU7Z09-Kw_|@=?g#F4OtX4_<~qc29kj}3vHbpND8!<9XzoOjZcsmXvPsVQGn=q z!Yu^tj)ba&iGerv!8a7ZM8Q4bc+jD1Q03sn3E7c}UhqNGLKkI2l|xNMTT71GJ_$kD zdLXCzy50tA>>o=GSK@)+<6MGP|K=+ah8V{NrhOJNmk4u7&Zi)Bzg|A=$H=e19(kzD+)}ZMhcToR07&2xJaU`f&4>{}+ z6e{3&hiSlcKe$juTEPOYpiwg_sDTXH@)hDA1ez-ag$Sg@0QCf#JSYh{LOUKX2Zh9g zwh#q41_ir9#_-@;A!6_&k!e&=3Xr zA=netc>xW}!YXIj*#e*~C(!wGl))jGymKUGIRQOI!#NV(+Cb5YQU>^gPLG21NWq(X z{QSXNi9+Jhvpb4DypqTjB7AGQvkz!-IBfkkI3=*z_Gf)X=0LOzu9DME+Xj2nZ4w@)I z?OMc&Vd(lHP#ZSDH7LZ>H8?&v#2>NY2-zm2Rp`#3E!?ocftn9>5KI*64^&S=)PPn% zKwABdo`?;XP`865QP<-jNq{y91-T%XPe{cRk~+{aCZOZTd>tdOD{%Jk1`pn29`tfx6=$NyuR@=-S|c399emsR-&u z_&^p&KQt0xQXnDJWz#Sj(7}wL4g}g65in`6MvxDX^9oE7vXKd|5?COh6gM#aFiBkI z`?-c7Mq+T8fKY+U7+6Y&wm{H=1||!04Ei2!NEsCm_9asGf%^eeHi8=FsC#Qsq(KgY zOcDp$zlsos0A=3kirq&-{^`^c6?%W z2-XMyZ{dP@1GKRpG?#(HF3{uytd50mCxAr{yon52LJKJppvJ(oK&xfc)judMMTrq` zFoCN?aQy}j-04G1xLzrMv$Q=dPus))h2ClY2%|Y;@ z0i-n|(4rWlXo9Xkg{%@pI|K)2CCrQ9#v-nQ7*zFvM{_}*#vJ~GI4sCD7&OR)(+tp& zMgh*Sg;OZ2ZXoS2n3EudI>;(;26c4w;DqZI9FKIIEt3mZOD8R7Q7f1$qIKoelZXV=n2||h?EpT@}RCJC|RI3 zvOorcWTANwyxj-f?TYtt4GDo9`G!2o1yTrh=Y4}P)+b41Z7lLXV7T|XomoR7Gt@=$|FbsyTbXf-AmrC zkuW7-(?O{Q(}ob}I^#50J@|IkRKUuH09c0=q%8oll?SCX0GWne7@CG7@PR<=YCt=jLj2)pTVPQHUVDkt znlOLRB0Yq32Ko93YC1()UEL-){}1Ii4bMP#6RR^Xj` zkg@JTjsYH?&Y+L0?7&0J&^2+89=odxgIiEAXtWMi-o!fwc*X~VuGR2!c7;oVXRAQNiO{kE zNy0w>JRyuO0dMm=26)0%!vx_1P+g!}&KE9-a=8R(6C_*)v@QpFP7r9Z3(V;Opo^10 zTeO1_mr|hUgDSwG9kRJB#6KRY3{?BWt%Qn&K<}DBb!l*9aG1YOJZLML4`{bO%t2`4 z@FN=05s*f5U4o&@cEIgQSI|01 z(6$H2lpp-8Kd@%RK??EyK`8tGT;Rn8lA7R1KhQdCNEzo6j3fi@IDnR;x_KfdC{a|v z%tsw7ae)Q|+(^tx9*9GOKu2MKdU(huMM0#%3tU0+&=e1LI%r_a)j1>{bYcQz1PLJ< zC8EyNhNEVzJHn)!u*I-#Jgi9W85u*wceCddVLUg^CJa7;$p!VyCP=FX1`hzPGaPa`^I)T@^VU+;|4eTg!aH)YDXrQ(4NV^5#aS5s+upT=B zx~{-8$`zNKBk154$Yw|MUN0zE{e3}MGv3Y72Y%%Ml5j{6d=D!q{ed>d`?`jBK<}4< zHs0VZNRUe)=lFw)2=rNba7uCv0CjqTKwF0(YxLp%18adUi$FwekgJ<(P!Rkk4HwX| zb$FE!?;PX_Y8HpM`XQY)1a`ik8)VJCqmO3@=#cTiP~_91K-m%5CEyDT5DVfUCIp8B zIf729M89$jl23v`=V?Gj^bxLw9QzOualnN;&@QYFw`dmvNOX6 zbTtw}ucMort8++jyl<#ah-U!kT-advxfh_gh8($uG&bfE3@I1Uv!JV=vp;B47P`2L zD|T_{e)CYs5xNKmfxEcqhJdmumXrwYlLdpl7UUWdinP=N61|{<>M$D9ASuY{Ja8TeT3cuzkM=$Q)WCh(L06L}`vb6v%3-X^Qq)vvM4TK0z*!j5N>Ht=dBBVin1MO8nOUp_k{2DL`OE6$*06~bglQUi4pAf*^W2IQnL z&?zU-i|bH2MKF7VTzx^KNT5^607l!;9~<3 z2SR|h>p-qMgQR+dsi1@diY??yACwV&JRO5U=Vv;3!2$)L2b8M7IS8XU2hs(ZrUwIYsLfizkS(*`O5V221GTnUPPN3^5JK>5wp8GMEtc=QXri3_0xv?~F0Ru`=6 zhmZlC^bffn6Fe4$kO&TS0@d%}(D!f*a|Lxq5Qh(dT<8q;F<1dwwE&K4M;DiP$j}$c zxF6^+Z^vLzg$=pp%Guu!bfy%nMT*=M@r8BrL5)!-FUWd9^s)dH8vcIZRf;~Iey}5l zLGJU19P0$~1*lv@uD7twq5 z_zD_O?D#pyd$>9x#U=E58fXg&lq(=RBw)u)xCF<8M(s`c2((TU;WCg3KA^j4T|9#Wd>kXu)4Qv$qaWx-CQ#)Q;)=K`HyGL=29>MeMmWMq zNbrDK9^hO6$|hJP9V6pWKeGgq_Id zf^JHP4~9>{fJ%r^CtMmqRRpND1oFHmDBq&?`w8L_|nox&?fV zgfld*P-ZV&g5$v{2)d9Cl!Dwm{XCH_%7$eehzf*LK#qr+7~~oV+O-QWg+cy9l18rx z!Rgu=bT1>8rUXPBEf0ZpK zBcVEAtwba%K}!WdbIs8Ge(+PCL7Bxd#1Xu>6&xT4J?^0SAirS9EFq{Rg^+?d!!;rR zve3~n1h#$wRJcM5e@HJP-Z=<#>M=rdFzm=oXAf7%T?uFwL!${4xu7McDBW{#8$BL8 z^aD=b$dX8L2#R9#cyxD$UW1EfolB5oJk(0qgd4)K;D806CkJY7pfpPmQt)6vu0{~j zexQZ)_~RHn_K2_*9z?L_h%fjmLWBaa;h-wO(b+ZL!_&pZ6?C^FQs)blrae4egFyEP zM}-27@IKOVN?4T(k%Sx6q+I8CZ)U1avF`czYiBkX*M=P=bvIRkq+k zKcs>OmUh8%!LA{o^?FFsh!w-Z;2Z5-T!YX}03{e`n-mcsAVFwl2?;Z#+p$5JEXWZu z$P4xhsO<;R0E#8#>j2QpSCGLVcfkq~@Vq{%+0YigFY*`>NSiO{%5jX*BajTJy@0Eu2$KipceI`hND9#qD?TtYyNK*qvSFr*WS>cM~@ zSJ2)GbS;ifj(#qn!^0!dlNPA8i#h@ZvI109!1gbIk~8=Y5X2-GszW`4;YvV7lBY9L zZwq9YYeWd>C_UJ=GH^aeb|5&V`S{>2gu$&dP*@^2=fDkBNL+wc+4zK^_Q^rv3y}py zELyrk(hHIe_45ow9w$bT1|P?b%WUXwVbJ{$h--rT?q0eN+8cedaPltAV-552oQH8LLclaa1evC zEO@gPcpX1N1uUUL5@9^(E?9&l_@otA&_yBOfg(pI(6w2}1sbfg4O-;~=_r8H0C)@o zVGK+aWQN5JcD5>Ld>6EZ8*TwY2k873@B|^KvP5a31cRn-K$lZPyoz(85Y&%{p5qPn zF>*-;OAwIU2WjD=6a;?$exPd}VFe^)0U?s>AX-8LK+y`Wicl6!!6F8__6;;I8w529 zVFs)W2JIROat2AmR-PTt z*--E(3B)rP#)AEVP9RfO9 zKg`iN5;TvGXc8mzLR(_ci1hRWtzASJ{Q;fM>wR@`XR-3<^{wpx!%Tqy=;j5z5$AkTZC80aQss zW+)&l6VNM1P}E=#Mo=>Z+II%sZV?Y!WP)%3k}PONI@0}dpjs!$G2YcN$j1}3^%7Eo zIfM2fz>m{~ssXkCL6scpN@egcLws-u=++nTx@(k~U3X_tor^YQj-0nZ=V(LtXiYc& zc((x1ZRnu$Z$Wtpc1r_Negh{m&;WLbe>`$V0#5_EIzu-&f$Q!77o>~^O-w{YKD49; z9jxZ>2Z?!@bQOTtN=>0mPC3|$Y4k{kBt@6_0MMcCCJ1->hlYUi5Y(BF z&5HoB|G9U z61D9LR|KgDLD!hLptse*u>y%B@X~hBxGD1LAy9 zzyyuJ`9qp{$UzI114Reg=p=glfod68w-{0*A*_L_1+7*E$8$XBfN@vQco<4Hae<%W zi^rkE6RlFUs(9W^nO?uBwa=0^dasI&I1^1e&PPC80SAlqNv!cXVk)nSx;~$l0F0 z0g(H>U0pEbVJC>78yN{2xd0ucgDDXd1R5kn7x#<6A?_anO4t|{`}v1Nf%aaYo8#o@ z671=Yq1?&Q1$1!|7S*6Nr(osa#wobQarO^#2?mRSlMVO~SkUSHt|3T*px%L_vp0$u zI4Y3Tg!+MJX+Q-lno3x91Xm>pSHXlm;pZM9nFf;ttuA#93W-FLf*)KI4_aIvgd_us zKxjV;VGU>;FUSotz6Mqc+71~S9PbwB;)l$4@dxc}gG_XTbVHRv3uDmw6@(lls9P({G``{wTBZj|he(znEARu2;DY3lJqK0*8rMR} zs9;fWISFd4g316)X|#D4gu5L>L;S%8;8cO)b+qIh?CR|A=K{Lm6h0LP9bNDYK=&9Z z8Nk&eNrF4`a0$?A!64V*U{_>+gZBM^&mn_N8z9^d)(w`2l!3_F{G34P8HdJTf5&)G z5({yKuX#e4>gFF5?g(lv!?%Hg%V=c7!GT8D*Pg-P13|#ED+u#p<2Z2rpv|-3(KCb^ zSh&Na&~qYO9DbY!YS9le4pIvvZ&O8>57!S`Tn3tuKuCdhPzJf+&LmKINR@;fm7o;{ zxKko@DRewkJ6c+TiF$%=Zv}1NL%0WA*nrZHryn?XB3TJpW&mF3jHDjq4$t6tuq1|J z#Ect42h?)VKtQ}>2-p&EL4~9lssv{~K_p>N$RcS3jR}Bzx{&FA@WIK*(x6!?EaegC zWIs@&ARr_N?K~ThvthE3b{M$G1dWFySK#0dKBAKZ(gi8wT!JAU#OW1KKhxho1e}6A z-9Xcvpwtd_GpJmQcZ_fY-6jueHiRIT^dNC0RiN`)p(C)M914~J<;c)raMKBNdpme2 z52d&Qjqf`LpcEgVC5V2oO~1%_&?NwO8g)Tj2!@e&p-29?`hkuZK(QTJ7gkwNDnx{7 zc(9|Zr(Z}A1Bl_|=;8)ufcsov7UW1ykR&)agT;J7!3t)DgO&w?4`~E*J^kQKLy!(= z?0}{G{6m8MoxQB%t=G;9&XF7wN1UXR>q}|oeAGF5}%tS0o0I30|Gfx+go4`wYz)BE;pzXkr)CxO# z2hv^u4VZzH21f>i4wVK8f@+iSU`J533f=(@-<}*E>F&?y{Galxs zcvn!D$k`RV2NC2fq#y@{C8}O{;}+xsXagOzfgj2R7pEXA0{y{_B3JNS4M;K`BoYkT z>KX(NbTA*(y22C+a&<=22daBPXC#6fZ9%@SE^re;yUl_@!vNss1~eSNWt`w@5-ZOTb2b&?P}-J0wA4*Nn0W9HQA5pUDVG>?R}Rv6~DX z#D`X2$ZqoW1uf?T9S?%tWTXQ!!9j}6-xzAJ8|CW@-s6bP1eg?ljad8!J*zq%5dheY zKvD-9BS0!OkOKpz3K7fLO$!P2^8=lgjypt96ks<55%Qp`AVKXja3=z^1s&N<7=epl zHHyV}l_G+SkTP(H;Z=kPFbrj|<|TS64+shc?dC?6MluH?jT{o5-mZ`Y1S&M4Lu(LI z!7?EsL7q~kamGwa*p9Dn8BcZCGa^Bgf@_={yv`Y74Qfdn8sjFcR%DCToEd~U4xulgZ!bR zvlupmj1G13@pJ}x8$%YLk08X?4IHsl^JyehmgV*}X% z@W~474gf_MUgJRuK=bc-6?r%Y!)9HvTLT*DMUQf1SNi#bPA0*x0<_HtuPeX>IR4NG zbM^-LO&i?-1o|p~< zr8HO2kz&|NuK=(N?z{d^#h$B?i>PJ!v~UqdC}21#5p7a)O!c5U_%`D z2U6o09PH}r>!l}DhHys_8_tGHnO73csT z(sctMb6_QdE9|%t;6Qm~MZ@bv-D7JzRwiVqEfl`$YS;8`qW?drtU)=gF-+q0#6+y5*b9hlcNhhIglPu z3Jn6yR-x;S@bz(Vb@st+HApw8FhYtHh^^3s7w+L1;tI|&7%CupJ3al}Lmb^*{g6vY z6cd77eLX=pe!+VPyw~ zNl>qXwras^5riw@-UP4Y!XCr$W0b%ig^i>k%z))b7guLbU-&v0OnDF22v0(4LjAzY zQ!#vj2n7G2z);s;y5q^vuLJF!8)KKzsar6N#O~MRUNDcz6w+(T`r3z*cw4s3!0?>$e z4GeXJpOpY|252r1+VlWTMS~_tP+O;HiUQ!H5Euu(ps93r3~_`VB@W)QgXE@AKk!hd z59oXVAJ=$~AXm_FWJn5-oCPxnT*W}Wfus^D5)ZGL@U{zJ!GuROsAa$qTv=R_nyVL- znwFEAT#}ie$KY68oSK`IQ|XqOlNy#DJK;n2D8?=G^eDr2#Z8WQDRDJZeme3 z7Crd|l|`B986}uf0Y&*KrO8+%obz)F67woCbvWnerDdj<7A1m015?^1wK%ybvjDqf zNM;GLpP+%_l9`yEmtS0xnOw}^lA2VSo}P%}b%2$u#yG=h{RmSiSn=46&2dDA5)$1}GezX&Nvic?EU3la-5^-^+j z7}A|{5{ru&ic5-I^Gb75ixNxnix_+pi;6Q6a~zRu3C+t*%u7s9O$n(iNM*b<%uCA;PAy@`DR4;zB>@;e55aXwEJrfAoSIx(lvz^Akmp>K3Jze1#}HWos7obHjC0`fS>e+6KR^PLk5iWxjIQy{+gPs&P7E@6O2SuulC zXR%}XuHOlI)OEG|hfWbn)@2`DLIh)ONWheZ(s zC>Z?H7}8Tq;z3LXaP&Zv1c>dCn3s~1%HW<_0^+$9<>x|0{L|8kQ%n5Q7(zgiVQd7o z-z7CIu{5W|IU}(MY@TCE3PVV~V^L9JB|~sYQE76CPhw?0aOTshrjE!82it>vX7#J8tT^Lds$`~>kk{MDN0vHk*N*FR27#QphM|-{VxA5Ax z?wRE3xe_c49NVj6!X!-|bTYAVurM(&Ft7+PFfcH&ak4NlFgS2But*39it2#RtF2=eeTurWyp2=WLB3UCN;NN_Q*OYm`WN^o*YurV+)3J8j_F)%Sn z2uQFpFfs{9urn}mNC*guu!98!CD<95nIvRcxmg$(WcgS)7-adF*#t$I*d&3zDm>8It8JJiYm{=K@*cg~VrZO;bGB9y5FmW?5 z@h~v)GBEKmF!3`m2{14TGB61-FbOj-i7+sUGBAlTFo`oTNiZ-;GB8OoFiA5o$uKa< zGBC+8Fv&A8DKIc8GB7DIFsU#wsWC9AGcXx3Fc~v2nJ_S!GBBAjFqtzjSuikJGB8;& zFj+G&*)TBKGBDXOFxfLOIWRCeGB7zYFgY_YxiB!fGBCL@Fu5}@c`z^qFfau%FaiGce^aFy%5ZGB9;9Fm*F9^)N8?GBEWqF!eJqO<-V}$iOs-foU=W z(-a1#sSHfh7?`FrFwJ0Kn#sU4i-Bo21JfJ^rnwAE^B9=sGcYY+U|Pt)w1|Oe2?Nt| z2BuXEOlujK)-f<`VPM+Ez_gu#X%_?2ZU&}33`~0&nD#L+?Pp**z`%5nf$0zf(_sdt zBMeMO8JLbSFdb)LI>Eqnl7Z-GRO9rM_3``#wnEo*^{byieVq{`wWa46E;$~zLW@Hj! zWD;d$5@TeNVq{WaWYS_}(q?4RVPw)}WYS|~(r09{Wn{8rWO~lXB*w%f&cr0a#Po=n z=`l0Y6K1BT%uLUinVvHQwWW~P74Owue&GAvB8EKG7NO!6#D3M@>D zEKEu)Ov)@wDlAN@EKF)FOzJF58Z1njEKFJ~Oxi3=IxI}OEKGVVO!_QL1}sd5EKEi$ zOvWrsCM-;*EKFuBOy(?17A#DbEKF7`Ox7$+HY`j&EKI&EOnxj({wz!ZEKGqcOhGJ6 z!7NN6EKH#+Okpfc;VeuMEKHFsOi?UM(JV|cEKIR1OmQqs@hnUUEKG?kOi3(E$t+AM zEKI2^Old4k=`2hcEKHd!Oj#^U*(^*sEKDUVOr!YY zNfxG4EKH|an9i^;on>J<$HH`;h3Ntd(?u4hODs&6S(vV{FkNL~y2iqEorUQJ3)4*& zrdupbw^^9(urS?aVY-eZOBSYA zEKILinBK53y=7r~$HMfUh3Nwe(?=GjPb^HIS(v`CFnwiV`o_ZaorUQq3)3$arr#_~ ze^{9QvM~K)VfxR)#K6kL$jZdT%EZjd#KOuX!pbDd$|T0hB+kkt!OA4b$|S|gB+bes z!^$Mf$|T3iB+tsEz{;e^%A~}~q|C~s!pfw|%B051q|VBu!OEn`%B020q|M5t!^)(~ z%B082q|eG^z{+IE%4EdKWX#HB!pdaI%4EjMWX{TD!OCRG%4EgLWW&m2%gSWO%4E;V z(;`-;#jH$ASecfxGA(0e zTF%O}f|Y3{E7K}grq!%WYgn1qvNEk>Wm?b5w1JgrBP-J;R;JCYOj}r)wz4waVP(3@ z%5;yF={_ryBO8+w8v848&fzNQv@4RBpXu{8&fnJQw$qZEE`iC8&f08&ftLQw|$bE*sMlHm0R)Ov~7qma{Rf zU}IXz#C$i}pZjcGF*(-t-mfbcBuRC>zr;Hm2ijOeffwPO>qbW@9?T z#&njA=^Pu=c{ZjCY)lu~m@cs~U1npt!p3x!jp-U2({(nc8*EHB*_dvzG2Lcky2HkF zmyPKj8`FI@rUz_H580R=u`xYnV|v2I^puV185`4cHl`PBOfT7(Ua>K~W@CE8#`Knr z=^Y!>dp4$zY)qfnm_D;HePLtz%Et7Kjp;iZ(+@VLpKMIO*qDB^G5uj<`pd@jkB#X+ z8xsRN6C*nl6FU<#I}-~#6DvCtJ3A8xI};~66Bj!ZH#-v#I}vfJUf#DJChi|kC7*qJV~GhJb4y2{RUi=F8aw3C}@7dO*xZl*olOnbSR_Hi@q=Vm&<&2*5P=@2*5VQ!`)+)PKgnT~NYE#PNb z$j`KhpJ_2a(-MBBrTk3G_?cJ)m@)*IG6k5j1eme~m~sS|as`<31eo##m0MkVQrb_}$mj#%v2ryk0V7ey2bX|by zh5*w|0j665Ot%G??g%j56=1q2z;s`L>45;#Ljk5o0!)tun4SnQJr!VjCcyMufa!$* z(@O!SR{~701(@ClFufIEdMCj2UV!O?0MkbSrcVM)p9Pq{2r&H#30C|Cdi~N z$fP02q$$XxCCD^Ikf~FcsY{rtTbQXwn5kEosZW@xUzllvFw;a~rb)s~lZBb42s2F; zW|}6WnU)AMEfr>3 zCd{;4m}!MD(@J5cRl-cGg_+g}Gp!Y7S|`l3UYKcvFw;h1rcJ_3n}wOS2s3RJX4)pq zv|X5KhcMGlVWwTeOuL1d_6RfW6=vEe%ydAQ>7X#vAz`M&!c0emnT`rG9TR3cF3fa7 znCYZ2(7g*FA;m1t#3IhbD$c|v&crUx#39baDbBesYRNpRhp?&nyE{gsau+26Q6_|_^n2Z&eOca<*6`0Hvn9LQJEEJe56_~6Pn5-3;Y!sMm6`1T4nCump z92A%w6_}h9n4A@uTojmG6`0%J*q76qp(nn3@!rniZH@6qs5SnA#MW+7*~O6qq^{ zn7S00x)qpu6qtGynEDi$`W2WaC@@V_V49@BG+BXZiUQMA1*T~VOw$#ZW+*VtRA8E= zz%*NdX^sNZTm`0i3QY4Am=-87EmUAyq`lK(bC@^hQVA`a>v{`{^ivrVD1*UBZOxqQhb|^6IRAAbrz_eR| zX^#TaUInIo3QYSIm<}i~9aLaCq`-7of$4|>(@_PcV+u^i6_`#aFr8FjI;FsLT7l_| z0@GOqrgI8R=M|VPC@@`AV7jEhbXkGviUQMB1*U5XOxG2dZYVI_RA9QLz;s)I>5c-^ zT?M9l3QYGEm>wuFJyc+Nq`>r8f$50?(^CbeX9`Tu6_{QqFuhb@dZobhT7l_}0@GUs zrgsWV?-iInC@_6gVEUxM^jU%FivrVE1*UHbOy3olekd^gRABn0!1P;z>5l@_Uj?Rr z3QYeMm>3k97!{eA6q%S6nOGE=SQVMr6q(o+nK%@gI2D<=6q&dcnRpbLcomuW6q)!H znFJJ>1QnTt6q$q-nM4$sL=~CD6q&>onIsgMBo&#Y6q%$InPe21WEGj@6q)1|nG_V6 z6cw426q%G2nN$>+R27-j6q(c&nKTrcG!>b&6q&RYnRFDHbQPKO6q)oDnG6(}3>BG- z6q$?_nM@R!Ocj~T6q(EwnJg5UEESoo6q&3QnQRo9Y!#X86q)Q5nH&_E92J?I6q%eA znOqc^Toswz6q(!=nLHGkJQbO|6q&pgnS2zPd=;7e6q)=LnF17<0u`Bp6q$k*nL-qq zLKT_96q&*mnIaUKA{CjU6q%wGnPL=~VilR<6q(``nGzJ45*3+}6q%A0nNk#)QWcrf z6q(W$nKBfaG8LJ!6q&LWnQ|1Fauu2K6q)iBnFq{uW`k!gw|(^N&KX^KqK6`5uzGR;(E znx)7zTajswBGX(&rg@4?^A(vEC^9WnWLl)iv{;d8i6YZdMJ7#UCM{(qZDl4MWhPx^ zCOu^)ePt#CWhO&qCL?7g3uPutWhN_SCRtS`IaMZkRVD>hCPh^ye^sUcRi;2yrXW?O zU{$6NRi;o?rZ82ea8;%VRi;Q)rYKdWXjP^dRi;=~rbbn!CRL_pRi+kIrdCy^HdUr} zRi+MArcPC+E>)&(Ri++Qre0O1K2@fERi+85OcPa^CaE$_R%M!^$~0A#X|pQR7FDLL zs!ZEdnYODkJym6TrpokOmFa~l(@RyRSE@{}RhizXGQCx0dZ)_tUX|&CD$_?*rcbI& zpH-Q@s4{(3W%{Pd^j(!nT#ZRWjY(3C$y1G~T8*hjjcKhK(>gV#^=eET)R;D^F^Q-% zNvbo+sx!%{GpVUFX{a+9t23FXGnuM0nW;0Gt20@sGg+!LS*bHwt25cCGuf&$*{L(x zt1~&MGdZd=IjJ)_t24Q%Gr6iWxv4X`t2241GkK~rd8spbt26niGx@4B`KdGct1|_t zGX<(M1*tOyt22eDGli-%g{d=zt21S*Gv%l=<*GB4sxy_TGnK0|Rj4ynsxwuoGgYfI z)u=Posx#H8Gu5jzHK;TF)MEOj#q?W?>5mrEUoED8T1@}7m>9H~7`2(0w3(Q-nOL-$ zShbnhw3*nonK-nWIJKF$w3)cInRv9Bc(s}Mw3+y|nFO?%1htujw3&ppnMAaiM75d3 zw3)=UnNAonoit)PWyEyai0O9r}-8&jsYrcCcl znckZ+eK2MEXv*}-l zW=u?GOw49XEM`osW=w2mOzdV%9A-?MW=vdWOx$KnJZ4P1W=wo$O#Eg{0%lBtW=uk6 zOu}YNB4$jYW=vvcOyXut5@t-2W=v9MOwwjdGGu#*FE#8Php4rt@Y@7tEM0nlW86W4dg{bj6J6su|NYGp6fiOgGG!ZkjROGGn@J z#&pMw>8=^mJu{~JW=s#vm>!xjJu+i@Y{vA&jOnQv(=#)s=VnYV%$Q!9F}*TldTqw^ z#*FE$8Phv6ruSw{AIz9OnlXJcWBP2y^u>(ns~OWbGp6rmOh3$+ews1;GGqE}#`MRG z>8}~nKQpHPW=ss`pz&2ERs}{zL0=G)1I$ch6k!z=%4FmN4f@J3Gca(lF)#|13Rbc) zFi8lNg2rRP3WXV16&PfN8HCt5!O8_iB?Ngm1qC=bB-lVkHZ(GeH!?8E3Gy&9up8kc^`I@?x-^3}0m)xPugfwAM2)Fz7@xGU#|S zG6V&?1h2U3^|Zt7vGcU#B=1*mGQvSRoULL)i&Kk=W872oi_%kLT=J7kb5rw5iethu zi%S!86oN}iQ!?{GE;fw`D9Q&-Q545G7v&chdt|1Bq!yRNz`YQYpOh67l$w*8Se%Lw z)hkFzVqmxu4YCd7G6sfsAhrU8jZQuQ$-xEVo%4%aa&mkVGxHd7i<9$0m*xFG1_n^M2ErgA z2yG8yf@UN^6OvFijB;TB=?G&;WGG^AW5{I4VMt~0WXNMkW5@?9QDFehUxqLgF(fkN zF%&c8fM-B68S)wO!14?X8(1QkKxPCplrR)AWHRJ2q+>D31gyq@0R~MNl0ad=V8Xz~ zz{udtkk63IPyn{0lEIH5ks+5M6>PpQc;>Z;A)ld`A)g_Qp@e~f!3L}c=33DFY%xPJ z*zN*`5(b3ZJQy+=QW*3Y!ojXGW-wv^g*yiWBiwyq45owo4B!}K7as#7 zI7CXoYV#Np8FG+Zh-rr&LkdFmhfc=6g zrogb0L4g6&-wF&y;Lx)Ghl>jX$X)(Wvp}H^au+O2K(2&@AU^j&!ajfj6uK!4r3}do zB}hJFV8GPNz+l1v3O`gm=%I)nh8AEwjts>N#SEzoxeQ4RISiEy3Jiqwfjv4(jE2By z2#kinXb6mkz-S1JhQMeDjE2By2#kinU<`o(Mg|^`yKF$jC>{-g(GVC7fzc2c4S~@R c7!85Z5Eu=C(GVC7fzc2c4S~@R7>*$T0Fu{w!T + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new frmDataloggerSettings()); + } + } +} diff --git a/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/TempLoggerHostApp.csproj b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/TempLoggerHostApp.csproj new file mode 100644 index 0000000000..e3b08928f8 --- /dev/null +++ b/Projects/TemperatureDataLogger/TempLoggerHostApp/TempLoggerHostApp/TempLoggerHostApp.csproj @@ -0,0 +1,95 @@ + + + + Debug + AnyCPU + 9.0.21022 + 2.0 + {A2D66069-8CF9-4104-828C-49A73D7DB5D1} + WinExe + Properties + TemperatureLoggerHostApp + TemperatureLoggerHostApp + v3.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + .\Hid.Net.dll + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + + + + + + Form + + + DataLoggerSettings.cs + + + + + DataLoggerSettings.cs + Designer + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + \ No newline at end of file diff --git a/Projects/TemperatureDataLogger/TemperatureDataLogger.txt b/Projects/TemperatureDataLogger/TemperatureDataLogger.txt index d31e24f36d..3e13fd3853 100644 --- a/Projects/TemperatureDataLogger/TemperatureDataLogger.txt +++ b/Projects/TemperatureDataLogger/TemperatureDataLogger.txt @@ -24,12 +24,14 @@ * Device * * - * USB Class: + * USB Classes: * Mass Storage Device + * Human Interface Device * * - * USB Subclass: + * USB Subclasses: * Bulk-Only Transport + * Keyboard Subclass * * * Relevant Standards: @@ -37,6 +39,7 @@ * USB Bulk-Only Transport Standard * SCSI Primary Commands Specification * SCSI Block Commands Specification + * USBIF HID Specification, USBIF HID Usage Tables * * * Usable Speeds: @@ -49,10 +52,12 @@ * Temperature Data Logger project. This project is a very basic USB data logger for the current temperature as reported by * the board's temperature sensor, writing the temperature to a file stored on the board's Dataflash in a FAT filesystem * each time a specified interval elapses. When inserted into a PC, the datalogger will appear as a standard USB Mass Storage - * device with a single text file, which contains the logged data. + * device with a single text file, which contains the logged data. Files are named according to the current date when the + * logging commences. * - * Currently there is no timestamp associated with the logged data; this project can be extended by the addition of a Real - * Time Clock chip to retain the current time/date which could be stored along with each sample. + * A DS1307 or compatible RTC IC is designed to be attached to the AVR's TWI bus, for the management of timestamps on the + * sampled data. This project will not function correctly if the RTC chip is omitted unless the DUMMY_RTC compile time token + * is specified - see \ref SSec_Options. * * Due to the host's need for exclusive access to the filesystem, the device will not log samples while connected to a host. * For the logger to store data, the Dataflash must first be formatted by the host so that it contains a valid FAT filesystem. @@ -68,16 +73,10 @@ * Description: * * - * LOG_FILENAME - * TempDataLogger.h - * Filename of the log file to write to on the device's FAT filesystem. - * - * - * LOG_INTERVAL_10MS - * TempDataLogger.h - * Time between each data sample, in tens of milliseconds. Each time this period elapses, a - * temperature sample is taken and the result stored to the Dataflash's FAT filesystem. - * + * DUMMY_RTC + * Makefile CDEFS + * When a DS1307 RTC chip is not fitted, this token can be defined to make the demo assume a 1/1/1 01:01:01 date/time + * stamp at all times, effectively transforming the project into a basic data logger with no specified sample times. * * */ diff --git a/Projects/TemperatureDataLogger/makefile b/Projects/TemperatureDataLogger/makefile index 1e495526c1..ab29bb1bea 100644 --- a/Projects/TemperatureDataLogger/makefile +++ b/Projects/TemperatureDataLogger/makefile @@ -130,6 +130,7 @@ SRC = $(TARGET).c \ Lib/SCSI.c \ Lib/FATFs/diskio.c \ Lib/FATFs/ff.c \ + Lib/DS1307.c \ $(LUFA_PATH)/LUFA/Drivers/Board/Temperature.c \ $(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/DevChapter9.c \ $(LUFA_PATH)/LUFA/Drivers/USB/LowLevel/Endpoint.c \ @@ -143,6 +144,8 @@ SRC = $(TARGET).c \ $(LUFA_PATH)/LUFA/Drivers/USB/HighLevel/ConfigDescriptor.c \ $(LUFA_PATH)/LUFA/Drivers/USB/Class/Device/MassStorage.c \ $(LUFA_PATH)/LUFA/Drivers/USB/Class/Host/MassStorage.c \ + $(LUFA_PATH)/LUFA/Drivers/USB/Class/Device/HID.c \ + $(LUFA_PATH)/LUFA/Drivers/USB/Class/Host/HID.c \ # List C++ source files here. (C dependencies are automatically generated.)