-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
691 lines (602 loc) · 23.1 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
/* Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License. */
// This sample C application demonstrates how to interface Azure Sphere devices with Azure IoT
// services. Using the Azure IoT SDK C APIs, it shows how to:
// 1. Use Device Provisioning Service (DPS) to connect to Azure IoT Hub/Central with
// certificate-based authentication
// receive a desired LED state from Azure IoT Hub/Central
// 3. Use Direct Methods to receive a "Trigger Alarm" command from Azure IoT Hub/Central
// You will need to provide four pieces of information to use this application, all of which are set
// in the app_manifest.json.
// 1. The Scope Id for the Device Provisioning Service - DPS (set in 'CmdArgs')
// 2. The Tenant Id obtained from 'azsphere tenant show-selected' (set in 'DeviceAuthentication')
// 3. The Azure DPS Global endpoint address 'global.azure-devices-provisioning.net'
// (set in 'AllowedConnections')
// 4. The Azure IoT Hub Endpoint address(es) that DPS is configured to direct this device to (set in
// 'AllowedConnections')
#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 <applibs/eventloop.h>
// By default, this sample 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. See
// https://github.com/Azure/azure-sphere-samples/tree/master/Hardware for more details.
//
// This #include imports the sample_hardware abstraction from that hardware definition.
#include <hw/avnet_aesms_mt3620.h>
#include "eventloop_timer_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 "AS5600/AS5600.h"
#include "vl53l0x/vl53l0x.h"
/// <summary>
/// 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.
/// </summary>
typedef enum
{
ExitCode_Success = 0,
ExitCode_TermHandler_SigTerm = 1,
ExitCode_Main_EventLoopFail = 2,
_Consume = 3,
ExitCode_AzureTimer_Consume = 4,
ExitCode_Init_EventLoop = 5,
ExitCode_Init_TwinStatusLed = 8,
ExitCode_Init_AzureTimer = 10,
} ExitCode;
static volatile sig_atomic_t exitCode = ExitCode_Success;
#include "parson.h" // used to parse Device Twin messages.
// Azure IoT defines.
static char *scopeId; // ScopeId for DPS
static IOTHUB_DEVICE_CLIENT_LL_HANDLE iothubClientHandle = NULL;
static const int keepalivePeriodSeconds = 20;
static bool iothubAuthenticated = false;
// Function declarations
static void SendEventCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void *context);
static void DeviceTwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payload,
size_t payloadSize, void *userContextCallback);
static void TwinReportState(const char *jsonState);
static void ReportedStateCallback(int result, void *context);
static int DeviceMethodCallback(const char *methodName, const unsigned char *payload,
size_t payloadSize, unsigned char **response, size_t *responseSize,
void *userContextCallback);
static const char *GetReasonString(IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason);
static const char *GetAzureSphereProvisioningResultString(
AZURE_SPHERE_PROV_RETURN_VALUE provisioningResult);
static void SendTelemetry(const char *jsonMessage);
static void SetupAzureClient(void);
static void UpdateSensorData(void);
static void SendRotationData(void);
static void AzureTimerEventHandler(EventLoopTimer *timer);
// Initialization/Cleanup
static ExitCode InitPeripheralsAndHandlers(void);
static void CloseFdAndPrintError(int fd, const char *fdName);
static void ClosePeripheralsAndHandlers(void);
// LED
static int deviceTwinStatusLedGpioFd = -1;
// Timer / polling
static EventLoop *eventLoop = NULL;
static EventLoopTimer *azureTimer = NULL;
// Azure IoT poll periods
static const int AzureIoTDefaultPollPeriodSeconds = 1; // poll azure iot every second
static const int AzureIoTPollPeriodsPerTelemetry = 1; // only send telemetry 1/5 of polls
static const int AzureIoTMinReconnectPeriodSeconds = 60; // back off when reconnecting
static const int AzureIoTMaxReconnectPeriodSeconds = 10 * 60; // back off limit
static int azureIoTPollPeriodSeconds = -1;
static int telemetryCount = 0;
static bool statusLedOn = false;
enum RollDirection
{
Forward = 0,
Backward = 1
} rollDirection;
static enum RollDirection previousDirection;
static float rotationHistory[4];
static float delta = 0;
static float lastDegrees = 0;
static int fullRotationCount = 0;
static bool syncRequired = true;
/// <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.
exitCode = ExitCode_TermHandler_SigTerm;
}
/// <summary>
/// Main entry point for this sample.
/// </summary>
int main(int argc, char *argv[])
{
Log_Debug("SmartTp Starting.\n");
AS5600_open(AVNET_AESMS_ISU2_I2C);
// 3 SCL GPIO37_MOSI2_RTS2_SCL2
// 4 SDA GPIO38_MISO2_RXD2_SDA2
bool isNetworkingReady = false;
if ((Networking_IsNetworkingReady(&isNetworkingReady) == -1) || !isNetworkingReady)
{
Log_Debug("WARNING: Network is not ready. Device cannot connect until network is ready.\n");
}
if (argc > 1)
{
scopeId = argv[1];
Log_Debug("Using Azure IoT DPS Scope ID %s\n", scopeId);
}
else
{
Log_Debug("ScopeId needs to be set in the app_manifest CmdArgs\n");
return -1;
}
exitCode = InitPeripheralsAndHandlers();
// Main loop
while (exitCode == ExitCode_Success)
{
// check if rotation is greater or less than last check
// did it complete a full rotation?
UpdateSensorData();
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;
}
/// <summary>
/// Azure timer event: Check connection status and send telemetry
/// </summary>
static void AzureTimerEventHandler(EventLoopTimer *timer)
{
if (ConsumeEventLoopTimerEvent(timer) != 0)
{
exitCode = ExitCode_AzureTimer_Consume;
return;
}
bool isNetworkReady = false;
if (Networking_IsNetworkingReady(&isNetworkReady) != -1)
{
if (isNetworkReady && !iothubAuthenticated)
{
SetupAzureClient();
}
}
else
{
Log_Debug("Failed to get Network state\n");
}
if (iothubAuthenticated)
{
telemetryCount++;
if (telemetryCount == AzureIoTPollPeriodsPerTelemetry)
{
telemetryCount = 0;
SendRotationData();
}
IoTHubDeviceClient_LL_DoWork(iothubClientHandle);
}
}
/// <summary>
/// Set up SIGTERM termination handler, initialize peripherals, and set up event handlers.
/// </summary>
/// <returns>
/// ExitCode_Success if all resources were allocated successfully; otherwise another
/// ExitCode value which indicates the specific failure.
/// </returns>
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;
}
// AVNET_AESMS_PIN12_GPIO9 is used to show Device Twin settings state
Log_Debug("Opening AVNET_AESMS_PIN12_GPIO9 as output.\n");
deviceTwinStatusLedGpioFd =
GPIO_OpenAsOutput(AVNET_AESMS_PIN12_GPIO9, GPIO_OutputMode_PushPull, GPIO_Value_High);
if (deviceTwinStatusLedGpioFd == -1)
{
Log_Debug("ERROR: Could not open AVNET_AESMS_PIN12_GPIO9: %s (%d).\n", strerror(errno), errno);
return ExitCode_Init_TwinStatusLed;
}
azureIoTPollPeriodSeconds = AzureIoTDefaultPollPeriodSeconds;
struct timespec azureTelemetryPeriod = {.tv_sec = 1, .tv_nsec = 0};
azureTimer = CreateEventLoopPeriodicTimer(eventLoop, &AzureTimerEventHandler, &azureTelemetryPeriod);
if (azureTimer == NULL)
{
return ExitCode_Init_AzureTimer;
}
return ExitCode_Success;
}
/// <summary>
/// Closes a file descriptor and prints an error on failure.
/// </summary>
/// <param name="fd">File descriptor to close</param>
/// <param name="fdName">File descriptor name to use in error message</param>
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);
}
}
}
/// <summary>
/// Close peripherals and handlers.
/// </summary>
static void ClosePeripheralsAndHandlers(void)
{
DisposeEventLoopTimer(azureTimer);
EventLoop_Close(eventLoop);
Log_Debug("Closing file descriptors\n");
// Leave the LEDs off
if (deviceTwinStatusLedGpioFd >= 0)
{
GPIO_SetValue(deviceTwinStatusLedGpioFd, GPIO_Value_High);
}
CloseFdAndPrintError(deviceTwinStatusLedGpioFd, "StatusLed");
}
/// <summary>
/// Callback when the Azure IoT connection state changes.
/// This can indicate that a new connection attempt has succeeded or failed.
/// It can also indicate than an existing connection has expired due to SAS token expiry.
/// </summary>
static void ConnectionStatusCallback(IOTHUB_CLIENT_CONNECTION_STATUS result,
IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason,
void *userContextCallback)
{
iothubAuthenticated = (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED);
Log_Debug("Azure IoT connection status: %s\n", GetReasonString(reason));
if (iothubAuthenticated)
{
// Send static device twin properties when connection is established
TwinReportState(
"{\"manufacturer\":\"Microsoft\",\"model\":\"Azure Sphere Sample Device\"}");
}
}
/// <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};
SetEventLoopTimerPeriod(azureTimer, &azureTelemetryPeriod);
Log_Debug("ERROR: Failed 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 = {.tv_sec = azureIoTPollPeriodSeconds, .tv_nsec = 0};
SetEventLoopTimerPeriod(azureTimer, &azureTelemetryPeriod);
iothubAuthenticated = true;
if (IoTHubDeviceClient_LL_SetOption(iothubClientHandle, OPTION_KEEP_ALIVE,
&keepalivePeriodSeconds) != IOTHUB_CLIENT_OK)
{
Log_Debug("ERROR: Failure setting Azure IoT Hub client option \"%s\".\n",
OPTION_KEEP_ALIVE);
return;
}
IoTHubDeviceClient_LL_SetDeviceTwinCallback(iothubClientHandle, DeviceTwinCallback, NULL);
IoTHubDeviceClient_LL_SetDeviceMethodCallback(iothubClientHandle, DeviceMethodCallback, NULL);
IoTHubDeviceClient_LL_SetConnectionStatusCallback(iothubClientHandle, ConnectionStatusCallback,
NULL);
}
/// <summary>
/// Callback invoked when a Direct Method is received from Azure IoT Hub.
/// </summary>
static int DeviceMethodCallback(const char *methodName, const unsigned char *payload,
size_t payloadSize, unsigned char **response, size_t *responseSize,
void *userContextCallback)
{
int result;
char *responseString;
Log_Debug("Received Device Method callback: Method name %s.\n", methodName);
if (strcmp("TriggerAlarm", methodName) == 0)
{
// Output alarm using Log_Debug
Log_Debug(" ----- ALARM TRIGGERED! -----\n");
responseString = "\"Alarm Triggered\""; // must be a JSON string (in quotes)
result = 200;
}
else
{
// All other method names are ignored
responseString = "{}";
result = -1;
}
// if 'response' is non-NULL, the Azure IoT library frees it after use, so copy it to heap
*responseSize = strlen(responseString);
*response = malloc(*responseSize);
memcpy(*response, responseString, *responseSize);
return result;
}
/// <summary>
/// Callback invoked when a Device Twin update is received from Azure IoT Hub.
/// </summary>
static void DeviceTwinCallback(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;
}
// The desired properties should have a "StatusLED" object
JSON_Object *LEDState = json_object_dotget_object(desiredProperties, "StatusLED");
if (LEDState != NULL)
{
// ... with a "value" field which is a Boolean
int statusLedValue = json_object_get_boolean(LEDState, "value");
if (statusLedValue != -1)
{
statusLedOn = statusLedValue == 1;
GPIO_SetValue(deviceTwinStatusLedGpioFd,
statusLedOn ? GPIO_Value_Low : GPIO_Value_High);
}
}
// Report current status LED state
if (statusLedOn)
{
TwinReportState("{\"StatusLED\":{\"value\":true}}");
}
else
{
TwinReportState("{\"StatusLED\":{\"value\":false}}");
}
cleanup:
// Release the allocated memory.
json_value_free(rootProperties);
free(nullTerminatedJsonString);
}
/// <summary>
/// Converts the Azure 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 Azure IoT Hub
/// </summary>
static void SendTelemetry(const char *jsonMessage)
{
Log_Debug("Sending Azure IoT Hub telemetry: %s.\n", jsonMessage);
bool isNetworkingReady = false;
if ((Networking_IsNetworkingReady(&isNetworkingReady) == -1) || !isNetworkingReady)
{
Log_Debug("WARNING: Cannot send Azure IoT Hub telemetry because the network is not up.\n");
return;
}
IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromString(jsonMessage);
if (messageHandle == 0)
{
Log_Debug("ERROR: unable to create a new IoTHubMessage.\n");
return;
}
if (IoTHubDeviceClient_LL_SendEventAsync(iothubClientHandle, messageHandle, SendEventCallback,
/*&callback_param*/ NULL) != IOTHUB_CLIENT_OK)
{
Log_Debug("ERROR: failure requesting IoTHubClient to send telemetry event.\n");
}
else
{
Log_Debug("INFO: IoTHubClient accepted the telemetry event for delivery.\n");
}
IoTHubMessage_Destroy(messageHandle);
}
/// <summary>
/// Callback invoked when the Azure IoT Hub send event request is processed.
/// </summary>
static void SendEventCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void *context)
{
Log_Debug("INFO: Azure IoT Hub send telemetry event callback: status code %d.\n", result);
}
/// <summary>
/// Enqueues a report containing Device Twin reported properties. The report is not sent
/// immediately, but it is sent on the next invocation of IoTHubDeviceClient_LL_DoWork().
/// </summary>
static void TwinReportState(const char *jsonState)
{
if (iothubClientHandle == NULL)
{
Log_Debug("ERROR: Azure IoT Hub client not initialized.\n");
}
else
{
if (IoTHubDeviceClient_LL_SendReportedState(
iothubClientHandle, (const unsigned char *)jsonState, strlen(jsonState),
ReportedStateCallback, NULL) != IOTHUB_CLIENT_OK)
{
Log_Debug("ERROR: Azure IoT Hub client error when reporting state '%s'.\n", jsonState);
}
else
{
Log_Debug("INFO: Azure IoT Hub client accepted request to report state '%s'.\n",
jsonState);
}
}
}
/// <summary>
/// Callback invoked when the Device Twin report state request is processed by Azure IoT Hub
/// client.
/// </summary>
static void ReportedStateCallback(int result, void *context)
{
Log_Debug("INFO: Azure IoT Hub Device Twin reported state callback: status code %d.\n", result);
}
#define TELEMETRY_BUFFER_SIZE 100
void UpdateSensorData(void)
{
float rotation = convertRawAngleToDegrees(getRawAngle());
if (rotation != lastDegrees)
{
rotationHistory[3] = rotationHistory[2]; // 2 290 340.. 10 2 340
rotationHistory[2] = rotationHistory[1];
rotationHistory[1] = rotationHistory[0];
rotationHistory[0] = rotation;
delta = abs(lastDegrees - rotationHistory[0]);
if ((rotationHistory[1] > rotationHistory[2]) && (rotationHistory[2] > rotationHistory[3]))
{
rollDirection = Forward;
}
else if ((rotationHistory[1] < rotationHistory[2]) && (rotationHistory[2] < rotationHistory[3]))
{
rollDirection = Backward;
}
if (rollDirection != previousDirection)
{
// someone is rolling it backwards!
}
previousDirection = rollDirection;
if ((delta > 200) && rotationHistory[0]!=0 && rotationHistory[2] !=0 && rotationHistory[3]!=0 )
{
// one full rotation?
fullRotationCount++;
}
syncRequired = true;
}
lastDegrees = rotation;
}
void SendRotationData(void)
{
if (syncRequired)
{
char telemetryBuffer[TELEMETRY_BUFFER_SIZE];
int len = snprintf(telemetryBuffer, TELEMETRY_BUFFER_SIZE, "{\"Degrees\":\"%3.2f\",\"Rotation\":\"%i\"}", rotationHistory[0], fullRotationCount);
if (len < 0 || len >= TELEMETRY_BUFFER_SIZE)
{
Log_Debug("ERROR: Cannot write telemetry to buffer.\n");
return;
}
SendTelemetry(telemetryBuffer);
}
syncRequired = false;
}