Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
Hand tools and fabrication machines | ||||||
| ||||||
|
As the workers needs are evolving, office spaces are consequently required to adopt new solutions on a regular basis. The demand for unconventional spaces like breakout spaces, office pods and phone booths is difficult to track with traditional tools as breakout facilities and social spaces typically cannot be booked and they are not always accurately defined.
With the proposed solution, it is possible to monitor their usage at any given time. This way, an organization can be sure to provide employee the right type and amount of space in the most seamless and efficient way. It can also adjust cleaning and catering according to the actual usage of each meeting room. Since the way a building is being used is not constant throughout the day and throughout the week, why should cleaning and catering be fixed? Monitoring office space utilization is the best way to efficiently direct support staff into cleaning and servicing only when required.
An organization can also better forecast a floor space demand by collecting data on how it is being used by occupants. Studies have shown that office spaces are rarely utilized more than 60% of full capacity. Having empty workstations is extremely costly for the organization as that space could obviously be used in a more productive way. By looking at the occupancy data from open office environment, workstations can be managed smarter, so that the average utilization rate will increase to the desired level.
Finally, the solution can act as a “Presence Sensor”. Since these office spaces are usually equipped with monitors or desktop lamps, when the sensor detects no presence, it will switch off the electric devices and switch them on again when a person enters into the space.
There are some existing solutions of connected presence sensors on the market. If these solutions can fit small sized enterprise, it become more difficult for larger organizations. Azure Sphere has many features allowing deployment of this kind of solution at a large scale (OTA update, device group administration, advanced security).
- Hardware:
To implement this device, I used a standard PIR sensor, a Grove Relay and a modified Wall USB charger (see schematics). The Grove Relay is connected to MT3620 Grove Port, the PIR sensor is connected to a GPIO Port. When a presence is detected, a message is sent to Azure IoT Hub. The devices logs also room temperature. The user can also interact with the device throw commands to open or close the relays (may be connected to a light or a monitor). A 3d enclosure hides the circuitry and the MT3620 module. This latter is powered through the USB port of the wall charger. I derivate internal connection of the wall charger through the 2-pin Relay to act as a switch for the device connected to it.
- Software:
A dashboard was designed with Azure IoT central. It gives :
- the physical situation of the monitored space in the office plan
- the live temperature of the room
- the detected presence events of the day. With this feature, if no presence has been detected on a day, there is no need too clean the room
- the real time information of the room occupancy
The user can also interact with the connected equipment by sending 'on' and 'off' commands.
- Source Code:
For implementing the embedded software, I've used the excellent tutorial of Cabe Atwell as a starting point (https://www.element14.com/community/groups/azuresphere/blog/2019/09/30/project-part-5-getting-started-with-microsoft-azure-guide-and-a-simple-azure-sphere-sensor-project).
All modified files have been provided in the code section.
-
/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
#pragma once
#include <signal.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
// applibs_versions.h defines the API struct versions to use for applibs APIs.
#include "applibs_versions.h"
#include <applibs/log.h>
#include <applibs/networking.h>
#include <applibs/gpio.h>
#include <applibs/storage.h>
#include "epoll_timerfd_utilities.h"
// By default, this sample is targeted at the MT3620 Reference Development Board (RDB).
// This can be changed using the project property "Target Hardware Definition Directory".
// This #include imports the sample_hardware abstraction from that hardware definition.
#include "sample_hardware.h"
#include "epoll_timerfd_utilities.h"
// Azure IoT SDK
#include <iothub_client_core_common.h>
#include <iothub_device_client_ll.h>
#include <iothub_client_options.h>
#include <iothubtransportmqtt.h>
#include <iothub.h>
#include <azure_sphere_provisioning.h>
#include "minmea.h"
#include "i2c.h"
#include "mt3620_avnet_dev.h"
#include "parson.h" // used to parse Device Twin messages.
#include "GroveRelay.h"
// Global variables
float old_acceleration_mg[3];
float old_angular_rate_dps[3];
int epollFd = -1;
// Termination state
volatile sig_atomic_t terminationRequired = false;
struct minmea_sentence_rmc frame;
//static volatile sig_atomic_t terminationRequired = false;
void* relay;
// Azure IoT Hub/Central defines.
#define SCOPEID_LENGTH 20
static char scopeId[SCOPEID_LENGTH]; // ScopeId for the Azure IoT Central application, set in
// app_manifest.json, CmdArgs
static IOTHUB_DEVICE_CLIENT_LL_HANDLE iothubClientHandle = NULL;
static const int keepalivePeriodSeconds = 20;
static bool iothubAuthenticated = false;
static void SendMessageCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void *context);
static void TwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payload,
size_t payloadSize, void *userContextCallback);
static void TwinReportBoolState(const char *propertyName, bool propertyValue);
static void ReportStatusCallback(int result, void *context);
static const char *GetReasonString(IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason);
static const char *getAzureSphereProvisioningResultString(
AZURE_SPHERE_PROV_RETURN_VALUE provisioningResult);
static void SendTelemetry(const unsigned char *key, const unsigned char *value);
static void SetupAzureClient(void);
// Function to send Temperature data/telemetry
static void SendTemperature(void);
// Initialization/Cleanup
static int InitPeripheralsAndHandlers(void);
static void ClosePeripheralsAndHandlers(void);
// File descriptors - initialized to invalid value
// PIR
static int sendPirMotionGpioFd = -1;
// Relay
static bool statusRelayOn = false;
// Timer / polling
static int pirPollTimerFd = -1;
static int azureTimerFd = -1;
//static int epollFd = -1; //duplicated
// Azure IoT poll periods
static const int AzureIoTDefaultPollPeriodSeconds = 5; //5 or 120
static const int AzureIoTMinReconnectPeriodSeconds = 60; //60 or 600
static const int AzureIoTMaxReconnectPeriodSeconds = 10 * 60; // 10 * 60 or 10 * 600
static int azureIoTPollPeriodSeconds = -1;
// Pir state variables
static GPIO_Value_Type sendPirMotionState = GPIO_Value_High;
static void PirPollTimerEventHandler(EventData *eventData);
static bool IsMotionDetected(int fd, GPIO_Value_Type *oldState);
static void SendPirMotionHandler(void);
static bool deviceIsUp = false; // Orientation
static void AzureTimerEventHandler(EventData *eventData);
/// <summary>
/// Signal handler for termination requests. This handler must be async-signal-safe.
/// </summary>
static void TerminationHandler(int signalNumber)
{
// Don't use Log_Debug here, as it is not guaranteed to be async-signal-safe.
terminationRequired = true;
}
/// <summary>
/// Main entry point for this sample.
/// </summary>
int main(int argc, char *argv[])
{
Log_Debug("IoT Hub/Central Application starting.\n");
if (argc == 2) {
Log_Debug("Setting Azure Scope ID %s\n", argv[1]);
strncpy(scopeId, argv[1], SCOPEID_LENGTH);
} else {
Log_Debug("ScopeId needs to be set in the app_manifest CmdArgs\n");
return -1;
}
if (InitPeripheralsAndHandlers() != 0) {
terminationRequired = true;
}
// Main loop
while (!terminationRequired) {
if (WaitForEventAndCallHandler(epollFd) != 0) {
terminationRequired = true;
}
}
ClosePeripheralsAndHandlers();
Log_Debug("Application exiting.\n");
return 0;
}
/// <summary>
/// Pir timer event: Check the status of PIR
/// </summary>
static void PirPollTimerEventHandler(EventData *eventData)
{
if (ConsumeTimerFdEvent(pirPollTimerFd) != 0) {
terminationRequired = true;
return;
}
SendPirMotionHandler();
}
/// <summary>
/// Azure timer event: Check connection status and send telemetry
/// </summary>
static void AzureTimerEventHandler(EventData *eventData)
{
if (ConsumeTimerFdEvent(azureTimerFd) != 0) {
terminationRequired = true;
return;
}
bool isNetworkReady = false;
if (Networking_IsNetworkingReady(&isNetworkReady) != -1) {
if (isNetworkReady && !iothubAuthenticated) {
SetupAzureClient();
}
} else {
Log_Debug("Failed to get Network state\n");
}
if (iothubAuthenticated) {
SendTemperature();
IoTHubDeviceClient_LL_DoWork(iothubClientHandle);
}
}
// event handler data structures. Only the event handler field needs to be populated.
static EventData pirPollEventData = {.eventHandler = &PirPollTimerEventHandler};
static EventData azureEventData = {.eventHandler = &AzureTimerEventHandler};
/// <summary>
/// Set up SIGTERM termination handler, initialize peripherals, and set up event handlers.
/// </summary>
/// <returns>0 on success, or -1 on failure</returns>
static int InitPeripheralsAndHandlers(void)
{
struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_handler = TerminationHandler;
sigaction(SIGTERM, &action, NULL);
epollFd = CreateEpollFd();
if (epollFd < 0) {
return -1;
}
if (initI2c() == -1) {
return -1;
}
// Open PIR GPIO as input
Log_Debug("Opening SAMPLE_PIR_GPIO as input\n");
sendPirMotionGpioFd = GPIO_OpenAsInput(SAMPLE_PIR_GPIO);
if (sendPirMotionGpioFd < 0) {
Log_Debug("ERROR: Could not open PIR GPIO: %s (%d).\n", strerror(errno), errno);
return -1;
}
// Open PIR GPIO as output
relay = GroveRelay_Open(SAMPLE_RELAY_GPIO);
if (relay == NULL) {
Log_Debug("ERROR: Could not open Relay GPIO: %s (%d).\n", strerror(errno), errno);
return -1;
}
// Set up a timer to poll for PIR events.
struct timespec pirCheckPeriod = {0, 1000 * 1000};
pirPollTimerFd =
CreateTimerFdAndAddToEpoll(epollFd, &pirCheckPeriod, &pirPollEventData, EPOLLIN);
if (pirPollTimerFd < 0) {
return -1;
}
azureIoTPollPeriodSeconds = AzureIoTDefaultPollPeriodSeconds;
struct timespec azureTelemetryPeriod = {azureIoTPollPeriodSeconds, 0};
azureTimerFd =
CreateTimerFdAndAddToEpoll(epollFd, &azureTelemetryPeriod, &azureEventData, EPOLLIN);
if (pirPollTimerFd < 0) {
return -1;
}
return 0;
}
/// <summary>
/// Close peripherals and handlers.
/// </summary>
static void ClosePeripheralsAndHandlers(void)
{
Log_Debug("Closing file descriptors\n");
CloseFdAndPrintError(pirPollTimerFd, "PirTimer");
CloseFdAndPrintError(azureTimerFd, "AzureTimer");
CloseFdAndPrintError(sendPirMotionGpioFd, "SendPirMotion");
CloseFdAndPrintError(epollFd, "Epoll");
}
/// <summary>
/// Sets the IoT Hub authentication state for the app
/// The SAS Token expires which will set the authentication state
/// </summary>
static void HubConnectionStatusCallback(IOTHUB_CLIENT_CONNECTION_STATUS result,
IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason,
void *userContextCallback)
{
iothubAuthenticated = (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED);
Log_Debug("IoT Hub Authenticated: %s\n", GetReasonString(reason));
}
/// <summary>
/// Sets up the Azure IoT Hub connection (creates the iothubClientHandle)
/// When the SAS Token for a device expires the connection needs to be recreated
/// which is why this is not simply a one time call.
/// </summary>
static void SetupAzureClient(void)
{
if (iothubClientHandle != NULL)
IoTHubDeviceClient_LL_Destroy(iothubClientHandle);
AZURE_SPHERE_PROV_RETURN_VALUE provResult =
IoTHubDeviceClient_LL_CreateWithAzureSphereDeviceAuthProvisioning(scopeId, 10000,
&iothubClientHandle);
Log_Debug("IoTHubDeviceClient_LL_CreateWithAzureSphereDeviceAuthProvisioning returned '%s'.\n",
getAzureSphereProvisioningResultString(provResult));
if (provResult.result != AZURE_SPHERE_PROV_RESULT_OK) {
// If we fail to connect, reduce the polling frequency, starting at
// AzureIoTMinReconnectPeriodSeconds and with a backoff up to
// AzureIoTMaxReconnectPeriodSeconds
if (azureIoTPollPeriodSeconds == AzureIoTDefaultPollPeriodSeconds) {
azureIoTPollPeriodSeconds = AzureIoTMinReconnectPeriodSeconds;
} else {
azureIoTPollPeriodSeconds *= 2;
if (azureIoTPollPeriodSeconds > AzureIoTMaxReconnectPeriodSeconds) {
azureIoTPollPeriodSeconds = AzureIoTMaxReconnectPeriodSeconds;
}
}
struct timespec azureTelemetryPeriod = {azureIoTPollPeriodSeconds, 0};
SetTimerFdToPeriod(azureTimerFd, &azureTelemetryPeriod);
Log_Debug("ERROR: failure to create IoTHub Handle - will retry in %i seconds.\n",
azureIoTPollPeriodSeconds);
return;
}
// Successfully connected, so make sure the polling frequency is back to the default
azureIoTPollPeriodSeconds = AzureIoTDefaultPollPeriodSeconds;
struct timespec azureTelemetryPeriod = {azureIoTPollPeriodSeconds, 0};
SetTimerFdToPeriod(azureTimerFd, &azureTelemetryPeriod);
iothubAuthenticated = true;
if (IoTHubDeviceClient_LL_SetOption(iothubClientHandle, OPTION_KEEP_ALIVE,
&keepalivePeriodSeconds) != IOTHUB_CLIENT_OK) {
Log_Debug("ERROR: failure setting option \"%s\"\n", OPTION_KEEP_ALIVE);
return;
}
IoTHubDeviceClient_LL_SetDeviceTwinCallback(iothubClientHandle, TwinCallback, NULL);
IoTHubDeviceClient_LL_SetConnectionStatusCallback(iothubClientHandle,
HubConnectionStatusCallback, NULL);
}
/// <summary>
/// Callback invoked when a Device Twin update is received from IoT Hub.
/// Updates local state for 'showEvents' (bool).
/// </summary>
/// <param name="payload">contains the Device Twin JSON document (desired and reported)</param>
/// <param name="payloadSize">size of the Device Twin JSON document</param>
static void TwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payload,
size_t payloadSize, void *userContextCallback)
{
size_t nullTerminatedJsonSize = payloadSize + 1;
char *nullTerminatedJsonString = (char *)malloc(nullTerminatedJsonSize);
if (nullTerminatedJsonString == NULL) {
Log_Debug("ERROR: Could not allocate buffer for twin update payload.\n");
abort();
}
// Copy the provided buffer to a null terminated buffer.
memcpy(nullTerminatedJsonString, payload, payloadSize);
// Add the null terminator at the end.
nullTerminatedJsonString[nullTerminatedJsonSize - 1] = 0;
JSON_Value *rootProperties = NULL;
rootProperties = json_parse_string(nullTerminatedJsonString);
if (rootProperties == NULL) {
Log_Debug("WARNING: Cannot parse the string as JSON content.\n");
goto cleanup;
}
JSON_Object *rootObject = json_value_get_object(rootProperties);
JSON_Object *desiredProperties = json_object_dotget_object(rootObject, "desired");
if (desiredProperties == NULL) {
desiredProperties = rootObject;
}
// Handle the Device Twin Desired Properties here.
JSON_Object *RelayState = json_object_dotget_object(desiredProperties, "StatusRelay");
if (RelayState != NULL) {
statusRelayOn = (bool)json_object_get_boolean(RelayState, "value");
if (statusRelayOn)
GroveRelay_On(relay);
else
GroveRelay_Off(relay);
TwinReportBoolState("StatusRelay", statusRelayOn);
}
cleanup:
// Release the allocated memory.
json_value_free(rootProperties);
free(nullTerminatedJsonString);
}
/// <summary>
/// Converts the IoT Hub connection status reason to a string.
/// </summary>
static const char *GetReasonString(IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason)
{
static char *reasonString = "unknown reason";
switch (reason) {
case IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN:
reasonString = "IOTHUB_CLIENT_CONNECTION_EXPIRED_SAS_TOKEN";
break;
case IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED:
reasonString = "IOTHUB_CLIENT_CONNECTION_DEVICE_DISABLED";
break;
case IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL:
reasonString = "IOTHUB_CLIENT_CONNECTION_BAD_CREDENTIAL";
break;
case IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED:
reasonString = "IOTHUB_CLIENT_CONNECTION_RETRY_EXPIRED";
break;
case IOTHUB_CLIENT_CONNECTION_NO_NETWORK:
reasonString = "IOTHUB_CLIENT_CONNECTION_NO_NETWORK";
break;
case IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR:
reasonString = "IOTHUB_CLIENT_CONNECTION_COMMUNICATION_ERROR";
break;
case IOTHUB_CLIENT_CONNECTION_OK:
reasonString = "IOTHUB_CLIENT_CONNECTION_OK";
break;
}
return reasonString;
}
/// <summary>
/// Converts AZURE_SPHERE_PROV_RETURN_VALUE to a string.
/// </summary>
static const char *getAzureSphereProvisioningResultString(
AZURE_SPHERE_PROV_RETURN_VALUE provisioningResult)
{
switch (provisioningResult.result) {
case AZURE_SPHERE_PROV_RESULT_OK:
return "AZURE_SPHERE_PROV_RESULT_OK";
case AZURE_SPHERE_PROV_RESULT_INVALID_PARAM:
return "AZURE_SPHERE_PROV_RESULT_INVALID_PARAM";
case AZURE_SPHERE_PROV_RESULT_NETWORK_NOT_READY:
return "AZURE_SPHERE_PROV_RESULT_NETWORK_NOT_READY";
case AZURE_SPHERE_PROV_RESULT_DEVICEAUTH_NOT_READY:
return "AZURE_SPHERE_PROV_RESULT_DEVICEAUTH_NOT_READY";
case AZURE_SPHERE_PROV_RESULT_PROV_DEVICE_ERROR:
return "AZURE_SPHERE_PROV_RESULT_PROV_DEVICE_ERROR";
case AZURE_SPHERE_PROV_RESULT_GENERIC_ERROR:
return "AZURE_SPHERE_PROV_RESULT_GENERIC_ERROR";
default:
return "UNKNOWN_RETURN_VALUE";
}
}
/// <summary>
/// Sends telemetry to IoT Hub
/// </summary>
/// <param name="key">The telemetry item to update</param>
/// <param name="value">new telemetry value</param>
static void SendTelemetry(const unsigned char *key, const unsigned char *value)
{
static char eventBuffer[100] = {0};
static const char *EventMsgTemplate = "{ \"%s\": \"%s\" }";
int len = snprintf(eventBuffer, sizeof(eventBuffer), EventMsgTemplate, key, value);
if (len < 0)
return;
Log_Debug("Sending IoT Hub Message: %s\n", eventBuffer);
IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromString(eventBuffer);
if (messageHandle == 0) {
Log_Debug("WARNING: unable to create a new IoTHubMessage\n");
return;
}
if (IoTHubDeviceClient_LL_SendEventAsync(iothubClientHandle, messageHandle, SendMessageCallback,
/*&callback_param*/ 0) != IOTHUB_CLIENT_OK) {
Log_Debug("WARNING: failed to hand over the message to IoTHubClient\n");
} else {
Log_Debug("INFO: IoTHubClient accepted the message for delivery\n");
}
IoTHubMessage_Destroy(messageHandle);
}
/// <summary>
/// Callback confirming message delivered to IoT Hub.
/// </summary>
/// <param name="result">Message delivery status</param>
/// <param name="context">User specified context</param>
static void SendMessageCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void *context)
{
Log_Debug("INFO: Message received by IoT Hub. Result is: %d\n", result);
}
/// <summary>
/// Creates and enqueues a report containing the name and value pair of a Device Twin reported
/// property. The report is not sent immediately, but it is sent on the next invocation of
/// IoTHubDeviceClient_LL_DoWork().
/// </summary>
/// <param name="propertyName">the IoT Hub Device Twin property name</param>
/// <param name="propertyValue">the IoT Hub Device Twin property value</param>
static void TwinReportBoolState(const char *propertyName, bool propertyValue)
{
if (iothubClientHandle == NULL) {
Log_Debug("ERROR: client not initialized\n");
} else {
static char reportedPropertiesString[30] = {0};
int len = snprintf(reportedPropertiesString, 30, "{\"%s\":%s}", propertyName,
(propertyValue == true ? "true" : "false"));
if (len < 0)
return;
if (IoTHubDeviceClient_LL_SendReportedState(
iothubClientHandle, (unsigned char *)reportedPropertiesString,
strlen(reportedPropertiesString), ReportStatusCallback, 0) != IOTHUB_CLIENT_OK) {
Log_Debug("ERROR: failed to set reported state for '%s'.\n", propertyName);
} else {
Log_Debug("INFO: Reported state for '%s' to value '%s'.\n", propertyName,
(propertyValue == true ? "true" : "false"));
}
}
}
/// <summary>
/// Callback invoked when the Device Twin reported properties are accepted by IoT Hub.
/// </summary>
static void ReportStatusCallback(int result, void *context)
{
Log_Debug("INFO: Device Twin reported properties update result: HTTP status code %d\n", result);
}
/// <summary>
/// Get Temperature and sends to IoT Hub.
/// </summary>
void SendTemperature(void)
{
Log_Debug("Temperature: %4.2f\r\n", lsm6dsoTemperature);
Log_Debug("Temperature1: %4.2f\r\n", lps22hhTemperature);
bool wyslij = 0;
if (lsm6dsoTemperature > -20 && lsm6dsoTemperature < 70)
wyslij = 1;
if (wyslij == 1)
{
char tempBuffer[20];
snprintf(tempBuffer, 20, "%5.5f", lsm6dsoTemperature);
SendTelemetry("temperature", (char*)tempBuffer);
snprintf(tempBuffer, 20, "%5.5f", lps22hhTemperature);
SendTelemetry("temperature1", (char*)tempBuffer);
}
}
/// <summary>
/// Check whether a motion is detected
/// </summary>
/// <param name="fd">The pir file descriptor</param>
/// <param name="oldState">Old state of the pir</param>
/// <returns>true if pressed, false otherwise</returns>
static bool IsMotionDetected(int fd, GPIO_Value_Type *oldState)
{
bool isMotionDetected = false;
GPIO_Value_Type newState;
int result = GPIO_GetValue(fd, &newState);
if (result != 0) {
Log_Debug("ERROR: Could not read pir GPIO: %s (%d).\n", strerror(errno), errno);
terminationRequired = true;
} else {
// motion is detected if it is low and different than last known state.
isMotionDetected = (newState != *oldState) && (newState == GPIO_Value_Low);
*oldState = newState;
}
return isMotionDetected;
}
/// <summary>
/// Detecting motion will:
/// Send a 'Motion Detected' event to Azure IoT Central
/// </summary>
static void SendPirMotionHandler(void)
{
if (IsMotionDetected(sendPirMotionGpioFd, &sendPirMotionState)) {
SendTelemetry("MotionDetected", "True");
}
}
#include "GroveRelay.h"
#include <stdlib.h>
//#include "../../applibs_versions.h"
#include <applibs/gpio.h>
typedef struct
{
int PinFd;
}
GroveRelayInstance;
void* GroveRelay_Open(GPIO_Id pinId)
{
GroveRelayInstance* this = (GroveRelayInstance*)malloc(sizeof(GroveRelayInstance));
this->PinFd = GPIO_OpenAsOutput(pinId, GPIO_OutputMode_PushPull, GPIO_Value_Low);
return this;
}
void GroveRelay_On(void* inst)
{
GroveRelayInstance* this = (GroveRelayInstance*)inst;
GPIO_SetValue(this->PinFd, GPIO_Value_High);
}
void GroveRelay_Off(void* inst)
{
GroveRelayInstance* this = (GroveRelayInstance*)inst;
GPIO_SetValue(this->PinFd, GPIO_Value_Low);
}
#pragma once
//#include "../../applibs_versions.h"
#include <applibs/gpio.h>
void* GroveRelay_Open(GPIO_Id pinId);
void GroveRelay_On(void* inst);
void GroveRelay_Off(void* inst);
/*
* Some of the code in this file was copied from ST Micro. Below is their required information.
*
* @attention
*
* <h2><center>© COPYRIGHT(c) 2018 STMicroelectronics</center></h2>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of STMicroelectronics nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
// applibs_versions.h defines the API struct versions to use for applibs APIs.
#include "applibs_versions.h"
#include <applibs/log.h>
#include <applibs/i2c.h>
#include "mt3620_avnet_dev.h"
#include "deviceTwin.h"
#include "azure_iot_utilities.h"
#include "build_options.h"
#include "i2c.h"
#include "lsm6dso_reg.h"
#include "lps22hh_reg.h"
/* Private variables ---------------------------------------------------------*/
static axis3bit16_t data_raw_acceleration;
static axis3bit16_t data_raw_angular_rate;
static axis3bit16_t raw_angular_rate_calibration;
static axis1bit32_t data_raw_pressure;
static axis1bit16_t data_raw_temperature;
//// static float acceleration_mg[3];
//// static float angular_rate_dps[3];
static float lsm6dsoTemperature_degC;
static float pressure_hPa;
static float lps22hhTemperature_degC;
static uint8_t whoamI, rst;
static int accelTimerFd = -1;
const uint8_t lsm6dsOAddress = LSM6DSO_ADDRESS; // Addr = 0x6A
lsm6dso_ctx_t dev_ctx;
lps22hh_ctx_t pressure_ctx;
//Extern variables
int i2cFd = -1;
extern int epollFd;
extern volatile sig_atomic_t terminationRequired;
//Private functions
// Routines to read/write to the LSM6DSO device
static int32_t platform_write(int *fD, uint8_t reg, uint8_t *bufp, uint16_t len);
static int32_t platform_read(int *fD, uint8_t reg, uint8_t *bufp, uint16_t len);
// Routines to read/write to the LPS22HH device connected to the LSM6DSO sensor hub
static int32_t lsm6dso_write_lps22hh_cx(void* ctx, uint8_t reg, uint8_t* data, uint16_t len);
static int32_t lsm6dso_read_lps22hh_cx(void* ctx, uint8_t reg, uint8_t* data, uint16_t len);
/// <summary>
/// Sleep for delayTime ms
/// </summary>
void HAL_Delay(int delayTime) {
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = delayTime * 10000;
nanosleep(&ts, NULL);
}
/// <summary>
/// Print latest data from on-board sensors.
/// </summary>
void AccelTimerEventHandler(EventData *eventData)
{
uint8_t reg;
lps22hh_reg_t lps22hhReg;
#if (defined(IOT_CENTRAL_APPLICATION) || defined(IOT_HUB_APPLICATION))
static bool firstPass = true;
#endif
// Consume the event. If we don't do this we'll come right back
// to process the same event again
if (ConsumeTimerFdEvent(accelTimerFd) != 0) {
terminationRequired = true;
return;
}
// Read the sensors on the lsm6dso device
//Read output only if new xl value is available
lsm6dso_xl_flag_data_ready_get(&dev_ctx, ®);
if (reg)
{
// Read acceleration field data
memset(data_raw_acceleration.u8bit, 0x00, 3 * sizeof(int16_t));
lsm6dso_acceleration_raw_get(&dev_ctx, data_raw_acceleration.u8bit);
acceleration_mg[0] = lsm6dso_from_fs4_to_mg(data_raw_acceleration.i16bit[0]);
acceleration_mg[1] = lsm6dso_from_fs4_to_mg(data_raw_acceleration.i16bit[1]);
acceleration_mg[2] = lsm6dso_from_fs4_to_mg(data_raw_acceleration.i16bit[2]);
//// Log_Debug("\nLSM6DSO: Acceleration [mg] : %.4lf, %.4lf, %.4lf\n",
//// acceleration_mg[0], acceleration_mg[1], acceleration_mg[2]);
}
lsm6dso_gy_flag_data_ready_get(&dev_ctx, ®);
if (reg)
{
// Read angular rate field data
memset(data_raw_angular_rate.u8bit, 0x00, 3 * sizeof(int16_t));
lsm6dso_angular_rate_raw_get(&dev_ctx, data_raw_angular_rate.u8bit);
// Before we store the mdps values subtract the calibration data we captured at startup.
angular_rate_dps[0] = (lsm6dso_from_fs2000_to_mdps(data_raw_angular_rate.i16bit[0] - raw_angular_rate_calibration.i16bit[0])) / 1000.0;
angular_rate_dps[1] = (lsm6dso_from_fs2000_to_mdps(data_raw_angular_rate.i16bit[1] - raw_angular_rate_calibration.i16bit[1])) / 1000.0;
angular_rate_dps[2] = (lsm6dso_from_fs2000_to_mdps(data_raw_angular_rate.i16bit[2] - raw_angular_rate_calibration.i16bit[2])) / 1000.0;
//// Log_Debug("LSM6DSO: Angular rate [dps] : %4.2f, %4.2f, %4.2f\r\n",
//// angular_rate_dps[0], angular_rate_dps[1], angular_rate_dps[2]);
}
lsm6dso_temp_flag_data_ready_get(&dev_ctx, ®);
if (reg)
{
// Read temperature data
memset(data_raw_temperature.u8bit, 0x00, sizeof(int16_t));
lsm6dso_temperature_raw_get(&dev_ctx, data_raw_temperature.u8bit);
lsm6dsoTemperature_degC = lsm6dso_from_lsb_to_celsius(data_raw_temperature.i16bit);
lsm6dsoTemperature = lsm6dsoTemperature_degC;
////MF Log_Debug("LSM6DSO: Temperature [degC]: %.2f\r\n", lsm6dsoTemperature_degC);
}
// Read the sensors on the lsm6dso device
lps22hh_read_reg(&pressure_ctx, LPS22HH_STATUS, (uint8_t *)&lps22hhReg, 1);
//Read output only if new value is available
if ((lps22hhReg.status.p_da == 1) && (lps22hhReg.status.t_da == 1))
{
memset(data_raw_pressure.u8bit, 0x00, sizeof(int32_t));
lps22hh_pressure_raw_get(&pressure_ctx, data_raw_pressure.u8bit);
pressure_hPa = lps22hh_from_lsb_to_hpa(data_raw_pressure.i32bit);
memset(data_raw_temperature.u8bit, 0x00, sizeof(int16_t));
lps22hh_temperature_raw_get(&pressure_ctx, data_raw_temperature.u8bit);
lps22hhTemperature_degC = lps22hh_from_lsb_to_celsius(data_raw_temperature.i16bit);
lps22hhTemperature = lps22hhTemperature_degC;
////MF Log_Debug("LPS22HH: Pressure [hPa] : %.2f\r\n", pressure_hPa);
////MF Log_Debug("LPS22HH: Temperature [degC]: %.2f\r\n", lps22hhTemperature_degC);
}
#if (defined(IOT_CENTRAL_APPLICATION) || defined(IOT_HUB_APPLICATION))
// We've seen that the first read of the Accelerometer data is garbage. If this is the first pass
// reading data, don't report it to Azure. Since we're graphing data in Azure, this data point
// will skew the data.
if (!firstPass) {
// Allocate memory for a telemetry message to Azure
char *pjsonBuffer = (char *)malloc(JSON_BUFFER_SIZE);
if (pjsonBuffer == NULL) {
Log_Debug("ERROR: not enough memory to send telemetry");
}
// construct the telemetry message
snprintf(pjsonBuffer, JSON_BUFFER_SIZE, "{\"gX\":\"%.4lf\", \"gY\":\"%.4lf\", \"gZ\":\"%.4lf\", \"pressure\": \"%.2f\", \"aX\": \"%4.2f\", \"aY\": \"%4.2f\", \"aZ\": \"%4.2f\"}",
acceleration_mg[0], acceleration_mg[1], acceleration_mg[2], pressure_hPa, angular_rate_dps[0], angular_rate_dps[1], angular_rate_dps[2]);
Log_Debug("\n[Info] Sending telemetry: %s\n", pjsonBuffer);
AzureIoT_SendMessage(pjsonBuffer);
free(pjsonBuffer);
}
firstPass = false;
#endif
}
/// <summary>
/// Initializes the I2C interface.
/// </summary>
/// <returns>0 on success, or -1 on failure</returns>
int initI2c(void) {
// Begin MT3620 I2C init
i2cFd = I2CMaster_Open(MT3620_RDB_HEADER4_ISU2_I2C);
if (i2cFd < 0) {
Log_Debug("ERROR: I2CMaster_Open: errno=%d (%s)\n", errno, strerror(errno));
return -1;
}
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 -1;
}
result = I2CMaster_SetTimeout(i2cFd, 100);
if (result != 0) {
Log_Debug("ERROR: I2CMaster_SetTimeout: errno=%d (%s)\n", errno, strerror(errno));
return -1;
}
// Start lsm6dso specific init
// Initialize lsm6dso mems driver interface
dev_ctx.write_reg = platform_write;
dev_ctx.read_reg = platform_read;
dev_ctx.handle = &i2cFd;
// Check device ID
lsm6dso_device_id_get(&dev_ctx, &whoamI);
if (whoamI != LSM6DSO_ID) {
Log_Debug("LSM6DSO not found!\n");
return -1;
}
else {
Log_Debug("LSM6DSO Found!\n");
}
// Restore default configuration
lsm6dso_reset_set(&dev_ctx, PROPERTY_ENABLE);
do {
lsm6dso_reset_get(&dev_ctx, &rst);
} while (rst);
// Disable I3C interface
lsm6dso_i3c_disable_set(&dev_ctx, LSM6DSO_I3C_DISABLE);
// Enable Block Data Update
lsm6dso_block_data_update_set(&dev_ctx, PROPERTY_ENABLE);
// Set Output Data Rate
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_12Hz5);
lsm6dso_gy_data_rate_set(&dev_ctx, LSM6DSO_GY_ODR_12Hz5);
// Set full scale
lsm6dso_xl_full_scale_set(&dev_ctx, LSM6DSO_4g);
lsm6dso_gy_full_scale_set(&dev_ctx, LSM6DSO_2000dps);
// Configure filtering chain(No aux interface)
// Accelerometer - LPF1 + LPF2 path
lsm6dso_xl_hp_path_on_out_set(&dev_ctx, LSM6DSO_LP_ODR_DIV_100);
lsm6dso_xl_filter_lp2_set(&dev_ctx, PROPERTY_ENABLE);
// lps22hh specific init
// Initialize lps22hh mems driver interface
pressure_ctx.read_reg = lsm6dso_read_lps22hh_cx;
pressure_ctx.write_reg = lsm6dso_write_lps22hh_cx;
pressure_ctx.handle = &i2cFd;
bool lps22hhDetected = false;
int failCount = 10;
while (!lps22hhDetected) {
// Enable pull up on master I2C interface.
lsm6dso_sh_pin_mode_set(&dev_ctx, LSM6DSO_INTERNAL_PULL_UP);
// Check if LPS22HH is connected to Sensor Hub
lps22hh_device_id_get(&pressure_ctx, &whoamI);
if (whoamI != LPS22HH_ID) {
Log_Debug("LPS22HH not found!\n");
}
else {
lps22hhDetected = true;
Log_Debug("LPS22HH Found!\n");
}
// Restore the default configuration
lps22hh_reset_set(&pressure_ctx, PROPERTY_ENABLE);
do {
lps22hh_reset_get(&pressure_ctx, &rst);
} while (rst);
// Enable Block Data Update
lps22hh_block_data_update_set(&pressure_ctx, PROPERTY_ENABLE);
//Set Output Data Rate
lps22hh_data_rate_set(&pressure_ctx, LPS22HH_10_Hz_LOW_NOISE);
// If we failed to detect the lps22hh device, then pause before trying again.
if (!lps22hhDetected) {
HAL_Delay(100);
}
if (failCount-- == 0) {
Log_Debug("Failed to read LSM22HH device ID, exiting\n");
return -1;
}
}
// Read the raw angular rate data from the device to use as offsets. We're making the assumption that the device
// is stationary.
uint8_t reg;
Log_Debug("LSM6DSO: Calibrating angular rate . . .\n");
Log_Debug("LSM6DSO: Please make sure the device is stationary.\n");
do {
// Read the calibration values
lsm6dso_gy_flag_data_ready_get(&dev_ctx, ®);
if (reg)
{
// Read angular rate field data to use for calibration offsets
memset(data_raw_angular_rate.u8bit, 0x00, 3 * sizeof(int16_t));
lsm6dso_angular_rate_raw_get(&dev_ctx, raw_angular_rate_calibration.u8bit);
}
// Read the angular data rate again and verify that after applying the calibration, we have 0 angular rate in all directions
lsm6dso_gy_flag_data_ready_get(&dev_ctx, ®);
if (reg)
{
// Read angular rate field data
memset(data_raw_angular_rate.u8bit, 0x00, 3 * sizeof(int16_t));
lsm6dso_angular_rate_raw_get(&dev_ctx, data_raw_angular_rate.u8bit);
// Before we store the mdps values subtract the calibration data we captured at startup.
angular_rate_dps[0] = lsm6dso_from_fs2000_to_mdps(data_raw_angular_rate.i16bit[0] - raw_angular_rate_calibration.i16bit[0]);
angular_rate_dps[1] = lsm6dso_from_fs2000_to_mdps(data_raw_angular_rate.i16bit[1] - raw_angular_rate_calibration.i16bit[1]);
angular_rate_dps[2] = lsm6dso_from_fs2000_to_mdps(data_raw_angular_rate.i16bit[2] - raw_angular_rate_calibration.i16bit[2]);
}
// If the angular values after applying the offset are not zero, then do it again!
} while (angular_rate_dps[0] == angular_rate_dps[1] == angular_rate_dps[2] == 0.0);
Log_Debug("LSM6DSO: Calibrating angular rate complete!\n");
// Init the epoll interface to periodically run the AccelTimerEventHandler routine where we read the sensors
// Define the period in the build_options.h file
struct timespec accelReadPeriod = { .tv_sec = ACCEL_READ_PERIOD_SECONDS,.tv_nsec = ACCEL_READ_PERIOD_NANO_SECONDS };
// event handler data structures. Only the event handler field needs to be populated.
static EventData accelEventData = { .eventHandler = &AccelTimerEventHandler };
accelTimerFd = CreateTimerFdAndAddToEpoll(epollFd, &accelReadPeriod, &accelEventData, EPOLLIN);
if (accelTimerFd < 0) {
return -1;
}
return 0;
}
/// <summary>
/// Closes the I2C interface File Descriptors.
/// </summary>
void closeI2c(void) {
CloseFdAndPrintError(i2cFd, "i2c");
CloseFdAndPrintError(accelTimerFd, "accelTimer");
}
/// <summary>
/// Writes data to the lsm6dso i2c device
/// </summary>
/// <returns>0</returns>
static int32_t platform_write(int *fD, uint8_t reg, uint8_t *bufp,
uint16_t len)
{
#ifdef ENABLE_READ_WRITE_DEBUG
Log_Debug("platform_write()\n");
Log_Debug("reg: %0x\n", reg);
Log_Debug("len: %0x\n", len);
Log_Debug("bufp contents: ");
for (int i = 0; i < len; i++) {
Log_Debug("%0x: ", bufp[i]);
}
Log_Debug("\n");
#endif
// Construct a new command buffer that contains the register to write to, then the data to write
uint8_t cmdBuffer[len + 1];
cmdBuffer[0] = reg;
for (int i = 0; i < len; i++) {
cmdBuffer[i + 1] = bufp[i];
}
#ifdef ENABLE_READ_WRITE_DEBUG
Log_Debug("cmdBuffer contents: ");
for (int i = 0; i < len + 1; i++) {
Log_Debug("%0x: ", cmdBuffer[i]);
}
Log_Debug("\n");
#endif
// Write the data to the device
int32_t retVal = I2CMaster_Write(*fD, lsm6dsOAddress, cmdBuffer, (size_t)len + 1);
if (retVal < 0) {
Log_Debug("ERROR: platform_write: errno=%d (%s)\n", errno, strerror(errno));
return -1;
}
#ifdef ENABLE_READ_WRITE_DEBUG
Log_Debug("Wrote %d bytes to device.\n\n", retVal);
#endif
return 0;
}
/// <summary>
/// Reads generic device register from the i2c interface
/// </summary>
/// <returns>0</returns>
/*
* @brief Read generic device register (platform dependent)
*
* @param handle customizable argument. In this examples is used in
* order to select the correct sensor bus handler.
* @param reg register to read
* @param bufp pointer to buffer that store the data read
* @param len number of consecutive register to read
*
*/
static int32_t platform_read(int *fD, uint8_t reg, uint8_t *bufp,
uint16_t len)
{
#ifdef ENABLE_READ_WRITE_DEBUG
Log_Debug("platform_read()\n");
Log_Debug("reg: %0x\n", reg);
Log_Debug("len: %d\n", len);
;
#endif
// Set the register address to read
int32_t retVal = I2CMaster_Write(i2cFd, lsm6dsOAddress, ®, 1);
if (retVal < 0) {
Log_Debug("ERROR: platform_read(write step): errno=%d (%s)\n", errno, strerror(errno));
return -1;
}
// Read the data into the provided buffer
retVal = I2CMaster_Read(i2cFd, lsm6dsOAddress, bufp, len);
if (retVal < 0) {
Log_Debug("ERROR: platform_read(read step): errno=%d (%s)\n", errno, strerror(errno));
return -1;
}
#ifdef ENABLE_READ_WRITE_DEBUG
Log_Debug("Read returned: ");
for (int i = 0; i < len; i++) {
Log_Debug("%0x: ", bufp[i]);
}
Log_Debug("\n\n");
#endif
return 0;
}
/*
* @brief Write lsm2mdl device register (used by configuration functions)
*
* @param handle customizable argument. In this examples is used in
* order to select the correct sensor bus handler.
* @param reg register to write
* @param bufp pointer to data to write in register reg
* @param len number of consecutive register to write
*
*/
static int32_t lsm6dso_write_lps22hh_cx(void* ctx, uint8_t reg, uint8_t* data,
uint16_t len)
{
axis3bit16_t data_raw_acceleration;
int32_t ret;
uint8_t drdy;
lsm6dso_status_master_t master_status;
lsm6dso_sh_cfg_write_t sh_cfg_write;
// Configure Sensor Hub to write to the LPS22HH, and send the write data
sh_cfg_write.slv0_add = (LPS22HH_I2C_ADD_L & 0xFEU) >> 1; // 7bit I2C address
sh_cfg_write.slv0_subadd = reg,
sh_cfg_write.slv0_data = *data,
ret = lsm6dso_sh_cfg_write(&dev_ctx, &sh_cfg_write);
/* Disable accelerometer. */
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_OFF);
/* Enable I2C Master. */
lsm6dso_sh_master_set(&dev_ctx, PROPERTY_ENABLE);
/* Enable accelerometer to trigger Sensor Hub operation. */
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_104Hz);
/* Wait Sensor Hub operation flag set. */
lsm6dso_acceleration_raw_get(&dev_ctx, data_raw_acceleration.u8bit);
do
{
HAL_Delay(20);
lsm6dso_xl_flag_data_ready_get(&dev_ctx, &drdy);
} while (!drdy);
do
{
HAL_Delay(20);
lsm6dso_sh_status_get(&dev_ctx, &master_status);
} while (!master_status.sens_hub_endop);
/* Disable I2C master and XL (trigger). */
lsm6dso_sh_master_set(&dev_ctx, PROPERTY_DISABLE);
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_OFF);
return ret;
}
/*
* @brief Read lsm2mdl device register (used by configuration functions)
*
* @param handle customizable argument. In this examples is used in
* order to select the correct sensor bus handler.
* @param reg register to read
* @param bufp pointer to buffer that store the data read
* @param len number of consecutive register to read
*
*/
static int32_t lsm6dso_read_lps22hh_cx(void* ctx, uint8_t reg, uint8_t* data, uint16_t len)
{
lsm6dso_sh_cfg_read_t sh_cfg_read;
axis3bit16_t data_raw_acceleration;
int32_t ret;
uint8_t drdy;
lsm6dso_status_master_t master_status;
/* Disable accelerometer. */
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_OFF);
// For each byte we need to read from the lps22hh
for (int i = 0; i < len; i++) {
/* Configure Sensor Hub to read LPS22HH. */
sh_cfg_read.slv_add = (LPS22HH_I2C_ADD_L &0xFEU) >> 1; /* 7bit I2C address */
sh_cfg_read.slv_subadd = reg+i;
sh_cfg_read.slv_len = 1;
// Call the command to read the data from the sensor hub.
// This data will be read from the device connected to the
// sensor hub, and saved into a register for us to read.
ret = lsm6dso_sh_slv0_cfg_read(&dev_ctx, &sh_cfg_read);
lsm6dso_sh_slave_connected_set(&dev_ctx, LSM6DSO_SLV_0);
/* Enable I2C Master and I2C master. */
lsm6dso_sh_master_set(&dev_ctx, PROPERTY_ENABLE);
/* Enable accelerometer to trigger Sensor Hub operation. */
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_104Hz);
/* Wait Sensor Hub operation flag set. */
lsm6dso_acceleration_raw_get(&dev_ctx, data_raw_acceleration.u8bit);
do {
HAL_Delay(20);
lsm6dso_xl_flag_data_ready_get(&dev_ctx, &drdy);
} while (!drdy);
do {
HAL_Delay(20);
lsm6dso_sh_status_get(&dev_ctx, &master_status);
} while (!master_status.sens_hub_endop);
/* Disable I2C master and XL(trigger). */
lsm6dso_sh_master_set(&dev_ctx, PROPERTY_DISABLE);
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_OFF);
// Read the data from the device. The call below reads
// all 18 sensor hub data. We just need the data from
// sensor hub 1, so copy that into our data array.
uint8_t buffer[18];
lsm6dso_sh_read_data_raw_get(&dev_ctx, buffer);
data[i] = buffer[1];
#ifdef ENABLE_READ_WRITE_DEBUG
Log_Debug("Read %d bytes: ", len);
for (int i = 0; i < len; i++) {
Log_Debug("[%0x] ", data[i]);
}
Log_Debug("\n", len);
#endif
}
/* Re-enable accelerometer */
lsm6dso_xl_data_rate_set(&dev_ctx, LSM6DSO_XL_ODR_104Hz);
return ret;
}
#pragma once
#include <stdbool.h>
#include "epoll_timerfd_utilities.h"
#define LSM6DSO_ID 0x6C // register value
#define LSM6DSO_ADDRESS 0x6A // I2C Address
volatile float acceleration_mg[3];
volatile float angular_rate_dps[3];
volatile float lsm6dsoTemperature;
volatile float lps22hhTemperature;
int initI2c(void);
void closeI2c(void);
Comments