/* Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License. */ // This sample C application for Azure Sphere uses the Azure Sphere I2C APIs to display // data from an accelerometer connected via I2C. // // It uses the APIs for the following Azure Sphere application libraries: // - log (messages shown in Visual Studio's Device Output window during debugging) // - i2c (communicates with LSM6DS3 accelerometer) // - eventloop (system invokes handlers for timer events) // https://github.com/JuergenSchwertl/AzureSphereSamples/tree/master/SphereBME280 #include #include #include //#include #include #include #include #include //#include // applibs_versions.h defines the API struct versions to use for applibs APIs. #include "applibs_versions.h" #include #include #include // The following #include imports a "sample appliance" definition. This app comes with multiple // implementations of the sample appliance, each in a separate directory, which allow the code to // run on different hardware. // // By default, this app targets hardware that follows the MT3620 Reference Development Board (RDB) // specification, such as the MT3620 Dev Kit from Seeed Studio. // // To target different hardware, you'll need to update CMakeLists.txt. For example, to target the // Avnet MT3620 Starter Kit, change the TARGET_DIRECTORY argument in the call to // azsphere_target_hardware_definition to "HardwareDefinitions/avnet_mt3620_sk". // // See https://aka.ms/AzureSphereHardwareDefinitions for more details. #include #include "eventloop_timer_utilities.h" #define POLYNOMIAL 0x31 //#define CMD_SOFT_RESET 0x30a2 //#define CMD_SINGLE_HIGH 0x2400 //#define CMD_HEATER_ENABLE 0x306d //#define CMD_HEATER_DISABLE 0x3066 /// /// Exit codes for this application. These are used for the /// application exit code. They must all be between zero and 255, /// where zero is reserved for successful termination. /// typedef enum { ExitCode_Success = 0, ExitCode_TermHandler_SigTerm = 1, ExitCode_Sht31Timer_Consume = 2, ExitCode_Sht31Timer_ReadStatus = 3, ExitCode_Sht31Timer_ReadTemperatureHumidity = 4, ExitCode_Sht31Timer_ReadTemperatureHumidityCrc1 = 5, ExitCode_Sht31Timer_ReadTemperatureHumidityCrc2 = 6, ExitCode_Sht31Timer_WriteTemperatureHumidity = 7, ExitCode_Init_EventLoop = 15, ExitCode_Init_Sht31Timer = 16, ExitCode_Init_OpenMaster = 17, ExitCode_Init_SetBusSpeed = 18, ExitCode_Init_SetTimeout = 19, ExitCode_Init_SetDefaultTarget = 20, ExitCode_Main_EventLoopFail = 21 } ExitCode; // Support functions. static void TerminationHandler(int signalNumber); static uint8_t CalcCRC8(const uint8_t* data, int dataSize); static void Sht31TimerEventHandler(EventLoopTimer *timer); static bool CheckTransferSize(const char *desc, size_t expectedBytes, ssize_t actualBytes); static ExitCode InitPeripheralsAndHandlers(void); static void CloseFdAndPrintError(int fd, const char *fdName); static void ClosePeripheralsAndHandlers(void); static void usleep(long usec); // File descriptors - initialized to invalid value static int i2cFd = -1; static EventLoop *eventLoop = NULL; static EventLoopTimer *sht31Timer = NULL; // I2C operation // SDO is tied to ground so the least significant bit of the address is zero. static const uint8_t sht31Address = 0x44; //static const uint8_t sht31Address = 0x45; // static const uint16_t sht31SingleHigh = 0x2400; // Termination state static volatile sig_atomic_t exitCode = ExitCode_Success; /// /// Signal handler for termination requests. This handler must be async-signal-safe. /// static void TerminationHandler(int signalNumber) { // Don't use Log_Debug here, as it is not guaranteed to be async-signal-safe. exitCode = ExitCode_TermHandler_SigTerm; } static uint8_t CalcCRC8(const uint8_t* data, int dataSize) { uint8_t crc = 0xff; for (int j = dataSize; j > 0; j--) { crc ^= *data++; for (int i = 8; i > 0; i--) { crc = (crc & 0x80) != 0 ? (uint8_t)(crc << 1 ^ POLYNOMIAL) : (uint8_t)(crc << 1); } } return crc; } /// /// Print latest data from sht31. /// static void Sht31TimerEventHandler(EventLoopTimer *timer) { static int iter = 1; if (ConsumeEventLoopTimerEvent(timer) != 0) { exitCode = ExitCode_Sht31Timer_Consume; return; } ssize_t transferredBytes; uint8_t writeData[2]; writeData[0] = (uint8_t)(sht31SingleHigh >> 8); writeData[1] = (uint8_t)(sht31SingleHigh & 0xff); // (I2C_DeviceAddress)sht31Address transferredBytes = I2CMaster_Write(i2cFd, sht31Address, sht31SingleHigh, sizeof(sht31SingleHigh)); if (!CheckTransferSize("I2CMaster_Write CMD_SINGLE_HIGH", sizeof(sht31SingleHigh), transferredBytes)) { exitCode = ExitCode_Sht31Timer_WriteTemperatureHumidity; return; } //transferredBytes = I2CMaster_Write(i2cFd, sht31Address, writeData, sizeof(writeData)); //if (!CheckTransferSize("I2CMaster_Write CMD_SINGLE_HIGH", sizeof(writeData), transferredBytes)) { // exitCode = ExitCode_Sht31Timer_WriteTemperatureHumidity; // return; //} usleep(20000); uint8_t readData[6]; transferredBytes = I2CMaster_Read(i2cFd, sht31Address, &readData, sizeof(readData)); if (!CheckTransferSize("I2CMaster_Read CMD_SINGLE_HIGH", sizeof(readData), transferredBytes)) { exitCode = ExitCode_Sht31Timer_ReadTemperatureHumidity; return; } // Read register value using AppLibs combination read and write API. //transferredBytes = // I2CMaster_WriteThenRead(i2cFd, sht31Address, &sht31SingleHigh, sizeof(sht31SingleHigh), // &readData, sizeof(readData)); //if (!CheckTransferSize("I2CMaster_WriteThenRead (WHO_AM_I)", // sizeof(whoAmIRegId) + sizeof(actualWhoAmI), transferredBytes)) { // return ExitCode_ReadWhoAmI_WriteThenRead; //} //if (transferredBytes == -1) { // exitCode = ExitCode_Sht31Timer_ReadTemperatureHumidity; // return; //} //if (transferredBytes < 0) { // Log_Debug("ERROR: seanWriteI2C: errno=%d (%s)\n", errno, strerror(errno)); // exitCode = ExitCode_Sht31Timer_ReadTemperatureHumidity; // return; //} if (readData[2] != CalcCRC8(&readData[0], 2)) { exitCode = ExitCode_Sht31Timer_ReadTemperatureHumidityCrc1; return; } if (readData[5] != CalcCRC8(&readData[3], 2)) { exitCode = ExitCode_Sht31Timer_ReadTemperatureHumidityCrc2; return; } uint16_t ST; ST = readData[0]; ST = (uint16_t)(ST << 8); ST = (uint16_t)(ST | readData[1]); uint16_t SRH; SRH = readData[3]; SRH = (uint16_t)(SRH << 8); SRH = (uint16_t)(SRH | readData[4]); float temperature = (float)ST * 175 / 0xffff - 45; float humidity = (float)SRH * 100 / 0xffff; //double g = (zRaw * 0.122) / 1000.0; //Log_Debug("INFO: %d: vertical acceleration: %.2lfg\n", iter, g); Log_Debug("INFO: %d: temperature: %.2lfg\n", iter, temperature); Log_Debug("INFO: %d: humidity : %.2lfg\n", iter, humidity); ++iter; } /// /// Checks the number of transferred bytes for I2C functions and prints an error /// message if the functions failed or if the number of bytes is different than /// expected number of bytes to be transferred. /// /// true on success, or false on failure static bool CheckTransferSize(const char *desc, size_t expectedBytes, ssize_t actualBytes) { if (actualBytes < 0) { Log_Debug("ERROR: %s: errno=%d (%s)\n", desc, errno, strerror(errno)); return false; } if (actualBytes != (ssize_t)expectedBytes) { Log_Debug("ERROR: %s: transferred %zd bytes; expected %zd\n", desc, actualBytes, expectedBytes); return false; } return true; } /// /// Set up SIGTERM termination handler, initialize peripherals, and set up event handlers. /// /// /// ExitCode_Success if all resources were allocated successfully; otherwise another /// ExitCode value which indicates the specific failure. /// static ExitCode InitPeripheralsAndHandlers(void) { struct sigaction action; memset(&action, 0, sizeof(struct sigaction)); action.sa_handler = TerminationHandler; sigaction(SIGTERM, &action, NULL); eventLoop = EventLoop_Create(); if (eventLoop == NULL) { Log_Debug("Could not create event loop.\n"); return ExitCode_Init_EventLoop; } // Print temperature and humidity data every 5-second. static const struct timespec accelReadPeriod = {.tv_sec = 5, .tv_nsec = 0}; sht31Timer = CreateEventLoopPeriodicTimer(eventLoop, &Sht31TimerEventHandler, &accelReadPeriod); if (sht31Timer == NULL) { return ExitCode_Init_Sht31Timer; } i2cFd = I2CMaster_Open(SAMPLE_LSM6DS3_I2C); if (i2cFd == -1) { Log_Debug("ERROR: I2CMaster_Open: errno=%d (%s)\n", errno, strerror(errno)); return ExitCode_Init_OpenMaster; } int result = I2CMaster_SetBusSpeed(i2cFd, I2C_BUS_SPEED_STANDARD); if (result != 0) { Log_Debug("ERROR: I2CMaster_SetBusSpeed: errno=%d (%s)\n", errno, strerror(errno)); return ExitCode_Init_SetBusSpeed; } result = I2CMaster_SetTimeout(i2cFd, 100); if (result != 0) { Log_Debug("ERROR: I2CMaster_SetTimeout: errno=%d (%s)\n", errno, strerror(errno)); return ExitCode_Init_SetTimeout; } return ExitCode_Success; } /// /// Closes a file descriptor and prints an error on failure. /// /// File descriptor to close /// File descriptor name to use in error message static void CloseFdAndPrintError(int fd, const char *fdName) { if (fd >= 0) { int result = close(fd); if (result != 0) { Log_Debug("ERROR: Could not close fd %s: %s (%d).\n", fdName, strerror(errno), errno); } } } /// /// Close peripherals and handlers. /// static void ClosePeripheralsAndHandlers(void) { DisposeEventLoopTimer(sht31Timer); EventLoop_Close(eventLoop); Log_Debug("Closing file descriptors.\n"); CloseFdAndPrintError(i2cFd, "i2c"); } static void usleep(long usec) { struct timespec req; struct timespec rem; req.tv_sec = usec / 1000000; req.tv_nsec = (usec % 1000000) * 1000; while (nanosleep(&req, &rem) != 0) { req.tv_sec = rem.tv_sec; req.tv_nsec = rem.tv_nsec; } } /// /// Main entry point for this application. /// int main(int argc, char *argv[]) { Log_Debug("I2C SHT31 temperature and humidity application starting.\n"); exitCode = InitPeripheralsAndHandlers(); // Use event loop to wait for events and trigger handlers, until an error or SIGTERM happens while (exitCode == ExitCode_Success) { EventLoop_Run_Result result = EventLoop_Run(eventLoop, -1, true); // Continue if interrupted by signal, e.g. due to breakpoint being set. if (result == EventLoop_Run_Failed && errno != EINTR) { exitCode = ExitCode_Main_EventLoopFail; } } ClosePeripheralsAndHandlers(); Log_Debug("Application exiting.\n"); return exitCode; }