diff --git a/README.md b/README.md index 3b4e148..0c8b4ce 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,59 @@ -# AzureIoT - Azure IoT library for Arduino +# AzureIoT - Azure IoT Hub library for Arduino -This library is a port of the [Microsoft Azure IoT device SDK for C](https://github.com/Azure/azure-iot-sdks/blob/master/c/readme.md) to Arduino. It allows you to use your Arduino/Genuino with the [Azure IoT Hub](https://azure.microsoft.com/en-us/services/iot-hub/). +This library is a port of the [Microsoft Azure IoT device SDK for C](https://github.com/Azure/azure-iot-sdks/blob/master/c/readme.md) to Arduino. It allows you to use several Arduino compatible boards with Azure IoT Hub. -For the Arduino/Genuino [MKR1000](https://www.arduino.cc/en/Main/ArduinoMKR1000) or [Zero](https://www.arduino.cc/en/Main/ArduinoBoardZero) and [WiFi Shield 101](https://www.arduino.cc/en/Main/ArduinoWiFiShield101) only. +Currently supported hardware: +- Atmel SAMD Based boards + - Arduino/Genuino [MKR1000](https://www.arduino.cc/en/Main/ArduinoMKR1000) + - Adafruit [Feather M0](https://www.adafruit.com/products/3010) + - Arduino/Genuino [Zero](https://www.arduino.cc/en/Main/ArduinoBoardZero) and [WiFi Shield 101](https://www.arduino.cc/en/Main/ArduinoWiFiShield101) +- ESP8266 based boards with [esp8266/arduino](https://github.com/esp8266/arduino) + - SparkFun [Thing](https://www.sparkfun.com/products/13711) + - Adafruit [Feather Huzzah](https://www.adafruit.com/products/2821) [![Microsoft Azure Certified][Microsoft-Azure-Certified-Badge]][azure-certifiedforiot] -## Getting started - -See the [Microsoft Azure IoT device SDK for C - Arduino WiFi Shield 101 and MKR1000 Setup Guide](https://github.com/Azure/azure-iot-sdks/blob/master/c/doc/run_sample_on_arduino_wifi101.md). +## Prerequisites + +You should have the following ready before beginning with any board: +- [Setup your IoT hub](https://github.com/Azure/azure-iot-sdks/blob/master/doc/setup_iothub.md) +- [Provision your device and get its credentials](https://github.com/Azure/azure-iot-sdks/blob/master/doc/manage_iot_hub.md) +- [Arduino IDE 1.6.8](https://www.arduino.cc/en/Main/Software) +- Install the `AzureIoTHub` library via the Arduino IDE Library Manager + +## ESP8266 +##### Sparkfun Thing, Adafruit Feather Huzzah, or generic ESP8266 board + +1. Install esp8266 board support into your Arduino IDE. + * Start Arduino and open Preferences window. + * Enter `http://arduino.esp8266.com/stable/package_esp8266com_index.json` into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas. + * Open Boards Manager from Tools > Board menu and install esp8266 platform 2.1.0 or later + * Select your ESP8266 board from Tools > Board menu after installation + +2. Open the AzureIoTHub ESP8266 sample from the Arduino IDE File->Examples menu. +3. Update Wifi SSID/Password in simplesample_http.ino + * Ensure you are using a wifi network that does not require additional manual steps after connection, such as opening a web browser. +4. Update IoT Hub Connection string in simplesample_http.c + + +## Adafruit Feather M0 +1. Install Feather M0 board support into your Arduino IDE. + * Start Arduino and open Preferences window. + * Enter `https://adafruit.github.io/arduino-board-index/package_adafruit_index.json` into Additional Board Manager URLs field. You can add multiple URLs, separating them with commas. + * Open Boards Manager from Tools > Board menu and install Adafruit SAMD Boards 1.0.7 or later. + * Select your Adafruit Feather M0 from Tools > Board menu after installation +2. Install the [Adafruit WINC1500 wifi library](https://learn.adafruit.com/adafruit-feather-m0-wifi-atwinc1500/using-the-wifi-module) +3. Install the `RTCZero` library from the Arduino IDE Library Manager. +2. Open the AzureIoTHub SAMD sample from the Arduino IDE File->Examples menu. +3. Update Wifi SSID/Password in simplesample_http.ino + * Ensure you are using a wifi network that does not require additional manual steps after connection, such as opening a web browser. +4. Update IoT Hub Connection string in simplesample_http.c + +## MKR1000 or Zero + Wifi101 +1. Open the AzureIoTHub SAMD sample from the Arduino IDE File->Examples menu. +2. Update Wifi SSID/Password in simplesample_http.ino + * Ensure you are using a wifi network that does not require additional manual steps after connection, such as opening a web browser. +3. Update IoT Hub Connection string in simplesample_http.c ## License diff --git a/examples/esp8266/command_center/command_center.ino b/examples/esp8266/command_center/command_center.ino new file mode 100644 index 0000000..87db917 --- /dev/null +++ b/examples/esp8266/command_center/command_center.ino @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This example only works with Arduino IDE 1.6.8 or later. + +#include +#include +#include "command_center_http.h" + + +const char ssid[] = "[SSID]"; // your WiFi SSID (name) +const char pass[] = "[PASSWORD]"; // your WiFi password (use for WPA, or use as key for WEP) +const char connectionString[] = "HostName=[HubName].azure-devices.net;DeviceId=[DeviceName];SharedAccessKey=[KEY]"; + +int status = WL_IDLE_STATUS; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void setup() { + initSerial(); + initWifi(); + initTime(); + + // This function doesn't exit. + simplesample_http_run(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void loop() { + // Not used. +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initSerial() { + // Start serial and initialize stdout + Serial.begin(115200); + Serial.setDebugOutput(true); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initWifi() { + // Attempt to connect to Wifi network: + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println("Connected to wifi"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initTime() { + time_t epochTime; + + configTime(0, 0, "pool.ntp.org", "time.nist.gov"); + + while (true) { + epochTime = time(NULL); + + if (epochTime == 0) { + Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry."); + delay(2000); + } else { + Serial.print("Fetched NTP epoch time is: "); + Serial.println(epochTime); + break; + } + } +} + diff --git a/examples/esp8266/command_center/command_center_http.c b/examples/esp8266/command_center/command_center_http.c new file mode 100644 index 0000000..480998c --- /dev/null +++ b/examples/esp8266/command_center/command_center_http.c @@ -0,0 +1,314 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#include +#include +#include +#include +#include + +#include "dht22.h" +#include "command_center_http.h" + +/* This sample uses the _LL APIs of iothub_client for example purposes. +That does not mean that HTTP only works with the _LL APIs. +Simply changing the using the convenience layer (functions not having _LL) +and removing calls to _DoWork will yield the same results. */ + +#include "AzureIoT.h" + + +//static const char* connectionString = "HostName=[host].azure-devices.net;DeviceId=[device];SharedAccessKey=[key]"; +static const char connectionString[] = "HostName=[host].azure-devices.net;DeviceId=[device];SharedAccessKey=[key]"; + +static int redLedPin = 12; +static int redLedState = LOW; +static int greenLedPin = 13; +static int greenLedState = LOW; + +// Define the Model +BEGIN_NAMESPACE(WeatherStation); + +DECLARE_MODEL(ContosoAnemometer, +WITH_DATA(ascii_char_ptr, DeviceId), +WITH_DATA(ascii_char_ptr, EventTime), +WITH_DATA(int, WindSpeed), +WITH_DATA(int, MTemperature), +WITH_DATA(int, Humidity), +WITH_ACTION(TurnFanOn), +WITH_ACTION(TurnFanOff), +WITH_ACTION(SetAirResistance, int, Position) +); + +END_NAMESPACE(WeatherStation); + +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES) + +EXECUTE_COMMAND_RESULT TurnFanOn(ContosoAnemometer* device) +{ + (void)device; + + LogInfo("Toggling red LED.\r\n"); + if (redLedState != LOW) + { + redLedState = LOW; + } + else + { + redLedState = HIGH; + } + digitalWrite(redLedPin, redLedState); + + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT TurnFanOff(ContosoAnemometer* device) +{ + (void)device; + + LogInfo("Toggling green LED.\r\n"); + if (greenLedState != LOW) + { + greenLedState = LOW; + } + else + { + greenLedState = HIGH; + } + digitalWrite(greenLedPin, greenLedState); + + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT SetAirResistance(ContosoAnemometer* device, int Position) +{ + (void)device; + LogInfo("Setting Air Resistance Position to %d.\r\n", Position); + return EXECUTE_COMMAND_SUCCESS; +} + +void sendCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + int messageTrackingId = (intptr_t)userContextCallback; + + LogInfo("Message Id: %d Received.\r\n", messageTrackingId); + + LogInfo("Result Call Back Called! Result is: %s \r\n", ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); +} + +static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) +{ + static unsigned int messageTrackingId; + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)(uintptr_t)messageTrackingId) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + IoTHubMessage_Destroy(messageHandle); + } + free((void*)buffer); + messageTrackingId++; +} + +/*this function "links" IoTHub to the serialization library*/ +static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + IOTHUBMESSAGE_DISPOSITION_RESULT result; + const unsigned char* buffer; + size_t size; + if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) + { + LogInfo("unable to IoTHubMessage_GetByteArray\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + /*buffer is not zero terminated*/ + char* temp = malloc(size + 1); + if (temp == NULL) + { + LogInfo("failed to malloc\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + memcpy(temp, buffer, size); + temp[size] = '\0'; + EXECUTE_COMMAND_RESULT executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); + result = + (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : + (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : + IOTHUBMESSAGE_REJECTED; + free(temp); + } + } + return result; +} + +void simplesample_http_run(void) +{ + initDht(); + + digitalWrite(redLedPin, redLedState); + pinMode(redLedPin, OUTPUT); + + digitalWrite(greenLedPin, greenLedState); + pinMode(greenLedPin, OUTPUT); + + if (serializer_init(NULL) != SERIALIZER_OK) + { + LogInfo("Failed on serializer_init\r\n"); + } + else + { + IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol); + srand((unsigned int)time(NULL)); + int avgWindSpeed = 10.0; + + if (iotHubClientHandle == NULL) + { + LogInfo("Failed on IoTHubClient_LL_Create\r\n"); + } + else + { + unsigned int minimumPollingTime = 9; /*because it can poll "after 9 seconds" polls will happen effectively at ~10 seconds*/ + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) + { + LogInfo("failure to set option \"MinimumPollingTime\"\r\n"); + } + +#ifdef MBED_BUILD_TIMESTAMP + // For mbed add the certificate information + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) + { + LogInfo("failure to set option \"TrustedCerts\"\r\n"); + } +#endif // MBED_BUILD_TIMESTAMP + + ContosoAnemometer* myWeather = CREATE_MODEL_INSTANCE(WeatherStation, ContosoAnemometer); + if (myWeather == NULL) + { + LogInfo("Failed on CREATE_MODEL_INSTANCE\r\n"); + } + else + { + if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK) + { + LogInfo("unable to IoTHubClient_SetMessageCallback\r\n"); + } + else + { + myWeather->DeviceId = "myFirstDevice"; + myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2); + { + unsigned char* destination; + size_t destinationSize; + if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->WindSpeed) != IOT_AGENT_OK) + { + LogInfo("Failed to serialize\r\n"); + } + else + { + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)1) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient\r\n"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + + IoTHubMessage_Destroy(messageHandle); + } + free(destination); + } + } + + /* wait for commands */ + long Prev_time_ms = millis(); + char buff[11]; + int timeNow = 0; + + while (1) + { + long Curr_time_ms = millis(); + if (Curr_time_ms >= Prev_time_ms + 5000) + { + Prev_time_ms = Curr_time_ms; + + timeNow = (int)time(NULL); + sprintf(buff, "%d", timeNow); + + float Temp_c__f, Humi_pct__f; + getNextSample(&Temp_c__f, &Humi_pct__f); + myWeather->DeviceId = "myFirstDevice"; + myWeather->EventTime = timeNow; + myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2); + myWeather->MTemperature = (int)Temp_c__f; + myWeather->Humidity = (int)Humi_pct__f; + { + unsigned char* destination; + size_t destinationSize; + if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->EventTime, myWeather->WindSpeed, myWeather->MTemperature, myWeather->Humidity) != IOT_AGENT_OK) + { + LogInfo("Failed to serialize\r\n"); + } + else + { + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)1) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient\r\n"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + + IoTHubMessage_Destroy(messageHandle); + } + free(destination); + } + } + } + + IoTHubClient_LL_DoWork(iotHubClientHandle); + ThreadAPI_Sleep(100); + } + } + + DESTROY_MODEL_INSTANCE(myWeather); + } + IoTHubClient_LL_Destroy(iotHubClientHandle); + } + serializer_deinit(); + } +} + + diff --git a/examples/esp8266/command_center/command_center_http.h b/examples/esp8266/command_center/command_center_http.h new file mode 100644 index 0000000..943ddf9 --- /dev/null +++ b/examples/esp8266/command_center/command_center_http.h @@ -0,0 +1,22 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef _cmd_ctr_HTTP_H +#define _cmd_ctr_HTTP_H + +#define MAX_CONNECTION_STRING_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void simplesample_http_run(void); + +#ifdef __cplusplus +} +#endif + +#endif//_cmd_ctr_HTTP_H + diff --git a/examples/esp8266/command_center/dht22.cpp b/examples/esp8266/command_center/dht22.cpp new file mode 100644 index 0000000..58977b3 --- /dev/null +++ b/examples/esp8266/command_center/dht22.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include +#include +#include +#include "dht22.h" + + +#define DHTPIN 2 // Pin which is connected to the DHT sensor. +#define DHTTYPE DHT22 // DHT 22 (AM2302) +// See guide for details on sensor wiring and usage: +// https://learn.adafruit.com/dht/overview +DHT_Unified dht(DHTPIN, DHTTYPE); +uint32_t delayMS; +uint32_t nextSampleAllowedMS = 0; + +void initDht(void) { + // Initialize device. + dht.begin(); + Serial.println("DHTxx Unified Sensor Example"); + // Print temperature sensor details. + sensor_t sensor; + dht.temperature().getSensor(&sensor); + Serial.println("------------------------------------"); + Serial.println("Temperature"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" *C"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" *C"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" *C"); + Serial.println("------------------------------------"); + // Print humidity sensor details. + dht.humidity().getSensor(&sensor); + Serial.println("------------------------------------"); + Serial.println("Humidity"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println("%"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println("%"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println("%"); + Serial.println("------------------------------------"); + // Set delay between sensor readings based on sensor details. + delayMS = sensor.min_delay / 1000; +} + +void getNextSample(float* Temperature, float* Humidity) +{ + // Enforce a delay between measurements. + uint32_t currTimeMS = millis(); + if (currTimeMS < nextSampleAllowedMS) return; + nextSampleAllowedMS = currTimeMS + delayMS; + + // Get temperature event and print its value. + sensors_event_t event; + dht.temperature().getEvent(&event); + if (isnan(event.temperature)) { + Serial.println("Error reading temperature!"); + } + else { + Serial.print("Temperature: "); + Serial.print(event.temperature); + Serial.println(" *C"); + *Temperature = event.temperature; + } + // Get humidity event and print its value. + dht.humidity().getEvent(&event); + if (isnan(event.relative_humidity)) { + Serial.println("Error reading humidity!"); + } + else { + Serial.print("Humidity: "); + Serial.print(event.relative_humidity); + Serial.println("%"); + *Humidity = event.relative_humidity; + } +} + + diff --git a/examples/esp8266/command_center/dht22.h b/examples/esp8266/command_center/dht22.h new file mode 100644 index 0000000..3e38817 --- /dev/null +++ b/examples/esp8266/command_center/dht22.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __DHT22_H +#define __DHT22_H + + +#ifdef __cplusplus +extern "C" { +#endif + +void initDht(void); +void getNextSample(float* Temperature, float* Humidity); + +#ifdef __cplusplus +} +#endif + + +#endif//__DHT22_H + diff --git a/examples/esp8266/remote_monitoring/dht22.cpp b/examples/esp8266/remote_monitoring/dht22.cpp new file mode 100644 index 0000000..58977b3 --- /dev/null +++ b/examples/esp8266/remote_monitoring/dht22.cpp @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include +#include +#include +#include "dht22.h" + + +#define DHTPIN 2 // Pin which is connected to the DHT sensor. +#define DHTTYPE DHT22 // DHT 22 (AM2302) +// See guide for details on sensor wiring and usage: +// https://learn.adafruit.com/dht/overview +DHT_Unified dht(DHTPIN, DHTTYPE); +uint32_t delayMS; +uint32_t nextSampleAllowedMS = 0; + +void initDht(void) { + // Initialize device. + dht.begin(); + Serial.println("DHTxx Unified Sensor Example"); + // Print temperature sensor details. + sensor_t sensor; + dht.temperature().getSensor(&sensor); + Serial.println("------------------------------------"); + Serial.println("Temperature"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" *C"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" *C"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" *C"); + Serial.println("------------------------------------"); + // Print humidity sensor details. + dht.humidity().getSensor(&sensor); + Serial.println("------------------------------------"); + Serial.println("Humidity"); + Serial.print ("Sensor: "); Serial.println(sensor.name); + Serial.print ("Driver Ver: "); Serial.println(sensor.version); + Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); + Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println("%"); + Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println("%"); + Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println("%"); + Serial.println("------------------------------------"); + // Set delay between sensor readings based on sensor details. + delayMS = sensor.min_delay / 1000; +} + +void getNextSample(float* Temperature, float* Humidity) +{ + // Enforce a delay between measurements. + uint32_t currTimeMS = millis(); + if (currTimeMS < nextSampleAllowedMS) return; + nextSampleAllowedMS = currTimeMS + delayMS; + + // Get temperature event and print its value. + sensors_event_t event; + dht.temperature().getEvent(&event); + if (isnan(event.temperature)) { + Serial.println("Error reading temperature!"); + } + else { + Serial.print("Temperature: "); + Serial.print(event.temperature); + Serial.println(" *C"); + *Temperature = event.temperature; + } + // Get humidity event and print its value. + dht.humidity().getEvent(&event); + if (isnan(event.relative_humidity)) { + Serial.println("Error reading humidity!"); + } + else { + Serial.print("Humidity: "); + Serial.print(event.relative_humidity); + Serial.println("%"); + *Humidity = event.relative_humidity; + } +} + + diff --git a/examples/esp8266/remote_monitoring/dht22.h b/examples/esp8266/remote_monitoring/dht22.h new file mode 100644 index 0000000..3e38817 --- /dev/null +++ b/examples/esp8266/remote_monitoring/dht22.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __DHT22_H +#define __DHT22_H + + +#ifdef __cplusplus +extern "C" { +#endif + +void initDht(void); +void getNextSample(float* Temperature, float* Humidity); + +#ifdef __cplusplus +} +#endif + + +#endif//__DHT22_H + diff --git a/examples/esp8266/remote_monitoring/remote_monitoring.c b/examples/esp8266/remote_monitoring/remote_monitoring.c new file mode 100644 index 0000000..280a332 --- /dev/null +++ b/examples/esp8266/remote_monitoring/remote_monitoring.c @@ -0,0 +1,264 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "AzureIoT.h" +#include "sdk/schemaserializer.h" +#include "dht22.h" + + +// Find under Microsoft Azure IoT Suite -> DEVICES -> -> Device Details and Authentication Keys +//static const char* deviceId = "[DEVICEID]"; +//static const char* deviceKey = "[KEY 1 or 2]"; +//static const char* hubName = "[HOSTNAME]"; +//static const char* hubSuffix = "azure-devices.net"; +static const char* deviceId = "[device-id]"; +static const char* deviceKey = "[device-key]"; +static const char* hubName = "[hub-name]"; +static const char* hubSuffix = "azure-devices.net"; + + +// Define the Model +BEGIN_NAMESPACE(Contoso); + +DECLARE_STRUCT(SystemProperties, + ascii_char_ptr, DeviceID, + _Bool, Enabled +); + +DECLARE_STRUCT(DeviceProperties, +ascii_char_ptr, DeviceID, +_Bool, HubEnabledState +); + +DECLARE_MODEL(Thermostat, + + /* Event data (temperature, external temperature and humidity) */ + WITH_DATA(int, Temperature), + WITH_DATA(int, ExternalTemperature), + WITH_DATA(int, Humidity), + WITH_DATA(ascii_char_ptr, DeviceId), + + /* Device Info - This is command metadata + some extra fields */ + WITH_DATA(ascii_char_ptr, ObjectType), + WITH_DATA(_Bool, IsSimulatedDevice), + WITH_DATA(ascii_char_ptr, Version), + WITH_DATA(DeviceProperties, DeviceProperties), + WITH_DATA(ascii_char_ptr_no_quotes, Commands), + + /* Commands implemented by the device */ + WITH_ACTION(SetTemperature, int, temperature), + WITH_ACTION(SetHumidity, int, humidity) +); + +END_NAMESPACE(Contoso); + +EXECUTE_COMMAND_RESULT SetTemperature(Thermostat* thermostat, int temperature) +{ + LogInfo("Received temperature %d\r\n", temperature); + thermostat->Temperature = temperature; + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT SetHumidity(Thermostat* thermostat, int humidity) +{ + LogInfo("Received humidity %d\r\n", humidity); + thermostat->Humidity = humidity; + return EXECUTE_COMMAND_SUCCESS; +} + +static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) +{ + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + + IoTHubMessage_Destroy(messageHandle); + } + free((void*)buffer); +} + +/*this function "links" IoTHub to the serialization library*/ +static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + IOTHUBMESSAGE_DISPOSITION_RESULT result; + const unsigned char* buffer; + size_t size; + if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) + { + LogInfo("unable to IoTHubMessage_GetByteArray\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + /*buffer is not zero terminated*/ + char* temp = malloc(size + 1); + if (temp == NULL) + { + LogInfo("failed to malloc\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + EXECUTE_COMMAND_RESULT executeCommandResult; + + memcpy(temp, buffer, size); + temp[size] = '\0'; + executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); + result = + (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : + (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : + IOTHUBMESSAGE_REJECTED; + free(temp); + } + } + return result; +} + +void remote_monitoring_run(void) +{ + initDht(); + + srand((unsigned int)time(NULL)); + if (serializer_init(NULL) != SERIALIZER_OK) + { + LogInfo("Failed on serializer_init\r\n"); + } + else + { + IOTHUB_CLIENT_CONFIG config; + IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle; + + config.deviceId = deviceId; + config.deviceKey = deviceKey; + config.iotHubName = hubName; + config.iotHubSuffix = hubSuffix; + config.protocol = HTTP_Protocol; + + iotHubClientHandle = IoTHubClient_LL_Create(&config); + if (iotHubClientHandle == NULL) + { + LogInfo("Failed on IoTHubClient_CreateFromConnectionString\r\n"); + } + else + { +#ifdef MBED_BUILD_TIMESTAMP + // For mbed add the certificate information + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) + { + LogInfo("failure to set option \"TrustedCerts\"\r\n"); + } +#endif // MBED_BUILD_TIMESTAMP + + Thermostat* thermostat = CREATE_MODEL_INSTANCE(Contoso, Thermostat); + if (thermostat == NULL) + { + LogInfo("Failed on CREATE_MODEL_INSTANCE\r\n"); + } + else + { + STRING_HANDLE commandsMetadata; + + if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, thermostat) != IOTHUB_CLIENT_OK) + { + LogInfo("unable to IoTHubClient_SetMessageCallback\r\n"); + } + else + { + + /* send the device info upon startup so that the cloud app knows + what commands are available and the fact that the device is up */ + thermostat->ObjectType = "DeviceInfo"; + thermostat->IsSimulatedDevice = false; + thermostat->Version = "1.0"; + thermostat->DeviceProperties.HubEnabledState = true; + thermostat->DeviceProperties.DeviceID = (char*)deviceId; + + commandsMetadata = STRING_new(); + if (commandsMetadata == NULL) + { + LogInfo("Failed on creating string for commands metadata\r\n"); + } + else + { + /* Serialize the commands metadata as a JSON string before sending */ + if (SchemaSerializer_SerializeCommandMetadata(GET_MODEL_HANDLE(Contoso, Thermostat), commandsMetadata) != SCHEMA_SERIALIZER_OK) + { + LogInfo("Failed serializing commands metadata\r\n"); + } + else + { + unsigned char* buffer; + size_t bufferSize; + thermostat->Commands = (char*)STRING_c_str(commandsMetadata); + + /* Here is the actual send of the Device Info */ + if (SERIALIZE(&buffer, &bufferSize, thermostat->ObjectType, thermostat->Version, thermostat->IsSimulatedDevice, thermostat->DeviceProperties, thermostat->Commands) != IOT_AGENT_OK) + { + LogInfo("Failed serializing\r\n"); + } + else + { + sendMessage(iotHubClientHandle, buffer, bufferSize); + } + + } + + STRING_delete(commandsMetadata); + } + + thermostat->DeviceId = (char*)deviceId; + int sendCycle = 10; + int currentCycle = 0; + while (1) + { + if(currentCycle >= sendCycle) { + float Temp; + float Humi; + getNextSample(&Temp, &Humi); + //thermostat->Temperature = 50 + (rand() % 10 + 2); + thermostat->Temperature = (int)round(Temp); + thermostat->ExternalTemperature = 55 + (rand() % 5 + 2); + //thermostat->Humidity = 50 + (rand() % 8 + 2); + thermostat->Humidity = (int)round(Humi); + currentCycle = 0; + unsigned char*buffer; + size_t bufferSize; + + LogInfo("Sending sensor value Temperature = %d, Humidity = %d\r\n", thermostat->Temperature, thermostat->Humidity); + + if (SERIALIZE(&buffer, &bufferSize, thermostat->DeviceId, thermostat->Temperature, thermostat->Humidity, thermostat->ExternalTemperature) != IOT_AGENT_OK) + { + LogInfo("Failed sending sensor value\r\n"); + } + else + { + sendMessage(iotHubClientHandle, buffer, bufferSize); + } + } + + IoTHubClient_LL_DoWork(iotHubClientHandle); + ThreadAPI_Sleep(100); + currentCycle++; + } + } + + DESTROY_MODEL_INSTANCE(thermostat); + } + IoTHubClient_LL_Destroy(iotHubClientHandle); + } + serializer_deinit(); + + } +} diff --git a/examples/esp8266/remote_monitoring/remote_monitoring.h b/examples/esp8266/remote_monitoring/remote_monitoring.h new file mode 100644 index 0000000..1a2fde3 --- /dev/null +++ b/examples/esp8266/remote_monitoring/remote_monitoring.h @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef REMOTE_MONITORING_H +#define REMOTE_MONITORING_H + +#ifdef __cplusplus +extern "C" { +#endif + + void remote_monitoring_run(void); + +#ifdef __cplusplus +} +#endif + +#endif /* REMOTE_MONITORING_H */ diff --git a/examples/esp8266/remote_monitoring/remote_monitoring.ino b/examples/esp8266/remote_monitoring/remote_monitoring.ino new file mode 100644 index 0000000..26fd0a5 --- /dev/null +++ b/examples/esp8266/remote_monitoring/remote_monitoring.ino @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// Please use an Arduino IDE 1.6.8 or greater + +#include +#include +#include "remote_monitoring.h" + +//char ssid[] = "[SSID]"; // your WiFi SSID (name) +//char pass[] = "[PASSWORD]"; // your WiFi password (use for WPA, or use as key for WEP) +char ssid[] = "[SSID]"; // your WiFi SSID (name) +char pass[] = "[PASSWORD]"; // your WiFi password (use for WPA, or use as key for WEP) +int status = WL_IDLE_STATUS; + +void setup() { + initSerial(); + initWifi(); + initTime(); +} + +void loop() { + // Run the Remote Monitoring from the Azure IoT Hub C SDK + // You must set the device id, device key, IoT Hub name and IotHub suffix in + // remote_monitoring.c + remote_monitoring_run(); +} + +void initSerial() { + // Start serial and initialize stdout + Serial.begin(115200); + Serial.setDebugOutput(true); +} + +void initWifi() { + // Attempt to connect to Wifi network: + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println("Connected to wifi"); +} + +void initTime() { + time_t epochTime; + + configTime(0, 0, "pool.ntp.org", "time.nist.gov"); + + while (true) { + epochTime = time(NULL); + + if (epochTime == 0) { + Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry."); + delay(2000); + } else { + Serial.print("Fetched NTP epoch time is: "); + Serial.println(epochTime); + break; + } + } +} + diff --git a/examples/esp8266/simplesample_http/simplesample_http.c b/examples/esp8266/simplesample_http/simplesample_http.c new file mode 100644 index 0000000..fb2b693 --- /dev/null +++ b/examples/esp8266/simplesample_http/simplesample_http.c @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include + +#include +#include +#include + +/* This sample uses the _LL APIs of iothub_client for example purposes. +That does not mean that HTTP only works with the _LL APIs. +Simply changing the using the convenience layer (functions not having _LL) +and removing calls to _DoWork will yield the same results. */ + +#include "AzureIoT.h" + +static const char* connectionString = "HostName=[host].azure-devices.net;DeviceId=[device];SharedAccessKey=[key]"; + +// Define the Model +BEGIN_NAMESPACE(WeatherStation); + +DECLARE_MODEL(ContosoAnemometer, +WITH_DATA(ascii_char_ptr, DeviceId), +WITH_DATA(int, WindSpeed), +WITH_ACTION(TurnFanOn), +WITH_ACTION(TurnFanOff), +WITH_ACTION(SetAirResistance, int, Position) +); + +END_NAMESPACE(WeatherStation); + +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES) + +EXECUTE_COMMAND_RESULT TurnFanOn(ContosoAnemometer* device) +{ + (void)device; + LogInfo("Turning fan on.\r\n"); + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT TurnFanOff(ContosoAnemometer* device) +{ + (void)device; + LogInfo("Turning fan off.\r\n"); + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT SetAirResistance(ContosoAnemometer* device, int Position) +{ + (void)device; + LogInfo("Setting Air Resistance Position to %d.\r\n", Position); + return EXECUTE_COMMAND_SUCCESS; +} + +void sendCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + int messageTrackingId = (intptr_t)userContextCallback; + + LogInfo("Message Id: %d Received.\r\n", messageTrackingId); + + LogInfo("Result Call Back Called! Result is: %s \r\n", ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); +} + +static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) +{ + static unsigned int messageTrackingId; + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)(uintptr_t)messageTrackingId) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + IoTHubMessage_Destroy(messageHandle); + } + free((void*)buffer); + messageTrackingId++; +} + +/*this function "links" IoTHub to the serialization library*/ +static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + IOTHUBMESSAGE_DISPOSITION_RESULT result; + const unsigned char* buffer; + size_t size; + if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) + { + LogInfo("unable to IoTHubMessage_GetByteArray\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + /*buffer is not zero terminated*/ + char* temp = malloc(size + 1); + if (temp == NULL) + { + LogInfo("failed to malloc\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + memcpy(temp, buffer, size); + temp[size] = '\0'; + EXECUTE_COMMAND_RESULT executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); + result = + (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : + (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : + IOTHUBMESSAGE_REJECTED; + free(temp); + } + } + return result; +} + +void simplesample_http_run(void) +{ + if (serializer_init(NULL) != SERIALIZER_OK) + { + LogInfo("Failed on serializer_init\r\n"); + } + else + { + IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol); + srand((unsigned int)time(NULL)); + int avgWindSpeed = 10.0; + + if (iotHubClientHandle == NULL) + { + LogInfo("Failed on IoTHubClient_LL_Create\r\n"); + } + else + { + unsigned int minimumPollingTime = 9; /*because it can poll "after 9 seconds" polls will happen effectively at ~10 seconds*/ + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) + { + LogInfo("failure to set option \"MinimumPollingTime\"\r\n"); + } + +#ifdef MBED_BUILD_TIMESTAMP + // For mbed add the certificate information + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) + { + LogInfo("failure to set option \"TrustedCerts\"\r\n"); + } +#endif // MBED_BUILD_TIMESTAMP + + ContosoAnemometer* myWeather = CREATE_MODEL_INSTANCE(WeatherStation, ContosoAnemometer); + if (myWeather == NULL) + { + LogInfo("Failed on CREATE_MODEL_INSTANCE\r\n"); + } + else + { + if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK) + { + LogInfo("unable to IoTHubClient_SetMessageCallback\r\n"); + } + else + { + myWeather->DeviceId = "myFirstDevice"; + myWeather->WindSpeed = avgWindSpeed + (rand() % 4 + 2); + { + unsigned char* destination; + size_t destinationSize; + if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->WindSpeed) != IOT_AGENT_OK) + { + LogInfo("Failed to serialize\r\n"); + } + else + { + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)1) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient\r\n"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + + IoTHubMessage_Destroy(messageHandle); + } + free(destination); + } + } + + /* wait for commands */ + while (1) + { + IoTHubClient_LL_DoWork(iotHubClientHandle); + ThreadAPI_Sleep(100); + } + } + + DESTROY_MODEL_INSTANCE(myWeather); + } + IoTHubClient_LL_Destroy(iotHubClientHandle); + } + serializer_deinit(); + } +} diff --git a/examples/sdk/simplesample_http/simplesample_http.h b/examples/esp8266/simplesample_http/simplesample_http.h similarity index 100% rename from examples/sdk/simplesample_http/simplesample_http.h rename to examples/esp8266/simplesample_http/simplesample_http.h diff --git a/examples/esp8266/simplesample_http/simplesample_http.ino b/examples/esp8266/simplesample_http/simplesample_http.ino new file mode 100644 index 0000000..f095e07 --- /dev/null +++ b/examples/esp8266/simplesample_http/simplesample_http.ino @@ -0,0 +1,62 @@ +// Please use an Arduino IDE 1.6.8 or greater + +#include +#include +#include "simplesample_http.h" + +char ssid[] = "ssid"; // your network SSID (name) +char pass[] = "password"; // your network password (use for WPA, or use as key for WEP) +int status = WL_IDLE_STATUS; + +void setup() { + initSerial(); + initWifi(); + initTime(); +} + +void loop() { + // Run the SimpleSample from the Azure IoT Hub SDK + // You must set the connection string in simplesample_http.c + simplesample_http_run(); +} + +void initSerial() { + // Start serial and initialize stdout + Serial.begin(115200); + Serial.setDebugOutput(true); +} + +void initWifi() { + // Attempt to connect to Wifi network: + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println("Connected to wifi"); +} + +void initTime() { + time_t epochTime; + + configTime(0, 0, "pool.ntp.org", "time.nist.gov"); + + while (true) { + epochTime = time(NULL); + + if (epochTime == 0) { + Serial.println("Fetching NTP epoch time failed! Waiting 2 seconds to retry."); + delay(2000); + } else { + Serial.print("Fetched NTP epoch time is: "); + Serial.println(epochTime); + break; + } + } +} \ No newline at end of file diff --git a/examples/sdk/README.md b/examples/samd/README.md similarity index 62% rename from examples/sdk/README.md rename to examples/samd/README.md index 768a007..a9e591d 100644 --- a/examples/sdk/README.md +++ b/examples/samd/README.md @@ -1,4 +1,3 @@ These examples are ports from the [Microsoft Azure IoT device SDK for C](https://github.com/Azure/azure-iot-sdks/blob/master/c/readme.md). - * [iothub_client_sample_http](iothub_client_sample_http) is from: https://github.com/Azure/azure-iot-sdks/tree/master/c/iothub_client/samples/iothub_client_sample_http * [simplesample_http](simplesample_http) is from: https://github.com/Azure/azure-iot-sdks/tree/master/c/serializer/samples/simplesample_http diff --git a/examples/samd/command_center/NTPClient.cpp b/examples/samd/command_center/NTPClient.cpp new file mode 100644 index 0000000..90dd08e --- /dev/null +++ b/examples/samd/command_center/NTPClient.cpp @@ -0,0 +1,92 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if defined(ARDUINO_ARCH_SAMD) +#include "NTPClient.h" + +#define LOCAL_UDP_PORT 2390 + +NTPClient::NTPClient() : + _udp() +{ +} + +int NTPClient::begin() +{ + return _udp.begin(LOCAL_UDP_PORT); +} + +uint32_t NTPClient::getEpochTime(const char* host, int port, unsigned long timeout) +{ + if (host == NULL || port < 1) { + return (uint32_t)-1; + } + + prepareRequest(); + sendRequest(host, port); + + if (!receiveResponse(timeout)) { + return (uint32_t)-1; + } + + return parseResponse(); +} + +void NTPClient::end() +{ + _udp.stop(); +} + +void NTPClient::prepareRequest() +{ + memset(_buffer, 0, NTP_PACKET_SIZE); + + // Initialize values needed to form NTP request + _buffer[0] = 0b11100011; // LI, Version, Mode + _buffer[1] = 0; // Stratum, or type of clock + _buffer[2] = 6; // Polling Interval + _buffer[3] = 0xEC; // Peer Clock Precision + + // 8 bytes of zero for Root Delay & Root Dispersion + + _buffer[12] = 49; + _buffer[13] = 0x4E; + _buffer[14] = 49; + _buffer[15] = 52; +} + +void NTPClient::sendRequest(const char* host, int port) +{ + _udp.beginPacket(host, port); + _udp.write(_buffer, NTP_PACKET_SIZE); + _udp.endPacket(); +} + +int NTPClient::receiveResponse(unsigned long timeout) +{ + long start = millis(); + int size = 0; + + while(size == 0 && (millis() - start) < timeout) { + size = _udp.parsePacket(); + } + + if (size != NTP_PACKET_SIZE) { + return 0; + } + + _udp.read(_buffer, NTP_PACKET_SIZE); + + return 1; +} + +uint32_t NTPClient::parseResponse() +{ + uint16_t high = word(_buffer[40], _buffer[41]); + uint16_t low = word(_buffer[42], _buffer[43]); + uint32_t ntpTime = high << 16 | low; // since 1900 + uint32_t epoch = ntpTime - 2208988800UL; // since 1970 + + return epoch; +} +#endif diff --git a/examples/samd/command_center/NTPClient.h b/examples/samd/command_center/NTPClient.h new file mode 100644 index 0000000..3990350 --- /dev/null +++ b/examples/samd/command_center/NTPClient.h @@ -0,0 +1,43 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) +#include +#include +#endif + +#ifndef NTPCLIENT_H +#define NTPCLIENT_H + +#define NTP_PACKET_SIZE 48 +#define NTP_PORT 123 + +#define DEFAULT_NTP_TIMEOUT 10000 + +class NTPClient +{ + public: + NTPClient(); + int begin(); + uint32_t getEpochTime(const char* host, int port = NTP_PORT, unsigned long timeout = DEFAULT_NTP_TIMEOUT); + void end(); + + private: + void prepareRequest(); + void sendRequest(const char* host, int port); + int receiveResponse(unsigned long timeout); + uint32_t parseResponse(); + + char _buffer[NTP_PACKET_SIZE]; +#ifdef ARDUINO_SAMD_FEATHER_M0 + Adafruit_WINC1500UDP _udp; +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) + WiFiUDP _udp; +#endif + +}; + +#endif diff --git a/examples/samd/command_center/command_center.ino b/examples/samd/command_center/command_center.ino new file mode 100644 index 0000000..7f055e4 --- /dev/null +++ b/examples/samd/command_center/command_center.ino @@ -0,0 +1,187 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Attunix, 2016. All rights reserved. +// +// This example code shows how to use an Adafruit Feather M0 module and Microsoft Azure for IoT +// applications. + +#include +#include +#include +#include +#include +#include +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#include +#include +#include +#include +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) +#include +#endif +#include +#include +#include "rem_ctrl_http.h" +#include "NTPClient.h" + + +#ifdef ARDUINO_SAMD_FEATHER_M0 +#define WINC_CS 8 +#define WINC_IRQ 7 +#define WINC_RST 4 +#define WINC_EN 2 + +// Setup the WINC1500 connection with the pins above and the default hardware SPI. +Adafruit_WINC1500 WiFi(WINC_CS, WINC_IRQ, WINC_RST); +#endif + + +static const char ssid[] = "[Your WiFi network SSID or name]"; +static const char pass[] = "[Your WiFi network WPA password or WEP key]"; +static const char* connectionString = "[Device Connection String]"; + +// The connection string is the one which begins with HostName=... and contains the DeviceId +// and SharedAccessKey for this particular Thing on the Internet. + +int status = WL_IDLE_STATUS; + + +#define SEALEVELPRESSURE_HPA (1013.25) +const int Bme280_cs_pin__i = 5; +bool Bme_init_result = false; +Adafruit_BME280 bme(Bme280_cs_pin__i); +void initTime(); + + +void setup() { + // The Feather M0 loses it's COMn connection with every reset. + // This 10 s delay allows you to reselect the COM port and open the serial monitor window. + delay(10000); + + Serial.begin(9600); + Serial.println("Azure_remote_monitoring Sketch."); +#ifdef WINC_EN + pinMode(WINC_EN, OUTPUT); + digitalWrite(WINC_EN, HIGH); +#endif + + Serial.println("Checking for the presence of the BME280 temp/humid/press module."); + Bme_init_result = bme.begin(); + if (Bme_init_result) + { + Serial.println("Found and initialized BME280 module."); + } + else + { + Serial.println("Warning! BME280 module not found."); + } + + Serial.println("Checking for the presence of the WiFi module."); + if (WiFi.status() == WL_NO_SHIELD) { + Serial.println("Warning! WiFi shield not found."); + } + else + { + Serial.println("WiFi module found."); + } + + Serial.println("Attempting to connect to Wifi network."); + while (status != WL_CONNECTED) { + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + if (status != WL_CONNECTED) { + // wait 10 seconds for connection: + delay(10000); + } + } + Serial.println("Connected to wifi"); + + if (rem_ctrl_set_connection_string(connectionString)) { + Serial.print("Connection string successfully set to \""); + Serial.print(connectionString); + Serial.println("\""); + } + else { + Serial.println("Unable to set connection string. (too long?)"); + } + + int Init_result__i = rem_ctrl_http_init(); + if (Init_result__i < 4) + { + Serial.println("Unable to initialize the Azure connection. Halting."); + } + + initTime(); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// The Arduino millisecond clock is used here to get a simplistic multitasking effect. +// This macro handles wrap-around correctly. +#define IS_TASK_TIME(curr_time_ms, next_task_time_ms) \ + ((curr_time_ms >= next_task_time_ms) && ( ! ((curr_time_ms ^ next_task_time_ms) & 0x80000000L))) +uint32_t const Sensor_read_period_ms__u32 = 15000; +uint32_t Sensor_read_next_ms__u32 = 0; +uint32_t const Azure_io_update_period_ms__u32 = 100; +uint32_t Azure_io_update_next_ms__u32 = 0; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void loop() { + uint32_t Curr_time_ms__u32 = millis(); + + if (IS_TASK_TIME(Curr_time_ms__u32, Sensor_read_next_ms__u32)) { + float Temp_c__f = bme.readTemperature(); + float Pres_hPa__f = bme.readPressure() / 100; + float Humi_pct__f = bme.readHumidity(); + printf("Temp=%.2f, Pres=%.2f, Humi=%.2f\n", Temp_c__f, Pres_hPa__f, Humi_pct__f); + rem_ctrl_http_send_data(Temp_c__f, Pres_hPa__f, Humi_pct__f); + + Sensor_read_next_ms__u32 = Curr_time_ms__u32 + Sensor_read_period_ms__u32; + } + + if (IS_TASK_TIME(Curr_time_ms__u32, Azure_io_update_next_ms__u32)) { + rem_ctrl_http_run(); + + Azure_io_update_next_ms__u32 = Curr_time_ms__u32 + Azure_io_update_period_ms__u32; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initTime() { +#ifdef ARDUINO_SAMD_FEATHER_M0 + Adafruit_WINC1500UDP _udp; +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) + WiFiUDP _udp; +#endif + + time_t epochTime = (time_t)-1; + + NTPClient ntpClient; + ntpClient.begin(); + + while (true) { + epochTime = ntpClient.getEpochTime("0.pool.ntp.org"); + + if (epochTime == (time_t)-1) { + Serial.println("Fetching NTP epoch time failed! Waiting 5 seconds to retry."); + delay(5000); + } else { + Serial.print("Fetched NTP epoch time is: "); + Serial.println(epochTime); + break; + } + } + + ntpClient.end(); + + struct timeval tv; + tv.tv_sec = epochTime; + tv.tv_usec = 0; + + settimeofday(&tv, NULL); +} + + diff --git a/examples/samd/command_center/rem_ctrl_http.c b/examples/samd/command_center/rem_ctrl_http.c new file mode 100644 index 0000000..d45c953 --- /dev/null +++ b/examples/samd/command_center/rem_ctrl_http.c @@ -0,0 +1,277 @@ +// Copyright (c) Attunix. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include +#include +#include +#include + +#ifdef ARDUINO +#include "AzureIoT.h" +#else +#include "serializer.h" +#include "iothub_client_ll.h" +#include "iothubtransporthttp.h" +#include "threadapi.h" +#endif + +#include "rem_ctrl_http.h" + + +static char connectionString[MAX_CONNECTION_STRING_LEN + 1]; +static int redLedPin = 12; +static int greenLedPin = 11; +char buff[11]; +int timeNow = 0; + +// Define the Model +BEGIN_NAMESPACE(RemoteMonitorExample); + +DECLARE_MODEL( + BME280_data +, WITH_DATA(ascii_char_ptr, DeviceId) +, WITH_DATA(ascii_char_ptr, EventTime) +, WITH_DATA(float, MTemperature) // Celcius +, WITH_DATA(float, Humidity) // Relative percent +, WITH_DATA(float, Pressure) // hPa +, WITH_ACTION(TurnFanOn) +, WITH_ACTION(TurnFanOff) +); + +END_NAMESPACE(RemoteMonitorExample); +BME280_data* myWeather = NULL; +IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle = NULL; + +DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES); + +EXECUTE_COMMAND_RESULT TurnFanOn(BME280_data* device) +{ + (void)device; + (void)printf("Turning Green LED on.\r\n"); + digitalWrite(greenLedPin, HIGH); + digitalWrite(redLedPin, LOW); + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT TurnFanOff(BME280_data* device) +{ + (void)device; + (void)printf("Turning red LED on.\r\n"); + digitalWrite(greenLedPin, LOW); + digitalWrite(redLedPin, HIGH); + return EXECUTE_COMMAND_SUCCESS; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +bool rem_ctrl_set_connection_string(const char * Connection_string__cp) +{ + size_t Len = strlen(Connection_string__cp); + if (Len > MAX_CONNECTION_STRING_LEN) return false; + + strcpy(connectionString, Connection_string__cp); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void sendCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) +{ + int messageTrackingId = (intptr_t)userContextCallback; + + (void)printf("Message Id: %d Received.\r\n", messageTrackingId); + + (void)printf("Result Call Back Called! Result is: %s \r\n", ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) +{ + static unsigned int messageTrackingId; + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); + if (messageHandle == NULL) + { + printf("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)(uintptr_t)messageTrackingId) != IOTHUB_CLIENT_OK) + { + printf("failed to hand over the message to IoTHubClient"); + } + else + { + printf("IoTHubClient accepted the message for delivery\r\n"); + } + IoTHubMessage_Destroy(messageHandle); + } + free((void*)buffer); + messageTrackingId++; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// This function "links" IoTHub to the serialization library. +static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + IOTHUBMESSAGE_DISPOSITION_RESULT result; + const unsigned char* buffer; + size_t size; + if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) + { + printf("unable to IoTHubMessage_GetByteArray\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + // Buffer is not zero terminated + char* temp = malloc(size + 1); + if (temp == NULL) + { + printf("failed to malloc\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + memcpy(temp, buffer, size); + temp[size] = '\0'; + EXECUTE_COMMAND_RESULT executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); + result = + (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : + (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : + IOTHUBMESSAGE_REJECTED; + free(temp); + } + } + return result; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +int Init_level__i = 0; +int rem_ctrl_http_init(void) +{ + digitalWrite(redLedPin, LOW); + pinMode(redLedPin, OUTPUT); + + digitalWrite(greenLedPin, LOW); + pinMode(greenLedPin, OUTPUT); + + if (serializer_init(NULL) != SERIALIZER_OK) + { + (void)printf("Failed on serializer_init\r\n"); + } + else + { + Init_level__i = 1; + + iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol); + if (iotHubClientHandle == NULL) + { + (void)printf("Failed on IoTHubClient_LL_Create\r\n"); + } + else + { + Init_level__i = 2; + + unsigned int minimumPollingTime = 9; // Because it can poll "after 9 seconds" polls will happen effectively at ~10 seconds. + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) + { + printf("failure to set option \"MinimumPollingTime\"\r\n"); + } + + myWeather = CREATE_MODEL_INSTANCE(RemoteMonitorExample, BME280_data); + if (myWeather == NULL) + { + (void)printf("Failed on CREATE_MODEL_INSTANCE\r\n"); + } + else + { + Init_level__i = 3; + + if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, myWeather) != IOTHUB_CLIENT_OK) + { + printf("unable to IoTHubClient_SetMessageCallback\r\n"); + } + else + { + Init_level__i = 4; + } + } + } + } + + return Init_level__i; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void rem_ctrl_http_deinit(void) +{ + if (Init_level__i >= 3) { + DESTROY_MODEL_INSTANCE(myWeather); + myWeather = NULL; + Init_level__i = 2; + } + + if (Init_level__i >= 2) { + IoTHubClient_LL_Destroy(iotHubClientHandle); + iotHubClientHandle = NULL; + Init_level__i = 1; + } + + if (Init_level__i >= 1) { + serializer_deinit(); + Init_level__i = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void rem_ctrl_http_send_data(float Temp_c__f, float Pres_hPa__f, float Humi_pct__f) +{ + // Don't run this function unless the initialization succeeded. + if (Init_level__i < 4) return; + + timeNow = (int)time(NULL); + sprintf(buff, "%d", timeNow); + + myWeather->DeviceId = "FeatherM0_w_BME280"; + myWeather->MTemperature = Temp_c__f; + myWeather->Pressure = Pres_hPa__f; + myWeather->Humidity = Humi_pct__f; + myWeather->EventTime = timeNow; + + unsigned char* destination; + size_t destinationSize; + if (SERIALIZE(&destination, &destinationSize, myWeather->DeviceId, myWeather->EventTime, myWeather->MTemperature, myWeather->Pressure, myWeather->Humidity) != IOT_AGENT_OK) + { + (void)printf("Failed to serialize\r\n"); + } + else + { + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(destination, destinationSize); + if (messageHandle == NULL) + { + printf("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, sendCallback, (void*)1) != IOTHUB_CLIENT_OK) + { + printf("failed to hand over the message to IoTHubClient"); + } + else + { + printf("IoTHubClient accepted the message for delivery\r\n"); + } + + IoTHubMessage_Destroy(messageHandle); + } + free(destination); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void rem_ctrl_http_run(void) +{ + IoTHubClient_LL_DoWork(iotHubClientHandle); +} + + diff --git a/examples/samd/command_center/rem_ctrl_http.h b/examples/samd/command_center/rem_ctrl_http.h new file mode 100644 index 0000000..3d9f220 --- /dev/null +++ b/examples/samd/command_center/rem_ctrl_http.h @@ -0,0 +1,44 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Copyright (c) Attunix. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef _REM_CTRL_HTTP_H +#define _REM_CTRL_HTTP_H + +#define MAX_CONNECTION_STRING_LEN (255) + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Param: Connection_string__cp An ASCIIZ string containing a set of parameters including a URL +// and connection keys. i.e. +// "HostName=AuricIoTHubPiC.azure-devices.net;DeviceId=08578f25-777e-438b-876b-e2cfed911ab0;SharedAccessKey=Vg7u5edztBGW89KwHWvsHw==" +// Returns: true if string updated. +// false if the string was too long (increase MAX_CONNECTION_STRING_LEN if you need to). +bool rem_ctrl_set_connection_string(const char * Connection_string__cp); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Returns: 4 upon a successful initialization. +// Values less than 4 indicate fewer successful steps during initialization. +int rem_ctrl_http_init(void); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Cleanup. May not be needed for embedded applications. +void rem_ctrl_http_deinit(void); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Call this when it's time to update the next sample in the outgoing stream. +void rem_ctrl_http_send_data(float Temp_c__f, float Pres_hPa__f, float Humi_pct__f); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Call this about 10 times per second. +void rem_ctrl_http_run(void); + +#ifdef __cplusplus +} +#endif + +#endif//_REM_CTRL_HTTP_H + diff --git a/examples/samd/remote_monitoring/NTPClient.cpp b/examples/samd/remote_monitoring/NTPClient.cpp new file mode 100644 index 0000000..90dd08e --- /dev/null +++ b/examples/samd/remote_monitoring/NTPClient.cpp @@ -0,0 +1,92 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#if defined(ARDUINO_ARCH_SAMD) +#include "NTPClient.h" + +#define LOCAL_UDP_PORT 2390 + +NTPClient::NTPClient() : + _udp() +{ +} + +int NTPClient::begin() +{ + return _udp.begin(LOCAL_UDP_PORT); +} + +uint32_t NTPClient::getEpochTime(const char* host, int port, unsigned long timeout) +{ + if (host == NULL || port < 1) { + return (uint32_t)-1; + } + + prepareRequest(); + sendRequest(host, port); + + if (!receiveResponse(timeout)) { + return (uint32_t)-1; + } + + return parseResponse(); +} + +void NTPClient::end() +{ + _udp.stop(); +} + +void NTPClient::prepareRequest() +{ + memset(_buffer, 0, NTP_PACKET_SIZE); + + // Initialize values needed to form NTP request + _buffer[0] = 0b11100011; // LI, Version, Mode + _buffer[1] = 0; // Stratum, or type of clock + _buffer[2] = 6; // Polling Interval + _buffer[3] = 0xEC; // Peer Clock Precision + + // 8 bytes of zero for Root Delay & Root Dispersion + + _buffer[12] = 49; + _buffer[13] = 0x4E; + _buffer[14] = 49; + _buffer[15] = 52; +} + +void NTPClient::sendRequest(const char* host, int port) +{ + _udp.beginPacket(host, port); + _udp.write(_buffer, NTP_PACKET_SIZE); + _udp.endPacket(); +} + +int NTPClient::receiveResponse(unsigned long timeout) +{ + long start = millis(); + int size = 0; + + while(size == 0 && (millis() - start) < timeout) { + size = _udp.parsePacket(); + } + + if (size != NTP_PACKET_SIZE) { + return 0; + } + + _udp.read(_buffer, NTP_PACKET_SIZE); + + return 1; +} + +uint32_t NTPClient::parseResponse() +{ + uint16_t high = word(_buffer[40], _buffer[41]); + uint16_t low = word(_buffer[42], _buffer[43]); + uint32_t ntpTime = high << 16 | low; // since 1900 + uint32_t epoch = ntpTime - 2208988800UL; // since 1970 + + return epoch; +} +#endif diff --git a/examples/samd/remote_monitoring/NTPClient.h b/examples/samd/remote_monitoring/NTPClient.h new file mode 100644 index 0000000..3990350 --- /dev/null +++ b/examples/samd/remote_monitoring/NTPClient.h @@ -0,0 +1,43 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) +#include +#include +#endif + +#ifndef NTPCLIENT_H +#define NTPCLIENT_H + +#define NTP_PACKET_SIZE 48 +#define NTP_PORT 123 + +#define DEFAULT_NTP_TIMEOUT 10000 + +class NTPClient +{ + public: + NTPClient(); + int begin(); + uint32_t getEpochTime(const char* host, int port = NTP_PORT, unsigned long timeout = DEFAULT_NTP_TIMEOUT); + void end(); + + private: + void prepareRequest(); + void sendRequest(const char* host, int port); + int receiveResponse(unsigned long timeout); + uint32_t parseResponse(); + + char _buffer[NTP_PACKET_SIZE]; +#ifdef ARDUINO_SAMD_FEATHER_M0 + Adafruit_WINC1500UDP _udp; +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) + WiFiUDP _udp; +#endif + +}; + +#endif diff --git a/examples/samd/remote_monitoring/bme280.cpp b/examples/samd/remote_monitoring/bme280.cpp new file mode 100644 index 0000000..4bd5a41 --- /dev/null +++ b/examples/samd/remote_monitoring/bme280.cpp @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include +#include +#include "bme280.h" + +const int Bme280_cs_pin__i = 5; +bool Bme_init_result = false; +Adafruit_BME280 bme(Bme280_cs_pin__i); + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initBme(void) +{ + Serial.println("Checking for the presence of the BME280 temp/humid/press module."); + Bme_init_result = bme.begin(); + if (Bme_init_result) + { + Serial.println("Found and initialized BME280 module."); + } + else + { + Serial.println("Warning! BME280 module not found."); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void getNextSample(float* Temperature, float* Humidity) +{ + *Temperature = bme.readTemperature(); // Deg C + //float Pres_hPa__f = bme.readPressure() / 100; + *Humidity = bme.readHumidity(); + //printf("Temp=%.2f, Pres=%.2f, Humi=%.2f\n", Temp_c__f, Pres_hPa__f, Humi_pct__f); + printf("Temp=%.2f, Humi=%.2f\n", *Temperature, *Humidity); +} + diff --git a/examples/samd/remote_monitoring/bme280.h b/examples/samd/remote_monitoring/bme280.h new file mode 100644 index 0000000..c288bf8 --- /dev/null +++ b/examples/samd/remote_monitoring/bme280.h @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef __BME280_H +#define __BME280_H + + +#ifdef __cplusplus +extern "C" { +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initBme(void); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void getNextSample(float* Temperature, float* Humidity); + +#ifdef __cplusplus +} +#endif + + +#endif//__BME280_H + diff --git a/examples/samd/remote_monitoring/remote_monitoring.c b/examples/samd/remote_monitoring/remote_monitoring.c new file mode 100644 index 0000000..eaae8b6 --- /dev/null +++ b/examples/samd/remote_monitoring/remote_monitoring.c @@ -0,0 +1,258 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include "AzureIoT.h" +#include "sdk/schemaserializer.h" +#include "bme280.h" + + +static const char* deviceId = "[device-id]"; +static const char* deviceKey = "[device-key]"; +static const char* hubName = "[hub-name]"; +static const char* hubSuffix = "azure-devices.net"; + +// Define the Model +BEGIN_NAMESPACE(Contoso); + +DECLARE_STRUCT(SystemProperties, + ascii_char_ptr, DeviceID, + _Bool, Enabled +); + +DECLARE_STRUCT(DeviceProperties, +ascii_char_ptr, DeviceID, +_Bool, HubEnabledState +); + +DECLARE_MODEL(Thermostat, + + /* Event data (temperature, external temperature and humidity) */ + WITH_DATA(int, Temperature), + WITH_DATA(int, ExternalTemperature), + WITH_DATA(int, Humidity), + WITH_DATA(ascii_char_ptr, DeviceId), + + /* Device Info - This is command metadata + some extra fields */ + WITH_DATA(ascii_char_ptr, ObjectType), + WITH_DATA(_Bool, IsSimulatedDevice), + WITH_DATA(ascii_char_ptr, Version), + WITH_DATA(DeviceProperties, DeviceProperties), + WITH_DATA(ascii_char_ptr_no_quotes, Commands), + + /* Commands implemented by the device */ + WITH_ACTION(SetTemperature, int, temperature), + WITH_ACTION(SetHumidity, int, humidity) +); + +END_NAMESPACE(Contoso); + +EXECUTE_COMMAND_RESULT SetTemperature(Thermostat* thermostat, int temperature) +{ + LogInfo("Received temperature %d\r\n", temperature); + thermostat->Temperature = temperature; + return EXECUTE_COMMAND_SUCCESS; +} + +EXECUTE_COMMAND_RESULT SetHumidity(Thermostat* thermostat, int humidity) +{ + LogInfo("Received humidity %d\r\n", humidity); + thermostat->Humidity = humidity; + return EXECUTE_COMMAND_SUCCESS; +} + +static void sendMessage(IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle, const unsigned char* buffer, size_t size) +{ + IOTHUB_MESSAGE_HANDLE messageHandle = IoTHubMessage_CreateFromByteArray(buffer, size); + if (messageHandle == NULL) + { + LogInfo("unable to create a new IoTHubMessage\r\n"); + } + else + { + if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messageHandle, NULL, NULL) != IOTHUB_CLIENT_OK) + { + LogInfo("failed to hand over the message to IoTHubClient"); + } + else + { + LogInfo("IoTHubClient accepted the message for delivery\r\n"); + } + + IoTHubMessage_Destroy(messageHandle); + } + free((void*)buffer); +} + +/*this function "links" IoTHub to the serialization library*/ +static IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubMessage(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) +{ + IOTHUBMESSAGE_DISPOSITION_RESULT result; + const unsigned char* buffer; + size_t size; + if (IoTHubMessage_GetByteArray(message, &buffer, &size) != IOTHUB_MESSAGE_OK) + { + LogInfo("unable to IoTHubMessage_GetByteArray\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + /*buffer is not zero terminated*/ + char* temp = malloc(size + 1); + if (temp == NULL) + { + LogInfo("failed to malloc\r\n"); + result = EXECUTE_COMMAND_ERROR; + } + else + { + EXECUTE_COMMAND_RESULT executeCommandResult; + + memcpy(temp, buffer, size); + temp[size] = '\0'; + executeCommandResult = EXECUTE_COMMAND(userContextCallback, temp); + result = + (executeCommandResult == EXECUTE_COMMAND_ERROR) ? IOTHUBMESSAGE_ABANDONED : + (executeCommandResult == EXECUTE_COMMAND_SUCCESS) ? IOTHUBMESSAGE_ACCEPTED : + IOTHUBMESSAGE_REJECTED; + free(temp); + } + } + return result; +} + +void remote_monitoring_run(void) +{ + initBme(); + + srand((unsigned int)time(NULL)); + if (serializer_init(NULL) != SERIALIZER_OK) + { + LogInfo("Failed on serializer_init\r\n"); + } + else + { + IOTHUB_CLIENT_CONFIG config; + IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle; + + config.deviceId = deviceId; + config.deviceKey = deviceKey; + config.iotHubName = hubName; + config.iotHubSuffix = hubSuffix; + config.protocol = HTTP_Protocol; + + iotHubClientHandle = IoTHubClient_LL_Create(&config); + if (iotHubClientHandle == NULL) + { + LogInfo("Failed on IoTHubClient_CreateFromConnectionString\r\n"); + } + else + { +#ifdef MBED_BUILD_TIMESTAMP + // For mbed add the certificate information + if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) + { + LogInfo("failure to set option \"TrustedCerts\"\r\n"); + } +#endif // MBED_BUILD_TIMESTAMP + + Thermostat* thermostat = CREATE_MODEL_INSTANCE(Contoso, Thermostat); + if (thermostat == NULL) + { + LogInfo("Failed on CREATE_MODEL_INSTANCE\r\n"); + } + else + { + STRING_HANDLE commandsMetadata; + + if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, IoTHubMessage, thermostat) != IOTHUB_CLIENT_OK) + { + LogInfo("unable to IoTHubClient_SetMessageCallback\r\n"); + } + else + { + + /* send the device info upon startup so that the cloud app knows + what commands are available and the fact that the device is up */ + thermostat->ObjectType = "DeviceInfo"; + thermostat->IsSimulatedDevice = false; + thermostat->Version = "1.0"; + thermostat->DeviceProperties.HubEnabledState = true; + thermostat->DeviceProperties.DeviceID = (char*)deviceId; + + commandsMetadata = STRING_new(); + if (commandsMetadata == NULL) + { + LogInfo("Failed on creating string for commands metadata\r\n"); + } + else + { + /* Serialize the commands metadata as a JSON string before sending */ + if (SchemaSerializer_SerializeCommandMetadata(GET_MODEL_HANDLE(Contoso, Thermostat), commandsMetadata) != SCHEMA_SERIALIZER_OK) + { + LogInfo("Failed serializing commands metadata\r\n"); + } + else + { + unsigned char* buffer; + size_t bufferSize; + thermostat->Commands = (char*)STRING_c_str(commandsMetadata); + + /* Here is the actual send of the Device Info */ + if (SERIALIZE(&buffer, &bufferSize, thermostat->ObjectType, thermostat->Version, thermostat->IsSimulatedDevice, thermostat->DeviceProperties, thermostat->Commands) != IOT_AGENT_OK) + { + LogInfo("Failed serializing\r\n"); + } + else + { + sendMessage(iotHubClientHandle, buffer, bufferSize); + } + + } + + STRING_delete(commandsMetadata); + } + + thermostat->DeviceId = (char*)deviceId; + int sendCycle = 10; + int currentCycle = 0; + while (1) + { + if(currentCycle >= sendCycle) { + float Temp; + float Humi; + getNextSample(&Temp, &Humi); + //thermostat->Temperature = 50 + (rand() % 10 + 2); + thermostat->Temperature = (int)round(Temp); + thermostat->ExternalTemperature = 55 + (rand() % 5 + 2); + //thermostat->Humidity = 50 + (rand() % 8 + 2); + thermostat->Humidity = (int)round(Humi); + currentCycle = 0; + unsigned char*buffer; + size_t bufferSize; + + LogInfo("Sending sensor value Temperature = %d, Humidity = %d\r\n", thermostat->Temperature, thermostat->Humidity); + + if (SERIALIZE(&buffer, &bufferSize, thermostat->DeviceId, thermostat->Temperature, thermostat->Humidity, thermostat->ExternalTemperature) != IOT_AGENT_OK) + { + LogInfo("Failed sending sensor value\r\n"); + } + else + { + sendMessage(iotHubClientHandle, buffer, bufferSize); + } + } + + IoTHubClient_LL_DoWork(iotHubClientHandle); + ThreadAPI_Sleep(100); + currentCycle++; + } + } + + DESTROY_MODEL_INSTANCE(thermostat); + } + IoTHubClient_LL_Destroy(iotHubClientHandle); + } + serializer_deinit(); + + } +} diff --git a/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.h b/examples/samd/remote_monitoring/remote_monitoring.h similarity index 63% rename from examples/sdk/iothub_client_sample_http/iothub_client_sample_http.h rename to examples/samd/remote_monitoring/remote_monitoring.h index da1f6c5..0807f8b 100644 --- a/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.h +++ b/examples/samd/remote_monitoring/remote_monitoring.h @@ -1,17 +1,17 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#ifndef SENDEVENTASYNC_H -#define SENDEVENTASYNC_H +#ifndef REMOTE_MONITORING_H +#define REMOTE_MONITORING_H #ifdef __cplusplus extern "C" { #endif - void iothub_client_sample_http_run(void); +void remote_monitoring_run(void); #ifdef __cplusplus } #endif -#endif /* SENDEVENTASYNC_H */ +#endif /* REMOTE_MONITORING_H */ diff --git a/examples/samd/remote_monitoring/remote_monitoring.ino b/examples/samd/remote_monitoring/remote_monitoring.ino new file mode 100644 index 0000000..bf957b8 --- /dev/null +++ b/examples/samd/remote_monitoring/remote_monitoring.ino @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// Use Arduino IDE 1.6.8 or later. + +#include +#include +#include +#include +#include +#include +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#include +#include +#include +#include +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) +#include +#endif +#include "remote_monitoring.h" +#include "NTPClient.h" + + +#ifdef ARDUINO_SAMD_FEATHER_M0 +#define WINC_CS 8 +#define WINC_IRQ 7 +#define WINC_RST 4 +#define WINC_EN 2 + +// Setup the WINC1500 connection with the pins above and the default hardware SPI. +Adafruit_WINC1500 WiFi(WINC_CS, WINC_IRQ, WINC_RST); +#endif + + +static const char ssid[] = "[Your WiFi network SSID or name]"; +static const char pass[] = "[Your WiFi network WPA password or WEP key]"; + +int status = WL_IDLE_STATUS; + +void setup() { + // The Feather M0 loses it's COMn connection with every reset. + // This 10 s delay allows you to reselect the COM port and open the serial monitor window. + delay(10000); + + initSerial(); + + #ifdef WINC_EN + pinMode(WINC_EN, OUTPUT); + digitalWrite(WINC_EN, HIGH); + #endif + initWifi(); + + initTime(); +} + +void loop() { + // Run the Remote Monitoring from the Azure IoT Hub C SDK + // You must set the device id, device key, IoT Hub name and IotHub suffix in + // remote_monitoring.c + remote_monitoring_run(); +} + +void initSerial() { + // Start serial and initialize stdout + Serial.begin(115200); + //Serial.setDebugOutput(true); +} + +void initWifi() { + // Attempt to connect to Wifi network: + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + + Serial.println("Connected to wifi"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +void initTime() { +#ifdef ARDUINO_SAMD_FEATHER_M0 + Adafruit_WINC1500UDP _udp; +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) + WiFiUDP _udp; +#endif + + time_t epochTime = (time_t)-1; + + NTPClient ntpClient; + ntpClient.begin(); + + while (true) { + epochTime = ntpClient.getEpochTime("0.pool.ntp.org"); + + if (epochTime == (time_t)-1) { + Serial.println("Fetching NTP epoch time failed! Waiting 5 seconds to retry."); + delay(5000); + } else { + Serial.print("Fetched NTP epoch time is: "); + Serial.println(epochTime); + break; + } + } + + ntpClient.end(); + + struct timeval tv; + tv.tv_sec = epochTime; + tv.tv_usec = 0; + + settimeofday(&tv, NULL); +} + + diff --git a/src/util/NTPClient.cpp b/examples/samd/simplesample_http/NTPClient.cpp similarity index 98% rename from src/util/NTPClient.cpp rename to examples/samd/simplesample_http/NTPClient.cpp index 9595a8b..3901a22 100644 --- a/src/util/NTPClient.cpp +++ b/examples/samd/simplesample_http/NTPClient.cpp @@ -1,6 +1,7 @@ // Copyright (c) Arduino. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#if defined(ARDUINO_ARCH_SAMD) #include "NTPClient.h" #define LOCAL_UDP_PORT 2390 @@ -88,3 +89,4 @@ uint32_t NTPClient::parseResponse() return epoch; } +#endif \ No newline at end of file diff --git a/src/util/NTPClient.h b/examples/samd/simplesample_http/NTPClient.h similarity index 72% rename from src/util/NTPClient.h rename to examples/samd/simplesample_http/NTPClient.h index d4683c3..b160f3d 100644 --- a/src/util/NTPClient.h +++ b/examples/samd/simplesample_http/NTPClient.h @@ -1,8 +1,13 @@ // Copyright (c) Arduino. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) #include #include +#endif #ifndef NTPCLIENT_H #define NTPCLIENT_H @@ -27,7 +32,12 @@ class NTPClient uint32_t parseResponse(); char _buffer[NTP_PACKET_SIZE]; +#ifdef ARDUINO_SAMD_FEATHER_M0 + Adafruit_WINC1500UDP _udp; +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) WiFiUDP _udp; +#endif + }; -#endif +#endif \ No newline at end of file diff --git a/examples/sdk/simplesample_http/simplesample_http.c b/examples/samd/simplesample_http/simplesample_http.c similarity index 96% rename from examples/sdk/simplesample_http/simplesample_http.c rename to examples/samd/simplesample_http/simplesample_http.c index ea97385..2e03948 100644 --- a/examples/sdk/simplesample_http/simplesample_http.c +++ b/examples/samd/simplesample_http/simplesample_http.c @@ -11,20 +11,9 @@ That does not mean that HTTP only works with the _LL APIs. Simply changing the using the convenience layer (functions not having _LL) and removing calls to _DoWork will yield the same results. */ -#ifdef ARDUINO #include "AzureIoT.h" -#else -#include "serializer.h" -#include "iothub_client_ll.h" -#include "iothubtransporthttp.h" -#include "threadapi.h" -#endif -#ifdef MBED_BUILD_TIMESTAMP -#include "certs.h" -#endif // MBED_BUILD_TIMESTAMP - -static const char* connectionString = "[device connection string]"; +static const char* connectionString = "HostName=[host].azure-devices.net;DeviceId=[device];SharedAccessKey=[key]"; // Define the Model BEGIN_NAMESPACE(WeatherStation); diff --git a/src/sdk/certs.h b/examples/samd/simplesample_http/simplesample_http.h similarity index 60% rename from src/sdk/certs.h rename to examples/samd/simplesample_http/simplesample_http.h index b22ad1d..baf6975 100644 --- a/src/sdk/certs.h +++ b/examples/samd/simplesample_http/simplesample_http.h @@ -1,18 +1,17 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#ifndef CERTS_H -#define CERTS_H +#ifndef SIMPLESAMPLEHTTP_H +#define SIMPLESAMPLEHTTP_H #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif - extern const char certificates[]; + void simplesample_http_run(void); #ifdef __cplusplus } #endif -#endif /* CERTS_H */ +#endif /* SIMPLESAMPLEHTTP_H */ diff --git a/examples/samd/simplesample_http/simplesample_http.ino b/examples/samd/simplesample_http/simplesample_http.ino new file mode 100644 index 0000000..23f813e --- /dev/null +++ b/examples/samd/simplesample_http/simplesample_http.ino @@ -0,0 +1,124 @@ +// Copyright (c) Arduino. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +#include + +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#include +#include +#include +#include +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) +#include +#endif + +#include "simplesample_http.h" +#include "NTPClient.h" +#include +#include + +#ifdef ARDUINO_SAMD_FEATHER_M0 +// Define the WINC1500 board connections below. +// If you're following the Adafruit WINC1500 board +// guide you don't need to modify these: +#define WINC_CS 8 +#define WINC_IRQ 7 +#define WINC_RST 4 +#define WINC_EN 2 // or, tie EN to VCC +// The SPI pins of the WINC1500 (SCK, MOSI, MISO) should be +// connected to the hardware SPI port of the Arduino. +// On an Uno or compatible these are SCK = #13, MISO = #12, MOSI = #11. +// On an Arduino Zero use the 6-pin ICSP header, see: +// https://www.arduino.cc/en/Reference/SPI + +// Setup the WINC1500 connection with the pins above and the default hardware SPI. +Adafruit_WINC1500 WiFi(WINC_CS, WINC_IRQ, WINC_RST); +#endif + +char ssid[] = ""; // your network SSID (name) +char pass[] = ""; // your network password (use for WPA, or use as key for WEP) +int status = WL_IDLE_STATUS; + +void setup() { + initSerial(); + initWifi(); + initTime(); +} + +void initSerial() { + Serial.begin(9600); + + while(!Serial) { + ; + } +} + +void initWifi() { +#ifdef WINC_EN + pinMode(WINC_EN, OUTPUT); + digitalWrite(WINC_EN, HIGH); +#endif + + // check for the presence of the shield : + if (WiFi.status() == WL_NO_SHIELD) { + Serial.println("WiFi shield not present"); + // don't continue: + while (true); + } + + // attempt to connect to Wifi network: + while (status != WL_CONNECTED) { + Serial.print("Attempting to connect to SSID: "); + Serial.println(ssid); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(ssid, pass); + + if (status != WL_CONNECTED) { + // wait 10 seconds for connection: + delay(10000); + } + } + + Serial.println("Connected to wifi"); +} + +void initTime() { +#ifdef ARDUINO_SAMD_FEATHER_M0 + Adafruit_WINC1500UDP _udp; +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) + WiFiUDP _udp; +#endif + + time_t epochTime = (time_t)-1; + + NTPClient ntpClient; + ntpClient.begin(); + + while (true) { + epochTime = ntpClient.getEpochTime("0.pool.ntp.org"); + + if (epochTime == (time_t)-1) { + Serial.println("Fetching NTP epoch time failed! Waiting 5 seconds to retry."); + delay(5000); + } else { + Serial.print("Fetched NTP epoch time is: "); + Serial.println(epochTime); + break; + } + } + + ntpClient.end(); + + struct timeval tv; + tv.tv_sec = epochTime; + tv.tv_usec = 0; + + settimeofday(&tv, NULL); + +} + +void loop() { + simplesample_http_run(); +} + diff --git a/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.c b/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.c deleted file mode 100644 index 11286a4..0000000 --- a/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.c +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include -#include - -/* This sample uses the _LL APIs of iothub_client for example purposes. - That does not mean that HTTP only works with the _LL APIs. - Simply changing the using the convenience layer (functions not having _LL) - and removing calls to _DoWork will yield the same results. */ - -#ifdef ARDUINO -#include "AzureIoT.h" -#else -#include "iothub_client_ll.h" -#include "iothub_message.h" -#include "crt_abstractions.h" -#include "iothubtransporthttp.h" -#endif - -#ifdef MBED_BUILD_TIMESTAMP -#include "certs.h" -#endif // MBED_BUILD_TIMESTAMP - -static const char* connectionString = "[device connection string]"; -static int callbackCounter; - -DEFINE_ENUM_STRINGS(IOTHUB_CLIENT_CONFIRMATION_RESULT, IOTHUB_CLIENT_CONFIRMATION_RESULT_VALUES); - -typedef struct EVENT_INSTANCE_TAG -{ - IOTHUB_MESSAGE_HANDLE messageHandle; - int messageTrackingId; // For tracking the messages within the user callback. -} EVENT_INSTANCE; - -static IOTHUBMESSAGE_DISPOSITION_RESULT ReceiveMessageCallback(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback) -{ - int* counter = (int*)userContextCallback; - const char* buffer; - size_t size; - if (IoTHubMessage_GetByteArray(message, (const unsigned char**)&buffer, &size) != IOTHUB_MESSAGE_OK) - { - printf("unable to retrieve the message data\r\n"); - } - else - { - (void)printf("Received Message [%d] with Data: <<<%.*s>>> & Size=%d\r\n", *counter, (int)size, buffer, (int)size); - } - - // Retrieve properties from the message - MAP_HANDLE mapProperties = IoTHubMessage_Properties(message); - if (mapProperties != NULL) - { - const char*const* keys; - const char*const* values; - size_t propertyCount = 0; - if (Map_GetInternals(mapProperties, &keys, &values, &propertyCount) == MAP_OK) - { - if (propertyCount > 0) - { - printf("Message Properties:\r\n"); - for (size_t index = 0; index < propertyCount; index++) - { - printf("\tKey: %s Value: %s\r\n", keys[index], values[index]); - } - printf("\r\n"); - } - } - } - - /* Some device specific action code goes here... */ - (*counter)++; - return IOTHUBMESSAGE_ACCEPTED; -} - -static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback) -{ - EVENT_INSTANCE* eventInstance = (EVENT_INSTANCE*)userContextCallback; - (void)printf("Confirmation[%d] received for message tracking id = %d with result = %s\r\n", callbackCounter, eventInstance->messageTrackingId, ENUM_TO_STRING(IOTHUB_CLIENT_CONFIRMATION_RESULT, result)); - /* Some device specific action code goes here... */ - callbackCounter++; - IoTHubMessage_Destroy(eventInstance->messageHandle); -} - -static char msgText[1024]; -static char propText[1024]; -#define MESSAGE_COUNT 5 - -void iothub_client_sample_http_run(void) -{ - IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle; - - EVENT_INSTANCE messages[MESSAGE_COUNT]; - - srand((unsigned int)time(NULL)); - double avgWindSpeed = 10.0; - - callbackCounter = 0; - int receiveContext = 0; - - (void)printf("Starting the IoTHub client sample HTTP...\r\n"); - - if ((iotHubClientHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, HTTP_Protocol)) == NULL) - { - (void)printf("ERROR: iotHubClientHandle is NULL!\r\n"); - } - else - { - unsigned int timeout = 241000; - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "timeout", &timeout) != IOTHUB_CLIENT_OK) - { - printf("failure to set option \"timeout\"\r\n"); - } - - unsigned int minimumPollingTime = 9; /*because it can poll "after 9 seconds" polls will happen effectively at ~10 seconds*/ - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "MinimumPollingTime", &minimumPollingTime) != IOTHUB_CLIENT_OK) - { - printf("failure to set option \"MinimumPollingTime\"\r\n"); - } - -#ifdef MBED_BUILD_TIMESTAMP - // For mbed add the certificate information - if (IoTHubClient_LL_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK) - { - printf("failure to set option \"TrustedCerts\"\r\n"); - } -#endif // MBED_BUILD_TIMESTAMP - - /* Setting Message call back, so we can receive Commands. */ - if (IoTHubClient_LL_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext) != IOTHUB_CLIENT_OK) - { - (void)printf("ERROR: IoTHubClient_LL_SetMessageCallback..........FAILED!\r\n"); - } - else - { - (void)printf("IoTHubClient_LL_SetMessageCallback...successful.\r\n"); - - /* Now that we are ready to receive commands, let's send some messages */ - for (int i = 0; i < MESSAGE_COUNT; i++) - { - sprintf_s(msgText, sizeof(msgText), "{\"deviceId\": \"myFirstDevice\",\"windSpeed\": %.2f}", avgWindSpeed + (rand() % 4 + 2)); - if ((messages[i].messageHandle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText))) == NULL) - { - (void)printf("ERROR: iotHubMessageHandle is NULL!\r\n"); - } - else - { - messages[i].messageTrackingId = i; - - MAP_HANDLE propMap = IoTHubMessage_Properties(messages[i].messageHandle); - sprintf_s(propText, sizeof(propText), "PropMsg_%d", i); - if (Map_AddOrUpdate(propMap, "PropName", propText) != MAP_OK) - { - (void)printf("ERROR: Map_AddOrUpdate Failed!\r\n"); - } - - if (IoTHubClient_LL_SendEventAsync(iotHubClientHandle, messages[i].messageHandle, SendConfirmationCallback, &messages[i]) != IOTHUB_CLIENT_OK) - { - (void)printf("ERROR: IoTHubClient_LL_SendEventAsync..........FAILED!\r\n"); - } - else - { - (void)printf("IoTHubClient_LL_SendEventAsync accepted message [%d] for transmission to IoT Hub.\r\n", i); - } - - } - } - } - - /* Wait for Commands. */ - while (1) - { - IoTHubClient_LL_DoWork(iotHubClientHandle); - } - - IoTHubClient_LL_Destroy(iotHubClientHandle); - } -} diff --git a/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.ino b/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.ino deleted file mode 100644 index 86c1aa8..0000000 --- a/examples/sdk/iothub_client_sample_http/iothub_client_sample_http.ino +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Arduino. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include - -#include "iothub_client_sample_http.h" - -char ssid[] = "yourNetwork"; // your network SSID (name) -char pass[] = "secretPassword"; // your network password (use for WPA, or use as key for WEP) -int status = WL_IDLE_STATUS; - -void setup() { - Serial.begin(9600); - - // check for the presence of the shield : - if (WiFi.status() == WL_NO_SHIELD) { - Serial.println("WiFi shield not present"); - // don't continue: - while (true); - } - - // attempt to connect to Wifi network: - while (status != WL_CONNECTED) { - Serial.print("Attempting to connect to SSID: "); - Serial.println(ssid); - // Connect to WPA/WPA2 network. Change this line if using open or WEP network: - status = WiFi.begin(ssid, pass); - - if (status != WL_CONNECTED) { - // wait 10 seconds for connection: - delay(10000); - } - } - Serial.println("Connected to wifi"); -} - -void loop() { - iothub_client_sample_http_run(); -} - diff --git a/examples/sdk/simplesample_http/simplesample_http.ino b/examples/sdk/simplesample_http/simplesample_http.ino deleted file mode 100644 index 75d9c62..0000000 --- a/examples/sdk/simplesample_http/simplesample_http.ino +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) Arduino. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include - -#include "simplesample_http.h" - -char ssid[] = "yourNetwork"; // your network SSID (name) -char pass[] = "secretPassword"; // your network password (use for WPA, or use as key for WEP) -int status = WL_IDLE_STATUS; - -void setup() { - Serial.begin(9600); - - // check for the presence of the shield : - if (WiFi.status() == WL_NO_SHIELD) { - Serial.println("WiFi shield not present"); - // don't continue: - while (true); - } - - // attempt to connect to Wifi network: - while (status != WL_CONNECTED) { - Serial.print("Attempting to connect to SSID: "); - Serial.println(ssid); - // Connect to WPA/WPA2 network. Change this line if using open or WEP network: - status = WiFi.begin(ssid, pass); - - if (status != WL_CONNECTED) { - // wait 10 seconds for connection: - delay(10000); - } - } - Serial.println("Connected to wifi"); -} - -void loop() { - simplesample_http_run(); -} - diff --git a/extras/iothub-client-sample-http-files.txt b/extras/iothub-client-sample-http-files.txt deleted file mode 100644 index b2823ce..0000000 --- a/extras/iothub-client-sample-http-files.txt +++ /dev/null @@ -1,2 +0,0 @@ -c/iothub_client/samples/iothub_client_sample_http/iothub_client_sample_http.h -c/iothub_client/samples/iothub_client_sample_http/iothub_client_sample_http.c diff --git a/extras/sdk-files.txt b/extras/sdk-files.txt deleted file mode 100644 index 3470fa4..0000000 --- a/extras/sdk-files.txt +++ /dev/null @@ -1,91 +0,0 @@ -LICENSE -c/common/adapters/agenttime.c -c/common/inc/agenttime.h -c/common/inc/base64.h -c/common/inc/buffer_.h -c/common/inc/certs.h -c/common/inc/crt_abstractions.h -c/common/inc/doublylinkedlist.h -c/common/inc/gballoc.h -c/common/inc/hmac.h -c/common/inc/hmacsha256.h -c/common/inc/httpapi.h -c/common/inc/httpapiex.h -c/common/inc/httpapiexsas.h -c/common/inc/httpheaders.h -c/common/inc/iot_logging.h -c/common/inc/lock.h -c/common/inc/macro_utils.h -c/common/inc/map.h -c/common/inc/mqttapi.h -c/common/inc/sastoken.h -c/common/inc/sha-private.h -c/common/inc/sha.h -c/common/inc/stdint_ce6.h -c/common/inc/string_tokenizer.h -c/common/inc/strings.h -c/common/inc/threadapi.h -c/common/inc/urlencode.h -c/common/inc/vector.h -c/common/src/base64.c -c/common/src/buffer.c -c/common/src/certs.c -c/common/src/crt_abstractions.c -c/common/src/doublylinkedlist.c -c/common/src/gballoc.c -c/common/src/hmac.c -c/common/src/hmacsha256.c -c/common/src/httpapiex.c -c/common/src/httpapiexsas.c -c/common/src/httpheaders.c -c/common/src/makefile -c/common/src/map.c -c/common/src/sastoken.c -c/common/src/sha1.c -c/common/src/sha224.c -c/common/src/sha384-512.c -c/common/src/string_tokenizer.c -c/common/src/strings.c -c/common/src/urlencode.c -c/common/src/usha.c -c/common/src/vector.c -c/iothub_client/inc/iothub_client.h -c/iothub_client/inc/iothub_client_amqp_internal.h -c/iothub_client/inc/iothub_client_ll.h -c/iothub_client/inc/iothub_client_private.h -c/iothub_client/inc/iothub_message.h -c/iothub_client/inc/iothubtransporthttp.h -c/iothub_client/inc/version.h -c/iothub_client/src/iothub_client.c -c/iothub_client/src/iothub_client_ll.c -c/iothub_client/src/iothub_message.c -c/iothub_client/src/iothubtransporthttp.c -c/iothub_client/src/version.c -c/serializer/inc/agenttypesystem.h -c/serializer/inc/codefirst.h -c/serializer/inc/commanddecoder.h -c/serializer/inc/datamarshaller.h -c/serializer/inc/datapublisher.h -c/serializer/inc/dataserializer.h -c/serializer/inc/iotdevice.h -c/serializer/inc/jsondecoder.h -c/serializer/inc/jsonencoder.h -c/serializer/inc/multitree.h -c/serializer/inc/schema.h -c/serializer/inc/schemalib.h -c/serializer/inc/schemaserializer.h -c/serializer/inc/serializer.h -c/serializer/src/agenttypesystem.c -c/serializer/src/codefirst.c -c/serializer/src/commanddecoder.c -c/serializer/src/datamarshaller.c -c/serializer/src/datapublisher.c -c/serializer/src/dataserializer.c -c/serializer/src/iotdevice.c -c/serializer/src/jsondecoder.c -c/serializer/src/jsonencoder.c -c/serializer/src/makefile -c/serializer/src/multitree.c -c/serializer/src/schema.c -c/serializer/src/schemalib.c -c/serializer/src/schemaserializer.c diff --git a/extras/simplesample-http-files.txt b/extras/simplesample-http-files.txt deleted file mode 100644 index 1852445..0000000 --- a/extras/simplesample-http-files.txt +++ /dev/null @@ -1,2 +0,0 @@ -c/serializer/samples/simplesample_http/simplesample_http.h -c/serializer/samples/simplesample_http/simplesample_http.c diff --git a/extras/sync-sdk.sh b/extras/sync-sdk.sh deleted file mode 100755 index 792a6ec..0000000 --- a/extras/sync-sdk.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -# Copyright (c) Arduino. All rights reserved. -# Licensed under the MIT license. See LICENSE file in the project root for full license information. - -SDK_DIR=$1 -SRC_SDK_DIR=../src/sdk -EXAMPLES_SDK_DIR=../examples/sdk -SDK_METADATA=$SRC_SDK_DIR/metadata.txt - -if [ -z "$SDK_DIR" ] -then - echo "Please specify Azure SDK directory as command line argument!" - exit 1 -fi - -if [ ! -d "$SDK_DIR" ] -then - echo "'$SDK_DIR' does not exist!" - exit 1 -fi - -copy_filelist() { - for file in `cat $1` - do - echo "\t$file" - cp "$2/$file" "$3" - done -} - -echo "Copying SDK C source files over" -copy_filelist "sdk-files.txt" "$SDK_DIR" "$SRC_SDK_DIR" - -echo "Copying SDK sample files over" -copy_filelist "iothub-client-sample-http-files.txt" "$SDK_DIR" "$EXAMPLES_SDK_DIR/iothub_client_sample_http" -copy_filelist "simplesample-http-files.txt" "$SDK_DIR" "$EXAMPLES_SDK_DIR/simplesample_http" - -echo "Storing SDK metadata" -git --git-dir "$SDK_DIR/.git" ls-remote --get-url > "$SDK_METADATA" -git --git-dir "$SDK_DIR/.git" rev-parse --abbrev-ref HEAD >> "$SDK_METADATA" -git --git-dir "$SDK_DIR/.git" rev-parse HEAD >> "$SDK_METADATA" -cat $SDK_METADATA diff --git a/library.properties b/library.properties index b1cecd6..ab0c092 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ -name=AzureIoT -version=0.1.0 +name=AzureIoTHub +version=0.2.0 author=Arduino maintainer=Arduino -sentence=Azure IoT library for Arduino. For the Arduino MKR1000 or Zero and WiFi Shield 101 only. -paragraph=Arduino port of the Azure IoT C device SDK. It allows you to use your Arduino with the Azure IoT Hub. +sentence=Azure IoT library for Arduino. For the Arduino MKR1000 or Zero and WiFi Shield 101, Adafruit Huzzah and Feather M0, or SparkFun Thing. +paragraph=Arduino port of the Azure IoT C device SDK. It allows you to use your Arduino with the Azure IoT Hub. See readme for more details. category=Communication url=https://github.com/arduino-libraries/AzureIoT -architectures=samd +architectures=samd,esp8266 diff --git a/src/AzureIoT.h b/src/AzureIoT.h index 6078ae6..950cf65 100644 --- a/src/AzureIoT.h +++ b/src/AzureIoT.h @@ -8,3 +8,6 @@ #include "sdk/iothub_client_ll.h" #include "sdk/iothub_message.h" #include "sdk/iothubtransporthttp.h" + + + diff --git a/src/esp8266/azcpgmspace.cpp b/src/esp8266/azcpgmspace.cpp new file mode 100644 index 0000000..07a4ab8 --- /dev/null +++ b/src/esp8266/azcpgmspace.cpp @@ -0,0 +1,11 @@ +#if defined(ARDUINO_ARCH_ESP8266) +#include "azcpgmspace.h" + +char* az_c_strncpy_P(char* dest, PGM_P src, size_t size) { + return strncpy_P(dest, src, size); +} + +size_t az_c_strlen_P(PGM_P s) { + return strlen_P(s); +} +#endif \ No newline at end of file diff --git a/src/esp8266/azcpgmspace.h b/src/esp8266/azcpgmspace.h new file mode 100644 index 0000000..fb6d1f6 --- /dev/null +++ b/src/esp8266/azcpgmspace.h @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file azcpgmspace.h +* @brief Function prototypes for pgmspace functions that need extern-ing to C +* +* @details These fucntions are just wrappers around existing ones in pgmspace.h that +* are not defined in a way to make them linkable from c libs. +*/ +#if defined(ARDUINO_ARCH_ESP8266) +#ifndef _AZCPGMSPACE_H +#define _AZCPGMSPACE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif +char* az_c_strncpy_P(char* dest, PGM_P src, size_t size); +size_t az_c_strlen_P(PGM_P s); +#ifdef __cplusplus +} +#endif + +#endif // _AZCPGMSPACE_H + +#endif // #if defined(ARDUINO_ARCH_ESP8266) \ No newline at end of file diff --git a/src/esp8266/libc_esp.c b/src/esp8266/libc_esp.c new file mode 100644 index 0000000..77c9128 --- /dev/null +++ b/src/esp8266/libc_esp.c @@ -0,0 +1,116 @@ +#if defined(ARDUINO_ARCH_ESP8266) + +#include +#include +#include + +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" + +int fprintf(FILE* file, const char* format, ...) { + // if(file != stderr || file != stdout) { + // return -1; + // } + int ret; + va_list arglist; + va_start(arglist, format); + ret = ets_vprintf(format, arglist); + va_end(arglist); + return ret; +} + +double difftime (time_t time1, time_t time0) +{ + double t1 = time1, t0 = time0; + return t1 - t0; +} + +// The below code was taken from newlib source and altered +// to remove support for options and build environments we +// do not plan to support. +// Support removed: ALLOW_NEGATIVE_CTYPE_INDEX, _MB_CAPABLE, _MB_EXTENDED_CHARSETS_ISO +// _MB_EXTENDED_CHARSETS_WINDOWS +// Environments not supported: Cygwin + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS 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 + +#define _CTYPE_DATA_0_127 \ + _C, _C, _C, _C, _C, _C, _C, _C, \ + _C, _C|_S, _C|_S, _C|_S, _C|_S, _C|_S, _C, _C, \ + _C, _C, _C, _C, _C, _C, _C, _C, \ + _C, _C, _C, _C, _C, _C, _C, _C, \ + _S|_B, _P, _P, _P, _P, _P, _P, _P, \ + _P, _P, _P, _P, _P, _P, _P, _P, \ + _N, _N, _N, _N, _N, _N, _N, _N, \ + _N, _N, _P, _P, _P, _P, _P, _P, \ + _P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U, \ + _U, _U, _U, _U, _U, _U, _U, _U, \ + _U, _U, _U, _U, _U, _U, _U, _U, \ + _U, _U, _U, _P, _P, _P, _P, _P, \ + _P, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L, \ + _L, _L, _L, _L, _L, _L, _L, _L, \ + _L, _L, _L, _L, _L, _L, _L, _L, \ + _L, _L, _L, _P, _P, _P, _P, _C + +#define _CTYPE_DATA_128_255 \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0, \ + 0, 0, 0, 0, 0, 0, 0, 0 + +_CONST char _ctype_[1 + 256] = { + 0, + _CTYPE_DATA_0_127, + _CTYPE_DATA_128_255 +}; + +_CONST char *__ctype_ptr__ = (char *) _ctype_; + +#endif // (ARDUINO_ARCH_ESP8266) \ No newline at end of file diff --git a/src/esp8266/sscanf.c b/src/esp8266/sscanf.c new file mode 100644 index 0000000..f73fe11 --- /dev/null +++ b/src/esp8266/sscanf.c @@ -0,0 +1,328 @@ +// The below was adapted from https://github.com/embox/embox +// Changes were made to allow for compilation in our config. +// The changes were mostly to remove support for certain funcitonality +// Such as reading from files or standard in +/* +Copyright 2008-2016, Mathematics and Mechanics faculty + of Saint-Petersburg State University. All rights reserved. +Copyright 2008-2016, Embox Ltd. All rights reserved. + +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. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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. +*/ + +#if defined(ARDUINO_ARCH_ESP8266) +#include +#include +#include + +#include "ets_sys.h" +#include "os_type.h" +#include "osapi.h" +#include "mem.h" + +/** + * convert digit character to integer + * @param digit character for converting + * @param base for converting + * @return converted symbol + * @return -1 if error + */ +int ch_to_digit(char ch, int base) { + ch = toupper(ch); + switch (base) { + case 16: { + if (ch >= '0' && ch <= '9') { + return (ch - '0'); + } else if (ch >= 'A' && ch <= 'F') { + return (ch - 'A' + 0x0A); + } + return -1; + } + case 10: { + if (ch >= '0' && ch <= '9') { + return (ch - '0'); + } + return -1; + } + case 8: { + if (ch >= '0' && ch <= '7') { + return (ch - '0'); + } + return -1; + } + default: + return -1; + } + return -1; +} + + +static int scanchar(const char **str) { + int ch; + if ((unsigned int)str >= 2) { + ch = **str; + (*str)++; + return ch; + + } +} + +static bool is_space(int ch) { + if ((ch == ' ') || (ch == '\t') || (ch == '\n')) + return true; + return false; +} + + +#define OPS_LEN_MIN 0x00000040 /* s_char (d, i); u_char (u, o, x, X); s_char* (n) */ +#define OPS_LEN_SHORT 0x00000080 /* short (d, i); u_short (u, o, x, X); short* (n) */ +#define OPS_LEN_LONG 0x00000100 /* long (d, i); u_long (u, o, x, X); wint_t (c); wchar_t(s); long* (n) */ +#define OPS_LEN_LONGLONG 0x00000200 /* llong (d, i); u_llong (u, o, x, X); llong* (n) */ +#define OPS_LEN_MAX 0x00000400 /* intmax_t (d, i); uintmax_t (u, o, x, X); intmax_t* (n) */ +#define OPS_LEN_SIZE 0x00000800 /* size_t (d, i, u, o, x, X); size_t* (n) */ +#define OPS_LEN_PTRDIFF 0x00001000 /* ptrdiff_t (d, i, u, o, x, X); ptrdiff_t* (n) */ +#define OPS_LEN_LONGFP 0x00002000 /* long double (f, F, e, E, g, G, a, A) */ + +static void unscanchar(const char **str, int ch) { + if ((unsigned int) str >= 2) { + (*str) --; + } +} + +static int trim_leading(const char **str) { + int ch; + + do { + ch = scanchar(str); + /*when break*/ + if (ch == EOF) + break; + } while (is_space(ch)); + + unscanchar(str, ch); + return ch; +} + + +static int scan_int(const char **in, int base, int width, int *res) { + int neg = 0; + int dst = 0; + int ch; + int i; + int not_empty = 0; + + if (EOF == (ch = trim_leading(in))) { + return EOF; + } + + if ((ch == '-') || (ch == '+')) { + neg = (ch == '-'); + scanchar(in); + } else { + dst = 0; + } + + for (i = 0; (ch = (int) scanchar(in)) != EOF; i++) { + if (!(base == 10 ? isdigit(ch) : isxdigit(ch)) || (0 == width)) { + unscanchar(in, ch); + /*end conversion*/ + break; + } + not_empty = 1; + dst = base * dst + ch_to_digit(ch, base); + } + + if (!not_empty) { + return -1; + } + + if (neg) + dst = -dst; + *res = dst; + return 0; +} + +static int scan(const char **in, const char *fmt, va_list args) { + int width; + int converted = 0; + int ops_len; + int err; + + while (*fmt != '\0') { + if (*fmt == '%') { + fmt++; + width = 80; + + if (*fmt == '\0') + break; + + if (isdigit((int) *fmt)) + width = 0; + + while (isdigit((int) *fmt)) { + + width = width * 10 + (*fmt++ - '0'); + } + + ops_len = 0; + switch (*fmt) { + case 'h': ops_len = *++fmt != 'h' ? OPS_LEN_SHORT : (++fmt, OPS_LEN_MIN); break; + case 'l': ops_len = *++fmt != 'l' ? OPS_LEN_LONG : (++fmt, OPS_LEN_LONGLONG); break; + case 'j': ops_len = OPS_LEN_MAX; ++fmt; break; + case 'z': ops_len = OPS_LEN_SIZE; ++fmt; break; + case 't': ops_len = OPS_LEN_PTRDIFF; ++fmt; break; + case 'L': ops_len = OPS_LEN_LONGFP; ++fmt; break; + } + if (*fmt == '\0') + break; + + switch (*fmt) { + case 's': { + char *dst = va_arg(args, char*); + char ch; + while (isspace(ch = scanchar(in))); + + while (ch != (char) EOF && width--) { + if (isspace(ch)) + break; + + width--; + *dst++ = (char) ch; + ch = scanchar(in); + } + + if (width == 80) // XXX + converted = EOF; + else { + *dst = '\0'; + ++converted; + } + } + break; + case 'c': { + int dst; + + dst = scanchar(in); + *va_arg(args, char*) = dst; + + if (dst == (char) EOF) + converted = EOF; + else + ++converted; + } + break; + case 'u': { + int dst; + if (0 != (err = scan_int(in, 10, width, &dst))) { + if (err == EOF) + converted = EOF; + goto out; + } + + switch (ops_len) { + default: /* FIXME */ + *va_arg(args, unsigned int*) = dst; + break; + case OPS_LEN_MIN: + *va_arg(args, unsigned char*) = dst; + break; + case OPS_LEN_SHORT: + *va_arg(args, unsigned short*) = dst; + break; + } + + ++converted; + } + break; + case 'f': /* TODO float scanf haven't realized */ + case 'd': { + int dst; + if (0 != (err = scan_int(in, 10, width, &dst))) { + if (err == EOF) + converted = EOF; + goto out; + } + + switch (ops_len) { + default: /* FIXME */ + memcpy(va_arg(args, int*), &dst, sizeof(dst)); + break; + case OPS_LEN_MIN: + *va_arg(args, char*) = dst; + break; + case OPS_LEN_SHORT: + *va_arg(args, short*) = dst; + break; + } + + ++converted; + } + break; + case 'o': { + int dst; + if (0 != (err = scan_int(in, 8, width, &dst))) { + if (err == EOF) + converted = EOF; + goto out; + } + + *va_arg(args, int*) = dst; + + ++converted; + } + break; + case 'x': { + int dst; + if (0 != (err = scan_int(in, 16, width, &dst))) { + if (err == EOF) + converted = EOF; + goto out; + } + *va_arg(args, int*) = dst; + + ++converted; + } + break; + } + fmt++; + } else { + if (*fmt++ != *(*in)++) { + return converted; + } + } + } + +out: + return converted; +} + +int sscanf(const char *out, const char *format, ...) { + va_list args; + int rv; + + va_start(args, format); + rv = scan(&out, format, args); + va_end (args); + + return rv; +} +#endif // (ARDUINO_ARCH_ESP8266) \ No newline at end of file diff --git a/src/httpapi.cpp b/src/httpapi.cpp index 6dc68fe..608b2a2 100644 --- a/src/httpapi.cpp +++ b/src/httpapi.cpp @@ -5,10 +5,9 @@ #include #include "sdk/httpapi.h" -#include "sdk/iot_logging.h" +#include "iot_logging.h" #include "util/HTTPSClient.h" -#include "util/NTPClient.h" #define POOL_SIZE 1 @@ -16,30 +15,6 @@ HTTPSClient httpsClients[POOL_SIZE]; HTTPAPI_RESULT HTTPAPI_Init(void) { - time_t epochTime = (time_t)-1; - NTPClient ntpClient; - - ntpClient.begin(); - while (true) { - epochTime = ntpClient.getEpochTime("0.pool.ntp.org"); - - if (epochTime == (time_t)-1) { - LogError("Fetching NTP epoch time failed!\n"); - delay(5000); - } else { - LogInfo("Fetched NTP epoch time is: %lu\n", epochTime); - break; - } - } - ntpClient.end(); - - - struct timeval tv; - tv.tv_sec = epochTime; - tv.tv_usec = 0; - - settimeofday(&tv, NULL); - for (int i = 0; i < POOL_SIZE; i++) { httpsClients[i] = HTTPSClient(); httpsClients[i].setTimeout(10000); @@ -92,7 +67,7 @@ static const char* HTTPRequestTypes[] = { "PATCH" }; -HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, +HTTPAPI_RESULT HTTPAPI_ExecuteRequest(HTTP_HANDLE handle, HTTPAPI_REQUEST_TYPE requestType, const char* relativePath, HTTP_HEADERS_HANDLE httpHeadersHandle, const unsigned char* content, size_t contentLength, unsigned int* statusCode, diff --git a/src/iot_logging.h b/src/iot_logging.h new file mode 100644 index 0000000..8a9955e --- /dev/null +++ b/src/iot_logging.h @@ -0,0 +1,54 @@ +#include +#define STRINGIFY(a) (#a) + +/* +ESP8266 has limited RAM so we force all logging strings to PROGMEM (flash) +and we leverage os_printf rather than printf. + +Time inclusion in errors is possible if NTP was successful, and can be added +in a later revision. +*/ + +#if defined(ARDUINO_ARCH_ESP8266) +#include "esp8266/azcpgmspace.h" +#define LogUsage(FORMAT, ...) { \ + const char* __localFORMAT = PSTR(FORMAT); \ + size_t __localFORMATSIZE = az_c_strlen_P(__localFORMAT) + 1; \ + char* __localREALSTR = (char*)malloc(__localFORMATSIZE * sizeof(char)); \ + __localREALSTR[__localFORMATSIZE-1] = '\0'; \ + az_c_strncpy_P(__localREALSTR, __localFORMAT, __localFORMATSIZE); \ + os_printf(__localREALSTR, ##__VA_ARGS__); \ + free(__localREALSTR); \ +} +#define LogInfo(FORMAT, ...) { \ + const char* __localFORMAT = PSTR(FORMAT); \ + size_t __localFORMATSIZE = az_c_strlen_P(__localFORMAT) + 1; \ + char* __localREALSTR = (char*)malloc(__localFORMATSIZE * sizeof(char)); \ + __localREALSTR[__localFORMATSIZE-1] = '\0'; \ + az_c_strncpy_P(__localREALSTR, __localFORMAT, __localFORMATSIZE); \ + os_printf(__localREALSTR, ##__VA_ARGS__); \ + free(__localREALSTR); \ +} + +#define LogError(FORMAT, ...) { \ + const char* __localFORMAT = PSTR(FORMAT); \ + size_t __localFORMATSIZE = az_c_strlen_P(__localFORMAT) + 1; \ + char* __localREALSTR = (char*)malloc(__localFORMATSIZE * sizeof(char)); \ + __localREALSTR[__localFORMATSIZE-1] = '\0'; \ + az_c_strncpy_P(__localREALSTR, __localFORMAT, __localFORMATSIZE); \ + os_printf(__localREALSTR, ##__VA_ARGS__); \ + free(__localREALSTR); \ +} + +#elif defined(ARDUINO_ARCH_SAMD) +#define LogUsage (void)printf +#define LogInfo(...) (void)printf("Info: " __VA_ARGS__) +#if defined _MSC_VER +#define LogError(FORMAT, ...) { time_t t = time(NULL); (void)fprintf(stderr,"Error: Time:%.24s File:%s Func:%s Line:%d " FORMAT, ctime(&t), __FILE__, __FUNCDNAME__, __LINE__, __VA_ARGS__); } +#else +#define LogError(FORMAT, ...) { time_t t = time(NULL); (void)fprintf(stderr,"Error: Time:%.24s File:%s Func:%s Line:%d " FORMAT, ctime(&t), __FILE__, __func__, __LINE__, ##__VA_ARGS__); } +#endif +#endif + + + diff --git a/src/stdio.cpp b/src/samd/stdio.cpp similarity index 95% rename from src/stdio.cpp rename to src/samd/stdio.cpp index fd6a988..2d7e307 100644 --- a/src/stdio.cpp +++ b/src/samd/stdio.cpp @@ -1,6 +1,6 @@ // Copyright (c) Arduino. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. - +#if defined(ARDUINO_ARCH_SAMD) #include #include "Arduino.h" @@ -29,3 +29,4 @@ extern "C" { return nChars; } } +#endif \ No newline at end of file diff --git a/src/time.cpp b/src/samd/time.cpp similarity index 93% rename from src/time.cpp rename to src/samd/time.cpp index 10d4eb0..62738b8 100644 --- a/src/time.cpp +++ b/src/samd/time.cpp @@ -1,6 +1,6 @@ // Copyright (c) Arduino. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. - +#if defined(ARDUINO_ARCH_SAMD) #include #include @@ -25,3 +25,4 @@ extern "C" { return 0; } } +#endif \ No newline at end of file diff --git a/src/sdk/LICENSE b/src/sdk/LICENSE deleted file mode 100644 index 49255e0..0000000 --- a/src/sdk/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -Microsoft Azure IoT SDKs -Copyright (c) Microsoft Corporation -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the ""Software""), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. \ No newline at end of file diff --git a/src/sdk/README.md b/src/sdk/README.md deleted file mode 100644 index 0474892..0000000 --- a/src/sdk/README.md +++ /dev/null @@ -1,7 +0,0 @@ -These source files are from the [Microsoft Azure IoT device SDK for C](https://github.com/Azure/azure-iot-sdks/blob/master/c/readme.md). - -Specifically: - - * https://github.com/Azure/azure-iot-sdks/tree/master/c/common (```adapters```, ```inc``` and ```src```) - * https://github.com/Azure/azure-iot-sdks/tree/master/c/iothub_client (```inc``` and ```src```) - * https://github.com/Azure/azure-iot-sdks/tree/master/c/serializer (```inc``` and ```src```) diff --git a/src/sdk/agenttypesystem.c b/src/sdk/agenttypesystem.c index 9040e3d..59271d3 100644 --- a/src/sdk/agenttypesystem.c +++ b/src/sdk/agenttypesystem.c @@ -8,6 +8,7 @@ #include "gballoc.h" #include "agenttypesystem.h" +#include #ifdef _MSC_VER #pragma warning(disable: 4756) /* Known warning for INFINITY */ @@ -17,13 +18,18 @@ #include #include +#include + +/*if ULLONG_MAX is defined by limits.h for whatever reasons... */ +#ifndef ULLONG_MAX +#define ULLONG_MAX 18446744073709551615 +#endif + #include "crt_abstractions.h" #include "jsonencoder.h" #include "multitree.h" -#include "agenttime.h" - #include "iot_logging.h" #define NaN_STRING "NaN" @@ -42,7 +48,25 @@ #define NAN ((float)(INFINITY * 0.0F)) #endif /* NAN */ -#define GUID_STRING_LENGHT 38 +#define GUID_STRING_LENGTH 38 + +// This is an artificial upper limit on floating point string length +// (e.g. the size of the string when printing %f). It is set to twice the +// maximum decimal precision plus 2. 1 for the decimal point and 1 for a +// sign (+/-) +// Unfortunately it is quite possible to print a float larger than this. +// An example of this would be printf("%.*f", MAX_FLOATING_POINT_STRING_LENGTH, 1.3); +// But currently no explicit requests for this exist in the file nor are +// any expected to reasonably occur when being used (numbers that hit +// this limit would be experiencing significant precision loss in storage anyway. +#define MAX_FLOATING_POINT_STRING_LENGTH (DECIMAL_DIG *2 + 2) + +// This maximum length is 11 for 32 bit integers (including the sign) +// optionally increase to 21 if longs are 64 bit +#define MAX_LONG_STRING_LENGTH ( 11 + (10 * (sizeof(long)/ 8))) + +// This is the maximum length for the largest 64 bit number (signed) +#define MAX_ULONG_LONG_STRING_LENGTH 20 DEFINE_ENUM_STRINGS(AGENT_DATA_TYPES_RESULT, AGENT_DATA_TYPES_RESULT_VALUES); @@ -1235,9 +1259,6 @@ void Destroy_AGENT_DATA_TYPE(AGENT_DATA_TYPE* agentData) } } -#define tempBufferSize 10240 -static char tempBuffer[tempBufferSize]; - static char hexDigitToChar(uint8_t hexDigit) { if (hexDigit < 10) return '0' + hexDigit; @@ -1394,53 +1415,115 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const { if (value->value.edmDateTimeOffset.hasFractionalSecond) { - if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.12llu%+.2d:%.2d\"", /*+ in printf forces the sign to appear*/ - value->value.edmDateTimeOffset.dateTime.tm_year+1900, - value->value.edmDateTimeOffset.dateTime.tm_mon+1, - value->value.edmDateTimeOffset.dateTime.tm_mday, - value->value.edmDateTimeOffset.dateTime.tm_hour, - value->value.edmDateTimeOffset.dateTime.tm_min, - value->value.edmDateTimeOffset.dateTime.tm_sec, - value->value.edmDateTimeOffset.fractionalSecond, - value->value.edmDateTimeOffset.timeZoneHour, - value->value.edmDateTimeOffset.timeZoneMinute) < 0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } - else if (STRING_concat(destination, tempBuffer) != 0) + size_t tempBufferSize = 1 + // \" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // . + MAX_ULONG_LONG_STRING_LENGTH + // %.12llu + 1 + MAX_LONG_STRING_LENGTH + // %+.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + //\" + 1; // " (terminating NULL); + + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) { result = AGENT_DATA_TYPES_ERROR; LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); } else { - result = AGENT_DATA_TYPES_OK; + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.12llu%+.2d:%.2d\"", /*+ in printf forces the sign to appear*/ + value->value.edmDateTimeOffset.dateTime.tm_year+1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec, + value->value.edmDateTimeOffset.fractionalSecond, + value->value.edmDateTimeOffset.timeZoneHour, + value->value.edmDateTimeOffset.timeZoneMinute) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + // Clean up temp buffer if allocated + free(tempBuffer); } } else { - if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d%+.2d:%.2d\"", /*+ in printf forces the sign to appear*/ - value->value.edmDateTimeOffset.dateTime.tm_year + 1900, - value->value.edmDateTimeOffset.dateTime.tm_mon+1, - value->value.edmDateTimeOffset.dateTime.tm_mday, - value->value.edmDateTimeOffset.dateTime.tm_hour, - value->value.edmDateTimeOffset.dateTime.tm_min, - value->value.edmDateTimeOffset.dateTime.tm_sec, - value->value.edmDateTimeOffset.timeZoneHour, - value->value.edmDateTimeOffset.timeZoneMinute) < 0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } - else if (STRING_concat(destination, tempBuffer) != 0) + size_t tempBufferSize = 1 + // \" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + MAX_LONG_STRING_LENGTH + // %+.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // \" + 1; // " terminating NULL + char* tempBuffer = (char*)malloc(tempBufferSize); + + if (tempBuffer == NULL) { result = AGENT_DATA_TYPES_ERROR; LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); } else { - result = AGENT_DATA_TYPES_OK; + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d%+.2d:%.2d\"", /*+ in printf forces the sign to appear*/ + value->value.edmDateTimeOffset.dateTime.tm_year + 1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec, + value->value.edmDateTimeOffset.timeZoneHour, + value->value.edmDateTimeOffset.timeZoneMinute) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + // Clean up temp buffer if allocated + free(tempBuffer); } } } @@ -1448,53 +1531,108 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const { if (value->value.edmDateTimeOffset.hasFractionalSecond) { - if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.12lluZ\"", /*+ in printf forces the sign to appear*/ - value->value.edmDateTimeOffset.dateTime.tm_year + 1900, - value->value.edmDateTimeOffset.dateTime.tm_mon+1, - value->value.edmDateTimeOffset.dateTime.tm_mday, - value->value.edmDateTimeOffset.dateTime.tm_hour, - value->value.edmDateTimeOffset.dateTime.tm_min, - value->value.edmDateTimeOffset.dateTime.tm_sec, - value->value.edmDateTimeOffset.fractionalSecond) < 0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } - else if (STRING_concat(destination, tempBuffer) != 0) + size_t tempBufferSize = 1 + //\" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // . + MAX_ULONG_LONG_STRING_LENGTH + // %.12llu + 1 + // Z + 1 + // \" + 1; // " (terminating NULL) + char* tempBuffer = (char*)malloc(tempBufferSize); + + if (tempBuffer == NULL) { result = AGENT_DATA_TYPES_ERROR; LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); } else { - result = AGENT_DATA_TYPES_OK; + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.12lluZ\"", /*+ in printf forces the sign to appear*/ + value->value.edmDateTimeOffset.dateTime.tm_year + 1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec, + value->value.edmDateTimeOffset.fractionalSecond) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); } } else { - if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ\"", - value->value.edmDateTimeOffset.dateTime.tm_year + 1900, - value->value.edmDateTimeOffset.dateTime.tm_mon+1, - value->value.edmDateTimeOffset.dateTime.tm_mday, - value->value.edmDateTimeOffset.dateTime.tm_hour, - value->value.edmDateTimeOffset.dateTime.tm_min, - value->value.edmDateTimeOffset.dateTime.tm_sec) < 0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } - else if (STRING_concat(destination, tempBuffer) != 0) + size_t tempBufferSize = 1 + // \" + MAX_LONG_STRING_LENGTH + // %.4d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // - + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // T + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // : + MAX_LONG_STRING_LENGTH + // %.2d + 1 + // Z + 1 + // \" + 1; // " (terminating null); + + char* tempBuffer = (char*)malloc(tempBufferSize); + + if (tempBuffer == NULL) { result = AGENT_DATA_TYPES_ERROR; LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); } else { - result = AGENT_DATA_TYPES_OK; + if (sprintf_s(tempBuffer, tempBufferSize, "\"%.4d-%.2d-%.2dT%.2d:%.2d:%.2dZ\"", + value->value.edmDateTimeOffset.dateTime.tm_year + 1900, + value->value.edmDateTimeOffset.dateTime.tm_mon+1, + value->value.edmDateTimeOffset.dateTime.tm_mday, + value->value.edmDateTimeOffset.dateTime.tm_hour, + value->value.edmDateTimeOffset.dateTime.tm_min, + value->value.edmDateTimeOffset.dateTime.tm_sec) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); } } - - } break; } @@ -1708,7 +1846,9 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const else { /*forward parse the string to scan for " and for \ that in JSON are \" respectively \\*/ - if (tempBufferSize < vlen + 5 * nControlCharacters + nEscapeCharacters + 3) + size_t tempBufferSize = vlen + 5 * nControlCharacters + nEscapeCharacters + 3 + 1; + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) { result = AGENT_DATA_TYPES_ERROR; LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); @@ -1765,6 +1905,8 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const { result = AGENT_DATA_TYPES_OK; } + + free(tempBuffer); } } @@ -1789,6 +1931,7 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const break; } +#ifndef NO_FLOATS case(EDM_SINGLE_TYPE): { /*C89 standard says: When a float is promoted to double or long double, or a double is promoted to long double, its value is unchanged*/ @@ -1832,19 +1975,34 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const result = AGENT_DATA_TYPES_OK; } } - else if(sprintf_s(tempBuffer, tempBufferSize, "%.*f", FLT_DIG, (double)(value->value.edmSingle.value))<0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } - else if (STRING_concat(destination, tempBuffer) != 0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } else { - result = AGENT_DATA_TYPES_OK; + size_t tempBufferSize = MAX_FLOATING_POINT_STRING_LENGTH; + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "%.*f", FLT_DIG, (double)(value->value.edmSingle.value)) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } } break; } @@ -1895,22 +2053,38 @@ AGENT_DATA_TYPES_RESULT AgentDataTypes_ToString(STRING_HANDLE destination, const } } /*Codes_SRS_AGENT_TYPE_SYSTEM_99_022:[ EDM_DOUBLE: doubleValue = decimalValue [ "e" [SIGN] 1*DIGIT ] / nanInfinity ; IEEE 754 binary64 floating-point number (15-17 decimal digits). The representation shall use DBL_DIG C #define*/ - else if(sprintf_s(tempBuffer, tempBufferSize, "%.*f", DBL_DIG, value->value.edmDouble.value)<0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } - else if (STRING_concat(destination, tempBuffer) != 0) - { - result = AGENT_DATA_TYPES_ERROR; - LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); - } else { - result = AGENT_DATA_TYPES_OK; + size_t tempBufferSize = DECIMAL_DIG * 2; + char* tempBuffer = (char*)malloc(tempBufferSize); + if (tempBuffer == NULL) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + if (sprintf_s(tempBuffer, tempBufferSize, "%.*f", DBL_DIG, value->value.edmDouble.value) < 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else if (STRING_concat(destination, tempBuffer) != 0) + { + result = AGENT_DATA_TYPES_ERROR; + LogError("(result = %s)\r\n", ENUM_TO_STRING(AGENT_DATA_TYPES_RESULT, result)); + } + else + { + result = AGENT_DATA_TYPES_OK; + } + + free(tempBuffer); + } } break; } +#endif case(EDM_COMPLEX_TYPE_TYPE) : { @@ -2785,6 +2959,93 @@ static void fill_tm_yday_and_tm_wday(struct tm* source) /*the function shall count days */ } +/*the following function does the same as sscanf(pos2, ":%02d", &sec)*/ +/*this function only exists because of optimizing valgrind time, otherwise sscanf would be just as good*/ +static int sscanf2d(const char *pos2, int* sec) +{ + int result; + size_t position = 1; + if ( + (pos2[0] == ':') && + (scanAndReadNDigitsInt(pos2, strlen(pos2), &position, sec, 2) == 0) + ) + { + result = 1; + } + else + { + result = 0; + } + + return result; + +} + +/*the following function does the same as (sscanf(pos2, ".%llu", &fractionalSeconds) != 1)*/ +static int sscanfllu(const char*pos2, unsigned long long* fractionalSeconds) +{ + int result; + + if (pos2[0] != '.') + { + /*doesn't start with '.' error out*/ + result = 0; + } + else + { + size_t index=1; + *fractionalSeconds = 0; + bool error = true; + while (IS_DIGIT(pos2[index])) + { + if ((ULLONG_MAX - (pos2[index]-'0'))/10 < *fractionalSeconds) + { + /*overflow... */ + error = true; + break; + } + else + { + *fractionalSeconds = *fractionalSeconds * 10 + (pos2[index] - '0'); + error = false; + } + index++; + } + + if (error) + { + result = 0; /*return 0 fields converted*/ + } + else + { + result = 1; /*1 field converted*/ + } + } + return result; +} + +/*the below function replaces sscanf(pos2, "%03d:%02d\"", &hourOffset, &minOffset)*/ +/*return 2 if success*/ + +static int sscanf3d2d(const char* pos2, int* hourOffset, int* minOffset) +{ + size_t position = 0; + int result; + if ( + (scanAndReadNDigitsInt(pos2, strlen(pos2), &position, hourOffset, 2) == 0) && + (pos2 += position, pos2[0] == ':') && + (scanAndReadNDigitsInt(pos2, strlen(pos2), &position, minOffset, 2) == 0) + ) + { + result = 2; + } + else + { + result = 0; + } + return result; +} + AGENT_DATA_TYPES_RESULT CreateAgentDataType_From_String(const char* source, AGENT_DATA_TYPE_TYPE type, AGENT_DATA_TYPE* agentData) { @@ -2920,10 +3181,29 @@ AGENT_DATA_TYPES_RESULT CreateAgentDataType_From_String(const char* source, AGEN /* Codes_SRS_AGENT_TYPE_SYSTEM_99_082:[ EDM_INT32] */ case EDM_INT32_TYPE: { - long long int32Value; - if ((sscanf(source, "%lld", &int32Value) != 1) || - (int32Value < -2147483648LL) || - (int32Value > 2147483647)) + int32_t int32Value; + unsigned char isNegative; + uint32_t uint32Value; + const char* pos; + size_t strLength; + + if (source[0] == '-') + { + isNegative = 1; + pos = &source[1]; + } + else + { + isNegative = 0; + pos = &source[0]; + } + + strLength = strlen(source); + + if ((sscanf(pos, "%" SCNu32, &uint32Value) != 1) || + (strLength > 11) || + ((uint32Value > 2147483648UL) && isNegative) || + ((uint32Value > 2147483647UL) && (!isNegative))) { /* Codes_SRS_AGENT_TYPE_SYSTEM_99_087:[ CreateAgentDataType_From_String shall return AGENT_DATA_TYPES_INVALID_ARG if source is not a valid string for a value of type type.] */ result = AGENT_DATA_TYPES_INVALID_ARG; @@ -2931,6 +3211,22 @@ AGENT_DATA_TYPES_RESULT CreateAgentDataType_From_String(const char* source, AGEN } else { + if (isNegative) + { + if (uint32Value == 2147483648UL) + { + int32Value = -2147483647L - 1L; + } + else + { + int32Value = -(int32_t)uint32Value; + } + } + else + { + int32Value = uint32Value; + } + agentData->type = EDM_INT32_TYPE; agentData->value.edmInt32.value = (int32_t)int32Value; result = AGENT_DATA_TYPES_OK; @@ -3100,7 +3396,7 @@ AGENT_DATA_TYPES_RESULT CreateAgentDataType_From_String(const char* source, AGEN pos2 += 3; if (*pos2 == ':') { - if (sscanf(pos2, ":%02d", &sec) != 1) + if (sscanf2d(pos2, &sec) != 1) { pos2 = NULL; } @@ -3113,7 +3409,7 @@ AGENT_DATA_TYPES_RESULT CreateAgentDataType_From_String(const char* source, AGEN if ((pos2 != NULL) && (*pos2 == '.')) { - if (sscanf(pos2, ".%llu", &fractionalSeconds) != 1) + if (sscanfllu(pos2, &fractionalSeconds) != 1) { pos2 = NULL; } @@ -3378,7 +3674,7 @@ result = AGENT_DATA_TYPES_OK; /*Codes_SRS_AGENT_TYPE_SYSTEM_99_097:[ EDM_GUID]*/ case EDM_GUID_TYPE: { - if (strlen(source) != GUID_STRING_LENGHT) + if (strlen(source) != GUID_STRING_LENGTH) { result = AGENT_DATA_TYPES_INVALID_ARG; } diff --git a/src/sdk/agenttypesystem.h b/src/sdk/agenttypesystem.h index ba8abaf..8a990d1 100644 --- a/src/sdk/agenttypesystem.h +++ b/src/sdk/agenttypesystem.h @@ -53,6 +53,47 @@ typedef struct EDM_BINARY_TAG DEFINE_ENUM(EDM_BOOLEANS, EDM_BOOLEANS_VALUES); +/*ispositiveinfinity*/ + +#ifdef _MSC_VER +#define ISPOSITIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_PINF) == _FPCLASS_PINF)) +#else +#if defined __STDC_VERSION__ +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +/*C99 compiler or C11*/ +#define ISPOSITIVEINFINITY(x) (isinf((x)) && (signbit((x))==0)) +#else +#error update this file to contain the latest C standard. +#endif +#else +#ifdef __cplusplus +#define ISPOSITIVEINFINITY(x) (std::isinf((x)) && (signbit((x))==0)) +#else +#error unknown (or C89) compiler, must provide a definition for ISPOSITIVEINFINITY +#endif +#endif +#endif + +#ifdef _MSC_VER +/*not exactly signbit*/ +#define ISNEGATIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_NINF) == _FPCLASS_NINF)) +#else +#if defined __STDC_VERSION__ +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +/*C99 compiler or C11*/ +#define ISNEGATIVEINFINITY(x) (isinf((x)) && (signbit((x))!=0)) +#else +#error update this file to contain the latest C standard. +#endif +#else +#ifdef __cplusplus +#define ISNEGATIVEINFINITY(x) (std::isinf((x)) && (signbit((x)) != 0)) +#else +#error unknown (or C89) compiler, must provide a definition for ISNEGATIVEINFINITY +#endif +#endif +#endif + /*Binary-valued logic.*/ typedef struct EDM_BOOLEAN_TAG { diff --git a/src/sdk/buffer.c b/src/sdk/buffer.c index 85634fd..67d70ef 100644 --- a/src/sdk/buffer.c +++ b/src/sdk/buffer.c @@ -328,6 +328,52 @@ int BUFFER_append(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2) return result; } +int BUFFER_prepend(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2) +{ + int result; + if ((handle1 == NULL) || (handle2 == NULL) || (handle1 == handle2)) + { + /* : [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __LINE__; + } + else + { + BUFFER* b1 = (BUFFER*)handle1; + BUFFER* b2 = (BUFFER*)handle2; + if (b1->buffer == NULL) + { + /* : [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __LINE__; + } + else if (b2->buffer == NULL) + { + /* : [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __LINE__; + } + else + { + unsigned char* temp = (unsigned char*)malloc(b1->size + b2->size); + if (temp == NULL) + { + /* : [BUFFER_append shall return a nonzero upon any error that is encountered.] */ + result = __LINE__; + } + else + { + // Append the BUFFER + memcpy(temp, b2->buffer, b2->size); + memcpy(&temp[b2->size], b1->buffer, b1->size); + free(b1->buffer); + b1->buffer = temp; + b1->size += b2->size; + result = 0; + } + } + } + return result; +} + + /* Codes_SRS_BUFFER_07_025: [BUFFER_u_char shall return a pointer to the underlying unsigned char*.] */ unsigned char* BUFFER_u_char(BUFFER_HANDLE handle) { diff --git a/src/sdk/buffer_.h b/src/sdk/buffer_.h index 107a1af..118786a 100644 --- a/src/sdk/buffer_.h +++ b/src/sdk/buffer_.h @@ -12,7 +12,7 @@ extern "C" #include #endif -typedef void* BUFFER_HANDLE; +typedef struct BUFFER_TAG* BUFFER_HANDLE; extern BUFFER_HANDLE BUFFER_new(void); extern BUFFER_HANDLE BUFFER_create(const unsigned char* source, size_t size); @@ -24,6 +24,7 @@ extern int BUFFER_enlarge(BUFFER_HANDLE handle, size_t enlargeSize); extern int BUFFER_content(BUFFER_HANDLE handle, const unsigned char** content); extern int BUFFER_size(BUFFER_HANDLE handle, size_t* size); extern int BUFFER_append(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2); +extern int BUFFER_prepend(BUFFER_HANDLE handle1, BUFFER_HANDLE handle2); extern unsigned char* BUFFER_u_char(BUFFER_HANDLE handle); extern size_t BUFFER_length(BUFFER_HANDLE handle); extern BUFFER_HANDLE BUFFER_clone(BUFFER_HANDLE handle); diff --git a/src/sdk/certs.c b/src/sdk/certs.c deleted file mode 100644 index adaf24b..0000000 --- a/src/sdk/certs.c +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -/* This file contains certs needed to communicate with Azure (IoT) */ - -const char certificates[] = -/* Baltimore */ -"-----BEGIN CERTIFICATE-----\r\n" -"MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ\r\n" -"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n" -"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX\r\n" -"DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y\r\n" -"ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy\r\n" -"VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr\r\n" -"mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr\r\n" -"IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK\r\n" -"mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu\r\n" -"XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy\r\n" -"dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye\r\n" -"jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1\r\n" -"BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3\r\n" -"DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92\r\n" -"9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx\r\n" -"jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0\r\n" -"Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz\r\n" -"ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS\r\n" -"R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp\r\n" -"-----END CERTIFICATE-----\r\n" -/* MSIT */ -"-----BEGIN CERTIFICATE-----\r\n" -"MIIFhjCCBG6gAwIBAgIEByeaqTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJJ\r\n" -"RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD\r\n" -"VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTEzMTIxOTIwMDczMloX\r\n" -"DTE3MTIxOTIwMDY1NVowgYsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n\r\n" -"dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y\r\n" -"YXRpb24xFTATBgNVBAsTDE1pY3Jvc29mdCBJVDEeMBwGA1UEAxMVTWljcm9zb2Z0\r\n" -"IElUIFNTTCBTSEEyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0eg3\r\n" -"p3aKcEsZ8CA3CSQ3f+r7eOYFumqtTicN/HJq2WwhxGQRlXMQClwle4hslAT9x9uu\r\n" -"e9xKCLM+FvHQrdswbdcaHlK1PfBHGQPifaa9VxM/VOo6o7F3/ELwY0lqkYAuMEnA\r\n" -"iusrr/466wddBvfp/YQOkb0JICnobl0JzhXT5+/bUOtE7xhXqwQdvDH593sqE8/R\r\n" -"PVGvG8W1e+ew/FO7mudj3kEztkckaV24Rqf/ravfT3p4JSchJjTKAm43UfDtWBpg\r\n" -"lPbEk9jdMCQl1xzrGZQ1XZOyrqopg3PEdFkFUmed2mdROQU6NuryHnYrFK7sPfkU\r\n" -"mYsHbrznDFberL6u23UykJ5jvXS/4ArK+DSWZ4TN0UI4eMeZtgzOtg/pG8v0Wb4R\r\n" -"DsssMsj6gylkeTyLS/AydGzzk7iWa11XWmjBzAx5ihne9UkCXgiAAYkMMs3S1pbV\r\n" -"S6Dz7L+r9H2zobl82k7X5besufIlXwHLjJaoKK7BM1r2PwiQ3Ov/OdgmyBKdHJqq\r\n" -"qcAWjobtZ1KWAH8Nkj092XA25epCbx+uleVbXfjQOsfU3neG0PyeTuLiuKloNwnE\r\n" -"OeOFuInzH263bR9KLxgJb95KAY8Uybem7qdjnzOkVHxCg2i4pd+/7LkaXRM72a1o\r\n" -"/SAKVZEhZPnXEwGgCF1ZiRtEr6SsxwUQ+kFKqPsCAwEAAaOCASAwggEcMBIGA1Ud\r\n" -"EwEB/wQIMAYBAf8CAQAwUwYDVR0gBEwwSjBIBgkrBgEEAbE+AQAwOzA5BggrBgEF\r\n" -"BQcCARYtaHR0cDovL2N5YmVydHJ1c3Qub21uaXJvb3QuY29tL3JlcG9zaXRvcnku\r\n" -"Y2ZtMA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUH\r\n" -"AwIwHwYDVR0jBBgwFoAU5Z1ZMIJHWMys+ghUNoZ7OrUETfAwQgYDVR0fBDswOTA3\r\n" -"oDWgM4YxaHR0cDovL2NkcDEucHVibGljLXRydXN0LmNvbS9DUkwvT21uaXJvb3Qy\r\n" -"MDI1LmNybDAdBgNVHQ4EFgQUUa8kJpz0aCJXgCYrO0ZiFXsezKUwDQYJKoZIhvcN\r\n" -"AQELBQADggEBAHaFxSMxH7Rz6qC8pe3fRUNqf2kgG4Cy+xzdqn+I0zFBNvf7+2ut\r\n" -"mIx4H50RZzrNS+yovJ0VGcQ7C6eTzuj8nVvoH8tWrnZDK8cTUXdBqGZMX6fR16p1\r\n" -"xRspTMn0baFeoYWTFsLLO6sUfUT92iUphir+YyDK0gvCNBW7r1t/iuCq7UWm6nnb\r\n" -"2DVmVEPeNzPR5ODNV8pxsH3pFndk6FmXudUu0bSR2ndx80oPSNI0mWCVN6wfAc0Q\r\n" -"negqpSDHUJuzbEl4K1iSZIm4lTaoNKrwQdKVWiRUl01uBcSVrcR6ozn7eQaKm6ZP\r\n" -"2SL6RE4288kPpjnngLJev7050UblVUfbvG4=\r\n" -"-----END CERTIFICATE-----\r\n" -/* *.azure-devices.net */ -"-----BEGIN CERTIFICATE-----\r\n" -"MIIGcjCCBFqgAwIBAgITWgABtrNbz7vBeV0QWwABAAG2szANBgkqhkiG9w0BAQsF\r\n" -"ADCBizELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT\r\n" -"B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEVMBMGA1UE\r\n" -"CxMMTWljcm9zb2Z0IElUMR4wHAYDVQQDExVNaWNyb3NvZnQgSVQgU1NMIFNIQTIw\r\n" -"HhcNMTUwODI3MDMxODA0WhcNMTcwODI2MDMxODA0WjAeMRwwGgYDVQQDDBMqLmF6\r\n" -"dXJlLWRldmljZXMubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\r\n" -"nXC/qBUdlnfIm5K3HYu0o/Mb5tNNcsr0xy4Do0Puwq2W1tz0ZHvIIS9VOANhkNCb\r\n" -"VyOncnP6dvmM/rYYKth/NQ8RUiZOYlROZ0SYC8cvxq9WOln4GXtEU8vNVqJbYrJj\r\n" -"rPMHfxqLzTE/0ZnQffnDT3iMUE9kFLHow0YgaSRU0KZsc9KAROmzBzu+QIB1WGKX\r\n" -"D7CN361tG1UuN68Bz7MSnbgk98Z+DjDxfusoDhiiy/Y9MLOJMt4WIy5BqL3lfLnn\r\n" -"r+JLqmpiFuyVUDacFQDprYJ1/AFgcsKYu/ydmASARPzqJhOGaC2sZP0U5oBOoBzI\r\n" -"bz4tfn8Bi0kJKmS53mQt+wIDAQABo4ICOTCCAjUwCwYDVR0PBAQDAgSwMB0GA1Ud\r\n" -"JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4EFgQUKpYehBSNA53Oxivn\r\n" -"aLCz3+eFUJ0wXQYDVR0RBFYwVIITKi5henVyZS1kZXZpY2VzLm5ldIIaKi5hbXFw\r\n" -"d3MuYXp1cmUtZGV2aWNlcy5uZXSCISouc3UubWFuYWdlbWVudC1henVyZS1kZXZp\r\n" -"Y2VzLm5ldDAfBgNVHSMEGDAWgBRRryQmnPRoIleAJis7RmIVex7MpTB9BgNVHR8E\r\n" -"djB0MHKgcKBuhjZodHRwOi8vbXNjcmwubWljcm9zb2Z0LmNvbS9wa2kvbXNjb3Jw\r\n" -"L2NybC9tc2l0d3d3Mi5jcmyGNGh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kv\r\n" -"bXNjb3JwL2NybC9tc2l0d3d3Mi5jcmwwcAYIKwYBBQUHAQEEZDBiMDwGCCsGAQUF\r\n" -"BzAChjBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL21zY29ycC9tc2l0d3d3\r\n" -"Mi5jcnQwIgYIKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLm1zb2NzcC5jb20wTgYDVR0g\r\n" -"BEcwRTBDBgkrBgEEAYI3KgEwNjA0BggrBgEFBQcCARYoaHR0cDovL3d3dy5taWNy\r\n" -"b3NvZnQuY29tL3BraS9tc2NvcnAvY3BzADAnBgkrBgEEAYI3FQoEGjAYMAoGCCsG\r\n" -"AQUFBwMBMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4ICAQCrjzOSW+X6v+UC\r\n" -"u+JkYyuypXN14pPLcGFbknJWj6DAyFWXKC8ihIYdtf/szWIO7VooplSTZ05u/JYu\r\n" -"ZYh7fAw27qih9CLhhfncXi5yzjgLMlD0mlbORvMJR/nMl7Yh1ki9GyLnpOqMmO+E\r\n" -"yTpOiE07Uyt2uWelLHjMY8kwy2bSRXIp7/+A8qHRaIIdXNtAKIK5jo068BJpo77h\r\n" -"4PljCb9JFdEt6sAKKuaP86Y+8oRZ7YzU4TLDCiK8P8n/gQXH0vvhOE/O0n7gWPqB\r\n" -"n8KxsnRicop6tB6GZy32Stn8w0qktmQNXOGU+hp8OL6irULWZw/781po6d78nmwk\r\n" -"1IFl2TB4+jgyblvJdTM0rx8vPf3F2O2kgsRNs9M5qCI7m+he43Bhue0Fj/h3oIIo\r\n" -"Qx7X/uqc8j3VTNE9hf2A4wksSRgRydjAYoo+bduNagC5s7Eucb4mBG0MMk7HAQU9\r\n" -"m/gyaxqth6ygDLK58wojSV0i4RiU01qZkHzqIWv5FhhMjbFwyKEc6U35Ps7kP/1O\r\n" -"fdGm13ONaYqDl44RyFsLFFiiDYxZFDSsKM0WDxbl9ULAlVc3WR85kEBK6I+pSQj+\r\n" -"7/Z5z2zTz9qOFWgB15SegTbjSR7uk9mEVnj9KDlGtG8W1or0EGrrEDP2CMsp0oEj\r\n" -"VTJbZAxEaZ3cVCKva5sQUxFMjwG32g==\r\n" -"-----END CERTIFICATE-----\r\n"; diff --git a/src/sdk/codefirst.c b/src/sdk/codefirst.c index c37d0f4..91bf989 100644 --- a/src/sdk/codefirst.c +++ b/src/sdk/codefirst.c @@ -441,6 +441,7 @@ SCHEMA_HANDLE CodeFirst_RegisterSchema(const char* schemaNamespace, const REFLEC AGENT_DATA_TYPE_TYPE CodeFirst_GetPrimitiveType(const char* typeName) { +#ifndef NO_FLOATS if (strcmp(typeName, "double") == 0) { return EDM_DOUBLE_TYPE; @@ -449,7 +450,9 @@ AGENT_DATA_TYPE_TYPE CodeFirst_GetPrimitiveType(const char* typeName) { return EDM_SINGLE_TYPE; } - else if (strcmp(typeName, "int") == 0) + else +#endif + if (strcmp(typeName, "int") == 0) { return EDM_INT32_TYPE; } diff --git a/src/sdk/condition.h b/src/sdk/condition.h new file mode 100644 index 0000000..d26940d --- /dev/null +++ b/src/sdk/condition.h @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef CONDITION_H +#define CONDITION_H + +#include "macro_utils.h" +#include "lock.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* COND_HANDLE; + +#define COND_RESULT_VALUES \ + COND_OK, \ + COND_INVALID_ARG, \ + COND_ERROR, \ + COND_TIMEOUT \ + +/** +* @brief Enumeration specifying the lock status. +*/ +DEFINE_ENUM(COND_RESULT, COND_RESULT_VALUES); + +/** +* @brief This API creates and returns a valid condition handle. +* +* @return A valid @c COND_HANDLE when successful or @c NULL otherwise. +*/ +extern COND_HANDLE Condition_Init(void); + +/** +* @brief unblock all currently working condition. +* +* @param handle A valid handle to the lock. +* +* @return Returns @c COND_OK when the condition object has been +* destroyed and @c COND_ERROR when an error occurs +* and @c COND_TIMEOUT when the handle times out. +*/ +extern COND_RESULT Condition_Post(COND_HANDLE handle); + +/** +* @brief block on the condition handle unti the thread is signalled +* or until the timeout_milliseconds is reached. +* +* @param handle A valid handle to the lock. +* +* @return Returns @c COND_OK when the condition object has been +* destroyed and @c COND_ERROR when an error occurs +* and @c COND_TIMEOUT when the handle times out. +*/ +extern COND_RESULT Condition_Wait(COND_HANDLE handle, LOCK_HANDLE lock, int timeout_milliseconds); + +/** +* @brief The condition instance is deinitialized. +* +* @param handle A valid handle to the condition. +* +* @return Returns @c COND_OK when the condition object has been +* destroyed and @c COND_ERROR when an error occurs. +*/ +extern void Condition_Deinit(COND_HANDLE handle); + +#ifdef __cplusplus +} +#endif + +#endif /* LOCK_H */ diff --git a/src/sdk/constmap.c b/src/sdk/constmap.c new file mode 100644 index 0000000..1e031f4 --- /dev/null +++ b/src/sdk/constmap.c @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include "gballoc.h" + +#include "map.h" +#include "constmap.h" +#include "iot_logging.h" +#include "refcount.h" + +DEFINE_ENUM_STRINGS(CONSTMAP_RESULT, CONSTMAP_RESULT_VALUES); + +typedef struct CONSTMAP_HANDLE_DATA_TAG +{ + MAP_HANDLE map; +} CONSTMAP_HANDLE_DATA; + +DEFINE_REFCOUNT_TYPE(CONSTMAP_HANDLE_DATA); + +#define LOG_CONSTMAP_ERROR(result) LogError("result = %s\r\n", ENUM_TO_STRING(CONSTMAP_RESULT, (result))); + +CONSTMAP_HANDLE ConstMap_Create(MAP_HANDLE sourceMap) +{ + CONSTMAP_HANDLE_DATA* result = REFCOUNT_TYPE_CREATE(CONSTMAP_HANDLE_DATA); + + if (result == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_048: [ConstMap_Create shall accept any non-NULL MAP_HANDLE as input.]*/ + /*Codes_SRS_CONSTMAP_17_001: [ConstMap_Create shall create an immutable map, populated by the key, value pairs in the source map.]*/ + result->map = Map_Clone(sourceMap); + if (result->map == NULL) + { + free(result); + /*Codes_SRS_CONSTMAP_17_002: [If during creation there are any errors, then ConstMap_Create shall return NULL.]*/ + result = NULL; + LOG_CONSTMAP_ERROR(CONSTMAP_ERROR); + } + + } + /*Codes_SRS_CONSTMAP_17_003: [Otherwise, it shall return a non-NULL handle that can be used in subsequent calls.]*/ + return (CONSTMAP_HANDLE)result; +} + +void ConstMap_Destroy(CONSTMAP_HANDLE handle) +{ + /*Codes_SRS_CONSTMAP_17_005: [If parameter handle is NULL then ConstMap_Destroy shall take no action.]*/ + if (handle == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_049: [ConstMap_Destroy shall decrement the internal reference count of the immutable map.]*/ + if (DEC_REF(CONSTMAP_HANDLE_DATA, handle) == DEC_RETURN_ZERO) + { + /*Codes_SRS_CONSTMAP_17_004: [If the reference count is zero, ConstMap_Destroy shall release all resources associated with the immutable map.]*/ + Map_Destroy(handle->map); + free(handle); + } + + } +} + +CONSTMAP_HANDLE ConstMap_Clone(CONSTMAP_HANDLE handle) +{ + /*Codes_SRS_CONSTMAP_17_038: [ConstMap_Clone returns NULL if parameter handle is NULL.] */ + if (handle == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_039: [ConstMap_Clone shall increase the internal reference count of the immutable map indicated by parameter handle]*/ + /*Codes_SRS_CONSTMAP_17_050: [ConstMap_Clone shall return the non-NULL handle. ]*/ + INC_REF(CONSTMAP_HANDLE_DATA, handle); + } + return (handle); +} + +static CONSTMAP_RESULT ConstMap_ErrorConvert(MAP_RESULT mapResult) +{ + CONSTMAP_RESULT result; + switch (mapResult) + { + case MAP_OK: + result = CONSTMAP_OK; + break; + case MAP_INVALIDARG: + result = CONSTMAP_INVALIDARG; + break; + case MAP_KEYNOTFOUND: + result = CONSTMAP_KEYNOTFOUND; + break; + default: + result = CONSTMAP_ERROR; + break; + } + return result; +} + +MAP_HANDLE ConstMap_CloneWriteable(CONSTMAP_HANDLE handle) +{ + MAP_HANDLE result = NULL; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_051: [ConstMap_CloneWriteable returns NULL if parameter handle is NULL. ]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_052: [ConstMap_CloneWriteable shall create a new, writeable map, populated by the key, value pairs in the parameter defined by handle.]*/ + /*Codes_SRS_CONSTMAP_17_053: [If during cloning, any operation fails, then ConstMap_CloneWriteableap_Clone shall return NULL.]*/ + /*Codes_SRS_CONSTMAP_17_054: [Otherwise, ConstMap_CloneWriteable shall return a non-NULL handle that can be used in subsequent calls.]*/ + result = Map_Clone(handle->map); + } + return result; +} + +bool ConstMap_ContainsKey(CONSTMAP_HANDLE handle, const char* key ) +{ + bool keyExists = false; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_024: [If parameter handle or key are NULL then ConstMap_ContainsKey shall return false.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + if (key == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_025: [Otherwise if a key exists then ConstMap_ContainsKey shall return true.]*/ + MAP_RESULT mapResult = Map_ContainsKey(handle->map, key, &keyExists); + if (mapResult != MAP_OK) + { + /*Codes_SRS_CONSTMAP_17_026: [If a key doesn't exist, then ConstMap_ContainsKey shall return false.]*/ + keyExists = false; + LOG_CONSTMAP_ERROR(ConstMap_ErrorConvert(mapResult)); + } + } + } + return keyExists; +} + +bool ConstMap_ContainsValue(CONSTMAP_HANDLE handle, const char* value) +{ + bool valueExists = false; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_027: [If parameter handle or value is NULL then ConstMap_ContainsValue shall return false.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + if (value == NULL) + { + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_028: [Otherwise, if a pair has its value equal to the parameter value, the ConstMap_ContainsValue shall return true.]*/ + MAP_RESULT mapResult = Map_ContainsValue(handle->map, value, &valueExists); + if (mapResult != MAP_OK) + { + /*Codes_SRS_CONSTMAP_17_029: [Otherwise, if such a does not exist, then ConstMap_ContainsValue shall return false.]*/ + LOG_CONSTMAP_ERROR(ConstMap_ErrorConvert(mapResult)); + } + } + } + return valueExists; +} + +const char* ConstMap_GetValue(CONSTMAP_HANDLE handle, const char* key) +{ + const char* value = NULL; + + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_040: [If parameter handle or key is NULL then ConstMap_GetValue returns NULL.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + if (key == NULL) + { + /*Codes_SRS_CONSTMAP_17_040: [If parameter handle or key is NULL then ConstMap_GetValue returns NULL.]*/ + LOG_CONSTMAP_ERROR(CONSTMAP_INVALIDARG); + } + else + { + /*Codes_SRS_CONSTMAP_17_041: [If the key is not found, then ConstMap_GetValue returns NULL.]*/ + /*Codes_SRS_CONSTMAP_17_042: [Otherwise, ConstMap_GetValue returns the key's value.]*/ + value = Map_GetValueFromKey(handle->map, key); + } + } + return value; +} + +CONSTMAP_RESULT ConstMap_GetInternals(CONSTMAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count) +{ + CONSTMAP_RESULT result; + if (handle == NULL) + { + /*Codes_SRS_CONSTMAP_17_046: [If parameter handle, keys, values or count is NULL then ConstMap_GetInternals shall return CONSTMAP_INVALIDARG.]*/ + result = CONSTMAP_INVALIDARG; + LOG_CONSTMAP_ERROR(result); + } + else + { + /*Codes_SRS_CONSTMAP_17_043: [ConstMap_GetInternals shall produce in *keys a pointer to an array of const char* having all the keys stored so far by the map.] + *Codes_SRS_CONSTMAP_17_044: [ConstMap_GetInternals shall produce in *values a pointer to an array of const char* having all the values stored so far by the map.] + *Codes_SRS_CONSTMAP_17_045: [ ConstMap_GetInternals shall produce in *count the number of stored keys and values.] + */ + MAP_RESULT mapResult = Map_GetInternals(handle->map, keys, values, count); + result = ConstMap_ErrorConvert(mapResult); + } + return result; +} \ No newline at end of file diff --git a/src/sdk/constmap.h b/src/sdk/constmap.h new file mode 100644 index 0000000..4a5ec5a --- /dev/null +++ b/src/sdk/constmap.h @@ -0,0 +1,131 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/** @file constmap.h +* @brief ConstMap is a module that implements a read-only dictionary +* of @c const char* keys to @c const char* values. +*/ + +#ifndef CONSTMAP_H +#define CONSTMAP_H + +#ifdef __cplusplus +#include +extern "C" +{ +#else +#include +#endif + + +#include "macro_utils.h" +#include "crt_abstractions.h" +#include "map.h" + +#define CONSTMAP_RESULT_VALUES \ + CONSTMAP_OK, \ + CONSTMAP_ERROR, \ + CONSTMAP_INVALIDARG, \ + CONSTMAP_KEYNOTFOUND + +/** @brief Enumeration specifying the status of calls to various APIs in this + * module. + */ +DEFINE_ENUM(CONSTMAP_RESULT, CONSTMAP_RESULT_VALUES); + +typedef struct CONSTMAP_HANDLE_DATA_TAG* CONSTMAP_HANDLE; + + +/** + * @brief Creates a new read-only map from a map handle. + * + * @param sourceMap The map from which we will populate key,value + * into the read-only map. + * + * @return A valid @c CONSTMAP_HANDLE or @c NULL in case an error occurs. + */ +extern CONSTMAP_HANDLE ConstMap_Create(MAP_HANDLE sourceMap); + + /** + * @brief Destroy a read-only map. Deallocate memory associated with handle. + * @param handle Handle to a read-only map. + */ +extern void ConstMap_Destroy(CONSTMAP_HANDLE handle); + + /** + * @brief Clone a read-only map from another read-only map. + * @param handle Handle to a read-only map. + * @return A valid @c CONSTMAP_HANDLE or @c NULL in case an error occurs. + */ +extern CONSTMAP_HANDLE ConstMap_Clone(CONSTMAP_HANDLE handle); + + /** + * @brief Create a map handle populated from the read-only map. + * @param handle Handle to a read-only map. + * @return A valid @c MAP_HANDLE or @c NULL in case an error occurs. + * + * The new MAP_HANDLE needs to be destroyed when it is no longer needed. + */ +extern MAP_HANDLE ConstMap_CloneWriteable(CONSTMAP_HANDLE handle); + +/** + * @brief This function returns a true if the map contains a key + * with the same value the parameter @p key. + * + * @param handle The handle to an existing map. + * @param key The key that the caller wants checked. + * + * @return The function returns @c true if the key exists + * in the map and @c false if key is not found or + * parameters are invalid. + */ +extern bool ConstMap_ContainsKey(CONSTMAP_HANDLE handle, const char* key); + +/** + * @brief This function returns @c true if at least one pair + * exists in the map where the entry's value is equal to the + * parameter @c value. + * + * @param handle The handle to an existing map. + * @param value The value that the caller wants checked. + * + * @return The function returns @c true if the value exists + * in the map and @c false if value is not found or + * parameters are invalid. + */ +extern bool ConstMap_ContainsValue(CONSTMAP_HANDLE handle, const char* value); + +/** + * @brief Retrieves the value of a stored key. + * + * @param handle The handle to an existing map. + * @param key The key to be looked up in the map. + * + * @return Returns @c NULL in case the input arguments are @c NULL or if the + * requested key is not found in the map. Returns a pointer to the + * key's value otherwise. + */ +extern const char* ConstMap_GetValue(CONSTMAP_HANDLE handle, const char* key); + + /** + * @brief Retrieves the complete list of keys and values from the map + * in @p values and @p keys. Also writes the size of the list + * in @p count. + * + * @param handle The handle to an existing map. + * @param keys The location where the list of keys is to be written. + * @param values The location where the list of values is to be written. + * @param count The number of stored keys and values is written at the + * location indicated by this pointer. + * + * @return Returns @c CONSTMAP_OK if the keys and values are retrieved + * and written successfully or an error code otherwise. + */ +extern CONSTMAP_RESULT ConstMap_GetInternals(CONSTMAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count); + + +#ifdef __cplusplus +} +#endif + +#endif /* CONSTMAP_H */ diff --git a/src/sdk/crt_abstractions.c b/src/sdk/crt_abstractions.c index 36b0580..40fdfad 100644 --- a/src/sdk/crt_abstractions.c +++ b/src/sdk/crt_abstractions.c @@ -213,7 +213,7 @@ int sprintf_s(char* dst, size_t dstSizeInBytes, const char* format, ...) #else /*not Microsoft compiler... */ #if defined __STDC_VERSION__ -#if ((__STDC_VERSION__ == 199901L) || ( __STDC_VERSION__ == 201112L)) +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) /*C99 compiler*/ va_list args; va_start(args, format); diff --git a/src/sdk/crt_abstractions.h b/src/sdk/crt_abstractions.h index 70590a2..e5f4c5a 100644 --- a/src/sdk/crt_abstractions.h +++ b/src/sdk/crt_abstractions.h @@ -33,10 +33,26 @@ typedef bool _Bool; #else #include #endif +#else +/* WINCE does not support bool as C datatype */ +#define __bool_true_false_are_defined 1 + +#define HAS_STDBOOL + +#define _Bool bool + +#ifdef __cplusplus +#define _CSTDBOOL_ +#else +typedef unsigned char bool; + +#define false 0 +#define true 1 +#endif #endif #else #if defined __STDC_VERSION__ -#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) /*C99 compiler or C11*/ #define HAS_STDBOOL #include @@ -88,20 +104,11 @@ extern int size_tToString(char* destination, size_t destinationSize, size_t valu /*else if running on C99 or C11, ISNAN shall be isnan*/ /*else if running on C89 ... #error and inform user*/ -#ifdef IN_OPENWRT -#undef isnan -#define isnan(x) __builtin_isnan(x) -#undef isinf -#define isinf(x) __builtin_isinf(x) -#undef signbit -#define signbit(x) __builtin_signbit(x) -#endif - #ifdef _MSC_VER #define ISNAN _isnan #else #if defined __STDC_VERSION__ -#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) /*C99 compiler or C11*/ #define ISNAN isnan #else @@ -118,52 +125,11 @@ extern int size_tToString(char* destination, size_t destinationSize, size_t valu #endif #endif -/*ispositiveinfinity*/ - -#ifdef _MSC_VER -#define ISPOSITIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_PINF) == _FPCLASS_PINF)) -#else -#if defined __STDC_VERSION__ -#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) -/*C99 compiler or C11*/ -#define ISPOSITIVEINFINITY(x) (isinf((x)) && (signbit((x))==0)) -#else -#error update this file to contain the latest C standard. -#endif -#else -#ifdef __cplusplus -#define ISPOSITIVEINFINITY(x) (std::isinf((x)) && (signbit((x))==0)) -#else -#error unknown (or C89) compiler, must provide a definition for ISPOSITIVEINFINITY -#endif -#endif -#endif - -#ifdef _MSC_VER -/*not exactly signbit*/ -#define ISNEGATIVEINFINITY(x) ((_finite((x))==0) && ((_fpclass((x)) & _FPCLASS_NINF) == _FPCLASS_NINF)) -#else -#if defined __STDC_VERSION__ -#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) -/*C99 compiler or C11*/ -#define ISNEGATIVEINFINITY(x) (isinf((x)) && (signbit((x))!=0)) -#else -#error update this file to contain the latest C standard. -#endif -#else -#ifdef __cplusplus -#define ISNEGATIVEINFINITY(x) (std::isinf((x)) && (signbit((x)) != 0)) -#else -#error unknown (or C89) compiler, must provide a definition for ISNEGATIVEINFINITY -#endif -#endif -#endif - #ifdef _MSC_VER #define INT64_PRINTF "%I64d" #else #if defined __STDC_VERSION__ -#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201112L)) +#if ((__STDC_VERSION__ == 199901L) || (__STDC_VERSION__ == 201000L) || (__STDC_VERSION__ == 201112L)) /*C99 compiler or C11*/ #define INT64_PRINTF "%" PRId64 "" #else diff --git a/src/sdk/datamarshaller.c b/src/sdk/datamarshaller.c index 31a5f5c..8f93ded 100644 --- a/src/sdk/datamarshaller.c +++ b/src/sdk/datamarshaller.c @@ -100,9 +100,9 @@ DATA_MARSHALLER_RESULT DataMarshaller_SendData(DATA_MARSHALLER_HANDLE dataMarsha else { size_t i; + bool includePropertyPath = dataMarshallerInstance->IncludePropertyPath; /* VS complains wrongly that result is not initialized */ result = DATA_MARSHALLER_ERROR; - bool includePropertyPath = dataMarshallerInstance->IncludePropertyPath; for (i = 0; i < valueCount; i++) { diff --git a/src/sdk/gb_stdio.c b/src/sdk/gb_stdio.c new file mode 100644 index 0000000..3d1ac28 --- /dev/null +++ b/src/sdk/gb_stdio.c @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*depending if the symbol GB_STDIO_INTERCEPT is defined, this file does the following + + a) if GB_STDIO_INTERCEPT is NOT defined, then the file shall be empty (almost:) + b) if GB_STDIO_INTERCEPT is defined, then the file shall call to the 'real' stdio.h functions from their gb_* synonyms*/ + +static const int avoid_a_warning_C4206 = 0; /* warning C4206: nonstandard extension used: translation unit is empty*/ + +#ifdef GB_STDIO_INTERCEPT + +#ifdef __cplusplus +#include +#include +#else +#include +#include +#endif + +/*this is fopen*/ +FILE *gb_fopen(const char * filename, const char * mode) +{ + return fopen(filename, mode); +} + +int gb_fclose(FILE *stream) +{ + return fclose(stream); +} + +int gb_fseek(FILE *stream, long int offset, int whence) +{ + return fseek(stream, offset, whence); +} + +long int gb_ftell(FILE *stream) +{ + return ftell(stream); +} + +int fprintf(FILE * stream, const char * format, ...) +{ + va_list args; + va_start(args, format); + vfprintf(stream, format, args); + va_end(args); +} + +#endif diff --git a/src/sdk/gb_stdio.h b/src/sdk/gb_stdio.h new file mode 100644 index 0000000..7e1c277 --- /dev/null +++ b/src/sdk/gb_stdio.h @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GB_STDIO_H +#define GB_STDIO_H + +/*this file, if included instead of has the following functionality: +1) if GB_STDIO_INTERCEPT is defined then + a) some of the stdio.h symbols shall be redefined, for example: fopen => gb_fopen + b) all "code" using the fopen will actually (because of the preprocessor) call to gb_fopen + c) gb_fopen shall blindly call into fopen, thus realizing a passthrough + + reason is: unittesting. fopen is comes with the C Run Time and cannot be mocked (that is, in the global namespace cannot exist a function called fopen + +2) if GB_STDIO_INTERCEPT is not defined then + a) it shall include => no passthrough, just direct linking. +*/ + +#ifndef GB_STDIO_INTERCEPT +#include +#else + +/*source level intercepting of function calls*/ +#define fopen fopen_never_called_never_implemented_always_forgotten +#define fclose fclose_never_called_never_implemented_always_forgotten +#define fseek fseek_never_called_never_implemented_always_forgotten +#define ftell ftell_never_called_never_implemented_always_forgotten +#define fprintf fprintf_never_called_never_implemented_always_forgotten + + + +#ifdef __cplusplus +#include +extern "C" +{ +#else +#include +#endif + +#undef fopen +#define fopen gb_fopen +extern FILE* gb_fopen(const char* filename, const char* mode); + + +#undef fclose +#define fclose gb_fclose +extern int fclose(FILE *stream); + +#undef fseek +#define fseek gb_fseek +extern int fseek(FILE *stream, long int offset, int whence); + +#undef ftell +#define ftell gb_ftell +extern long int ftell(FILE *stream); + +#undef fprintf +#define fprintf gb_fprintf +extern int fprintf(FILE * stream, const char * format, ...); + + +#ifdef __cplusplus +} +#endif + +#endif /*GB_STDIO_INTERCEPT*/ + +#endif /* GB_STDIO_H */ diff --git a/src/sdk/gb_time.c b/src/sdk/gb_time.c new file mode 100644 index 0000000..d30e687 --- /dev/null +++ b/src/sdk/gb_time.c @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/*depending if the symbol GB_TIME_INTERCEPT is defined, this file does the following + + a) if GB_TIME_INTERCEPT is NOT defined, then the file shall be empty (almost:) + b) if GB_TIME_INTERCEPT is defined, then the file shall call to the 'real' time.h functions from their gb_* synonyms*/ + +static const int avoid_a_warning_C4206 = 0; /* warning C4206: nonstandard extension used: translation unit is empty*/ + +#ifdef GB_TIME_INTERCEPT + +#ifdef __cplusplus +#include +#else +#include +#endif + +/*this is time*/ +time_t gb_time(time_t *timer); +{ + return time(timer); +} + +/*this is localtime*/ +struct tm *gb_localtime(const time_t *timer) +{ + return gb_localtime(timer); +} + +size_t gb_strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr) +{ + return strftime(s, maxsize, format, timeptr); +} + +#endif diff --git a/src/sdk/gb_time.h b/src/sdk/gb_time.h new file mode 100644 index 0000000..6141380 --- /dev/null +++ b/src/sdk/gb_time.h @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef GB_TIME_H +#define GB_TIME_H + +/*this file, if included instead of has the following functionality: +1) if GB_TIME_INTERCEPT is defined then + a) some of the time.h symbols shall be redefined, for example: time => gb_time + b) all "code" using the time will actually (because of the preprocessor) call to gb_time + c) gb_time shall blindly call into time, thus realizing a passthrough + + reason is: unittesting. time comes with the C Run Time and cannot be mocked (that is, in the global namespace cannot exist a function called time + +2) if GB_TIME_INTERCEPT is not defined then + a) it shall include => no passthrough, just direct linking. +*/ + +#ifndef GB_TIME_INTERCEPT +#include +#else + +/*source level intercepting of function calls*/ +#define time time_never_called_never_implemented_always_forgotten +#define localtime localtime_never_called_never_implemented_always_forgotten +#define strftime strftime_never_called_never_implemented_always_forgotten + +#ifdef __cplusplus +#include +extern "C" +{ +#else +#include +#endif + +#undef time +#define time gb_time +extern time_t time(time_t *timer); + +#undef localtime +#define localtime gb_localtime +extern struct tm *localtime(const time_t *timer); + +#undef strftime +#define strftime gb_strftime +extern size_t strftime(char * s, size_t maxsize, const char * format, const struct tm * timeptr); + + +#ifdef __cplusplus +} +#endif + +#endif /*GB_TIME_INTERCEPT*/ + +#endif /* GB_TIME_H */ diff --git a/src/sdk/hmacsha256.c b/src/sdk/hmacsha256.c index e39c790..8a64d6c 100644 --- a/src/sdk/hmacsha256.c +++ b/src/sdk/hmacsha256.c @@ -28,7 +28,7 @@ HMACSHA256_RESULT HMACSHA256_ComputeHash(const unsigned char* key, size_t keyLen else { if ((BUFFER_enlarge(hash, 32) != 0) || - (hmac(SHA256, payload, payloadLen, key, keyLen, BUFFER_u_char(hash) ) != 0)) + (hmac(SHA256, payload, (int)payloadLen, key, (int)keyLen, BUFFER_u_char(hash) ) != 0)) { result = HMACSHA256_ERROR; } diff --git a/src/sdk/httpapi.h b/src/sdk/httpapi.h index b8cca2c..cfa1c4d 100644 --- a/src/sdk/httpapi.h +++ b/src/sdk/httpapi.h @@ -24,7 +24,7 @@ extern "C" { #include #endif -typedef void* HTTP_HANDLE; +typedef struct HTTP_HANDLE_DATA_TAG* HTTP_HANDLE; #define AMBIGUOUS_STATUS_CODE (300) diff --git a/src/sdk/httpapiex.h b/src/sdk/httpapiex.h index b8d0cbe..dbd2866 100644 --- a/src/sdk/httpapiex.h +++ b/src/sdk/httpapiex.h @@ -26,7 +26,7 @@ extern "C" { #include #endif -typedef void* HTTPAPIEX_HANDLE; +typedef struct HTTPAPIEX_HANDLE_DATA_TAG* HTTPAPIEX_HANDLE; #define HTTPAPIEX_RESULT_VALUES \ HTTPAPIEX_OK, \ diff --git a/src/sdk/httpapiexsas.c b/src/sdk/httpapiexsas.c index 04c8b3d..fcd1027 100644 --- a/src/sdk/httpapiexsas.c +++ b/src/sdk/httpapiexsas.c @@ -8,7 +8,9 @@ #include "gballoc.h" #include +#include +#include "agenttime.h" #include "strings.h" #include "buffer_.h" #include "sastoken.h" diff --git a/src/sdk/httpapiexsas.h b/src/sdk/httpapiexsas.h index 525ebfa..e41960c 100644 --- a/src/sdk/httpapiexsas.h +++ b/src/sdk/httpapiexsas.h @@ -14,7 +14,7 @@ extern "C" { #endif -typedef void* HTTPAPIEX_SAS_HANDLE; +typedef struct HTTPAPIEX_SAS_STATE_TAG* HTTPAPIEX_SAS_HANDLE; extern HTTPAPIEX_SAS_HANDLE HTTPAPIEX_SAS_Create(STRING_HANDLE key, STRING_HANDLE uriResource, STRING_HANDLE keyName); diff --git a/src/sdk/httpheaders.h b/src/sdk/httpheaders.h index 2ab9a3e..245d164 100644 --- a/src/sdk/httpheaders.h +++ b/src/sdk/httpheaders.h @@ -41,7 +41,7 @@ HTTP_HEADERS_ERROR \ /** @brief Enumeration specifying the status of calls to various APIs in this module. */ DEFINE_ENUM(HTTP_HEADERS_RESULT, HTTP_HEADERS_RESULT_VALUES); -typedef void* HTTP_HEADERS_HANDLE; +typedef struct HTTP_HEADERS_HANDLE_DATA_TAG* HTTP_HEADERS_HANDLE; /** * @brief Produces a @c HTTP_HANDLE that can later be used in subsequent calls to the module. diff --git a/src/sdk/iot_logging.h b/src/sdk/iot_logging.h deleted file mode 100644 index bff8f91..0000000 --- a/src/sdk/iot_logging.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef LOGGING_H -#define LOGGING_H -#include -#include "agenttime.h" - -#define STRINGIFY(a) (#a) - -#define LogUsage (void)printf - - -#define LogInfo(...) (void)printf("Info: " __VA_ARGS__) - -#if defined _MSC_VER -#define LogError(FORMAT, ...) { time_t t = time(NULL); (void)fprintf(stderr,"Error: Time:%.24s File:%s Func:%s Line:%d " FORMAT, ctime(&t), __FILE__, __FUNCDNAME__, __LINE__, __VA_ARGS__); } -#else -#define LogError(FORMAT, ...) { time_t t = time(NULL); (void)fprintf(stderr,"Error: Time:%.24s File:%s Func:%s Line:%d " FORMAT, ctime(&t), __FILE__, __func__, __LINE__, ##__VA_ARGS__); } -#endif - -#endif /* LOGGING_H */ diff --git a/src/sdk/iothub_client.c b/src/sdk/iothub_client.c index d22ff20..ac9687c 100644 --- a/src/sdk/iothub_client.c +++ b/src/sdk/iothub_client.c @@ -9,6 +9,7 @@ #include #include +#include #include "crt_abstractions.h" #include "iothub_client.h" #include "iothub_client_ll.h" @@ -24,6 +25,9 @@ typedef struct IOTHUB_CLIENT_INSTANCE_TAG sig_atomic_t StopThread; } IOTHUB_CLIENT_INSTANCE; +/*used by unittests only*/ +const size_t IoTHubClient_ThreadTerminationOffset = offsetof(IOTHUB_CLIENT_INSTANCE, StopThread); + static int ScheduleWork_Thread(void* threadArgument) { IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)threadArgument; diff --git a/src/sdk/iothub_client.h b/src/sdk/iothub_client.h index 25d92a7..2e3d3c7 100644 --- a/src/sdk/iothub_client.h +++ b/src/sdk/iothub_client.h @@ -13,6 +13,7 @@ */ #ifndef IOTHUB_CLIENT_H +#define IOTHUB_CLIENT_H #include "iothub_client_ll.h" @@ -21,150 +22,150 @@ extern "C" { #endif - typedef void* IOTHUB_CLIENT_HANDLE; + typedef struct IOTHUB_CLIENT_INSTANCE_TAG* IOTHUB_CLIENT_HANDLE; - /** - * @brief Creates a IoT Hub client for communication with an existing - * IoT Hub using the specified connection string parameter. - * - * @param connectionString Pointer to a character string - * @param protocol Function pointer for protocol implementation - * - * Sample connection string: - *
- *
HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];
- *
- * - * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when - * invoking other functions for IoT Hub client and @c NULL on failure. - */ + /** + * @brief Creates a IoT Hub client for communication with an existing + * IoT Hub using the specified connection string parameter. + * + * @param connectionString Pointer to a character string + * @param protocol Function pointer for protocol implementation + * + * Sample connection string: + *
+ *
HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];
+ *
+ * + * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ extern IOTHUB_CLIENT_HANDLE IoTHubClient_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol); - /** - * @brief Creates a IoT Hub client for communication with an existing IoT - * Hub using the specified parameters. - * - * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure - * - * The API does not allow sharing of a connection across multiple - * devices. This is a blocking call. - * - * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when - * invoking other functions for IoT Hub client and @c NULL on failure. - */ + /** + * @brief Creates a IoT Hub client for communication with an existing IoT + * Hub using the specified parameters. + * + * @param config Pointer to an @c IOTHUB_CLIENT_CONFIG structure + * + * The API does not allow sharing of a connection across multiple + * devices. This is a blocking call. + * + * @return A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when + * invoking other functions for IoT Hub client and @c NULL on failure. + */ extern IOTHUB_CLIENT_HANDLE IoTHubClient_Create(const IOTHUB_CLIENT_CONFIG* config); - /** - * @brief Disposes of resources allocated by the IoT Hub client. This is a - * blocking call. - * - * @param iotHubClientHandle The handle created by a call to the create function. - */ + /** + * @brief Disposes of resources allocated by the IoT Hub client. This is a + * blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + */ extern void IoTHubClient_Destroy(IOTHUB_CLIENT_HANDLE iotHubClientHandle); - /** - * @brief Asynchronous call to send the message specified by @p eventMessageHandle. - * - * @param iotHubClientHandle The handle created by a call to the create function. - * @param eventMessageHandle The handle to an IoT Hub message. - * @param eventConfirmationCallback The callback specified by the device for receiving - * confirmation of the delivery of the IoT Hub message. - * This callback can be expected to invoke the - * ::IoTHubClient_SendEventAsync function for the - * same message in an attempt to retry sending a failing - * message. The user can specify a @c NULL value here to - * indicate that no callback is required. - * @param userContextCallback User specified context that will be provided to the - * callback. This can be @c NULL. - * - * @b NOTE: The application behavior is undefined if the user calls - * the ::IoTHubClient_Destroy function from within any callback. - * - * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. - */ + /** + * @brief Asynchronous call to send the message specified by @p eventMessageHandle. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param eventMessageHandle The handle to an IoT Hub message. + * @param eventConfirmationCallback The callback specified by the device for receiving + * confirmation of the delivery of the IoT Hub message. + * This callback can be expected to invoke the + * ::IoTHubClient_SendEventAsync function for the + * same message in an attempt to retry sending a failing + * message. The user can specify a @c NULL value here to + * indicate that no callback is required. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ extern IOTHUB_CLIENT_RESULT IoTHubClient_SendEventAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback); - /** - * @brief This function returns the current sending status for IoTHubClient. - * - * @param iotHubClientHandle The handle created by a call to the create function. - * @param iotHubClientStatus The sending state is populated at the address pointed - * at by this parameter. The value will be set to - * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently - * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY - * if there are. - * - * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. - */ + /** + * @brief This function returns the current sending status for IoTHubClient. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param iotHubClientStatus The sending state is populated at the address pointed + * at by this parameter. The value will be set to + * @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently + * no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY + * if there are. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ extern IOTHUB_CLIENT_RESULT IoTHubClient_GetSendStatus(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus); - /** - * @brief Sets up the message callback to be invoked when IoT Hub issues a - * message to the device. This is a blocking call. - * - * @param iotHubClientHandle The handle created by a call to the create function. - * @param messageCallback The callback specified by the device for receiving - * messages from IoT Hub. - * @param userContextCallback User specified context that will be provided to the - * callback. This can be @c NULL. - * - * @b NOTE: The application behavior is undefined if the user calls - * the ::IoTHubClient_Destroy function from within any callback. - * - * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. - */ + /** + * @brief Sets up the message callback to be invoked when IoT Hub issues a + * message to the device. This is a blocking call. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param messageCallback The callback specified by the device for receiving + * messages from IoT Hub. + * @param userContextCallback User specified context that will be provided to the + * callback. This can be @c NULL. + * + * @b NOTE: The application behavior is undefined if the user calls + * the ::IoTHubClient_Destroy function from within any callback. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ extern IOTHUB_CLIENT_RESULT IoTHubClient_SetMessageCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback); - /** - * @brief This function returns in the out parameter @p lastMessageReceiveTime - * what was the value of the @c time function when the last message was - * received at the client. - * - * @param iotHubClientHandle The handle created by a call to the create function. - * @param lastMessageReceiveTime Out parameter containing the value of @c time function - * when the last message was received. - * - * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. - */ + /** + * @brief This function returns in the out parameter @p lastMessageReceiveTime + * what was the value of the @c time function when the last message was + * received at the client. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param lastMessageReceiveTime Out parameter containing the value of @c time function + * when the last message was received. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ extern IOTHUB_CLIENT_RESULT IoTHubClient_GetLastMessageReceiveTime(IOTHUB_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime); - /** - * @brief This API sets a runtime option identified by parameter @p optionName - * to a value pointed to by @p value. @p optionName and the data type - * @p value is pointing to are specific for every option. - * - * @param iotHubClientHandle The handle created by a call to the create function. - * @param optionName Name of the option. - * @param value The value. - * - * The options that can be set via this API are: - * - @b timeout - the maximum time in milliseconds a communication is - * allowed to use. @p value is a pointer to an @c unsigned @c int with - * the timeout value in milliseconds. This is only supported for the HTTP - * protocol as of now. When the HTTP protocol uses CURL, the meaning of - * the parameter is total request time. When the HTTP protocol uses - * winhttp, the meaning is the same as the @c dwSendTimeout and - * @c dwReceiveTimeout parameters of the - * - * WinHttpSetTimeouts API. - * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only - * when CURL is used. It has the same meaning as CURL's option with the same - * name. @p value is pointer to a long. - * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only - * when CURL is used. It has the same meaning as CURL's option with the same - * name. @p value is pointer to a long. - * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only - * when CURL is used. It has the same meaning as CURL's option with the same - * name. @p value is pointer to a long. - * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only - * when CURL is used. It has the same meaning as CURL's option with the same - * name. @p value is pointer to a long. - * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only - * when CURL is used. It has the same meaning as CURL's option with the same - * name. @p value is pointer to a long. - * - * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. - */ + /** + * @brief This API sets a runtime option identified by parameter @p optionName + * to a value pointed to by @p value. @p optionName and the data type + * @p value is pointing to are specific for every option. + * + * @param iotHubClientHandle The handle created by a call to the create function. + * @param optionName Name of the option. + * @param value The value. + * + * The options that can be set via this API are: + * - @b timeout - the maximum time in milliseconds a communication is + * allowed to use. @p value is a pointer to an @c unsigned @c int with + * the timeout value in milliseconds. This is only supported for the HTTP + * protocol as of now. When the HTTP protocol uses CURL, the meaning of + * the parameter is total request time. When the HTTP protocol uses + * winhttp, the meaning is the same as the @c dwSendTimeout and + * @c dwReceiveTimeout parameters of the + * + * WinHttpSetTimeouts API. + * - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * - @b CURLOPT_VERBOSE - only available for HTTP protocol and only + * when CURL is used. It has the same meaning as CURL's option with the same + * name. @p value is pointer to a long. + * + * @return IOTHUB_CLIENT_OK upon success or an error code upon failure. + */ extern IOTHUB_CLIENT_RESULT IoTHubClient_SetOption(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* optionName, const void* value); #ifdef __cplusplus diff --git a/src/sdk/iothub_client_amqp_internal.h b/src/sdk/iothub_client_amqp_internal.h deleted file mode 100644 index 251b681..0000000 --- a/src/sdk/iothub_client_amqp_internal.h +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef IOTHUB_CLIENT_AMQP_INTERNAL_H -#define IOTHUB_CLIENT_AMQP_INTERNAL_H - -#include "proton/message.h" -#include "proton/messenger.h" - -#include "strings.h" -#include "doublylinkedlist.h" -#include "crt_abstractions.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - -#define PROTON_MAP_OPERATIONS_KEY "operation" -#define PROTON_MAP_PUT_TOKEN_OPERATION "put-token" -#define PROTON_MAP_TYPE_KEY "type" -#define PROTON_MAP_TOKEN_TYPE "azure-devices.net:sastoken" -#define PROTON_MAP_NAME_KEY "name" -#define PROTON_MAP_STATUS_CODE "status-code" -#define PROTON_MAP_STATUS_DESCRIPTION "status-description" - -#define AMQPS_SCHEME "amqps://" - -#define EPOCH_TIME_T_VALUE (time_t)0 - -// -// The following items are in units of seconds -#define CBS_DEFAULT_REQUEST_ACCEPT_TIME (60) -#define CBS_DEFAULT_REPLY_TIME (60) -#define SAS_TOKEN_DEFAULT_LIFETIME (3600) -#define SAS_TOKEN_DEFAULT_REFRESH_LINE (600) -#define EVENT_TIMEOUT_DEFAULT (30) - - -typedef struct AMQP_TRANSPORT_STATE_TAG -{ - // - // String generated by the create routine that is passed to the underlying transport; it is the "destination" address of the - // event operations. - // - STRING_HANDLE eventAddress; - - // - // String generated by the create routine that is passed to the underlying transport; it is the "source" address of messages. - // - STRING_HANDLE messageAddress; - - // - // Obtained from the config structure. Since this will be passed in url's we need to url encode it. We keep it in the - // device state because it is used whenever we need to (re)initialize the proton messenger, and it is also used as the name of the - // device key in SAS tokens which are periodically generated during this current life of the device. - STRING_HANDLE urledDeviceId; - - // - // We need to store the device key as it is needed to periodically generate a new SAS token. - STRING_HANDLE deviceKey; - - // - // Built up from the urlDeviceId and a portion of the relative url path, this is used as the "audience" for CBS tokens. As such - // we need to keep it around for SAS cbs maintainence. Also, it used during formation of various other strings. - STRING_HANDLE devicesPortionPath; - - // - // This is the url of CBS for the particualar IoT hub. It is used to periodically send a put-token request to authenticate the device. - STRING_HANDLE cbsAddress; - - // - // Needed for device scoped key names. - STRING_HANDLE zeroLengthString; - - // - // This is the proton object that controls all aspects of the connection to the cloud. - // - pn_messenger_t* messenger; - - // - // This controls whether we need to reinitialize (or initialize) the lower level transport connection to the cloud service. - bool messengerInitialized; - - // - // This is a proton object that is used to build up an event item or to hold a received message. Since we are only doing - // one operation at a time, it's helpful to just pre-allocate it. We have been assured by the proton folk that subsequent to a - // pn_messenger_put, the contents of a message can be immediately cleared. - pn_message_t* message; - - // - // Pointer to a shared list (with the transport independent upper layer) that contains event items to send. - PDLIST_ENTRY waitingToSend; - - // - // Holds the work descriptions of currently "in flight" event items. - DLIST_ENTRY workInProgress; - - // - // Holds the work descriptions that aren't being used. - DLIST_ENTRY availableWorkItems; - - // - // Flag to indicate whether we should pull for messages. - bool DoWork_PullMessages; - - // - // Flag to indicate that we've done a put token operation to the CBS and are now awaiting a reply. - bool waitingForPutTokenReply; - - // - // Holds the expiry (seconds since the epoch) that was last used for the SAS put to the CBS - size_t lastExpiryUsed; - - // - // Because of the organization of the receive processing it simply more straight forward to set a global - // in the state to indicate we've successfully gone through a full request reply cycle for the CBS SAS token - // renewal. - bool putTokenWasSuccessful; - - // - // We will use the expiration value for a SAS that is being sent to the CBS as the message id of actual amqp xfer frame. - // While processing responses from return subscription to the CBS we look for this message id. - size_t sendTokenMessageId; - - // - // This is where messengers go when they just won't stop. - DLIST_ENTRY messengerCorral; - - // - // We need to save the client handle for those times we're completing something and don't have that handle. - IOTHUB_CLIENT_LL_HANDLE savedClientHandle; - - // - // The proton subscription used to retrieve messages. Obtained at initialization of the messenger. - pn_subscription_t* messageSubscription; - - // - // The proton subscription used to retrieve replys to putting the SAS token to the CBS. Obtained at initialization of the messenger. - pn_subscription_t* cbsSubscription; - - // - // The maximum amount of time to wait for CBS request to be accepted by service. This does - // NOT mean how long it takes for a round trip. Just how long it takes for the service to put - // the request in a terminal state. Units in seconds. - size_t cbsRequestAcceptTime; - - // - // The maximum amount of time to wait for a CBS reply to come in. Units is in seconds. - size_t cbsReplyTime; - - // - // This will hold the amount of time (in seconds) that a SAS Token will have for it's expiration. - size_t sasTokenLifetime; - - // - // This will hold how many seconds before the expiration of a SAS token we will try to start renewing - // it. - size_t sasRefreshLine; - - // - // The number of seconds that an event item may be pending before the transport decides that the - // connection is not working. - size_t eventTimeout; - - // - // A string containing the trusted certificates to be passed down to Proton - char* trustedCertificates; -} AMQP_TRANSPORT_STATE, *PAMQP_TRANSPORT_STATE; - -#ifdef __cplusplus -} -#endif - -#endif /* IOTHUB_CLIENT_AMQP_INTERNAL_H */ diff --git a/src/sdk/iothub_client_ll.c b/src/sdk/iothub_client_ll.c index e5a2b41..60099d3 100644 --- a/src/sdk/iothub_client_ll.c +++ b/src/sdk/iothub_client_ll.c @@ -12,7 +12,7 @@ #include "iothub_client_ll.h" #include "iothub_client_private.h" #include "doublylinkedlist.h" -#include "version.h" +#include "iothub_client_version.h" #include "iot_logging.h" @@ -38,7 +38,7 @@ static const char PROTOCOL_GATEWAY_HOST[] = "GatewayHostName"; IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol) { - IOTHUB_CLIENT_LL_HANDLE* result = NULL; + IOTHUB_CLIENT_LL_HANDLE result = NULL; /*Codes_SRS_IOTHUBCLIENT_LL_05_001: [IoTHubClient_LL_CreateFromConnectionString shall obtain the version string by a call to IoTHubClient_GetVersionString.]*/ /*Codes_SRS_IOTHUBCLIENT_LL_05_002: [IoTHubClient_LL_CreateFromConnectionString shall print the version string to standard output.]*/ @@ -81,8 +81,8 @@ IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* c config->iotHubSuffix = NULL; config->deviceId = NULL; config->deviceKey = NULL; - /* Codes_SRS_IOTHUBCLIENT_LL_04_002: [If it does not, it shall pass the protocolGatewayHostName NULL.] */ - config->protocolGatewayHostName = NULL; + /* Codes_SRS_IOTHUBCLIENT_LL_04_002: [If it does not, it shall pass the protocolGatewayHostName NULL.] */ + config->protocolGatewayHostName = NULL; if ((connString = STRING_construct(connectionString)) == NULL) { @@ -125,7 +125,7 @@ IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* c { /* SRS_IOTHUBCLIENT_LL_12_010: [IoTHubClient_LL_CreateFromConnectionString shall fill up the IOTHUB_CLIENT_CONFIG structure using the following mapping: iotHubName = Name, iotHubSuffix = Suffix, deviceId = DeviceId, deviceKey = SharedAccessKey] */ const char* s_token = STRING_c_str(tokenString); - if (strcmp(s_token, HOSTNAME_TOKEN) == 0) + if (strcmp(s_token, HOSTNAME_TOKEN) == 0) { /* SRS_IOTHUBCLIENT_LL_12_009: [IoTHubClient_LL_CreateFromConnectionString shall split the value of HostName to Name and Suffix using the first "." as a separator] */ STRING_TOKENIZER_HANDLE tokenizer2 = NULL; @@ -176,7 +176,7 @@ IOTHUB_CLIENT_LL_HANDLE IoTHubClient_LL_CreateFromConnectionString(const char* c config->deviceKey = STRING_c_str(deviceKeyString); } } - /* Codes_SRS_IOTHUBCLIENT_LL_04_001: [IoTHubClient_LL_CreateFromConnectionString shall verify the existence of key/value pair GatewayHostName. If it does exist it shall pass the value to IoTHubClient_LL_Create API.] */ + /* Codes_SRS_IOTHUBCLIENT_LL_04_001: [IoTHubClient_LL_CreateFromConnectionString shall verify the existence of key/value pair GatewayHostName. If it does exist it shall pass the value to IoTHubClient_LL_Create API.] */ else if (strcmp(s_token, PROTOCOL_GATEWAY_HOST) == 0) { protocolGateway = STRING_clone(valueString); diff --git a/src/sdk/iothub_client_ll.h b/src/sdk/iothub_client_ll.h index 395e1bc..ee13bea 100644 --- a/src/sdk/iothub_client_ll.h +++ b/src/sdk/iothub_client_ll.h @@ -29,6 +29,8 @@ #include "iothub_message.h" +#include "xio.h" + #ifdef __cplusplus extern "C" { @@ -81,7 +83,7 @@ DEFINE_ENUM(TRANSPORT_TYPE, TRANSPORT_TYPE_VALUES); */ DEFINE_ENUM(IOTHUBMESSAGE_DISPOSITION_RESULT, IOTHUBMESSAGE_DISPOSITION_RESULT_VALUES); -typedef void* IOTHUB_CLIENT_LL_HANDLE; +typedef struct IOTHUB_CLIENT_LL_HANDLE_DATA_TAG* IOTHUB_CLIENT_LL_HANDLE; typedef void(*IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK)(IOTHUB_CLIENT_CONFIRMATION_RESULT result, void* userContextCallback); typedef IOTHUBMESSAGE_DISPOSITION_RESULT (*IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC)(IOTHUB_MESSAGE_HANDLE message, void* userContextCallback); typedef const void*(*IOTHUB_CLIENT_TRANSPORT_PROVIDER)(void); @@ -89,23 +91,23 @@ typedef const void*(*IOTHUB_CLIENT_TRANSPORT_PROVIDER)(void); /** @brief This struct captures IoTHub client configuration. */ typedef struct IOTHUB_CLIENT_CONFIG_TAG { - /** @brief A function pointer that is passed into the @c IoTHubClientCreate. - * A function definition for AMQP, @c DeviceClientProvideAmqpResources, - * is defined in the include @c iothubtransportamqp.h. A function - * definition for HTTP, @c DeviceClientProvideHttpResources, is defined - * in the include @c iothubtransporthttp.h */ + /** @brief A function pointer that is passed into the @c IoTHubClientCreate. + * A function definition for AMQP, @c DeviceClientProvideAmqpResources, + * is defined in the include @c iothubtransportamqp.h. A function + * definition for HTTP, @c DeviceClientProvideHttpResources, is defined + * in the include @c iothubtransporthttp.h */ IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol; /** @brief A string that identifies the device. */ const char* deviceId; - /** @brief The device key used to authenticate the device. */ + /** @brief The device key used to authenticate the device. */ const char* deviceKey; /** @brief The IoT Hub name to which the device is connecting. */ const char* iotHubName; - /** @brief IoT Hub suffix goes here, e.g., private.azure-devices-int.net. */ + /** @brief IoT Hub suffix goes here, e.g., private.azure-devices-int.net. */ const char* iotHubSuffix; const char* protocolGatewayHostName; diff --git a/src/sdk/iothub_client_private.h b/src/sdk/iothub_client_private.h index e07f522..5360ab4 100644 --- a/src/sdk/iothub_client_private.h +++ b/src/sdk/iothub_client_private.h @@ -29,21 +29,12 @@ DEFINE_ENUM(IOTHUB_BATCHSTATE_RESULT, IOTHUB_BATCHSTATE_RESULT_VALUES); #define MESSAGE_ENDPOINT "/messages/devicebound" #define MESSAGE_ENDPOINT_HTTP "/messages/devicebound" #define MESSAGE_ENDPOINT_HTTP_ETAG "/messages/devicebound/" +#define CLIENT_DEVICE_TYPE_PREFIX "iothubclient" +#define CLIENT_DEVICE_BACKSLASH "/" #define CBS_REPLY_TO "cbs" #define CBS_ENDPOINT "/$" CBS_REPLY_TO -#define API_VERSION "?api-version=2015-08-15-preview" +#define API_VERSION "?api-version=2016-02-03" #define REJECT_QUERY_PARAMETER "&reject" -#define PROTON_EVENT_OUTGOING_WINDOW_SIZE (10) -#define PROTON_CBS_OUTGOING_WINDOW_SIZE (1) -#define PROTON_MESSAGE_INCOMING_WINDOW_SIZE (1) -#define PROTON_CBS_INCOMING_WINDOW_SIZE (1) -#define PROTON_INCOMING_WINDOW_SIZE (PROTON_MESSAGE_INCOMING_WINDOW_SIZE + PROTON_CBS_INCOMING_WINDOW_SIZE) -#define PROTON_OUTGOING_WINDOW_SIZE (PROTON_EVENT_OUTGOING_WINDOW_SIZE + PROTON_CBS_OUTGOING_WINDOW_SIZE) -#define PROTON_PROCESSING_YIELD_IN_MILLISECONDS (100) -#define PROTON_MESSENGER_STOP_TRIES (10) -#define PROTON_MAXIMUM_EVENT_LENGTH (256*1024) - - extern void IoTHubClient_LL_SendComplete(IOTHUB_CLIENT_LL_HANDLE handle, PDLIST_ENTRY completed, IOTHUB_BATCHSTATE_RESULT result); extern IOTHUBMESSAGE_DISPOSITION_RESULT IoTHubClient_LL_MessageCallback(IOTHUB_CLIENT_LL_HANDLE handle, IOTHUB_MESSAGE_HANDLE message); @@ -63,19 +54,24 @@ typedef struct IOTHUB_MESSAGE_LIST_TAG }IOTHUB_MESSAGE_LIST; typedef void* TRANSPORT_HANDLE; +typedef void* IOTHUB_DEVICE_HANDLE; typedef IOTHUB_CLIENT_RESULT(*pfIoTHubTransport_SetOption)(TRANSPORT_HANDLE handle, const char *optionName, const void* value); typedef TRANSPORT_HANDLE(*pfIoTHubTransport_Create)(const IOTHUBTRANSPORT_CONFIG* config); typedef void (*pfIoTHubTransport_Destroy)(TRANSPORT_HANDLE handle); -typedef int (*pfIoTHubTransport_Subscribe)(TRANSPORT_HANDLE handle); -typedef void (*pfIoTHubTransport_Unsubscribe)(TRANSPORT_HANDLE handle); +typedef IOTHUB_DEVICE_HANDLE(*pfIotHubTransport_Register)(TRANSPORT_HANDLE handle, const char* deviceId, const char* deviceKey, PDLIST_ENTRY waitingToSend); +typedef void(*pfIotHubTransport_Unregister)(IOTHUB_DEVICE_HANDLE deviceHandle); +typedef int (*pfIoTHubTransport_Subscribe)(IOTHUB_DEVICE_HANDLE handle); +typedef void (*pfIoTHubTransport_Unsubscribe)(IOTHUB_DEVICE_HANDLE handle); typedef void (*pfIoTHubTransport_DoWork)(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle); -typedef IOTHUB_CLIENT_RESULT(*pfIoTHubTransport_GetSendStatus)(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus); +typedef IOTHUB_CLIENT_RESULT(*pfIoTHubTransport_GetSendStatus)(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus); #define TRANSPORT_PROVIDER_FIELDS \ pfIoTHubTransport_SetOption IoTHubTransport_SetOption; \ pfIoTHubTransport_Create IoTHubTransport_Create; \ pfIoTHubTransport_Destroy IoTHubTransport_Destroy; \ +pfIotHubTransport_Register IoTHubTransport_Register; \ +pfIotHubTransport_Unregister IoTHubTransport_Unegister; \ pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe; \ pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe; \ pfIoTHubTransport_DoWork IoTHubTransport_DoWork; \ diff --git a/src/sdk/version.h b/src/sdk/iothub_client_version.h similarity index 82% rename from src/sdk/version.h rename to src/sdk/iothub_client_version.h index fd0f4b0..308b370 100644 --- a/src/sdk/version.h +++ b/src/sdk/iothub_client_version.h @@ -5,10 +5,10 @@ * @brief Functions for managing the client SDK version. */ -#ifndef IOTHUB_VERSION_H -#define IOTHUB_VERSION_H +#ifndef IOTHUB_CLIENT_VERSION_H +#define IOTHUB_CLIENT_VERSION_H -#define IOTHUB_SDK_VERSION "1.0.0-preview.7" +#define IOTHUB_SDK_VERSION "1.0.1" #ifdef __cplusplus extern "C" @@ -28,4 +28,4 @@ extern "C" } #endif -#endif // IOTHUB_VERSION_H +#endif // IOTHUB_CLIENT_VERSION_H diff --git a/src/sdk/iothub_message.c b/src/sdk/iothub_message.c index abf1d2d..8507487 100644 --- a/src/sdk/iothub_message.c +++ b/src/sdk/iothub_message.c @@ -32,7 +32,7 @@ typedef struct IOTHUB_MESSAGE_HANDLE_DATA_TAG static bool ContainsOnlyUsAscii(const char* asciiValue) { - bool result = true;; + bool result = true; const char* iterator = asciiValue; while (iterator != NULL && *iterator != '\0') { diff --git a/src/sdk/iothubtransporthttp.c b/src/sdk/iothubtransporthttp.c index e0f6943..7ebd052 100644 --- a/src/sdk/iothubtransporthttp.c +++ b/src/sdk/iothubtransporthttp.c @@ -8,6 +8,7 @@ #include "gballoc.h" #include +#include "iothub_client_version.h" #include "iothub_client_private.h" #include "iothubtransporthttp.h" @@ -47,6 +48,8 @@ static TRANSPORT_PROVIDER thisTransportProvider = IoTHubTransportHttp_SetOption, /*pfIoTHubTransport_SetOption IoTHubTransport_SetOption; */ IoTHubTransportHttp_Create, /*pfIoTHubTransport_Create IoTHubTransport_Create; */ IoTHubTransportHttp_Destroy, /*pfIoTHubTransport_Destroy IoTHubTransport_Destroy; */ + IoTHubTransportHttp_Register, /* pfIotHubTransport_Register IoTHubTransport_Register; */ + IoTHubTransportHttp_Unregister, /* pfIotHubTransport_Unregister IoTHubTransport_Unegister; */ IoTHubTransportHttp_Subscribe, /*pfIoTHubTransport_Subscribe IoTHubTransport_Subscribe; */ IoTHubTransportHttp_Unsubscribe, /*pfIoTHubTransport_Unsubscribe IoTHubTransport_Unsubscribe; */ IoTHubTransportHttp_DoWork, /*pfIoTHubTransport_DoWork IoTHubTransport_DoWork; */ @@ -77,15 +80,24 @@ typedef struct HTTPTRANSPORT_HANDLE_DATA_TAG DLIST_ENTRY eventConfirmations; /*holds items for event confirmations*/ }HTTPTRANSPORT_HANDLE_DATA; +IOTHUB_DEVICE_HANDLE IoTHubTransportHttp_Register(TRANSPORT_HANDLE handle, const char* deviceId, const char* deviceKey, PDLIST_ENTRY waitingToSend) +{ + return (IOTHUB_DEVICE_HANDLE)handle; +} + +void IoTHubTransportHttp_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle) +{ + return; +} -/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_003: [Otherwise IoTHubTransportHttp_Create shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/events?api-version=2015-08-15-preview".]*/ +/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_003: [Otherwise IoTHubTransportHttp_Create shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/events?api-version=2016-02-03".]*/ static void destroy_eventHTTPrelativePath(HTTPTRANSPORT_HANDLE_DATA* handleData) { STRING_delete(handleData->eventHTTPrelativePath); handleData->eventHTTPrelativePath = NULL; } -/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_003: [Otherwise IoTHubTransportHttp_Create shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/events?api-version=2015-08-15-preview".]*/ +/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_003: [Otherwise IoTHubTransportHttp_Create shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/events?api-version=2016-02-03".]*/ static bool create_eventHTTPrelativePath(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config) { bool result; @@ -112,14 +124,14 @@ static bool create_eventHTTPrelativePath(HTTPTRANSPORT_HANDLE_DATA* handleData, return result; } -/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_034: [Otherwise, IoTHubTransportHttp_Create shall create an immutable string (further called "message HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/devicebound?api-version=2015-08-15-preview".]*/ +/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_034: [Otherwise, IoTHubTransportHttp_Create shall create an immutable string (further called "message HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/devicebound?api-version=2016-02-03".]*/ static void destroy_messageHTTPrelativePath(HTTPTRANSPORT_HANDLE_DATA* handleData) { STRING_delete(handleData->messageHTTPrelativePath); handleData->messageHTTPrelativePath = NULL; } -/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_034: [Otherwise, IoTHubTransportHttp_Create shall create an immutable string (further called "message HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/devicebound?api-version=2015-08-15-preview".]*/ +/*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_034: [Otherwise, IoTHubTransportHttp_Create shall create an immutable string (further called "message HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/devicebound?api-version=2016-02-03".]*/ static bool create_messageHTTPrelativePath(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config) { bool result; @@ -167,7 +179,8 @@ static void destroy_eventHTTPrequestHeaders(HTTPTRANSPORT_HANDLE_DATA* handleDat "Authorization":" " "Content-Type":"application/vnd.microsoft.iothub.json" "Accept":"application/json" -"Connection":"Keep-Alive"]*/ +"Connection":"Keep-Alive" +"User-Agent":"iothubclient1.0.0]*/ static bool create_eventHTTPrequestHeaders(HTTPTRANSPORT_HANDLE_DATA* handleData, const IOTHUBTRANSPORT_CONFIG* config) { bool result; @@ -202,7 +215,8 @@ static bool create_eventHTTPrequestHeaders(HTTPTRANSPORT_HANDLE_DATA* handleData (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "iothub-to", STRING_c_str(temp)) == HTTP_HEADERS_OK) && (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Authorization", " ") == HTTP_HEADERS_OK) && (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Accept", "application/json") == HTTP_HEADERS_OK) && - (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Connection", "Keep-Alive") == HTTP_HEADERS_OK) + (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "Connection", "Keep-Alive") == HTTP_HEADERS_OK) && + (HTTPHeaders_AddHeaderNameValuePair(handleData->eventHTTPrequestHeaders, "User-Agent", CLIENT_DEVICE_TYPE_PREFIX CLIENT_DEVICE_BACKSLASH IOTHUB_SDK_VERSION) == HTTP_HEADERS_OK) )) { result = false; @@ -467,7 +481,7 @@ TRANSPORT_HANDLE IoTHubTransportHttp_Create(const IOTHUBTRANSPORT_CONFIG* config } else { - /*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_003: [Otherwise IoTHubTransportHttp_Create shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/events?api-version=2015-08-15-preview".]*/ + /*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_003: [Otherwise IoTHubTransportHttp_Create shall create an immutable string (further called "event HTTP relative path") from the following pieces: "/devices/" + URL_ENCODED(config->upperConfig->deviceId) + "/messages/events?api-version=2016-02-03".]*/ result = (HTTPTRANSPORT_HANDLE_DATA*)malloc(sizeof(HTTPTRANSPORT_HANDLE_DATA)); if (result == NULL) { @@ -817,9 +831,9 @@ static MAKE_PAYLOAD_RESULT makePayload(HTTPTRANSPORT_HANDLE_DATA* handleData, ST { bool isFirst = true; PDLIST_ENTRY actual; + bool keepGoing = true; /*keepGoing gets sometimes to false from within the loop*/ /*either all the items enter the list or only some*/ result = MAKE_PAYLOAD_OK; /*optimistically initializing it*/ - bool keepGoing = true; /*keepGoing gets sometimes to false from within the loop*/ while (keepGoing && ((actual = handleData->waitingToSend->Flink) != handleData->waitingToSend)) { size_t messageSize; @@ -1105,6 +1119,9 @@ static void DoEvent(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClien { size_t i; bool goOn = true; + const char* msgId; + const char* corrId; + for (i = 0; (i < count) && goOn; i++) { /*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_123: [Every property name shall add to the message size the length of the property name + the length of the property value + 16 bytes.] */ @@ -1149,7 +1166,7 @@ static void DoEvent(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClien } // Add the Message Id and the Correlation Id - const char* msgId = IoTHubMessage_GetMessageId(message->messageHandle); + msgId = IoTHubMessage_GetMessageId(message->messageHandle); if (goOn && msgId != NULL) { if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_MESSAGE_ID, msgId) != HTTP_HEADERS_OK) @@ -1159,7 +1176,7 @@ static void DoEvent(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClien } } - const char* corrId = IoTHubMessage_GetCorrelationId(message->messageHandle); + corrId = IoTHubMessage_GetCorrelationId(message->messageHandle); if (goOn && corrId != NULL) { if (HTTPHeaders_ReplaceHeaderNameValuePair(clonedEventHTTPrequestHeaders, IOTHUB_CORRELATION_ID, corrId) != HTTP_HEADERS_OK) @@ -1244,7 +1261,7 @@ static void abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, const { /*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_050: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters: -requestType: POST --relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2015-08-15-preview" +-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-02-03" - requestHttpHeadersHandle: an HTTP headers instance containing the following Authorization: " " If-Match: value of ETag @@ -1254,7 +1271,7 @@ static void abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, const - responseContent: NULL]*/ /*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_051: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters: -requestType: DELETE --relativePath: abandon relative path begin + value of ETag + "?api-version=2015-08-15-preview" +-relativePath: abandon relative path begin + value of ETag + "?api-version=2016-02-03" - requestHttpHeadersHandle: an HTTP headers instance containing the following Authorization: " " If-Match: value of ETag @@ -1264,7 +1281,7 @@ static void abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, const - responseContent: NULL]*/ /*Codes_SRS_IOTHUBTRANSPORTTHTTP_02_077: [_DoWork shall call HTTPAPIEX_SAS_ExecuteRequest with the following parameters: -requestType: DELETE --relativePath: abandon relative path begin + value of ETag +"?api-version=2015-08-15-preview" + "&reject" +-relativePath: abandon relative path begin + value of ETag +"?api-version=2016-02-03" + "&reject" - requestHttpHeadersHandle: an HTTP headers instance containing the following Authorization: " " If-Match: value of ETag @@ -1332,7 +1349,7 @@ static void abandonOrAcceptMessage(HTTPTRANSPORT_HANDLE_DATA* handleData, const handleData->sasObject, handleData->httpApiExHandle, (action == ABANDON) ? HTTPAPI_REQUEST_POST : HTTPAPI_REQUEST_DELETE, /*-requestType: POST */ - STRING_c_str(fullAbandonRelativePath), /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2015-08-15-preview" */ + STRING_c_str(fullAbandonRelativePath), /*-relativePath: abandon relative path begin (as created by _Create) + value of ETag + "/abandon?api-version=2016-02-03" */ abandonRequestHttpHeaders, /*- requestHttpHeadersHandle: an HTTP headers instance containing the following */ NULL, /*- requestContent: NULL */ &statusCode, /*- statusCode: a pointer to unsigned int which might be examined for logging */ diff --git a/src/sdk/iothubtransporthttp.h b/src/sdk/iothubtransporthttp.h index 2c78dbe..fbb0a21 100644 --- a/src/sdk/iothubtransporthttp.h +++ b/src/sdk/iothubtransporthttp.h @@ -14,12 +14,15 @@ extern "C" extern TRANSPORT_HANDLE IoTHubTransportHttp_Create(const IOTHUBTRANSPORT_CONFIG* config); extern void IoTHubTransportHttp_Destroy(TRANSPORT_HANDLE handle); - extern int IoTHubTransportHttp_Subscribe(TRANSPORT_HANDLE handle); - extern void IoTHubTransportHttp_Unsubscribe(TRANSPORT_HANDLE handle); + extern IOTHUB_DEVICE_HANDLE IoTHubTransportHttp_Register(TRANSPORT_HANDLE handle, const char* deviceId, const char* deviceKey, PDLIST_ENTRY waitingToSend); + extern void IoTHubTransportHttp_Unregister(IOTHUB_DEVICE_HANDLE deviceHandle); + + extern int IoTHubTransportHttp_Subscribe(IOTHUB_DEVICE_HANDLE handle); + extern void IoTHubTransportHttp_Unsubscribe(IOTHUB_DEVICE_HANDLE handle); extern void IoTHubTransportHttp_DoWork(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_LL_HANDLE iotHubClientHandle); - extern IOTHUB_CLIENT_RESULT IoTHubTransportHttp_GetSendStatus(TRANSPORT_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus); + extern IOTHUB_CLIENT_RESULT IoTHubTransportHttp_GetSendStatus(IOTHUB_DEVICE_HANDLE handle, IOTHUB_CLIENT_STATUS *iotHubClientStatus); extern IOTHUB_CLIENT_RESULT IoTHubTransportHttp_SetOption(TRANSPORT_HANDLE handle, const char* optionName, const void* value); extern const void* HTTP_Protocol(void); diff --git a/src/sdk/list.c b/src/sdk/list.c new file mode 100644 index 0000000..7f43060 --- /dev/null +++ b/src/sdk/list.c @@ -0,0 +1,254 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include +#include "gballoc.h" +#include "list.h" + +typedef struct LIST_ITEM_INSTANCE_TAG +{ + const void* item; + void* next; +} LIST_ITEM_INSTANCE; + +typedef struct LIST_INSTANCE_TAG +{ + LIST_ITEM_INSTANCE* head; +} LIST_INSTANCE; + +LIST_HANDLE list_create(void) +{ + LIST_INSTANCE* result; + + /* Codes_SRS_LIST_01_001: [list_create shall create a new list and return a non-NULL handle on success.] */ + result = (LIST_INSTANCE*)malloc(sizeof(LIST_INSTANCE)); + if (result != NULL) + { + /* Codes_SRS_LIST_01_002: [If any error occurs during the list creation, list_create shall return NULL.] */ + result->head = NULL; + } + + return result; +} + +void list_destroy(LIST_HANDLE list) +{ + /* Codes_SRS_LIST_01_004: [If the list argument is NULL, no freeing of resources shall occur.] */ + if (list != NULL) + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + + while (list_instance->head != NULL) + { + LIST_ITEM_INSTANCE* current_item = list_instance->head; + list_instance->head = current_item->next; + free(current_item); + } + + /* Codes_SRS_LIST_01_003: [list_destroy shall free all resources associated with the list identified by the handle argument.] */ + free(list_instance); + } +} + +LIST_ITEM_HANDLE list_add(LIST_HANDLE list, const void* item) +{ + LIST_ITEM_INSTANCE* result; + + /* Codes_SRS_LIST_01_006: [If any of the arguments is NULL, list_add shall not add the item to the list and return NULL.] */ + if ((list == NULL) || + (item == NULL)) + { + result = NULL; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + result = (LIST_ITEM_INSTANCE*)malloc(sizeof(LIST_ITEM_INSTANCE)); + + if (result == NULL) + { + /* Codes_SRS_LIST_01_007: [If allocating the new list node fails, list_add shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_LIST_01_005: [list_add shall add one item to the tail of the list and on success it shall return a handle to the added item.] */ + result->next = NULL; + result->item = item; + + if (list_instance->head == NULL) + { + list_instance->head = result; + } + else + { + LIST_ITEM_INSTANCE* current = list_instance->head; + while (current->next != NULL) + { + current = current->next; + } + + current->next = result; + } + } + } + + return result; +} + +int list_remove(LIST_HANDLE list, LIST_ITEM_HANDLE item) +{ + int result; + + /* Codes_SRS_LIST_01_024: [If any of the arguments list or item_handle is NULL, list_remove shall fail and return a non-zero value.] */ + if ((list == NULL) || + (item == NULL)) + { + result = __LINE__; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + LIST_ITEM_INSTANCE* current_item = list_instance->head; + LIST_ITEM_INSTANCE* previous_item = NULL; + + while (current_item != NULL) + { + if (current_item == item) + { + if (previous_item != NULL) + { + previous_item->next = current_item->next; + } + else + { + list_instance->head = current_item->next; + } + + free(current_item); + + break; + } + previous_item = current_item; + current_item = current_item->next; + } + + if (current_item == NULL) + { + /* Codes_SRS_LIST_01_025: [If the item item_handle is not found in the list, then list_remove shall fail and return a non-zero value.] */ + result = __LINE__; + } + else + { + /* Codes_SRS_LIST_01_023: [list_remove shall remove a list item from the list and on success it shall return 0.] */ + result = 0; + } + } + + return result; +} + +LIST_ITEM_HANDLE list_get_head_item(LIST_HANDLE list) +{ + LIST_ITEM_HANDLE result; + + if (list == NULL) + { + /* Codes_SRS_LIST_01_009: [If the list argument is NULL, list_get_head_item shall return NULL.] */ + result = NULL; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + + /* Codes_SRS_LIST_01_008: [list_get_head_item shall return the head of the list.] */ + /* Codes_SRS_LIST_01_010: [If the list is empty, list_get_head_item_shall_return NULL.] */ + result = list_instance->head; + } + + return result; +} + +LIST_ITEM_HANDLE list_get_next_item(LIST_ITEM_HANDLE item_handle) +{ + LIST_ITEM_HANDLE result; + + if (item_handle == NULL) + { + /* Codes_SRS_LIST_01_019: [If item_handle is NULL then list_get_next_item shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_LIST_01_018: [list_get_next_item shall return the next item in the list following the item item_handle.] */ + result = ((LIST_ITEM_INSTANCE*)item_handle)->next; + } + + return result; +} + +const void* list_item_get_value(LIST_ITEM_HANDLE item_handle) +{ + const void* result; + + if (item_handle == NULL) + { + /* Codes_SRS_LIST_01_021: [If item_handle is NULL, list_item_get_value shall return NULL.] */ + result = NULL; + } + else + { + /* Codes_SRS_LIST_01_020: [list_item_get_value shall return the value associated with the list item identified by the item_handle argument.] */ + result = ((LIST_ITEM_INSTANCE*)item_handle)->item; + } + + return result; +} + +LIST_ITEM_HANDLE list_find(LIST_HANDLE list, LIST_MATCH_FUNCTION match_function, const void* match_context) +{ + LIST_ITEM_HANDLE result; + + if ((list == NULL) || + (match_function == NULL)) + { + /* Codes_SRS_LIST_01_012: [If the list or the match_function argument is NULL, list_find shall return NULL.] */ + result = NULL; + } + else + { + LIST_INSTANCE* list_instance = (LIST_INSTANCE*)list; + LIST_ITEM_INSTANCE* current = list_instance->head; + + /* Codes_SRS_LIST_01_011: [list_find shall iterate through all items in a list and return the first one that satisfies a certain match function.] */ + while (current != NULL) + { + /* Codes_SRS_LIST_01_014: [list find shall determine whether an item satisfies the match criteria by invoking the match function for each item in the list until a matching item is found.] */ + /* Codes_SRS_LIST_01_013: [The match_function shall get as arguments the list item being attempted to be matched and the match_context as is.] */ + if (match_function((LIST_ITEM_HANDLE)current, match_context) == true) + { + /* Codes_SRS_LIST_01_017: [If the match function returns true, list_find shall consider that item as matching.] */ + break; + } + + /* Codes_SRS_LIST_01_016: [If the match function returns false, list_find shall consider that item as not matching.] */ + current = current->next; + } + + if (current == NULL) + { + /* Codes_SRS_LIST_01_015: [If the list is empty, list_find shall return NULL.] */ + result = NULL; + } + else + { + result = current; + } + } + + return result; +} diff --git a/src/sdk/list.h b/src/sdk/list.h new file mode 100644 index 0000000..5582d7c --- /dev/null +++ b/src/sdk/list.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef LIST_H +#define LIST_H + +#ifdef __cplusplus +extern "C" { +#include "cstdbool" +#else +#include "stdbool.h" +#endif /* __cplusplus */ + +typedef struct LIST_INSTANCE_TAG* LIST_HANDLE; +typedef struct LIST_ITEM_INSTANCE_TAG* LIST_ITEM_HANDLE; +typedef bool (*LIST_MATCH_FUNCTION)(LIST_ITEM_HANDLE list_item, const void* match_context); + +extern LIST_HANDLE list_create(void); +extern void list_destroy(LIST_HANDLE list); +extern LIST_ITEM_HANDLE list_add(LIST_HANDLE list, const void* item); +extern int list_remove(LIST_HANDLE list, LIST_ITEM_HANDLE item_handle); +extern LIST_ITEM_HANDLE list_get_head_item(LIST_HANDLE list); +extern LIST_ITEM_HANDLE list_get_next_item(LIST_ITEM_HANDLE item_handle); +extern LIST_ITEM_HANDLE list_find(LIST_HANDLE list, LIST_MATCH_FUNCTION match_function, const void* match_context); + +extern const void* list_item_get_value(LIST_ITEM_HANDLE item_handle); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* LIST_H */ diff --git a/src/sdk/makefile b/src/sdk/makefile new file mode 100644 index 0000000..2c07299 --- /dev/null +++ b/src/sdk/makefile @@ -0,0 +1,6 @@ +!if 0 +Copyright (c) Microsoft. All rights reserved. +Licensed under the MIT license. See LICENSE file in the project root for full license information. +!endif + +!INCLUDE $(_MAKEENVROOT)\makefile.def diff --git a/src/sdk/map.c b/src/sdk/map.c index 581c31d..67a7a06 100644 --- a/src/sdk/map.c +++ b/src/sdk/map.c @@ -9,6 +9,7 @@ #include "map.h" #include "iot_logging.h" +#include "strings.h" DEFINE_ENUM_STRINGS(MAP_RESULT, MAP_RESULT_VALUES); @@ -35,7 +36,7 @@ MAP_HANDLE Map_Create(MAP_FILTER_CALLBACK mapFilterFunc) result->count = 0; result->mapFilterCallback = mapFilterFunc; } - return result; + return (MAP_HANDLE)result; } void Map_Destroy(MAP_HANDLE handle) @@ -157,7 +158,7 @@ MAP_HANDLE Map_Clone(MAP_HANDLE handle) } } } - return result; + return (MAP_HANDLE)result; } static int Map_IncreaseStorageKeysValues(MAP_HANDLE_DATA* handleData) @@ -222,6 +223,7 @@ static void Map_DecreaseStorageKeysValues(MAP_HANDLE_DATA* handleData) else { /*certainly > 1...*/ + char** undoneValues; char** undoneKeys = (char**)realloc(handleData->keys, sizeof(char*)* (handleData->count - 1)); if (undoneKeys == NULL) { @@ -232,7 +234,7 @@ static void Map_DecreaseStorageKeysValues(MAP_HANDLE_DATA* handleData) handleData->keys = undoneKeys; } - char** undoneValues = (char**)realloc(handleData->values, sizeof(char*)* (handleData->count - 1)); + undoneValues = (char**)realloc(handleData->values, sizeof(char*)* (handleData->count - 1)); if (undoneValues == NULL) { LogError("CATASTROPHIC error, unable to undo through realloc to a smaller size\r\n"); @@ -584,3 +586,95 @@ MAP_RESULT Map_GetInternals(MAP_HANDLE handle, const char*const** keys, const ch } return result; } + +STRING_HANDLE Map_ToJSON(MAP_HANDLE handle) +{ + STRING_HANDLE result; + /*Codes_SRS_MAP_02_052: [If parameter handle is NULL then Map_ToJSON shall return NULL.] */ + if (handle == NULL) + { + result = NULL; + LogError("invalid arg (NULL)"); + } + else + { + /*Codes_SRS_MAP_02_048: [Map_ToJSON shall produce a STRING_HANDLE representing the content of the MAP.] */ + result = STRING_construct("{"); + if (result == NULL) + { + LogError("STRING_construct failed"); + } + else + { + size_t i; + MAP_HANDLE_DATA* handleData = (MAP_HANDLE_DATA *)handle; + /*Codes_SRS_MAP_02_049: [If the MAP is empty, then Map_ToJSON shall produce the string "{}".*/ + bool breakFor = false; /*used to break out of for*/ + for (i = 0; (i < handleData->count) && (!breakFor); i++) + { + /*add one entry to the JSON*/ + /*Codes_SRS_MAP_02_050: [If the map has properties then Map_ToJSON shall produce the following string:{"name1":"value1", "name2":"value2" ...}]*/ + STRING_HANDLE key = STRING_new_JSON(handleData->keys[i]); + if (key == NULL) + { + LogError("STRING_new_JSON failed"); + STRING_delete(result); + result = NULL; + breakFor = true; + } + else + { + STRING_HANDLE value = STRING_new_JSON(handleData->values[i]); + if (value == NULL) + { + LogError("STRING_new_JSON failed"); + STRING_delete(result); + result = NULL; + breakFor = true; + } + else + { + if (!( + ((i>0) ? (STRING_concat(result, ",") == 0) : 1) && + (STRING_concat_with_STRING(result, key) == 0) && + (STRING_concat(result, ":") == 0) && + (STRING_concat_with_STRING(result, value) == 0) + )) + { + LogError("failed to build the JSON"); + STRING_delete(result); + result = NULL; + breakFor = true; + } + else + { + /*all nice, go to the next element in the map*/ + } + STRING_delete(value); + } + STRING_delete(key); + } + } + + if (breakFor) + { + LogError("error happened during JSON string builder"); + } + else + { + if (STRING_concat(result, "}") != 0) + { + LogError("failed to build the JSON"); + STRING_delete(result); + result = NULL; + } + else + { + /*return as is, JSON has been build*/ + } + } + } + } + return result; + +} \ No newline at end of file diff --git a/src/sdk/map.h b/src/sdk/map.h index 863a543..0cbc1d4 100644 --- a/src/sdk/map.h +++ b/src/sdk/map.h @@ -35,7 +35,7 @@ extern "C" */ DEFINE_ENUM(MAP_RESULT, MAP_RESULT_VALUES); -typedef void* MAP_HANDLE; +typedef struct MAP_HANDLE_DATA_TAG* MAP_HANDLE; typedef int (*MAP_FILTER_CALLBACK)(const char* mapProperty, const char* mapValue); @@ -192,6 +192,9 @@ extern const char* Map_GetValueFromKey(MAP_HANDLE handle, const char* key); */ extern MAP_RESULT Map_GetInternals(MAP_HANDLE handle, const char*const** keys, const char*const** values, size_t* count); +/*this API creates a JSON object from the content of the map*/ +extern STRING_HANDLE Map_ToJSON(MAP_HANDLE handle); + #ifdef __cplusplus } #endif diff --git a/src/sdk/metadata.txt b/src/sdk/metadata.txt deleted file mode 100644 index 84e3431..0000000 --- a/src/sdk/metadata.txt +++ /dev/null @@ -1,3 +0,0 @@ -git@github.com:arduino-libraries/azure-iot-sdks.git -arduino -2bcecebd1ebbb0129db503c01ae15431e92a66e4 diff --git a/src/sdk/mqttapi.h b/src/sdk/mqttapi.h deleted file mode 100644 index 969d853..0000000 --- a/src/sdk/mqttapi.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#ifndef MQTTAPI_H -#define MQTTAPI_H - -#ifdef __cplusplus -extern "C" { -#include -#else -#include -#endif -#include "macro_utils.h" -#include "crt_abstractions.h" - -typedef void* MQTTAPI_HANDLE; -typedef void* MQTTAPI_TOPIC_HANDLE; - -#define MQTTAPI_RESULT_VALUES \ -MQTTAPI_OK, \ -MQTTAPI_INVALID_ARG, \ -MQTTAPI_ERROR_CONNECTED_INSTANCE, \ -MQTTAPI_ERROR \ - -DEFINE_ENUM(MQTTAPI_RESULT, MQTTAPI_RESULT_VALUES); - -#define MQTTAPI_CONFIRMATION_RESULT_VALUES \ - MQTTAPI_CONFIRMATION_OK, \ - MQTTAPI_CONFIRMATION_BECAUSE_DESTROY, \ - MQTTAPI_CONFIRMATION_ERROR \ - -DEFINE_ENUM(MQTTAPI_CONFIRMATION_RESULT, MQTTAPI_CONFIRMATION_RESULT_VALUES); - -typedef struct -{ - /** The length of the MQTT message payload in bytes. */ - size_t payloadlen; - /** A pointer to the payload of the MQTT message. */ - unsigned char* payload; -} MQTTAPI_Message; - -typedef struct -{ - const char* deviceId; - const char* deviceKey; - /** - * String used to connect with a sas token. Format is .\devices\ - */ - const char* sasTokenSr; - /** - * String with the Server URI to be connected. Format required protocol://host:port - */ - const char* serverURI; -} MQTTAPI_ConnectOptions; - -typedef bool MQTTAPI_MessageArrived(void* context, const MQTTAPI_Message* message); -typedef void MQTTAPI_DeliveryComplete(void* context, MQTTAPI_CONFIRMATION_RESULT result); - -extern MQTTAPI_HANDLE MQTTAPI_Create(const MQTTAPI_ConnectOptions* options); -extern MQTTAPI_RESULT MQTTAPI_SetMessageCallback(MQTTAPI_HANDLE instance, void* context, MQTTAPI_MessageArrived * ma); -extern MQTTAPI_RESULT MQTTAPI_SetDeliveryCompletedCallback(MQTTAPI_HANDLE instance, MQTTAPI_DeliveryComplete* dc); - - -extern void MQTTAPI_Destroy(MQTTAPI_HANDLE instance); - -extern MQTTAPI_RESULT MQTTAPI_PublishMessage(MQTTAPI_HANDLE instance, const char* topicName, const MQTTAPI_Message* msg, void* context); - -extern MQTTAPI_TOPIC_HANDLE MQTTAPI_Subscribe(MQTTAPI_HANDLE instance, const char* topic); -extern void MQTTAPI_Unsubscribe(MQTTAPI_TOPIC_HANDLE topicInstance); - -extern void MQTTAPI_DoWork(MQTTAPI_HANDLE instance); - -#ifdef __cplusplus -} -#endif - -#endif /* MQTTAPI_H*/ diff --git a/src/sdk/platform.h b/src/sdk/platform.h new file mode 100644 index 0000000..87ea2c3 --- /dev/null +++ b/src/sdk/platform.h @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef PLATFORM_H +#define PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#include "xio.h" + + extern int platform_init(void); + extern void platform_deinit(void); + extern const IO_INTERFACE_DESCRIPTION* platform_get_default_tlsio(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* PLATFORM_H */ diff --git a/src/sdk/refcount.h b/src/sdk/refcount.h new file mode 100644 index 0000000..4ca527a --- /dev/null +++ b/src/sdk/refcount.h @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +/*this header contains macros for ref_counting a variable. + +There are no upper bound checks related to uint32_t overflow because we expect that bigger issues are in +the system when more than 4 billion references exist to the same variable. In the case when such an overflow +occurs, the object's ref count will reach zero (while still having 0xFFFFFFFF references) and likely the +controlling code will take the decision to free the object's resources. Then, any of the 0xFFFFFFFF references +will interact with deallocated memory / resources resulting in an undefined behavior. +*/ + +#ifndef REFCOUNT_H +#define REFCOUNT_H + +#ifdef __cplusplus +#include +#include +extern "C" +{ +#else +#include +#include +#endif + + +#include "macro_utils.h" + +#define REFCOUNT_TYPE(type) \ +struct C2(C2(REFCOUNT_, type), _TAG) + +#define REFCOUNT_SHORT_TYPE(type) \ +C2(REFCOUNT_, type) + +#define REFCOUNT_TYPE_DECLARE_CREATE(type) C2(REFCOUNT_SHORT_TYPE(type), _Create) +#define REFCOUNT_TYPE_CREATE(type) C2(REFCOUNT_SHORT_TYPE(type), _Create)() + +/*this introduces a new refcount'd type based on another type */ +/*and an initializer for that new type that also sets the ref count to 1. The type must not have a flexible array*/ +/*the newly allocated memory shall be free'd by free()*/ +/*and the ref counting is handled internally by the type in the _Create/ _Clone /_Destroy functions */ + +#define DEFINE_REFCOUNT_TYPE(type) \ +REFCOUNT_TYPE(type) \ +{ \ + type counted; \ + uint32_t count; \ +}; \ +static type* REFCOUNT_TYPE_DECLARE_CREATE(type) (void) \ +{ \ + REFCOUNT_TYPE(type)* result = (REFCOUNT_TYPE(type)*)malloc(sizeof(REFCOUNT_TYPE(type))); \ + if (result != NULL) \ + { \ + result->count = 1; \ + } \ + return (type*)result; \ +} \ + +/*the following macros increment/decrement a ref count in an atomic way, depending on the platform*/ +/*The following mechanisms are considered in this order +C11 + - will result in #include + - will use atomic_fetch_add/sub; + - about the return value: "Atomically, the value pointed to by object immediately before the effects" +windows + - will result in #include "windows.h" + - will use InterlockedIncrement/InterlockedDecrement; + - about the return value: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683580(v=vs.85).aspx "The function returns the resulting decremented value" +gcc + - will result in no include (for gcc these are intrinsics build in) + - will use __sync_fetch_and_add/sub + - about the return value: "... returns the value that had previously been in memory." (https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gcc/Atomic-Builtins.html#Atomic-Builtins) +other cases + - if REFCOUNT_ATOMIC_DONTCARE is defined, then + will result in ++/-- used for increment/decrement. + - if it is not defined, then error out + +It seems windows is "one off" because it returns the value "after" the decrement, as opposed to C11 standard and gcc that return the value "before". +The macro DEC_RETURN_ZERO will be "0" on windows, and "1" on the other cases. +*/ + +/*if macro DEC_REF returns DEC_RETURN_ZERO that means the ref count has reached zero.*/ +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ == 201112) && (__STDC_NO_ATOMICS__!=1) && !defined(__GNUC__) +#include +#define DEC_RETURN_ZERO (1) +#define INC_REF(type, var) atomic_fetch_add((&((REFCOUNT_TYPE(type)*)var)->count), 1) +#define DEC_REF(type, var) atomic_fetch_sub((&((REFCOUNT_TYPE(type)*)var)->count), 1) + +#elif defined(WIN32) +#include "windows.h" +#define DEC_RETURN_ZERO (0) +#define INC_REF(type, var) InterlockedIncrement(&(((REFCOUNT_TYPE(type)*)var)->count)) +#define DEC_REF(type, var) InterlockedDecrement(&(((REFCOUNT_TYPE(type)*)var)->count)) + +#elif defined(__GNUC__) +#define DEC_RETURN_ZERO (0) +#define INC_REF(type, var) __sync_add_and_fetch((&((REFCOUNT_TYPE(type)*)var)->count), 1) +#define DEC_REF(type, var) __sync_sub_and_fetch((&((REFCOUNT_TYPE(type)*)var)->count), 1) + +#else +#if defined(REFCOUNT_ATOMIC_DONTCARE) +#define DEC_RETURN_ZERO (0) +#define INC_REF(type, var) ++(&(((REFCOUNT_TYPE(type)*)var)->count)) +#define DEC_REF(type, var) --(&(((REFCOUNT_TYPE(type)*)var)->count)) +#else +#error do not know how to atomically increment and decrement a uint32_t :(. Platform support needs to be extended to your platform. +#endif /*defined(REFCOUNT_ATOMIC_DONTCARE)*/ +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif /*REFCOUNT_H*/ + + diff --git a/src/sdk/sastoken.c b/src/sdk/sastoken.c index 90ba5f5..a9790d4 100644 --- a/src/sdk/sastoken.c +++ b/src/sdk/sastoken.c @@ -58,7 +58,7 @@ STRING_HANDLE SASToken_Create(STRING_HANDLE key, STRING_HANDLE scope, STRING_HAN ((toBeHashed = STRING_new()) == NULL) || ((result = STRING_new()) == NULL)) { - LogError("Unable to allocate memory to prepare SAS token.\r\n") + LogError("Unable to allocate memory to prepare SAS token.\r\n"); } else { diff --git a/src/sdk/sastoken.h b/src/sdk/sastoken.h index 03a86c1..ac7a4e0 100644 --- a/src/sdk/sastoken.h +++ b/src/sdk/sastoken.h @@ -5,7 +5,6 @@ #define SASTOKEN_H #include "strings.h" -#include "buffer_.h" #ifdef __cplusplus extern "C" { diff --git a/src/sdk/schemalib.c b/src/sdk/schemalib.c index 02e8864..6c8c0d1 100644 --- a/src/sdk/schemalib.c +++ b/src/sdk/schemalib.c @@ -2,9 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #include +#ifndef WINCE #ifdef _CRTDBG_MAP_ALLOC #include #endif +#endif #include "gballoc.h" #include "schemalib.h" diff --git a/src/sdk/socketio.h b/src/sdk/socketio.h new file mode 100644 index 0000000..df8e6ed --- /dev/null +++ b/src/sdk/socketio.h @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef SOCKETIO_H +#define SOCKETIO_H + +#ifdef __cplusplus +extern "C" { +#include +#else +#include +#endif /* __cplusplus */ + +#include "xio.h" +#include "xlogging.h" + +typedef struct SOCKETIO_CONFIG_TAG +{ + const char* hostname; + int port; + void* accepted_socket; +} SOCKETIO_CONFIG; + +extern CONCRETE_IO_HANDLE socketio_create(void* io_create_parameters, LOGGER_LOG logger_log); +extern void socketio_destroy(CONCRETE_IO_HANDLE socket_io); +extern int socketio_open(CONCRETE_IO_HANDLE socket_io, ON_IO_OPEN_COMPLETE on_io_open_complete, ON_BYTES_RECEIVED on_bytes_received, ON_IO_ERROR on_io_error, void* callback_context); +extern int socketio_close(CONCRETE_IO_HANDLE socket_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context); +extern int socketio_send(CONCRETE_IO_HANDLE socket_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context); +extern void socketio_dowork(CONCRETE_IO_HANDLE socket_io); +extern const IO_INTERFACE_DESCRIPTION* socketio_get_interface_description(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SOCKETIO_H */ diff --git a/src/sdk/string_tokenizer.c b/src/sdk/string_tokenizer.c index bd17278..7a9ebaf 100644 --- a/src/sdk/string_tokenizer.c +++ b/src/sdk/string_tokenizer.c @@ -20,172 +20,174 @@ typedef struct STRING_TOKEN_TAG { - const char* inputString; - const char* currentPos; - size_t sizeOfinputString; + const char* inputString; + const char* currentPos; + size_t sizeOfinputString; } STRING_TOKEN; STRING_TOKENIZER_HANDLE STRING_TOKENIZER_create(STRING_HANDLE handle) { - STRING_TOKEN *result; - char* inputStringToMalloc; - - /* Codes_SRS_STRING_04_001: [STRING_TOKENIZER_create shall return an NULL STRING_TOKENIZER_HANDLE if parameter handle is NULL] */ - if (handle == NULL) - { - LogError("Invalid Argument. Handle cannot be NULL.\r\n"); - result = NULL; - } - /* Codes_SRS_STRING_04_002: [STRING_TOKENIZER_create shall allocate a new STRING_TOKENIZER_HANDLE having the content of the STRING_HANDLE copied and current position pointing at the beginning of the string] */ - else if((result = (STRING_TOKEN *)malloc(sizeof(STRING_TOKEN))) == NULL) - { - LogError("Memory Allocation failed. Cannot allocate STRING_TOKENIZER.\r\n"); - } - else if ((mallocAndStrcpy_s(&inputStringToMalloc, STRING_c_str(handle))) != 0) - { - LogError("Memory Allocation Failed. Cannot allocate and copy string Content.\r\n"); - free(result); - result = NULL; - } - else - { - result->inputString = inputStringToMalloc; - result->currentPos = result->inputString; //Current Pos will point to the initial position of Token. - result->sizeOfinputString = strlen(result->inputString); //Calculate Size of Current String - } + STRING_TOKEN *result; + char* inputStringToMalloc; + + /* Codes_SRS_STRING_04_001: [STRING_TOKENIZER_create shall return an NULL STRING_TOKENIZER_HANDLE if parameter handle is NULL] */ + if (handle == NULL) + { + LogError("Invalid Argument. Handle cannot be NULL.\r\n"); + result = NULL; + } + /* Codes_SRS_STRING_04_002: [STRING_TOKENIZER_create shall allocate a new STRING_TOKENIZER_HANDLE having the content of the STRING_HANDLE copied and current position pointing at the beginning of the string] */ + else if((result = (STRING_TOKEN *)malloc(sizeof(STRING_TOKEN))) == NULL) + { + LogError("Memory Allocation failed. Cannot allocate STRING_TOKENIZER.\r\n"); + } + else if ((mallocAndStrcpy_s(&inputStringToMalloc, STRING_c_str(handle))) != 0) + { + LogError("Memory Allocation Failed. Cannot allocate and copy string Content.\r\n"); + free(result); + result = NULL; + } + else + { + result->inputString = inputStringToMalloc; + result->currentPos = result->inputString; //Current Pos will point to the initial position of Token. + result->sizeOfinputString = strlen(result->inputString); //Calculate Size of Current String + } - return (STRING_TOKENIZER_HANDLE)result; + return (STRING_TOKENIZER_HANDLE)result; } int STRING_TOKENIZER_get_next_token(STRING_TOKENIZER_HANDLE tokenizer, STRING_HANDLE output, const char* delimiters) { - int result; - /* Codes_SRS_STRING_04_004: [STRING_TOKENIZER_get_next_token shall return a nonzero value if any of the 3 parameters is NULL] */ - if (tokenizer == NULL || output == NULL || delimiters == NULL) - { - result = __LINE__; - } - else - { - STRING_TOKEN* token = (STRING_TOKEN*)tokenizer; - /* Codes_SRS_STRING_04_011: [Each subsequent call to STRING_TOKENIZER_get_next_token starts searching from the saved position on t and behaves as described above.] */ - size_t remainingInputStringSize = token->sizeOfinputString - (token->currentPos - token->inputString); - size_t delimitterSize = strlen(delimiters); - - /* First Check if we reached the end of the string*/ - /* Codes_SRS_STRING_TOKENIZER_04_014: [STRING_TOKENIZER_get_next_token shall return nonzero value if t contains an empty string.] */ - if (remainingInputStringSize == 0) - { - result = __LINE__; - } - else if (delimitterSize == 0) - { - LogError("Empty delimiters parameter.\r\n"); - result = __LINE__; - } - else - { - size_t i; - /* Codes_SRS_STRING_04_005: [STRING_TOKENIZER_get_next_token searches the string inside STRING_TOKENIZER_HANDLE for the first character that is NOT contained in the current delimiter] */ - for (i = 0; i < remainingInputStringSize; i++) - { - bool foundDelimitter = false; - for (size_t j = 0; j < delimitterSize; j++) - { - if (token->currentPos[i] == delimiters[j]) - { - foundDelimitter = true; - break; - } - } - - /* Codes_SRS_STRING_04_007: [If such a character is found, STRING_TOKENIZER_get_next_token consider it as the start of a token.] */ - if (!foundDelimitter) - { - break; - } - } - - /* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */ - //At this point update Current Pos to the character of the last token found or end of String. - token->currentPos += i; - - //Update the remainingInputStringSize - remainingInputStringSize -= i; - - /* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */ - if (remainingInputStringSize == 0) - { - result = __LINE__; - } - else - { - bool foundDelimitter = false; - char* endOfTokenPosition=NULL; - size_t amountOfCharactersToCopy; - //At this point the Current Pos is pointing to a character that is point to a nonDelimiter. So, now search for a Delimiter, till the end of the String. - /*Codes_SRS_STRING_04_008: [STRING_TOKENIZER_get_next_token than searches from the start of a token for a character that is contained in the delimiters string.] */ - /* Codes_SRS_STRING_04_009: [If no such character is found, STRING_TOKENIZER_get_next_token extends the current token to the end of the string inside t, copies the token to output and returns 0.] */ - /* Codes_SRS_STRING_04_010: [If such a character is found, STRING_TOKENIZER_get_next_token consider it the end of the token and copy it's content to output, updates the current position inside t to the next character and returns 0.] */ - for (size_t j = 0; j < delimitterSize; j++) - { - if ((endOfTokenPosition = strchr(token->currentPos, delimiters[j])) != NULL) - { - foundDelimitter = true; - break; - } - } - - //If token not found, than update the EndOfToken to the end of the inputString; - if (endOfTokenPosition == NULL) - { - amountOfCharactersToCopy = remainingInputStringSize; - } - else - { - amountOfCharactersToCopy = endOfTokenPosition - token->currentPos; - } - - //copy here the string to output. - if (STRING_copy_n(output, token->currentPos, amountOfCharactersToCopy) != 0) - { - LogError("Problem copying token to output String.\r\n"); - result = __LINE__; - } - else - { - //Update the Current position. - //Check if end of String reached so, currentPos points to the end of String. - if (foundDelimitter) - { - token->currentPos += amountOfCharactersToCopy + 1; - } - else - { - token->currentPos += amountOfCharactersToCopy; - } - - result = 0; //Result will be on the output. - } - } - } - } - - return result; + int result; + /* Codes_SRS_STRING_04_004: [STRING_TOKENIZER_get_next_token shall return a nonzero value if any of the 3 parameters is NULL] */ + if (tokenizer == NULL || output == NULL || delimiters == NULL) + { + result = __LINE__; + } + else + { + STRING_TOKEN* token = (STRING_TOKEN*)tokenizer; + /* Codes_SRS_STRING_04_011: [Each subsequent call to STRING_TOKENIZER_get_next_token starts searching from the saved position on t and behaves as described above.] */ + size_t remainingInputStringSize = token->sizeOfinputString - (token->currentPos - token->inputString); + size_t delimitterSize = strlen(delimiters); + + /* First Check if we reached the end of the string*/ + /* Codes_SRS_STRING_TOKENIZER_04_014: [STRING_TOKENIZER_get_next_token shall return nonzero value if t contains an empty string.] */ + if (remainingInputStringSize == 0) + { + result = __LINE__; + } + else if (delimitterSize == 0) + { + LogError("Empty delimiters parameter.\r\n"); + result = __LINE__; + } + else + { + size_t i; + size_t j; + /* Codes_SRS_STRING_04_005: [STRING_TOKENIZER_get_next_token searches the string inside STRING_TOKENIZER_HANDLE for the first character that is NOT contained in the current delimiter] */ + for (i = 0; i < remainingInputStringSize; i++) + { + bool foundDelimitter = false; + for (j = 0; j < delimitterSize; j++) + { + if (token->currentPos[i] == delimiters[j]) + { + foundDelimitter = true; + break; + } + } + + /* Codes_SRS_STRING_04_007: [If such a character is found, STRING_TOKENIZER_get_next_token consider it as the start of a token.] */ + if (!foundDelimitter) + { + break; + } + } + + /* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */ + //At this point update Current Pos to the character of the last token found or end of String. + token->currentPos += i; + + //Update the remainingInputStringSize + remainingInputStringSize -= i; + + /* Codes_SRS_STRING_04_006: [If no such character is found, then STRING_TOKENIZER_get_next_token shall return a nonzero Value (You've reach the end of the string or the string consists with only delimiters).] */ + if (remainingInputStringSize == 0) + { + result = __LINE__; + } + else + { + bool foundDelimitter = false; + char* endOfTokenPosition=NULL; + size_t amountOfCharactersToCopy; + size_t j; + //At this point the Current Pos is pointing to a character that is point to a nonDelimiter. So, now search for a Delimiter, till the end of the String. + /*Codes_SRS_STRING_04_008: [STRING_TOKENIZER_get_next_token than searches from the start of a token for a character that is contained in the delimiters string.] */ + /* Codes_SRS_STRING_04_009: [If no such character is found, STRING_TOKENIZER_get_next_token extends the current token to the end of the string inside t, copies the token to output and returns 0.] */ + /* Codes_SRS_STRING_04_010: [If such a character is found, STRING_TOKENIZER_get_next_token consider it the end of the token and copy it's content to output, updates the current position inside t to the next character and returns 0.] */ + for (j = 0; j < delimitterSize; j++) + { + if ((endOfTokenPosition = strchr(token->currentPos, delimiters[j])) != NULL) + { + foundDelimitter = true; + break; + } + } + + //If token not found, than update the EndOfToken to the end of the inputString; + if (endOfTokenPosition == NULL) + { + amountOfCharactersToCopy = remainingInputStringSize; + } + else + { + amountOfCharactersToCopy = endOfTokenPosition - token->currentPos; + } + + //copy here the string to output. + if (STRING_copy_n(output, token->currentPos, amountOfCharactersToCopy) != 0) + { + LogError("Problem copying token to output String.\r\n"); + result = __LINE__; + } + else + { + //Update the Current position. + //Check if end of String reached so, currentPos points to the end of String. + if (foundDelimitter) + { + token->currentPos += amountOfCharactersToCopy + 1; + } + else + { + token->currentPos += amountOfCharactersToCopy; + } + + result = 0; //Result will be on the output. + } + } + } + } + + return result; } /* Codes_SRS_STRING_TOKENIZER_04_012: [STRING_TOKENIZER_destroy shall free the memory allocated by the STRING_TOKENIZER_create ] */ void STRING_TOKENIZER_destroy(STRING_TOKENIZER_HANDLE t) { - /* Codes_SRS_STRING_TOKENIZER_04_013: [When the t argument is NULL, then STRING_TOKENIZER_destroy shall not attempt to free] */ - if (t != NULL) - { - STRING_TOKEN* value = (STRING_TOKEN*)t; - free((char*)value->inputString); - value->inputString = NULL; - free(value); - } + /* Codes_SRS_STRING_TOKENIZER_04_013: [When the t argument is NULL, then STRING_TOKENIZER_destroy shall not attempt to free] */ + if (t != NULL) + { + STRING_TOKEN* value = (STRING_TOKEN*)t; + free((char*)value->inputString); + value->inputString = NULL; + free(value); + } } diff --git a/src/sdk/string_tokenizer.h b/src/sdk/string_tokenizer.h index 0cdd82f..c4a7c7d 100644 --- a/src/sdk/string_tokenizer.h +++ b/src/sdk/string_tokenizer.h @@ -12,7 +12,7 @@ extern "C" #else #endif -typedef void* STRING_TOKENIZER_HANDLE; +typedef struct STRING_TOKEN_TAG* STRING_TOKENIZER_HANDLE; extern STRING_TOKENIZER_HANDLE STRING_TOKENIZER_create(STRING_HANDLE handle); extern int STRING_TOKENIZER_get_next_token(STRING_TOKENIZER_HANDLE t, STRING_HANDLE output, const char* delimiters); diff --git a/src/sdk/strings.c b/src/sdk/strings.c index f7d3d7e..84b627b 100644 --- a/src/sdk/strings.c +++ b/src/sdk/strings.c @@ -80,7 +80,7 @@ STRING_HANDLE STRING_clone(STRING_HANDLE handle) /*not much to do, result is NULL from malloc*/ } } - return result; + return (STRING_HANDLE)result; } /* Codes_SRS_STRING_07_003: [STRING_construct shall allocate a new string with the value of the specified const char*.] */ @@ -281,7 +281,7 @@ STRING_HANDLE STRING_new_JSON(const char* source) } } - return result; + return (STRING_HANDLE)result; } /*this function will concatenate to the string s1 the string s2, resulting in s1+s2*/ diff --git a/src/sdk/strings.h b/src/sdk/strings.h index e2b49d3..c4c901d 100644 --- a/src/sdk/strings.h +++ b/src/sdk/strings.h @@ -12,7 +12,7 @@ extern "C" #include #endif -typedef void* STRING_HANDLE; +typedef struct STRING_TAG* STRING_HANDLE; extern STRING_HANDLE STRING_new(void); extern STRING_HANDLE STRING_clone(STRING_HANDLE handle); diff --git a/src/sdk/vector.h b/src/sdk/vector.h index f8f0b10..5c51fd0 100644 --- a/src/sdk/vector.h +++ b/src/sdk/vector.h @@ -14,7 +14,7 @@ extern "C" #include #endif -typedef void* VECTOR_HANDLE; +typedef struct VECTOR_TAG* VECTOR_HANDLE; typedef bool(*PREDICATE_FUNCTION)(const void* element, const void* value); diff --git a/src/sdk/version.c b/src/sdk/version.c index 6efdb9c..6a03554 100644 --- a/src/sdk/version.c +++ b/src/sdk/version.c @@ -1,7 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -#include "version.h" +#include "iothub_client_version.h" const char* IoTHubClient_GetVersionString(void) { diff --git a/src/sdk/xio.c b/src/sdk/xio.c new file mode 100644 index 0000000..dd0a428 --- /dev/null +++ b/src/sdk/xio.c @@ -0,0 +1,164 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#ifdef _CRTDBG_MAP_ALLOC +#include +#endif +#include +#include "gballoc.h" +#include "xio.h" + +typedef struct XIO_INSTANCE_TAG +{ + const IO_INTERFACE_DESCRIPTION* io_interface_description; + XIO_HANDLE concrete_xio_handle; +} XIO_INSTANCE; + +XIO_HANDLE xio_create(const IO_INTERFACE_DESCRIPTION* io_interface_description, const void* xio_create_parameters, LOGGER_LOG logger_log) +{ + XIO_INSTANCE* xio_instance; + /* Codes_SRS_XIO_01_003: [If the argument io_interface_description is NULL, xio_create shall return NULL.] */ + if ((io_interface_description == NULL) || + /* Codes_SRS_XIO_01_004: [If any io_interface_description member is NULL, xio_create shall return NULL.] */ + (io_interface_description->concrete_io_create == NULL) || + (io_interface_description->concrete_io_destroy == NULL) || + (io_interface_description->concrete_io_open == NULL) || + (io_interface_description->concrete_io_close == NULL) || + (io_interface_description->concrete_io_send == NULL) || + (io_interface_description->concrete_io_dowork == NULL)) + { + xio_instance = NULL; + } + else + { + xio_instance = (XIO_INSTANCE*)malloc(sizeof(XIO_INSTANCE)); + + /* Codes_SRS_XIO_01_017: [If allocating the memory needed for the IO interface fails then xio_create shall return NULL.] */ + if (xio_instance != NULL) + { + /* Codes_SRS_XIO_01_001: [xio_create shall return on success a non-NULL handle to a new IO interface.] */ + xio_instance->io_interface_description = io_interface_description; + + /* Codes_SRS_XIO_01_002: [In order to instantiate the concrete IO implementation the function concrete_io_create from the io_interface_description shall be called, passing the xio_create_parameters and logger_log arguments.] */ + xio_instance->concrete_xio_handle = xio_instance->io_interface_description->concrete_io_create((void*)xio_create_parameters, logger_log); + + /* Codes_SRS_XIO_01_016: [If the underlying concrete_io_create call fails, xio_create shall return NULL.] */ + if (xio_instance->concrete_xio_handle == NULL) + { + free(xio_instance); + xio_instance = NULL; + } + } + } + return (XIO_HANDLE)xio_instance; +} + +void xio_destroy(XIO_HANDLE xio) +{ + /* Codes_SRS_XIO_01_007: [If the argument io is NULL, xio_destroy shall do nothing.] */ + if (xio != NULL) + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_006: [xio_destroy shall also call the concrete_io_destroy function that is member of the io_interface_description argument passed to xio_create, while passing as argument to concrete_io_destroy the result of the underlying concrete_io_create handle that was called as part of the xio_create call.] */ + xio_instance->io_interface_description->concrete_io_destroy(xio_instance->concrete_xio_handle); + + /* Codes_SRS_XIO_01_005: [xio_destroy shall free all resources associated with the IO handle.] */ + free(xio_instance); + } +} + +int xio_open(XIO_HANDLE xio, ON_IO_OPEN_COMPLETE on_io_open_complete, ON_BYTES_RECEIVED on_bytes_received, ON_IO_ERROR on_io_error, void* callback_context) +{ + int result; + + if (xio == NULL) + { + /* Codes_SRS_XIO_01_021: [If handle is NULL, xio_open shall return a non-zero value.] */ + result = __LINE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_019: [xio_open shall call the specific concrete_io_open function specified in xio_create, passing the receive_callback and receive_callback_context arguments.] */ + if (xio_instance->io_interface_description->concrete_io_open(xio_instance->concrete_xio_handle, on_io_open_complete, on_bytes_received, on_io_error, callback_context) != 0) + { + /* Codes_SRS_XIO_01_022: [If the underlying concrete_io_open fails, xio_open shall return a non-zero value.] */ + result = __LINE__; + } + else + { + /* Codes_SRS_XIO_01_020: [On success, xio_open shall return 0.] */ + result = 0; + } + } + + return result; +} + +int xio_close(XIO_HANDLE xio, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) +{ + int result; + + if (xio == NULL) + { + /* Codes_SRS_XIO_01_025: [If handle is NULL, xio_close shall return a non-zero value.] */ + result = __LINE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_023: [xio_close shall call the specific concrete_io_close function specified in xio_create.] */ + if (xio_instance->io_interface_description->concrete_io_close(xio_instance->concrete_xio_handle, on_io_close_complete, callback_context) != 0) + { + /* Codes_SRS_XIO_01_026: [If the underlying concrete_io_close fails, xio_close shall return a non-zero value.] */ + result = __LINE__; + } + else + { + /* Codes_SRS_XIO_01_024: [On success, xio_close shall return 0.] */ + result = 0; + } + } + + return result; +} + +int xio_send(XIO_HANDLE xio, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context) +{ + int result; + + /* Codes_SRS_XIO_01_011: [No error check shall be performed on buffer and size.] */ + /* Codes_SRS_XIO_01_010: [If handle is NULL, xio_send shall return a non-zero value.] */ + if (xio == NULL) + { + result = __LINE__; + } + else + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_008: [xio_send shall pass the sequence of bytes pointed to by buffer to the concrete IO implementation specified in xio_create, by calling the concrete_io_send function while passing down the buffer and size arguments to it.] */ + /* Codes_SRS_XIO_01_009: [On success, xio_send shall return 0.] */ + /* Codes_SRS_XIO_01_015: [If the underlying concrete_io_send fails, xio_send shall return a non-zero value.] */ + /* Codes_SRS_XIO_01_027: [xio_send shall pass to the concrete_io_send function the on_send_complete and callback_context arguments.] */ + result = xio_instance->io_interface_description->concrete_io_send(xio_instance->concrete_xio_handle, buffer, size, on_send_complete, callback_context); + } + + return result; +} + +void xio_dowork(XIO_HANDLE xio) +{ + /* Codes_SRS_XIO_01_018: [When the handle argument is NULL, xio_dowork shall do nothing.] */ + if (xio != NULL) + { + XIO_INSTANCE* xio_instance = (XIO_INSTANCE*)xio; + + /* Codes_SRS_XIO_01_012: [xio_dowork shall call the concrete XIO implementation specified in xio_create, by calling the concrete_io_dowork function.] */ + xio_instance->io_interface_description->concrete_io_dowork(xio_instance->concrete_xio_handle); + } +} diff --git a/src/sdk/xio.h b/src/sdk/xio.h new file mode 100644 index 0000000..3aded52 --- /dev/null +++ b/src/sdk/xio.h @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef XIO_H +#define XIO_H + +#include "xlogging.h" + +#ifdef __cplusplus +#include +extern "C" { +#else +#include +#endif /* __cplusplus */ + + typedef struct XIO_INSTANCE_TAG* XIO_HANDLE; + typedef void* CONCRETE_IO_HANDLE; + + typedef enum IO_SEND_RESULT_TAG + { + IO_SEND_OK, + IO_SEND_ERROR, + IO_SEND_CANCELLED + } IO_SEND_RESULT; + + typedef enum IO_OPEN_RESULT_TAG + { + IO_OPEN_OK, + IO_OPEN_ERROR, + IO_OPEN_CANCELLED + } IO_OPEN_RESULT; + + typedef void(*ON_BYTES_RECEIVED)(void* context, const unsigned char* buffer, size_t size); + typedef void(*ON_SEND_COMPLETE)(void* context, IO_SEND_RESULT send_result); + typedef void(*ON_IO_OPEN_COMPLETE)(void* context, IO_OPEN_RESULT open_result); + typedef void(*ON_IO_CLOSE_COMPLETE)(void* context); + typedef void(*ON_IO_ERROR)(void* context); + + typedef CONCRETE_IO_HANDLE(*IO_CREATE)(void* io_create_parameters, LOGGER_LOG logger_log); + typedef void(*IO_DESTROY)(CONCRETE_IO_HANDLE concrete_io); + typedef int(*IO_OPEN)(CONCRETE_IO_HANDLE concrete_io, ON_IO_OPEN_COMPLETE on_io_open_complete, ON_BYTES_RECEIVED on_bytes_received, ON_IO_ERROR on_io_error, void* callback_context); + typedef int(*IO_CLOSE)(CONCRETE_IO_HANDLE concrete_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context); + typedef int(*IO_SEND)(CONCRETE_IO_HANDLE concrete_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context); + typedef void(*IO_DOWORK)(CONCRETE_IO_HANDLE concrete_io); + + typedef struct IO_INTERFACE_DESCRIPTION_TAG + { + IO_CREATE concrete_io_create; + IO_DESTROY concrete_io_destroy; + IO_OPEN concrete_io_open; + IO_CLOSE concrete_io_close; + IO_SEND concrete_io_send; + IO_DOWORK concrete_io_dowork; + } IO_INTERFACE_DESCRIPTION; + + extern XIO_HANDLE xio_create(const IO_INTERFACE_DESCRIPTION* io_interface_description, const void* io_create_parameters, LOGGER_LOG logger_log); + extern void xio_destroy(XIO_HANDLE xio); + extern int xio_open(XIO_HANDLE xio, ON_IO_OPEN_COMPLETE on_io_open_complete, ON_BYTES_RECEIVED on_bytes_received, ON_IO_ERROR on_io_error, void* callback_context); + extern int xio_close(XIO_HANDLE xio, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context); + extern int xio_send(XIO_HANDLE xio, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context); + extern void xio_dowork(XIO_HANDLE xio); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XIO_H */ diff --git a/src/sdk/xlogging.h b/src/sdk/xlogging.h new file mode 100644 index 0000000..caad5f2 --- /dev/null +++ b/src/sdk/xlogging.h @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#ifndef XLOGGING_H +#define XLOGGING_H +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef void(*LOGGER_LOG)(unsigned int options, char* format, ...); + +#define LOG_LINE 0x01 + +#define LOG(logger, ...) if (logger != NULL) logger(__VA_ARGS__) + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* XLOGGING_H */ diff --git a/src/update_sdk.cmd b/src/update_sdk.cmd new file mode 100644 index 0000000..d488b68 --- /dev/null +++ b/src/update_sdk.cmd @@ -0,0 +1,41 @@ +REM This script replaces the SDK folder with the latest bits from the Azure SDK repository. +REM It removes some files we do not need. It currently targets the develop branch until fixes are +REM merged into master. + +if exist "%~dp0\temp" rd /s /q "%~dp0\temp" +if exist "%~dp0\sdk" rd /s /q "%~dp0\sdk" + +mkdir "%~dp0\temp" +mkdir "%~dp0\sdk" + +cd /D "%~dp0\temp" + +git clone https://github.com/Azure/azure-iot-sdks.git --recursive + +cd azure-iot-sdks +echo Upstream HEAD @ > metadata.txt +git rev-parse HEAD >> metadata.txt + +cd "%~dp0\temp" + +copy "%~dp0\temp\azure-iot-sdks\metadata.txt" "%~dp0\sdk" + +copy "%~dp0\temp\azure-iot-sdks\c\iothub_client\src\" "%~dp0\sdk" +copy "%~dp0\temp\azure-iot-sdks\c\iothub_client\inc\" "%~dp0\sdk" +copy "%~dp0\temp\azure-iot-sdks\c\serializer\src\" "%~dp0\sdk" +copy "%~dp0\temp\azure-iot-sdks\c\serializer\inc\" "%~dp0\sdk" +copy "%~dp0\temp\azure-iot-sdks\c\azure-c-shared-utility\c\src\" "%~dp0\sdk" +copy "%~dp0\temp\azure-iot-sdks\c\azure-c-shared-utility\c\inc\" "%~dp0\sdk" +copy "%~dp0\temp\azure-iot-sdks\c\azure-c-shared-utility\c\adapters\agenttime.c" "%~dp0\sdk" + +del "%~dp0\sdk\iot_logging.h" +del "%~dp0\sdk\iothubtransportamqp.*" +del "%~dp0\sdk\iothubtransportamqp_websockets.*" +del "%~dp0\sdk\iothubtransportmqtt.*" +del "%~dp0\sdk\tickcounter.*" +del "%~dp0\sdk\tlsio*.*" +del "%~dp0\sdk\constbuffer.*" + +cd .. + +rmdir /S "%~dp0\temp" diff --git a/src/util/HTTPSClient.cpp b/src/util/HTTPSClient.cpp index 6041ebf..55a0310 100644 --- a/src/util/HTTPSClient.cpp +++ b/src/util/HTTPSClient.cpp @@ -3,6 +3,9 @@ #include "HTTPSClient.h" +// Uncomment the below to see HTTP traffic in +// your debug output. + // #define DEBUG_STREAM Serial HTTPSClient::HTTPSClient() : @@ -68,7 +71,7 @@ int HTTPSClient::sendBody(const unsigned char *content, int length) int HTTPSClient::readStatus() { int statusCode = -1; - + String status = readLine(); status.trim(); @@ -147,4 +150,4 @@ int HTTPSClient::readBody(unsigned char *content, int length) DEBUG_STREAM.write(content, length); #endif return 1; -} +} \ No newline at end of file diff --git a/src/util/HTTPSClient.h b/src/util/HTTPSClient.h index c6db329..31ed38d 100644 --- a/src/util/HTTPSClient.h +++ b/src/util/HTTPSClient.h @@ -1,15 +1,27 @@ // Copyright (c) Arduino. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. - -#include -#include +// #ifndef HTTPSCLIENT_H #define HTTPSCLIENT_H +#ifdef ARDUINO_SAMD_FEATHER_M0 +#include +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) +#include +#elif defined(ESP8266) +#include +#endif + #define HTTPS_PORT 443 +#ifdef ARDUINO_SAMD_FEATHER_M0 +class HTTPSClient : public Adafruit_WINC1500SSLClient +#elif defined(ARDUINO_SAMD_ZERO) || defined(ARDUINO_SAMD_MKR1000) class HTTPSClient : public WiFiSSLClient +#elif defined(ESP8266) +class HTTPSClient : public WiFiClientSecure +#endif { public: HTTPSClient(); @@ -29,4 +41,4 @@ class HTTPSClient : public WiFiSSLClient size_t _contentLength; }; -#endif +#endif \ No newline at end of file