├── LICENSE.txt ├── NOTICE.txt ├── README.md ├── samples ├── EdisonSNSSample.ino ├── Edison_Maraca │ ├── AWShelperFunctions.cpp │ ├── AWShelperFunctions.h │ ├── Edison_Maraca.ino │ ├── HardwareFunctions.cpp │ ├── HardwareFunctions.h │ ├── keys.cpp │ └── keys.h ├── GalileoSample.ino ├── MediaTek_Maraca_HTTPS │ ├── AWShelperFunctions.cpp │ ├── AWShelperFunctions.h │ ├── HardwareFunctions.cpp │ ├── HardwareFunctions.h │ ├── MediaTek_Maraca_HTTPS.ino │ ├── keys.cpp │ └── keys.h ├── SparkGetItemSample.ino ├── SparkPutItemSample.ino └── SparkSNSSample.ino └── src ├── common ├── AWSClient.cpp ├── AWSClient.h ├── AWSClient2.cpp ├── AWSClient2.h ├── AWSClient4.cpp ├── AWSClient4.h ├── AWSFoundationalTypes.cpp ├── AWSFoundationalTypes.h ├── AmazonDynamoDBClient.cpp ├── AmazonDynamoDBClient.h ├── AmazonIOTClient.cpp ├── AmazonIOTClient.h ├── AmazonKinesisClient.cpp ├── AmazonKinesisClient.h ├── AmazonS3Client.cpp ├── AmazonS3Client.h ├── AmazonSNSClient.cpp ├── AmazonSNSClient.h ├── DeviceIndependentInterfaces.cpp ├── DeviceIndependentInterfaces.h ├── ESP8266AWSImplementations.h ├── ESP8266AWSImplentations.cpp ├── Utils.cpp ├── Utils.h ├── jsmn.c ├── jsmn.h ├── sha256.cpp └── sha256.h ├── edison ├── EdisonAWSImplementations.cpp └── EdisonAWSImplementations.h ├── esp8266 ├── ESP8266AWSImplementations.h └── ESP8266AWSImplentations.cpp ├── galileo ├── GalileoAWSImplementations.cpp └── GalileoAWSImplementations.h ├── mediatek ├── MtkAWSImplementations.cpp └── MtkAWSImplementations.h └── sparkcore ├── SparkAWSImplementations.cpp └── SparkAWSImplementations.h /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | 4 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 5 | 6 | 1. Definitions. 7 | 8 | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. 9 | 10 | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. 11 | 12 | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 13 | 14 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. 15 | 16 | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. 17 | 18 | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. 19 | 20 | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). 21 | 22 | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. 23 | 24 | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." 25 | 26 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 27 | 28 | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 29 | 30 | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 31 | 32 | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: 33 | 34 | 1. You must give any other recipients of the Work or Derivative Works a copy of this License; and 35 | 2. You must cause any modified files to carry prominent notices stating that You changed the files; and 36 | 3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and 37 | 4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. 38 | 39 | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 40 | 41 | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 42 | 43 | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 44 | 45 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 46 | 47 | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 48 | 49 | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. 50 | 51 | END OF TERMS AND CONDITIONS 52 | 53 | 54 | Note: Other license terms may apply to certain, identified software files contained within or distributed with the accompanying software if such terms are included in the directory containing the accompanying software. Such other license terms will then apply in lieu of the terms of the software license above. 55 | 56 | JSMN code subject to the MIT License: 57 | 58 | Copyright (c) 2010 Serge A. Zaitsev 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy 61 | of this software and associated documentation files (the "Software"), to deal 62 | in the Software without restriction, including without limitation the rights 63 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 64 | copies of the Software, and to permit persons to whom the Software is 65 | furnished to do so, subject to the following conditions: 66 | 67 | The above copyright notice and this permission notice shall be included in 68 | all copies or substantial portions of the Software. 69 | 70 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 71 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 72 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 73 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 74 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 75 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 76 | THE SOFTWARE. 77 | 78 | Portable C++ Hashing Library (SHA256 files only) subject to this zlib License: 79 | All source code published on http://create.stephan-brumme.com and its sub-pages is licensed similar to the zlib license: 80 | 81 | This software is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. 82 | Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 83 | 84 | 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 85 | 2. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 86 | 3. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 87 | -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | Version 1 of Experimental AWS SDK for Arduino, and Samples 2 | 3 | Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.  4 | 5 | This product includes software developed by Amazon Technologies, Inc (http://www.amazon.com/).  6 | 7 | Licensed under the Apache License Version 2.0 8 | 9 | See the License for the specific language governing permissions and limitations under the License. 10 | 11 | ********************** 12 | THIRD PARTY COMPONENTS 13 | ********************** 14 | This software includes third party software subject to the following copyrights: 15 | 16 | - jsmn, minimalistic JSON parser in C - Copyright (c) 2010 Serge A. Zaitsev. Includes minor modifications. 17 | - Portable C++ Hashing Library (SHA256 files only) - Copyright (c) 2014 Stephan Brumme. Includes minor modifications. 18 | 19 | 20 | The licenses for these third party components are included in LICENSE 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Version 1 of the Experimental AWS SDK for Arduino 2 | 3 | An experimental SDK for contacting AWS Services on Arduino-compatible devices. Currently it supports Amazon DynamoDB, Amazon Kinesis and Amazon SNS. More services coming soon. 4 | 5 | All Amazon DynamoDB operations are supported. The code for creating, serializing, and deserializing Kinesis input and output objects is included, but the devices that the experimental SDK has been tested on do not have readily available HTTPS support. This code has been included so it can be used by those who want to do further experimenting with Kinesis and HTTPS. 6 | 7 | The SDK is extensible to non-Arduino-compatible devices by implementing the interfaces in `DeviceIndependentInterfaces.cpp`/`.h`. See `SparkAWSImplementations.cpp`/`.h` and `EdisonAWSImplementations.cpp`/`.h` for examples of this. 8 | 9 | ## Folder Structure 10 | 11 | * /common contains all the common source code 12 | * /sparkcore contains Spark IO Core device-specific implementations 13 | * /edison contains Intel Edison device-specfic implementations 14 | * /galileo contains Intel Galileo device-specific implementations 15 | * /mediatek contains MediaTek LinkIt One device-specific implementations 16 | 17 | 18 | Depending on the device that you are working, Simply copy the those device-specific implementations to the Common directory so you can test out your samples using Arduino IDE. 19 | 20 | Happy experimenting! 21 | 22 | ## Getting Started with the Samples 23 | 24 | Trying the samples is a good way to get started with using the SDK. 25 | 26 | Getting the samples working has the following steps: setting up the DynamoDB table, importing the SDK and copying over the sample, creating the `keys.h` and `keys.cpp` files, and setting up the hardware. These steps are outlined for the samples for both the Spark core and Intel Galileo, so be sure you are following only the directions corresponding to the device and sample you are using. 27 | 28 | If you are using a device other than Spark or Galileo, you may want to read through these steps anyway before implementing the interfaces in `DeviceIndependentInterfaces.cpp`/`.h` for your device. 29 | 30 | ### Step 1: Setting up the DynamoDB Table 31 | 32 | For either device you will need to set up a DynamoDB table with the same name, hash key, and range key as in the sample you are using. These values are defined as constants in the sample, i.e. `HASH_KEY_NAME` and `TABLE_NAME`. 33 | 34 | You can follow the steps below to get the tables set up with the right values, chosing the right set of instructions based on which sample you are using. 35 | 36 | #### Table used by SparkGetItemSample and GalileoSample: 37 | 38 | * Log into the [AWS Console](http://console.aws.amazon.com/) and navigate to DynamoDB. 39 | * Click on the "Create Table" button. 40 | * Enter "AWSArduinoSDKDemo" as the *Table Name*, "DemoName" as the *Hash Attribute Name*, and "id" as the *Range Attribute Name*. Be sure to mark *DemoName* as *String* and *id* as *Number*. 41 | * For this example, we won't add indexes, so just press continue on the *Add Indexes (optional)* page. 42 | * Just one *Read Capacity Unit* and one *Write Capacity Unit* will be enough for this demo. Press continue with these values. 43 | * Uncheck *Use Basic Alarms* and continue again. 44 | * Check that the information is correct on the *Review* page, then create the table! 45 | * After the table has finished creating, double click on it to explore it. Here you should press the *New Item* button and create an item with the following values: 46 | * "DemoName": *String*, "Colors" 47 | * "id": *Number*, "1" 48 | * "R", *Number* "255" 49 | * "G", *Number* "255" 50 | * "B", *Number* "255" 51 | 52 | #### Table used by SparkPutItemSample: 53 | 54 | * Log into the [AWS Console](http://console.aws.amazon.com/) and navigate to DynamoDB. 55 | * Click on the "Create Table" button. 56 | * Enter "AWSArduinoSDKTests" as the *Table Name*, "device" as the *Hash Attribute Name*, and "Time" as the *Range Attribute Name*. Be sure to mark both as *String*. 57 | * For this example, we won't add indexes, so just press continue on the *Add Indexes (optional)* page. 58 | * Just one *Read Capacity Unit* and one *Write Capacity Unit* will be enough for this demo. Press continue with these values. 59 | * Uncheck *Use Basic Alarms* and continue again. 60 | * Check that the information is correct on the *Review* page, then create the table! 61 | 62 | ### Step 2: Importing SDK and Copying Sample 63 | 64 | This step is different for the Spark Core and Intel Galileo. 65 | 66 | #### Connected Maraca Sample (Edison/SparkCore/MediaTek) 67 | 68 | follow the step by step guide: http://bit.ly/aws-iot-hackseries 69 | 70 | #### Intel Galileo/Edison Sample 71 | 72 | With Galileo or Edison, you should be using the Arduino IDE from Intel as it includes Galileo and Edison libraries. [Link to Intel-Arduino IDE](https://communities.intel.com/docs/DOC-22226). 73 | 74 | Make an `AWSArduinoSDK` directory in the Arduino IDE's `libraries` directory (e.g. `~/Arduino/libraries/AWSArduinoSDK`). 75 | 76 | Move all of the files from the SDK's `src/common` directory into the `AWSArduinoSDK` directory. Import the library. 77 | 78 | Create a new sketch with the Arduino IDE and copy and paste the sample code into it. 79 | 80 | 81 | #### Spark IO Core Sample 82 | 83 | This assumes you already have your Spark set up and are able to program it with Spark Build. If you do not, head over to [Spark's website](http://docs.spark.io/). 84 | 85 | Open up the Spark Build web page and create a new app. Name it whatever you would like. 86 | 87 | Copy the contents of the sample you are using into the `.ino` file of your new app. 88 | 89 | Next you need to import the SDK. Because the Spark Build IDE isn't local to your machine, you can't just `cp` the files over. Instead use the "+" tab in the top right corner of the Spark Build page to create a new file for each `.cpp`/`.h` file in the `src/` directory, except `GalileoAWSImplementations` and `AmazonKinesisClient`. Then copy and paste the contents of each file. 90 | 91 | 92 | 93 | ### Step 3: Creating `keys.h` and `keys.cpp` 94 | 95 | You will need to create and add `keys.h` and `keys.cpp` into the `AWSArduinoSDK` directory you made. These files define the `awsKeyID` and `awsSecKey` values used by the sketch, the files may be structured as following: 96 | 97 | ``` 98 | // keys.h 99 | #ifndef KEYS_H_ 100 | #define KEYS_H_ 101 | 102 | extern const char* awsKeyID; // Declare these variables to 103 | extern const char* awsSecKey; // be accessible by the sketch 104 | 105 | #endif 106 | ``` 107 | 108 | ``` 109 | // keys.cpp 110 | #include "keys.h" 111 | 112 | const char* awsKeyID = "YOUR AWS KEY ID HERE"; 113 | const char* awsSecKey = "YOUR AWS SECRET KEY HERE"; 114 | ``` 115 | 116 | Add these files as you added the source files. That is: with Spark, use the "+" button, and with Galileo, move them to the `AWSArduinoSDK` directory under Arduino's `libraries` directory. 117 | 118 | ### Step 4: Setting up Hardware 119 | 120 | To use the samples you must have the correct breadboard wiring. The samples use different wiring, but use the following rules to create them: 121 | 122 | Buttons: Connect buttons by wiring one leg of the button to the 3v or 5v pin. Connect one leg to ground with a resistor, and also wire it to the pin that is reading the value. 123 | 124 | RGB LED: For the multicolored LED, wire the cathode (the longest leg) to ground, then connect the remaining 3 legs to the corresponding input pins with a resistor. 125 | 126 | #### Spark Core 127 | 128 | Both spark samples use just one button connected to the D2 pin. 129 | 130 | #### Intel Galileo 131 | 132 | This sample uses five buttons and a RGB LED. 133 | 134 | The RGB LED has the red leg connected to pin 6, the green leg connected to pin 9, and the blue leg connected to pin 10. 135 | 136 | Buttons: 137 | 138 | * Button for performing PutItem should be connected to pin 2 139 | * Button for performing GetItem should be connected to pin 4 140 | * Button for changing red color value should be connected to pin 7 141 | * Button for changing green color value should be connected to pin 8 142 | * Button for changing blue color value should be connected to pin 12 143 | 144 | For Galileo/Edison, after the wiring is finished, you should be able to connect it to power, connect it to your computer via usb, and compile and upload the code with the Arduino IDE. Be sure to refer to the comments in the samples for help. 145 | 146 | For Spark, after the wiring is finished, you should be able to connect it to your computer via USB, and *Flash* the code. Be sure to refer to the comments in the samples for help. 147 | 148 | #### ESP8266 149 | 150 | You can use these libraries with the [Arduino ESP8266](https://github.com/esp8266/arduino):. 151 | 152 | ``` 153 | #include 154 | #include 155 | #include "Esp8266AWSImplementations.h" 156 | 157 | Esp8266HttpClient httpClient; 158 | Esp8266DateTimeProvider dateTimeProvider; 159 | 160 | AmazonIOTClient iotClient; 161 | ActionError actionError; 162 | 163 | void setup() { 164 | Serial.begin(115200); 165 | delay(10); 166 | 167 | // Connect to WAP 168 | Serial.print("Connecting to "); 169 | Serial.println(ssid); 170 | WiFi.begin(ssid, password); 171 | 172 | while (WiFi.status() != WL_CONNECTED) { 173 | delay(500); 174 | Serial.print("."); 175 | } 176 | Serial.println(""); 177 | Serial.println("WiFi connected"); 178 | Serial.println("IP address: "); 179 | Serial.println(WiFi.localIP()); 180 | 181 | iotClient.setAWSRegion("eu-west-1"); 182 | iotClient.setAWSEndpoint("amazonaws.com"); 183 | iotClient.setAWSDomain("foobar.iot.eu-west-1.amazonaws.com"); 184 | iotClient.setAWSPath("/things/example-1/shadow"); 185 | iotClient.setAWSKeyID("ID"); 186 | iotClient.setAWSSecretKey("SECRET"); 187 | iotClient.setHttpClient(&httpClient); 188 | iotClient.setDateTimeProvider(&dateTimeProvider); 189 | } 190 | 191 | void loop(){ 192 | char* shadow = "{\"state\":{\"reported\": {\"foobar\": "bar"}}}"; 193 | 194 | char* result = iotClient.update_shadow(shadow, actionError); 195 | Serial.print(result); 196 | 197 | delay(60000); 198 | } 199 | 200 | ``` 201 | -------------------------------------------------------------------------------- /samples/EdisonSNSSample.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sample publishes a message to an Amazon SNS target (topic or endpoint). 3 | * 4 | * The first attempt will always fail because of the clock implementation on the Edison. 5 | * The library uses the timestamp in the AWS response to seed the clock for subsequent attempts. 6 | * 7 | * For this demo to work, add your AWS keys in "awsSecKey" and "awsKeyID", create an SNS topic 8 | * or endpoint and populate the TARGET_ARN with the topic or endpoint ARN. 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | /* AWS credentials */ 18 | const char* awsSecKey = ""; 19 | const char* awsKeyID = ""; 20 | 21 | /* Wi-Fi */ 22 | char ssid[] = ""; // your network SSID (name) 23 | char pass[] = ""; // your network password (use for WPA, or use as key for WEP) 24 | int status = WL_IDLE_STATUS; 25 | 26 | /* Constants for connecting to Amazon SNS. */ 27 | static const char* TARGET_ARN = ""; // replace each ':' with '%3A' 28 | static const char* AWS_REGION = ""; // us-west-2 etc 29 | static const char* AWS_ENDPOINT = "amazonaws.com"; 30 | 31 | /* Light the LED while a message is transmitted. */ 32 | int led = 13; 33 | 34 | /* Device independent implementations required for AmazonSNSClient to function. */ 35 | EdisonHttpClient httpClient; 36 | EdisonDateTimeProvider dateTimeProvider; 37 | 38 | /* AWS objects. */ 39 | AmazonSNSClient snsClient; 40 | PublishInput publishInput; 41 | ActionError actionError; 42 | 43 | void setup() { 44 | /* Begin serial communication. */ 45 | Serial.begin(9600); 46 | delay(5000); 47 | 48 | pinMode(led, OUTPUT); 49 | 50 | /* Require a Wi-Fi shield. */ 51 | if (WL_NO_SHIELD == WiFi.status()) { 52 | Serial.println("Wi-Fi shield not present."); 53 | 54 | /* Do not continue. */ 55 | while(true); 56 | } 57 | 58 | String fv = WiFi.firmwareVersion(); 59 | if (fv != "1.1.0") { 60 | Serial.println("Please upgrade the Wi-Fi firmware"); 61 | } 62 | 63 | /* Attempt to connect to the Wi-fi network. */ 64 | while (status != WL_CONNECTED) { 65 | Serial.print("Attempting to connect to SSID: "); 66 | Serial.println(ssid); 67 | 68 | /* Connect to WPA/WPA2 network. Change this line if using open or WEP network. */ 69 | status = WiFi.begin(ssid, pass); 70 | 71 | /* Wait 10 seconds for connection. */ 72 | delay(10000); 73 | } 74 | 75 | Serial.println("Connected to wifi"); 76 | printWifiStatus(); 77 | 78 | /* Initialize Amazon SNS client. */ 79 | snsClient.setAWSRegion(AWS_REGION); 80 | snsClient.setAWSEndpoint(AWS_ENDPOINT); 81 | snsClient.setAWSSecretKey(awsSecKey); 82 | snsClient.setAWSKeyID(awsKeyID); 83 | snsClient.setHttpClient(&httpClient); 84 | snsClient.setDateTimeProvider(&dateTimeProvider); 85 | } 86 | 87 | void loop() { 88 | /* Repeat SNS attempt every 60s. */ 89 | delay(60000); 90 | 91 | /* Light up the LED. */ 92 | digitalWrite(led, HIGH); 93 | Serial.println("Start Loop"); 94 | 95 | MinimalString dateTime(dateTimeProvider.getDateTime()); 96 | dateTimeProvider.sync(dateTime.getCStr()); 97 | 98 | /* Delivery attempt to Amazon SNS. */ 99 | publishInput.setMessage(dateTime); 100 | publishInput.setTargetArn(TARGET_ARN); 101 | PublishOutput publishOutput = snsClient.publish(publishInput, actionError); 102 | Serial.println(publishOutput.getMessageId().getCStr()); 103 | 104 | /* Finish loop and turn off the LED. */ 105 | Serial.println(dateTime.getCStr()); 106 | Serial.println("Finished loop"); 107 | delay(1000); 108 | digitalWrite(led, LOW); 109 | } 110 | 111 | void printWifiStatus() { 112 | // print the SSID of the network you're attached to: 113 | Serial.print("SSID: "); 114 | Serial.println(WiFi.SSID()); 115 | 116 | // print your WiFi shield's IP address: 117 | IPAddress ip = WiFi.localIP(); 118 | Serial.print("IP Address: "); 119 | Serial.println(ip); 120 | 121 | // print the received signal strength: 122 | long rssi = WiFi.RSSI(); 123 | Serial.print("signal strength (RSSI):"); 124 | Serial.print(rssi); 125 | Serial.println(" dBm"); 126 | } 127 | 128 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/AWShelperFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "AWShelperFunctions.h" 3 | #include "HardwareFunctions.h" 4 | #include "keys.h" 5 | #include 6 | 7 | AmazonSNSClient snsClient; 8 | AmazonDynamoDBClient ddbClient; 9 | AmazonKinesisClient kClient; 10 | 11 | /* Device independent implementations required for AmazonDynamoDBClient to 12 | * function. */ 13 | EdisonHttpClient httpClient; 14 | //kinesis 15 | EdisonHttpCurlClient httpCurlClient; 16 | EdisonDateTimeProvider dateTimeProvider; 17 | 18 | PublishInput publishInput; 19 | GetItemInput getItemInput; 20 | PutItemInput putItemInput; 21 | AttributeValue hashKey; 22 | AttributeValue rangeKey; 23 | ActionError actionError; 24 | 25 | void SNSClient_Setup() { 26 | snsClient.setAWSRegion(AWS_REGION); 27 | snsClient.setAWSEndpoint(AWS_ENDPOINT); 28 | snsClient.setAWSSecretKey(awsSecKey); 29 | snsClient.setAWSKeyID(awsKeyID); 30 | snsClient.setHttpClient(&httpClient); 31 | snsClient.setDateTimeProvider(&dateTimeProvider); 32 | } 33 | 34 | void DynamoBDClient_Setup() { 35 | ddbClient.setAWSRegion(AWS_REGION); 36 | ddbClient.setAWSEndpoint(AWS_ENDPOINT); 37 | ddbClient.setAWSSecretKey(awsSecKey); 38 | ddbClient.setAWSKeyID(awsKeyID); 39 | ddbClient.setHttpClient(&httpClient); 40 | ddbClient.setDateTimeProvider(&dateTimeProvider); 41 | } 42 | 43 | void KinesisClient_Setup() { 44 | kClient.setAWSRegion(AWS_REGION); 45 | kClient.setAWSEndpoint(AWS_ENDPOINT); 46 | kClient.setAWSSecretKey(awsSecKey); 47 | kClient.setAWSKeyID(awsKeyID); 48 | kClient.setHttpClient(&httpCurlClient); 49 | kClient.setDateTimeProvider(&dateTimeProvider); 50 | } 51 | 52 | void putDynamoDb() { 53 | // Put device & datestamp record in DynamoDB table 54 | // Create an Item. 55 | AttributeValue deviceIdValue; 56 | deviceIdValue.setS(HASH_KEY_VALUE); 57 | AttributeValue timeValue; 58 | // Getting current time for Time attribute. 59 | timeValue.setN(dateTimeProvider.getDateTime()); 60 | AttributeValue deviceValue; 61 | deviceValue.setS(ATTRIBUTE_VALUE); 62 | 63 | MinimalKeyValuePair att1(HASH_KEY_NAME, deviceIdValue); 64 | MinimalKeyValuePair att2(RANGE_KEY_NAME, timeValue); 65 | MinimalKeyValuePair att3(ATTRIBUTE_NAME, deviceValue); 66 | MinimalKeyValuePair itemArray[] = { att1, 67 | att2, att3 }; 68 | 69 | // Set values for putItemInput. 70 | putItemInput.setItem(MinimalMap(itemArray, 3)); 71 | putItemInput.setTableName(TABLE_NAME); 72 | 73 | // perform putItem and check for errors. 74 | PutItemOutput putItemOutput = ddbClient.putItem(putItemInput, actionError); 75 | switch (actionError) { 76 | case NONE_ACTIONERROR: 77 | Serial.println("DynamoDB PutItem succeeded!"); 78 | break; 79 | case INVALID_REQUEST_ACTIONERROR: 80 | Serial.print("ERROR: "); 81 | Serial.println(putItemOutput.getErrorMessage().getCStr()); 82 | break; 83 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 84 | Serial.println( 85 | "ERROR: Required arguments were not set for PutItemInput"); 86 | break; 87 | case RESPONSE_PARSING_ACTIONERROR: 88 | Serial.println("ERROR: Problem parsing http response of PutItem"); 89 | break; 90 | case CONNECTION_ACTIONERROR: 91 | Serial.println("ERROR: Connection problem"); 92 | break; 93 | } 94 | } 95 | 96 | void putKinesis(double realAccelX, double realAccelY, double realAccelZ) { 97 | // light the grove LED to indicate posting to AWS service 98 | 99 | PutRecordInput putRecordInput; 100 | putRecordInput.setStreamName(streamName); 101 | 102 | char buffer[255]; 103 | String dataSource; 104 | dataSource = String("{\"device_id\":\""); 105 | dataSource += HASH_KEY_VALUE; 106 | dataSource += String("\",\"time\":"); 107 | dataSource += dateTimeProvider.getDateTime(); 108 | dataSource += String(",\"device\":\""); 109 | dataSource += ATTRIBUTE_VALUE; 110 | dataSource += String("\",\"sensors\":[{\"telemetryData\": {\"xval\":"); 111 | sprintf(buffer, "%f", realAccelX); 112 | dataSource += buffer; 113 | dataSource += String(",\"yval\":"); 114 | sprintf(buffer, "%f", realAccelY); 115 | dataSource += buffer; 116 | dataSource += String(",\"zval\":"); 117 | sprintf(buffer, "%f", realAccelZ); 118 | dataSource += buffer; 119 | dataSource += String("}}]}"); 120 | dataSource.toCharArray(buffer, 255); 121 | Serial.println(buffer); 122 | Serial.println(dateTimeProvider.getDateTime()); 123 | char* data = base64Encode(buffer); 124 | putRecordInput.setData(data); 125 | delete[] data; 126 | putRecordInput.setPartitionKey(partitionKey); 127 | 128 | // simple blind putRecord, no way to read output of System call for now 129 | kClient.putRecord(putRecordInput, actionError); 130 | Serial.println("\nKinesis record posted to Stream"); 131 | // switch off the Grove LED again - message posted 132 | 133 | } 134 | 135 | void putSns() { 136 | 137 | // set datetime as the message to TARGET_ARN 138 | MinimalString dateTime(dateTimeProvider.getDateTime()); 139 | dateTimeProvider.sync(dateTime.getCStr()); 140 | 141 | // Delivery attempt to Amazon SNS. 142 | publishInput.setMessage(dateTime); 143 | publishInput.setTargetArn(TARGET_ARN); 144 | PublishOutput publishOutput = snsClient.publish(publishInput, actionError); 145 | Serial.print("Sent message ID: "); 146 | Serial.println(publishOutput.getMessageId().getCStr()); 147 | } 148 | 149 | 150 | 151 | void indicateServiceThroughLED_blink_Buzzer(AWS_Service_ID service) { 152 | 153 | int blinkCount = 0; 154 | 155 | switch (service) { 156 | 157 | case KINESIS: 158 | blinkCount = 1; 159 | break; 160 | 161 | case DYNAMO_DB: 162 | blinkCount = 2; 163 | break; 164 | 165 | case SNS: 166 | blinkCount = 3; 167 | break; 168 | 169 | default: 170 | blinkCount = 10; 171 | break; 172 | } 173 | blinkGroveLed(blinkCount); 174 | 175 | } 176 | 177 | 178 | AWS_Service_ID changeService(AWS_Service_ID currentServiceId) { 179 | AWS_Service_ID nextServiceId; 180 | 181 | Serial.print("Button pressed - changing mode. New mode: "); 182 | 183 | nextServiceId = (AWS_Service_ID)(currentServiceId + 1); 184 | if (nextServiceId >= END_NOSERVICE) { 185 | nextServiceId = KINESIS; 186 | } 187 | 188 | indicateServiceThroughLED_blink_Buzzer(nextServiceId); 189 | 190 | switch (nextServiceId) { 191 | case 0: 192 | Serial.println("Kinesis"); 193 | break; 194 | case 1: 195 | Serial.println("DynamoDB"); 196 | break; 197 | case 2: 198 | Serial.println("SNS"); 199 | break; 200 | } 201 | return nextServiceId; 202 | } 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/AWShelperFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_HELPER_FUNCTIONS_H_ 2 | #define AWS_HELPER_FUNCTIONS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum AWS_Service_ID{ 9 | KINESIS = 0, 10 | DYNAMO_DB = 1, 11 | SNS = 2, 12 | END_NOSERVICE = 3 13 | }; 14 | 15 | void SNSClient_Setup(); 16 | void KinesisClient_Setup(); 17 | void DynamoBDClient_Setup(); 18 | 19 | void putDynamoDb(); 20 | void putKinesis(double realAccelX, double realAccelY, double realAccelZ); 21 | void putSns(); 22 | 23 | void indicateServiceThroughLED_blink_Buzzer(AWS_Service_ID service); 24 | AWS_Service_ID changeService(AWS_Service_ID currentServiceId); 25 | 26 | #endif 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/Edison_Maraca.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "keys.h" 4 | 5 | #include "HardwareFunctions.h" 6 | #include "AWShelperFunctions.h" 7 | 8 | // It is an Arduino hack to include libraries that are referenced in other libraries. 9 | // For example WiFi is used in AWS libraries but we still need to include in the main sketch file for it to be recognized. 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | AWS_Service_ID CurrentService; 16 | 17 | void setup() { 18 | 19 | /* Begin serial communication. */ 20 | Serial.begin(9600); 21 | digitalWrite(BUZZER_PIN, LOW); 22 | 23 | if (true == Edison_Wifi_Setup(ssid, pass)) { 24 | Serial.println("Connected to wifi"); 25 | printWifiStatus(); 26 | } 27 | 28 | configureIO(); 29 | accelerometerInit(); 30 | 31 | SNSClient_Setup(); 32 | DynamoBDClient_Setup(); 33 | KinesisClient_Setup(); 34 | 35 | CurrentService = DYNAMO_DB; 36 | indicateServiceThroughLED_blink_Buzzer(DYNAMO_DB); 37 | 38 | Serial.println("Setup complete! Looping main program"); 39 | Serial.println("Initial mode: DynamoDB"); 40 | 41 | } 42 | 43 | unsigned long currentTime = 0; 44 | unsigned long LastPostTime = 0; 45 | enum { 46 | POSTING_PERIOD_MS = 500 47 | }; 48 | 49 | int buttonVal = LOW; 50 | int prevButtonVal = LOW; 51 | 52 | double xyz[3]; 53 | 54 | void loop() { 55 | 56 | readAcceleration(xyz); 57 | if (isAccelerometerShaking(xyz)) { 58 | printAcceleration(xyz); 59 | // light the onboard LED 60 | digitalWrite(EDISON_LED_PIN, HIGH); 61 | 62 | currentTime = millis(); 63 | if ((currentTime - LastPostTime) > POSTING_PERIOD_MS) { 64 | currentTime = millis(); 65 | switch (CurrentService) { 66 | case 0: 67 | Serial.println("Posting to Kinesis"); 68 | putKinesis(xyz[0], xyz[1], xyz[2]); 69 | break; 70 | case 1: 71 | Serial.println("Posting to DynamoDB"); 72 | putDynamoDb(); 73 | break; 74 | case 2: 75 | Serial.println("Posting to SNS"); 76 | putSns(); 77 | break; 78 | default: 79 | Serial.println("Wrong selection"); 80 | break; 81 | } 82 | LastPostTime = currentTime; 83 | } 84 | } else { 85 | //Serial.println("Not publishing..."); 86 | digitalWrite(EDISON_LED_PIN, LOW); 87 | } 88 | 89 | // check if button has been pressed 90 | buttonVal = digitalRead(BUTTON_PIN); 91 | if (prevButtonVal == LOW && buttonVal == HIGH) { 92 | CurrentService = changeService(CurrentService); 93 | } 94 | prevButtonVal = buttonVal; 95 | delay(200); 96 | } 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/HardwareFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "HardwareFunctions.h" 2 | #include 3 | 4 | void configureIO() { 5 | pinMode(EDISON_LED_PIN, OUTPUT); 6 | pinMode(GROVE_LED_PIN, OUTPUT); 7 | pinMode(BUZZER_PIN, OUTPUT); 8 | pinMode(BUTTON_PIN, INPUT); 9 | } 10 | 11 | void blinkGroveLed(int times) { 12 | // blink the LED and beep the buzzer n times to indicate new service 13 | for (int i = 0; i < times; i++) { 14 | digitalWrite(GROVE_LED_PIN, HIGH); 15 | digitalWrite(BUZZER_PIN, HIGH); 16 | delay(100); 17 | digitalWrite(GROVE_LED_PIN, LOW); 18 | digitalWrite(BUZZER_PIN, LOW); 19 | delay(100); 20 | } 21 | } 22 | 23 | ADXL345 adxl; 24 | double previousXYZ[3] = { 0, 0, 0 }; 25 | double currentXYZ[3]; 26 | double nThreshold; 27 | 28 | void accelerometerInit() { 29 | adxl.powerOn(); 30 | nThreshold = 0.6; // Need to calibrate it 31 | } 32 | 33 | void readAcceleration(double *xyz){ 34 | adxl.getAcceleration(xyz); 35 | } 36 | 37 | bool isAccelerometerShaking(double *xyz) { 38 | bool ret_val = false; 39 | double absX, absY, absZ; 40 | 41 | absX = abs(previousXYZ[0] - xyz[0]); 42 | absY = abs(previousXYZ[1] - xyz[1]); 43 | absZ = abs(previousXYZ[2] - xyz[2]); 44 | 45 | if(absX > nThreshold || absY > nThreshold || absZ > nThreshold){ 46 | ret_val = true; 47 | } 48 | 49 | previousXYZ[0] = xyz[0]; 50 | previousXYZ[1] = xyz[1]; 51 | previousXYZ[2] = xyz[2]; 52 | 53 | return ret_val; 54 | } 55 | 56 | void printAcceleration(double *xyz){ 57 | Serial.println("Acceleration values: "); 58 | Serial.print(xyz[0]); 59 | Serial.print(","); 60 | Serial.print(xyz[1]); 61 | Serial.print(","); 62 | Serial.print(xyz[2]); 63 | Serial.print("\n"); 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/HardwareFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef HARDWARE_FUNCTIONS_H_ 2 | #define HARDWARE_FUNCTIONS_H_ 3 | 4 | enum pinConfig{ 5 | EDISON_LED_PIN = 13, 6 | GROVE_LED_PIN = 3, 7 | BUZZER_PIN = 6, 8 | BUTTON_PIN = 7 9 | }; 10 | 11 | void configureIO(); 12 | void blinkGroveLed(int times); 13 | 14 | void accelerometerInit(); 15 | void readAcceleration(double *xyz); 16 | bool isAccelerometerShaking(double *xyz); 17 | void printAcceleration(double *xyz); 18 | 19 | #endif 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/keys.cpp: -------------------------------------------------------------------------------- 1 | // keys.cpp 2 | #include "keys.h" 3 | 4 | const char* awsKeyID = "AKIAJMWERWERWRRSJPQRQ"; // REPLACE with your access key id 5 | const char* awsSecKey = "C6y02fWVSDFSFSFFSV1oz03pbuqf/Fq"; // REPLACE with your secret access key 6 | 7 | 8 | // Init and connect Edison wifi to local wlan 9 | char* ssid = "AWS"; // your network SSID (name) 10 | char* pass = "Codehappy123"; // your network password (use for WPA, or use as key for WEP) 11 | 12 | // Common AWS constants 13 | const char* AWS_REGION = "us-west-2"; // us-west-2 etc 14 | const char* AWS_ENDPOINT = "amazonaws.com"; 15 | 16 | /* Constants for connecting to Amazon SNS. */ 17 | const char* TARGET_ARN = "arn:aws:sns:us-west-2:395547068926:edisonstack-DeviceSNSTopic-KFDdsfsWNLD"; // replace each ':' with '%3A' 18 | 19 | 20 | /* Contants describing DynamoDB table and values being used. */ 21 | const char* HASH_KEY_NAME = "device_id"; 22 | const char* HASH_KEY_VALUE = "jin1234"; // REPLACE with your team or particpant name e.g Team38 23 | const char* RANGE_KEY_NAME = "time"; 24 | const char* RANGE_KEY_VALUE = "1"; 25 | const char* TABLE_NAME = "edisonstack-DeviceDataTable-1JsdfU5PX4B"; //REPLACE with your DDB Table name e.g. hackday-2014-team38 26 | const char* ATTRIBUTE_NAME = "device"; 27 | const char* ATTRIBUTE_VALUE = "edison"; 28 | 29 | /* Constants describing Kinesis stream */ 30 | const char* streamName = "edisonstack-DeviceStream-1QK5sdfsfJEVX"; //REPLACE with your stream name e.g. hackday-2014-team38-TeamStream-1K5DAIU8GCRXY 31 | const char* partitionKey = "partitionKey"; 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /samples/Edison_Maraca/keys.h: -------------------------------------------------------------------------------- 1 | // keys.h 2 | #ifndef KEYS_H_ 3 | #define KEYS_H_ 4 | 5 | extern const char* awsKeyID; // Declare these variables to 6 | extern const char* awsSecKey; // be accessible by the sketch 7 | 8 | extern const char* AWS_REGION; 9 | extern const char* AWS_ENDPOINT; 10 | 11 | extern char* ssid; // your network SSID (name) 12 | extern char* pass; // your network password (use for WPA, or use as key for WEP) 13 | 14 | extern const char* HASH_KEY_NAME; 15 | extern const char* HASH_KEY_VALUE; 16 | extern const char* RANGE_KEY_NAME; 17 | extern const char* RANGE_KEY_VALUE; 18 | extern const char* TABLE_NAME; 19 | extern const char* ATTRIBUTE_NAME; 20 | extern const char* ATTRIBUTE_VALUE; 21 | 22 | extern const char* streamName; 23 | extern const char* partitionKey; 24 | 25 | extern const char* TARGET_ARN; 26 | 27 | #endif 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /samples/GalileoSample.ino: -------------------------------------------------------------------------------- 1 | /* This sample uses PutItem and GetItem on a DynamoDB table to save the state 2 | * of a RGB LED. Pressing one button gets the saved color values from 0-255 and 3 | * another button saves the current color values. Three additional buttons 4 | * cycle the brightness of each color of the LED. This demo can be used with 5 | * multiple devices on the same table and item in order to synchronize their 6 | * colors. 7 | * 8 | * For this demo to work you must have keys.h/.ccp files that contain your AWS 9 | * access keys and define "awsSecKey" and "awsKeyID", a DynamoDB table with the 10 | * name defined by the constant TABLE_NAME with hash and range keys as defined 11 | * by constants HASH_KEY_NAME/RANGE_KEY_NAME, and and item in that table with 12 | * attributes as defined by HASH_KEY_VALUE/RANGE_KEY_VALUE and number 13 | * attributes R G and B. */ 14 | 15 | #include "GalileoAWSImplementations.h" 16 | #include "AmazonDynamoDBClient.h" 17 | #include "AWSFoundationalTypes.h" 18 | #include "keys.h" 19 | #include 20 | 21 | /* Constants for incrementing color. */ 22 | static const int INCREMENT = 50; 23 | static const int MAX_COLOR = 255; 24 | /* Contants describing DynamoDB table and values being used. */ 25 | static const char* HASH_KEY_NAME = "DemoName"; 26 | static const char* HASH_KEY_VALUE = "Colors"; 27 | static const char* RANGE_KEY_NAME = "id"; 28 | static const char* RANGE_KEY_VALUE = "1"; 29 | static const char* TABLE_NAME = "AWSArduinoSDKDemo"; 30 | static const int KEY_SIZE = 2; 31 | /* Constants for connecting to DynamoDB. */ 32 | static const char* AWS_REGION = "us-east-1"; 33 | static const char* AWS_ENDPOINT = "amazonaws.com"; 34 | 35 | /* Pins for the R G and B leads to a RGB LED. Be sure to use a resistor with 36 | * each lead. */ 37 | static const int R_LED_PIN = 6; 38 | static const int G_LED_PIN = 9; 39 | static const int B_LED_PIN = 10; 40 | 41 | /* For buttons or tilt switches, have the pin connected to one side of the button and also 42 | * connected to ground with a resistor. Have the other side of the button 43 | * connected to 5v/3v. */ 44 | /* The pin connected to the button for putItem. */ 45 | static const int PUT_SWITCH_PIN = 2; 46 | /* The pin connected to the button for putItem. */ 47 | static const int GET_SWITCH_PIN = 4; 48 | /* The pin connected to the buttons to change the brightness of the RGB LED. */ 49 | static const int R_SWITCH_PIN = 7; 50 | static const int G_SWITCH_PIN = 8; 51 | static const int B_SWITCH_PIN = 12; 52 | 53 | /* 0 or 1 to determine state of the switches */ 54 | int putSwitchState = 0; 55 | int getSwitchState = 0; 56 | int rSwitchState = 0; 57 | int gSwitchState = 0; 58 | int bSwitchState = 0; 59 | 60 | /* Color LEDs values 0-255. */ 61 | int R = 0; 62 | int G = 0; 63 | int B = 0; 64 | 65 | /* Device independent implementations required for AmazonDynamoDBClient to 66 | * function. */ 67 | GalileoHttpClient httpClient; 68 | GalileoDateTimeProvider dateTimeProvider; 69 | 70 | AmazonDynamoDBClient ddbClient; 71 | 72 | /* Reused objects. */ 73 | GetItemInput getItemInput; 74 | PutItemInput putItemInput; 75 | AttributeValue hashKey; 76 | AttributeValue rangeKey; 77 | ActionError actionError; 78 | 79 | void refreshColor() { 80 | analogWrite(R_LED_PIN, R); 81 | analogWrite(G_LED_PIN, G); 82 | analogWrite(B_LED_PIN, B); 83 | } 84 | 85 | void setup() { 86 | /* Begin serial communication. */ 87 | Serial.begin(9600); 88 | /* Ethernet is supposed to start up when the Galileo is powered on with the 89 | * ethernet cable in. This can fail, so calling this command ensures it is 90 | * started. */ 91 | system("ifup eth0"); 92 | /* Initialize ddbClient. */ 93 | ddbClient.setAWSRegion(AWS_REGION); 94 | ddbClient.setAWSEndpoint(AWS_ENDPOINT); 95 | ddbClient.setAWSSecretKey(awsSecKey); 96 | ddbClient.setAWSKeyID(awsKeyID); 97 | ddbClient.setHttpClient(&httpClient); 98 | ddbClient.setDateTimeProvider(&dateTimeProvider); 99 | /* Open switchPins to be read from and led pins to write to. */ 100 | pinMode(PUT_SWITCH_PIN, INPUT); 101 | pinMode(GET_SWITCH_PIN, INPUT); 102 | pinMode(R_SWITCH_PIN, INPUT); 103 | pinMode(G_SWITCH_PIN, INPUT); 104 | pinMode(B_SWITCH_PIN, INPUT); 105 | pinMode(R_LED_PIN, OUTPUT); 106 | pinMode(G_LED_PIN, OUTPUT); 107 | pinMode(B_LED_PIN, OUTPUT); 108 | } 109 | 110 | void loop() { 111 | int prevPutSwitchState = putSwitchState; 112 | int prevGetSwitchState = getSwitchState; 113 | int prevRSwitchState = rSwitchState; 114 | int prevGSwitchState = gSwitchState; 115 | int prevBSwitchState = bSwitchState; 116 | /* Read the state of switches/buttons pins. */ 117 | putSwitchState = digitalRead(PUT_SWITCH_PIN); 118 | getSwitchState = digitalRead(GET_SWITCH_PIN); 119 | rSwitchState = digitalRead(R_SWITCH_PIN); 120 | gSwitchState = digitalRead(G_SWITCH_PIN); 121 | bSwitchState = digitalRead(B_SWITCH_PIN); 122 | 123 | /* For any awitch/button, if the state has changed from high to low, the 124 | * button has been pressed (or the tilt switch has been tilted. */ 125 | if (prevRSwitchState == HIGH && rSwitchState == LOW) { 126 | /* Increase the brightness of the color (Red in this case) by INCREMENT 127 | * amount, topping out at (MAX_COLOR). */ 128 | R = (R + INCREMENT) % (MAX_COLOR + 1); 129 | /* If the brightness just went over max color and wrapped around, set 130 | * the brightness to 0. */ 131 | R = R < INCREMENT ? 0 : R; 132 | refreshColor(); 133 | } 134 | if (prevGSwitchState == HIGH && gSwitchState == LOW) { 135 | G = (G + INCREMENT) % (MAX_COLOR + 1); 136 | G = G < INCREMENT ? 0 : G; 137 | refreshColor(); 138 | } 139 | if (prevBSwitchState == HIGH && bSwitchState == LOW) { 140 | B = (B + INCREMENT) % (MAX_COLOR + 1); 141 | B = B < INCREMENT ? 0 : B; 142 | refreshColor(); 143 | } 144 | if (prevPutSwitchState == HIGH && putSwitchState == LOW) { 145 | /* Create an Item. */ 146 | char numberBuffer[4]; 147 | AttributeValue demoName; 148 | demoName.setS(HASH_KEY_VALUE); 149 | AttributeValue entryNumber; 150 | entryNumber.setN(RANGE_KEY_VALUE); 151 | 152 | /* For R G and B, create an AttributeValue, convert the number to a 153 | * string (AttributeValue object represents numbers as strings), and 154 | * use setN to apply that value to the AttributeValue. */ 155 | AttributeValue rAttributeValue; 156 | sprintf(numberBuffer, "%d", R); 157 | rAttributeValue.setN(numberBuffer); 158 | AttributeValue gAttributeValue; 159 | sprintf(numberBuffer, "%d", G); 160 | gAttributeValue.setN(numberBuffer); 161 | AttributeValue bAttributeValue; 162 | sprintf(numberBuffer, "%d", B); 163 | bAttributeValue.setN(numberBuffer); 164 | 165 | /* Create the Key-value pairs and make an array of them. */ 166 | MinimalKeyValuePair < MinimalString, AttributeValue 167 | > att1(HASH_KEY_NAME, demoName); 168 | MinimalKeyValuePair < MinimalString, AttributeValue 169 | > att2(RANGE_KEY_NAME, entryNumber); 170 | MinimalKeyValuePair < MinimalString, AttributeValue 171 | > att3("R", rAttributeValue); 172 | MinimalKeyValuePair < MinimalString, AttributeValue 173 | > att4("G", gAttributeValue); 174 | MinimalKeyValuePair < MinimalString, AttributeValue 175 | > att5("B", bAttributeValue); 176 | MinimalKeyValuePair itemArray[] = { att1, 177 | att2, att3, att4, att5 }; 178 | 179 | /* Set values for putItemInput. */ 180 | putItemInput.setItem(MinimalMap < AttributeValue > (itemArray, 5)); 181 | putItemInput.setTableName(TABLE_NAME); 182 | 183 | /* perform putItem and check for errors. */ 184 | PutItemOutput putItemOutput = ddbClient.putItem(putItemInput, 185 | actionError); 186 | switch (actionError) { 187 | case NONE_ACTIONERROR: 188 | Serial.println("PutItem succeeded!"); 189 | break; 190 | case INVALID_REQUEST_ACTIONERROR: 191 | Serial.print("ERROR: "); 192 | Serial.println(putItemOutput.getErrorMessage().getCStr()); 193 | break; 194 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 195 | Serial.println( 196 | "ERROR: Required arguments were not set for PutItemInput"); 197 | break; 198 | case RESPONSE_PARSING_ACTIONERROR: 199 | Serial.println("ERROR: Problem parsing http response of PutItem"); 200 | break; 201 | case CONNECTION_ACTIONERROR: 202 | Serial.println("ERROR: Connection problem"); 203 | break; 204 | } 205 | /* wait to not double-record */ 206 | delay(750); 207 | } 208 | 209 | if (prevGetSwitchState == HIGH && getSwitchState == LOW) { 210 | /* Set the string and number values for the range and hash Keys, respectively. */ 211 | hashKey.setS(HASH_KEY_VALUE); 212 | rangeKey.setN(RANGE_KEY_VALUE); 213 | 214 | /* Create key-value pairs out of the hash and range keys, and create 215 | * a map out off them, which is the key. */ 216 | MinimalKeyValuePair < MinimalString, AttributeValue 217 | > pair1(HASH_KEY_NAME, hashKey); 218 | MinimalKeyValuePair < MinimalString, AttributeValue 219 | > pair2(RANGE_KEY_NAME, rangeKey); 220 | MinimalKeyValuePair keyArray[] = { pair1, 221 | pair2 }; 222 | getItemInput.setKey(MinimalMap < AttributeValue > (keyArray, KEY_SIZE)); 223 | 224 | /* Looking to get the R G and B values */ 225 | MinimalString attributesToGet[] = { "R", "G", "B" }; 226 | getItemInput.setAttributesToGet( 227 | MinimalList < MinimalString > (attributesToGet, 3)); 228 | 229 | /* Set other values. */ 230 | getItemInput.setTableName(TABLE_NAME); 231 | 232 | /* Perform getItem and check for errors. */ 233 | GetItemOutput getItemOutput = ddbClient.getItem(getItemInput, 234 | actionError); 235 | switch (actionError) { 236 | case NONE_ACTIONERROR: 237 | Serial.println("GetItem succeeded!"); 238 | { 239 | /* Get the "item" from the getItem output. */ 240 | MinimalMap < AttributeValue > attributeMap = 241 | getItemOutput.getItem(); 242 | AttributeValue av; 243 | /* Get the rgb values and set the led with them. */ 244 | attributeMap.get("R", av); 245 | R = atoi(av.getN().getCStr()); 246 | attributeMap.get("G", av); 247 | G = atoi(av.getN().getCStr()); 248 | attributeMap.get("B", av); 249 | B = atoi(av.getN().getCStr()); 250 | refreshColor(); 251 | } 252 | break; 253 | case INVALID_REQUEST_ACTIONERROR: 254 | Serial.print("ERROR: "); 255 | Serial.println(getItemOutput.getErrorMessage().getCStr()); 256 | break; 257 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 258 | Serial.println( 259 | "ERROR: Required arguments were not set for GetItemInput"); 260 | break; 261 | case RESPONSE_PARSING_ACTIONERROR: 262 | Serial.println("ERROR: Problem parsing http response of GetItem\n"); 263 | break; 264 | case CONNECTION_ACTIONERROR: 265 | Serial.println("ERROR: Connection problem"); 266 | break; 267 | } 268 | 269 | /* wait to not double-record */ 270 | delay(750); 271 | } 272 | delay(50); 273 | } 274 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/AWShelperFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "AWShelperFunctions.h" 3 | #include "HardwareFunctions.h" 4 | #include "keys.h" 5 | #include 6 | 7 | AmazonSNSClient snsClient; 8 | AmazonDynamoDBClient ddbClient; 9 | AmazonKinesisClient kClient; 10 | 11 | /* Device independent implementations required for AmazonDynamoDBClient to 12 | * function. */ 13 | MtkHttpClient httpClient; 14 | //kinesis 15 | MtkHttpCurlClient httpCurlClient; 16 | MtkDateTimeProvider dateTimeProvider; 17 | 18 | PublishInput publishInput; 19 | GetItemInput getItemInput; 20 | PutItemInput putItemInput; 21 | AttributeValue hashKey; 22 | AttributeValue rangeKey; 23 | ActionError actionError; 24 | 25 | void SNSClient_Setup() { 26 | snsClient.setAWSRegion(AWS_REGION); 27 | snsClient.setAWSEndpoint(AWS_ENDPOINT); 28 | snsClient.setAWSSecretKey(awsSecKey); 29 | snsClient.setAWSKeyID(awsKeyID); 30 | snsClient.setHttpClient(&httpClient); 31 | snsClient.setDateTimeProvider(&dateTimeProvider); 32 | } 33 | 34 | void DynamoBDClient_Setup() { 35 | ddbClient.setAWSRegion(AWS_REGION); 36 | ddbClient.setAWSEndpoint(AWS_ENDPOINT); 37 | ddbClient.setAWSSecretKey(awsSecKey); 38 | ddbClient.setAWSKeyID(awsKeyID); 39 | ddbClient.setHttpClient(&httpClient); 40 | ddbClient.setDateTimeProvider(&dateTimeProvider); 41 | } 42 | 43 | void KinesisClient_Setup() { 44 | kClient.setAWSRegion(AWS_REGION); 45 | kClient.setAWSEndpoint(AWS_ENDPOINT); 46 | kClient.setAWSSecretKey(awsSecKey); 47 | kClient.setAWSKeyID(awsKeyID); 48 | kClient.setHttpClient(&httpClient); 49 | kClient.setDateTimeProvider(&dateTimeProvider); 50 | } 51 | 52 | void putDynamoDb() { 53 | // Put device & datestamp record in DynamoDB table 54 | // Create an Item. 55 | AttributeValue deviceIdValue; 56 | deviceIdValue.setS(HASH_KEY_VALUE); 57 | AttributeValue timeValue; 58 | // Getting current time for Time attribute. 59 | timeValue.setN(dateTimeProvider.getDateTime()); 60 | AttributeValue deviceValue; 61 | deviceValue.setS(ATTRIBUTE_VALUE); 62 | 63 | MinimalKeyValuePair att1(HASH_KEY_NAME, deviceIdValue); 64 | MinimalKeyValuePair att2(RANGE_KEY_NAME, timeValue); 65 | MinimalKeyValuePair att3(ATTRIBUTE_NAME, deviceValue); 66 | MinimalKeyValuePair itemArray[] = { att1, 67 | att2, att3 }; 68 | 69 | // Set values for putItemInput. 70 | putItemInput.setItem(MinimalMap(itemArray, 3)); 71 | putItemInput.setTableName(TABLE_NAME); 72 | 73 | // perform putItem and check for errors. 74 | PutItemOutput putItemOutput = ddbClient.putItem(putItemInput, actionError); 75 | switch (actionError) { 76 | case NONE_ACTIONERROR: 77 | Serial.println("DynamoDB PutItem succeeded!"); 78 | break; 79 | case INVALID_REQUEST_ACTIONERROR: 80 | Serial.print("ERROR: "); 81 | Serial.println(putItemOutput.getErrorMessage().getCStr()); 82 | break; 83 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 84 | Serial.println("ERROR: Required arguments were not set for PutItemInput"); 85 | break; 86 | case RESPONSE_PARSING_ACTIONERROR: 87 | Serial.println("ERROR: Problem parsing http response of PutItem"); 88 | break; 89 | case CONNECTION_ACTIONERROR: 90 | Serial.println("ERROR: Connection problem"); 91 | break; 92 | } 93 | } 94 | 95 | void putKinesis(double realAccelX, double realAccelY, double realAccelZ) { 96 | // light the grove LED to indicate posting to AWS service 97 | 98 | PutRecordInput putRecordInput; 99 | putRecordInput.setStreamName(streamName); 100 | 101 | char buffer[255]; 102 | String dataSource; 103 | dataSource = String("{\"device_id\":\""); 104 | dataSource += HASH_KEY_VALUE; 105 | dataSource += String("\",\"time\":"); 106 | dataSource += dateTimeProvider.getDateTime(); 107 | dataSource += String(",\"device\":\""); 108 | dataSource += ATTRIBUTE_VALUE; 109 | dataSource += String("\",\"sensors\":[{\"telemetryData\": {\"xval\":"); 110 | sprintf(buffer, "%f", realAccelX); 111 | dataSource += buffer; 112 | dataSource += String(",\"yval\":"); 113 | sprintf(buffer, "%f", realAccelY); 114 | dataSource += buffer; 115 | dataSource += String(",\"zval\":"); 116 | sprintf(buffer, "%f", realAccelZ); 117 | dataSource += buffer; 118 | dataSource += String("}}]}"); 119 | dataSource.toCharArray(buffer, 255); 120 | Serial.println(buffer); 121 | Serial.println(dateTimeProvider.getDateTime()); 122 | char* data = base64Encode(buffer); 123 | putRecordInput.setData(data); 124 | delete[] data; 125 | putRecordInput.setPartitionKey(partitionKey); 126 | 127 | // simple blind putRecord, no way to read output of System call for now 128 | PutRecordOutput putRecordOutput = kClient.putRecord(putRecordInput, actionError); 129 | 130 | switch (actionError) { 131 | case NONE_ACTIONERROR: 132 | Serial.println("Kinesis record posted to Stream"); 133 | Serial.println("Sequence ID: "); 134 | Serial.println(putRecordOutput.getSequenceNumber().getCStr()); 135 | break; 136 | case INVALID_REQUEST_ACTIONERROR: 137 | Serial.print("ERROR: "); 138 | Serial.println(putRecordOutput.getErrorMessage().getCStr()); 139 | break; 140 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 141 | Serial.println("ERROR: Required arguments were not set for PutRecordInput"); 142 | break; 143 | case RESPONSE_PARSING_ACTIONERROR: 144 | Serial.println("ERROR: Problem parsing http response of PutRecord"); 145 | break; 146 | case CONNECTION_ACTIONERROR: 147 | Serial.println("ERROR: Connection problem"); 148 | break; 149 | } 150 | } 151 | 152 | void putSns() { 153 | 154 | // set datetime as the message to TARGET_ARN 155 | MinimalString dateTime(dateTimeProvider.getDateTime()); 156 | dateTimeProvider.sync(dateTime.getCStr()); 157 | 158 | // Delivery attempt to Amazon SNS. 159 | publishInput.setMessage(dateTime); 160 | publishInput.setTargetArn(TARGET_ARN); 161 | PublishOutput publishOutput = snsClient.publish(publishInput, actionError); 162 | Serial.print("Sent message ID: "); 163 | Serial.println(publishOutput.getMessageId().getCStr()); 164 | } 165 | 166 | 167 | 168 | void indicateServiceThroughLED_blink_Buzzer(AWS_Service_ID service) { 169 | 170 | int blinkCount = 0; 171 | 172 | switch (service) { 173 | 174 | case KINESIS: 175 | blinkCount = 1; 176 | break; 177 | 178 | case DYNAMO_DB: 179 | blinkCount = 2; 180 | break; 181 | 182 | case SNS: 183 | blinkCount = 3; 184 | break; 185 | 186 | default: 187 | blinkCount = 10; 188 | break; 189 | } 190 | blinkGroveLed(blinkCount); 191 | 192 | } 193 | 194 | 195 | AWS_Service_ID changeService(AWS_Service_ID currentServiceId) { 196 | AWS_Service_ID nextServiceId; 197 | 198 | Serial.print("Button pressed - changing mode. New mode: "); 199 | 200 | nextServiceId = (AWS_Service_ID)(currentServiceId + 1); 201 | if (nextServiceId >= END_NOSERVICE) { 202 | nextServiceId = KINESIS; 203 | } 204 | 205 | indicateServiceThroughLED_blink_Buzzer(nextServiceId); 206 | 207 | switch (nextServiceId) { 208 | case 0: 209 | Serial.println("Kinesis"); 210 | break; 211 | case 1: 212 | Serial.println("DynamoDB"); 213 | break; 214 | case 2: 215 | Serial.println("SNS"); 216 | break; 217 | } 218 | return nextServiceId; 219 | } 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/AWShelperFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef AWS_HELPER_FUNCTIONS_H_ 2 | #define AWS_HELPER_FUNCTIONS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | enum AWS_Service_ID{ 9 | KINESIS = 0, 10 | DYNAMO_DB = 1, 11 | SNS = 2, 12 | END_NOSERVICE = 3 13 | }; 14 | 15 | void SNSClient_Setup(); 16 | void KinesisClient_Setup(); 17 | void DynamoBDClient_Setup(); 18 | 19 | void putDynamoDb(); 20 | void putKinesis(double realAccelX, double realAccelY, double realAccelZ); 21 | void putSns(); 22 | 23 | void indicateServiceThroughLED_blink_Buzzer(AWS_Service_ID service); 24 | AWS_Service_ID changeService(AWS_Service_ID currentServiceId); 25 | 26 | #endif 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/HardwareFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "HardwareFunctions.h" 2 | #include 3 | 4 | void configureIO() { 5 | pinMode(MEDIATEK_LED_PIN, OUTPUT); 6 | pinMode(GROVE_LED_PIN, OUTPUT); 7 | pinMode(BUZZER_PIN, OUTPUT); 8 | pinMode(BUTTON_PIN, INPUT); 9 | } 10 | 11 | void blinkGroveLed(int times) { 12 | // blink the LED and beep the buzzer n times to indicate new service 13 | for (int i = 0; i < times; i++) { 14 | digitalWrite(GROVE_LED_PIN, HIGH); 15 | digitalWrite(BUZZER_PIN, HIGH); 16 | delay(100); 17 | digitalWrite(GROVE_LED_PIN, LOW); 18 | digitalWrite(BUZZER_PIN, LOW); 19 | delay(100); 20 | } 21 | } 22 | 23 | ADXL345 adxl; 24 | double previousXYZ[3] = { 0, 0, 0 }; 25 | double currentXYZ[3]; 26 | double nThreshold; 27 | 28 | void accelerometerInit() { 29 | adxl.powerOn(); 30 | nThreshold = 0.6; // Need to calibrate it 31 | } 32 | 33 | void readAcceleration(double *xyz){ 34 | adxl.getAcceleration(xyz); 35 | } 36 | 37 | bool isAccelerometerShaking(double *xyz) { 38 | bool ret_val = false; 39 | double absX, absY, absZ; 40 | 41 | absX = abs(previousXYZ[0] - xyz[0]); 42 | absY = abs(previousXYZ[1] - xyz[1]); 43 | absZ = abs(previousXYZ[2] - xyz[2]); 44 | 45 | if(absX > nThreshold || absY > nThreshold || absZ > nThreshold){ 46 | ret_val = true; 47 | } 48 | 49 | previousXYZ[0] = xyz[0]; 50 | previousXYZ[1] = xyz[1]; 51 | previousXYZ[2] = xyz[2]; 52 | 53 | return ret_val; 54 | } 55 | 56 | void printAcceleration(double *xyz){ 57 | Serial.println("Acceleration values: "); 58 | Serial.print(xyz[0]); 59 | Serial.print(","); 60 | Serial.print(xyz[1]); 61 | Serial.print(","); 62 | Serial.print(xyz[2]); 63 | Serial.print("\n"); 64 | } 65 | 66 | 67 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/HardwareFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef HARDWARE_FUNCTIONS_H_ 2 | #define HARDWARE_FUNCTIONS_H_ 3 | 4 | enum pinConfig{ 5 | MEDIATEK_LED_PIN = 13, 6 | GROVE_LED_PIN = 3, 7 | BUZZER_PIN = 6, 8 | BUTTON_PIN = 7 9 | }; 10 | 11 | void configureIO(); 12 | void blinkGroveLed(int times); 13 | 14 | void accelerometerInit(); 15 | void readAcceleration(double *xyz); 16 | bool isAccelerometerShaking(double *xyz); 17 | void printAcceleration(double *xyz); 18 | 19 | #endif 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/MediaTek_Maraca_HTTPS.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include "keys.h" 11 | #include "HardwareFunctions.h" 12 | #include "AWShelperFunctions.h" 13 | 14 | // It is an Arduino hack to include libraries that are referenced in other libraries. 15 | // For example WiFi is used in AWS libraries but we still need to include in the main sketch file for it to be recognized. 16 | 17 | AWS_Service_ID CurrentService; 18 | 19 | void setup() { 20 | 21 | Serial.println("Begin Setup.."); 22 | Serial.println(CurrentService); 23 | 24 | /* Begin serial communication. */ 25 | Serial.begin(9600); 26 | digitalWrite(BUZZER_PIN, LOW); 27 | 28 | // Connect to WiFi (function loops until successful connection is made) 29 | Mtk_Wifi_Setup(ssid, pass); 30 | printWifiStatus(); 31 | 32 | configureIO(); 33 | accelerometerInit(); 34 | 35 | SNSClient_Setup(); 36 | DynamoBDClient_Setup(); 37 | KinesisClient_Setup(); 38 | 39 | CurrentService = SNS; 40 | indicateServiceThroughLED_blink_Buzzer(DYNAMO_DB); 41 | 42 | Serial.println("Setup complete! Looping main program"); 43 | Serial.println("Initial mode: DynamoDB"); 44 | 45 | } 46 | 47 | unsigned long currentTime = 0; 48 | unsigned long LastPostTime = 0; 49 | enum { 50 | POSTING_PERIOD_MS = 500 51 | }; 52 | 53 | int buttonVal = LOW; 54 | int prevButtonVal = LOW; 55 | 56 | double xyz[3]; 57 | 58 | void loop() { 59 | 60 | readAcceleration(xyz); 61 | if (isAccelerometerShaking(xyz)) { 62 | printAcceleration(xyz); 63 | // light the onboard LED 64 | digitalWrite(MEDIATEK_LED_PIN, HIGH); 65 | 66 | currentTime = millis(); 67 | if ((currentTime - LastPostTime) > POSTING_PERIOD_MS) { 68 | currentTime = millis(); 69 | switch (CurrentService) { 70 | case 0: 71 | Serial.println("Posting to Kinesis"); 72 | putKinesis(xyz[0], xyz[1], xyz[2]); 73 | break; 74 | case 1: 75 | Serial.println("Posting to DynamoDB"); 76 | putDynamoDb(); 77 | break; 78 | case 2: 79 | Serial.println("Posting to SNS"); 80 | putSns(); 81 | break; 82 | default: 83 | Serial.println("Wrong selection"); 84 | break; 85 | } 86 | LastPostTime = currentTime; 87 | } 88 | } else { 89 | //Serial.println("Not publishing..."); 90 | digitalWrite(MEDIATEK_LED_PIN, LOW); 91 | } 92 | 93 | // check if button has been pressed 94 | buttonVal = digitalRead(BUTTON_PIN); 95 | if (prevButtonVal == LOW && buttonVal == HIGH) { 96 | CurrentService = changeService(CurrentService); 97 | } 98 | prevButtonVal = buttonVal; 99 | delay(200); 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/keys.cpp: -------------------------------------------------------------------------------- 1 | // keys.cpp 2 | #include "keys.h" 3 | 4 | const char* awsKeyID = "AKIAJ7PddDHZKY2A"; // REPLACE with your access key id 5 | const char* awsSecKey = "X0pgkwoHwNdd5BB7XF6FuEOqNuxyCeo"; // REPLACE with your secret access key 6 | 7 | 8 | // Init and connect Edison wifi to local wlan 9 | char* ssid = "Ladoo"; // your network SSID (name) 10 | char* pass = "ff"; // your network password (use for WPA, or use as key for WEP) 11 | 12 | // Common AWS constants 13 | const char* AWS_REGION = "us-west-2"; // us-west-2 etc 14 | const char* AWS_ENDPOINT = "amazonaws.com"; 15 | 16 | /* Constants for connecting to Amazon SNS. */ 17 | const char* TARGET_ARN = "arn:aws:sns:us-west-2:4444:m10new-DeviceSNSTopic-NQFKQ2FO6LGR"; // replace each ':' with '%3A' 18 | 19 | 20 | /* Contants describing DynamoDB table and values being used. */ 21 | const char* HASH_KEY_NAME = "device_id"; 22 | const char* HASH_KEY_VALUE = "jin1234"; // REPLACE with your team or particpant name e.g Team38 23 | const char* RANGE_KEY_NAME = "time"; 24 | const char* RANGE_KEY_VALUE = "1"; 25 | const char* TABLE_NAME = "m10new-DeviceDataTable-10WX560ZVUEIX"; //REPLACE with your DDB Table name e.g. hackday-2014-team38 26 | const char* ATTRIBUTE_NAME = "device"; 27 | const char* ATTRIBUTE_VALUE = "mediatek"; 28 | 29 | /* Constants describing Kinesis stream */ 30 | const char* streamName = "m10new-DeviceStream-YQQ8HEN47JZF"; //REPLACE with your stream name e.g. hackday-2014-team38-TeamStream-1K5DAIU8GCRXY 31 | const char* partitionKey = "partitionKey"; 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /samples/MediaTek_Maraca_HTTPS/keys.h: -------------------------------------------------------------------------------- 1 | // keys.h 2 | #ifndef KEYS_H_ 3 | #define KEYS_H_ 4 | 5 | extern const char* awsKeyID; // Declare these variables to 6 | extern const char* awsSecKey; // be accessible by the sketch 7 | 8 | extern const char* AWS_REGION; 9 | extern const char* AWS_ENDPOINT; 10 | 11 | extern char* ssid; // your network SSID (name) 12 | extern char* pass; // your network password (use for WPA, or use as key for WEP) 13 | 14 | extern const char* HASH_KEY_NAME; 15 | extern const char* HASH_KEY_VALUE; 16 | extern const char* RANGE_KEY_NAME; 17 | extern const char* RANGE_KEY_VALUE; 18 | extern const char* TABLE_NAME; 19 | extern const char* ATTRIBUTE_NAME; 20 | extern const char* ATTRIBUTE_VALUE; 21 | 22 | extern const char* streamName; 23 | extern const char* partitionKey; 24 | 25 | extern const char* TARGET_ARN; 26 | 27 | #endif 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /samples/SparkGetItemSample.ino: -------------------------------------------------------------------------------- 1 | #include "AmazonDynamoDBClient.h" 2 | #include "SparkAWSImplementations.h" 3 | #include "AWSFoundationalTypes.h" 4 | #include "keys.h" 5 | #include 6 | 7 | /* 8 | * 9 | * This sample uses GetItem on a DynamoDB table to retrieve the state of a RGB 10 | * LED. Pressing the button gets the saved color values from 0-255. 11 | * 12 | * For this demo to work you must have keys.h/.ccp files that contain your AWS 13 | * access keys and define "awsSecKey" and "awsKeyID", a DynamoDB table with the 14 | * name defined by the constant TABLE_NAME with hash and range keys as defined 15 | * by constants HASH_KEY_NAME/RANGE_KEY_NAME, and and item in that table with 16 | * attributes as defined by HASH_KEY_VALUE/RANGE_KEY_VALUE and number 17 | * attributes R G and B. 18 | * 19 | */ 20 | 21 | /* D2 is the pin connected to the button/switch. For buttons or tilt switches, 22 | * have the pin connected to one side of the button and also connected to 23 | * ground with a resistor. Have the other side of the button connected to 24 | * 5v/3v. */ 25 | static const int SWITCH_PIN = D2; 26 | /* Contants describing DynamoDB table and values being used. */ 27 | static const char* HASH_KEY_NAME = "DemoName"; 28 | static const char* HASH_KEY_VALUE = "Colors"; 29 | static const char* RANGE_KEY_NAME = "id"; 30 | static const char* RANGE_KEY_VALUE = "1"; 31 | static const char* TABLE_NAME = "AWSArduinoSDKDemo"; 32 | static const int KEY_SIZE = 2; 33 | /* Constants for connecting to DynamoDB. */ 34 | static const char* AWS_REGION = "us-east-1"; 35 | static const char* AWS_ENDPOINT = "amazonaws.com"; 36 | 37 | /* 0 or 1 to determine state of the switch */ 38 | int switchState = 0; 39 | 40 | /* Device independent implementations required for AmazonDynamoDBClient to 41 | * function. */ 42 | SparkHttpClient httpClient; 43 | SparkDateTimeProvider dateTimeProvider; 44 | 45 | AmazonDynamoDBClient ddbClient; 46 | 47 | GetItemInput getItemInput; 48 | AttributeValue hashKey; 49 | AttributeValue rangeKey; 50 | ActionError actionError; 51 | 52 | void setup() { 53 | /* Begin serial communication. */ 54 | Serial.begin(9600); 55 | /* Take control of spark's built-int rgb led. */ 56 | RGB.control(true); 57 | /* Initialize ddbClient. */ 58 | ddbClient.setAWSRegion(AWS_REGION); 59 | ddbClient.setAWSEndpoint(AWS_ENDPOINT); 60 | ddbClient.setAWSSecretKey(awsSecKey); 61 | ddbClient.setAWSKeyID(awsKeyID); 62 | ddbClient.setHttpClient(&httpClient); 63 | ddbClient.setDateTimeProvider(&dateTimeProvider); 64 | /* Open SWITCH_PIN to be read from. */ 65 | pinMode(SWITCH_PIN, INPUT); 66 | } 67 | 68 | void loop() { 69 | int prevSwitchState = switchState; 70 | /* Read the state of the button/switch. */ 71 | switchState = digitalRead(SWITCH_PIN); 72 | 73 | /* If state has changed back to low a full button press has passed */ 74 | if (prevSwitchState == HIGH && switchState == LOW) { 75 | /* Set the string and number values for the range and hash Keys, 76 | * respectively. */ 77 | hashKey.setS(HASH_KEY_VALUE); 78 | rangeKey.setN(RANGE_KEY_VALUE); 79 | 80 | /* Create key-value pairs out of the hash and range keys, and create 81 | * a map out off them, which is the key. */ 82 | MinimalKeyValuePair < MinimalString, AttributeValue 83 | > pair1(HASH_KEY_NAME, hashKey); 84 | MinimalKeyValuePair < MinimalString, AttributeValue 85 | > pair2(RANGE_KEY_NAME, rangeKey); 86 | MinimalKeyValuePair keyArray[] = { pair1, 87 | pair2 }; 88 | getItemInput.setKey(MinimalMap < AttributeValue > (keyArray, KEY_SIZE)); 89 | 90 | /* Looking to get the R G and B values */ 91 | MinimalString attributesToGet[] = { "R", "G", "B" }; 92 | getItemInput.setAttributesToGet( 93 | MinimalList < MinimalString > (attributesToGet, 3)); 94 | 95 | /* Set other values. */ 96 | getItemInput.setTableName(TABLE_NAME); 97 | 98 | /* Perform getItem and check for errors. */ 99 | GetItemOutput getItemOutput = ddbClient.getItem(getItemInput, 100 | actionError); 101 | switch (actionError) { 102 | case NONE_ACTIONERROR: 103 | Serial.println("GetItem succeeded!"); 104 | { 105 | /* Get the "item" from the getItem output. */ 106 | MinimalMap < AttributeValue > attributeMap = 107 | getItemOutput.getItem(); 108 | AttributeValue av; 109 | /* Get the rgb values and set the led with them. */ 110 | attributeMap.get("R", av); 111 | int R = atoi(av.getN().getCStr()); 112 | attributeMap.get("G", av); 113 | int G = atoi(av.getN().getCStr()); 114 | attributeMap.get("B", av); 115 | int B = atoi(av.getN().getCStr()); 116 | RGB.color(R, G, B); 117 | } 118 | break; 119 | case INVALID_REQUEST_ACTIONERROR: 120 | Serial.print("ERROR: "); 121 | Serial.println(getItemOutput.getErrorMessage().getCStr()); 122 | break; 123 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 124 | Serial.println( 125 | "ERROR: Required arguments were not set for GetItemInput"); 126 | break; 127 | case RESPONSE_PARSING_ACTIONERROR: 128 | Serial.println("ERROR: Problem parsing http response of GetItem\n"); 129 | break; 130 | case CONNECTION_ACTIONERROR: 131 | Serial.println("ERROR: Connection problem"); 132 | break; 133 | } 134 | 135 | /* wait to not double-record */ 136 | delay(750); 137 | } 138 | delay(50); 139 | } 140 | -------------------------------------------------------------------------------- /samples/SparkPutItemSample.ino: -------------------------------------------------------------------------------- 1 | #include "AmazonDynamoDBClient.h" 2 | #include "SparkAWSImplementations.h" 3 | #include "AWSFoundationalTypes.h" 4 | #include "keys.h" 5 | 6 | /* 7 | * 8 | * This sample uses PutItem on a DynamoDB to record every time a button is 9 | * pressed (or a tilt switch is tilted if using one instead of a button). 10 | * 11 | * For this demo to work you must have keys.h/.ccp files that contain your AWS 12 | * access keys and define "awsSecKey" and "awsKeyID", a DynamoDB table with the 13 | * name defined by the constant TABLE_NAME with hash and range keys as defined 14 | * by constants HASH_KEY_NAME/RANGE_KEY_NAME. 15 | * 16 | */ 17 | 18 | /* D2 is the pin connected to the tilt switch. For buttons or tilt switches, 19 | * have the pin connected to one side of the button and also connected to 20 | * ground with a resistor. Have the other side of the button connected to 21 | * 5v/3v. */ 22 | const int SWITCH_PIN = D2; 23 | 24 | /* Contants describing DynamoDB table and values being used. */ 25 | static const char* HASH_KEY_NAME = "device"; 26 | static const char* HASH_KEY_VALUE = "Spark"; 27 | static const char* RANGE_KEY_NAME = "Time"; 28 | const char* TABLE_NAME = "AWSArduinoSDKTests"; 29 | /* Constants for connecting to DynamoDB. */ 30 | const char* AWS_REGION = "us-east-1"; 31 | const char* AWS_ENDPOINT = "amazonaws.com"; 32 | 33 | /* 0 or 1 to determine state of the switch */ 34 | int switchState = 0; 35 | 36 | /* Device independent implementations required for AmazonDynamoDBClient to 37 | * function. */ 38 | SparkHttpClient httpClient; 39 | SparkDateTimeProvider dateTimeProvider; 40 | 41 | AmazonDynamoDBClient ddbClient; 42 | 43 | PutItemInput putItemInput; 44 | ActionError actionError; 45 | 46 | void setup() { 47 | /* Begin serial communication. */ 48 | Serial.begin(9600); 49 | /* Initialize ddbClient. */ 50 | ddbClient.setAWSRegion(AWS_REGION); 51 | ddbClient.setAWSEndpoint(AWS_ENDPOINT); 52 | ddbClient.setAWSSecretKey(awsSecKey); 53 | ddbClient.setAWSKeyID(awsKeyID); 54 | ddbClient.setHttpClient(&httpClient); 55 | ddbClient.setDateTimeProvider(&dateTimeProvider); 56 | /* Open SWITCH_PIN to be read from. */ 57 | pinMode(SWITCH_PIN, INPUT); 58 | } 59 | 60 | void loop() { 61 | int prevSwitchState = switchState; 62 | /* Read the state of the tilt switch. */ 63 | switchState = digitalRead(SWITCH_PIN); 64 | 65 | /* If state has changed back to low a button press (or a full tilt if using 66 | * a tilt switch) has passed */ 67 | if (prevSwitchState == HIGH && switchState == LOW) { 68 | /* Create an Item. */ 69 | AttributeValue deviceValue; 70 | deviceValue.setS(HASH_KEY_VALUE); 71 | AttributeValue timeValue; 72 | /* Getting current time for Time attribute. */ 73 | timeValue.setS(dateTimeProvider.getDateTime()); 74 | MinimalKeyValuePair < MinimalString, AttributeValue 75 | > att1(HASH_KEY_NAME, deviceValue); 76 | MinimalKeyValuePair < MinimalString, AttributeValue 77 | > att2(RANGE_KEY_NAME, timeValue); 78 | MinimalKeyValuePair itemArray[] = { att1, 79 | att2}; 80 | 81 | /* Set values for putItemInput. */ 82 | putItemInput.setItem(MinimalMap < AttributeValue > (itemArray, 2)); 83 | putItemInput.setTableName(TABLE_NAME); 84 | 85 | /* perform putItem and check for errors. */ 86 | PutItemOutput putItemOutput = ddbClient.putItem(putItemInput, 87 | actionError); 88 | switch (actionError) { 89 | case NONE_ACTIONERROR: 90 | Serial.println("PutItem succeeded!"); 91 | break; 92 | case INVALID_REQUEST_ACTIONERROR: 93 | Serial.print("ERROR: "); 94 | Serial.println(putItemOutput.getErrorMessage().getCStr()); 95 | break; 96 | case MISSING_REQUIRED_ARGS_ACTIONERROR: 97 | Serial.println( 98 | "ERROR: Required arguments were not set for PutItemInput"); 99 | break; 100 | case RESPONSE_PARSING_ACTIONERROR: 101 | Serial.println("ERROR: Problem parsing http response of PutItem"); 102 | break; 103 | case CONNECTION_ACTIONERROR: 104 | Serial.println("ERROR: Connection problem"); 105 | break; 106 | } 107 | /* wait to not double-record */ 108 | delay(2000); 109 | } 110 | delay(150); 111 | } 112 | -------------------------------------------------------------------------------- /samples/SparkSNSSample.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * This sample publishes a message to an Amazon SNS target (topic or endpoint). 3 | * 4 | * For this demo to work, add your AWS keys in "awsSecKey" and "awsKeyID", create an SNS topic 5 | * or endpoint and populate the TARGET_ARN with the topic or endpoint ARN. 6 | * 7 | */ 8 | 9 | #include "AmazonSNSClient.h" 10 | #include "SparkAWSImplementations.h" 11 | #include "AWSFoundationalTypes.h" 12 | 13 | /* AWS credentials */ 14 | const char* awsKeyID = ""; 15 | const char* awsSecKey = ""; 16 | 17 | /* Constants for connecting to Amazon SNS. */ 18 | static const char* TARGET_ARN = ""; // replace each ':' with '%3A' 19 | static const char* AWS_REGION = ""; // us-west-2 etc 20 | static const char* AWS_ENDPOINT = "amazonaws.com"; 21 | 22 | /* Light the LED while a message is transmitted. */ 23 | int led = 13; 24 | 25 | /* Device independent implementations required for AmazonSNSClient to function. */ 26 | SparkHttpClient httpClient; 27 | SparkDateTimeProvider dateTimeProvider; 28 | 29 | /* AWS objects. */ 30 | AmazonSNSClient snsClient; 31 | PublishInput publishInput; 32 | ActionError actionError; 33 | 34 | void setup() { 35 | /* Begin serial communication. */ 36 | Serial.begin(9600); 37 | delay(5000); 38 | 39 | pinMode(led, OUTPUT); 40 | 41 | /* Initialize Amazon SNS client. */ 42 | snsClient.setAWSRegion(AWS_REGION); 43 | snsClient.setAWSEndpoint(AWS_ENDPOINT); 44 | snsClient.setAWSSecretKey(awsSecKey); 45 | snsClient.setAWSKeyID(awsKeyID); 46 | snsClient.setHttpClient(&httpClient); 47 | snsClient.setDateTimeProvider(&dateTimeProvider); 48 | } 49 | 50 | void loop() { 51 | /* Repeat SNS attempt every 60s. */ 52 | delay(60000); 53 | 54 | /* Light up the LED. */ 55 | digitalWrite(led, HIGH); 56 | Serial.println("Start Loop"); 57 | 58 | MinimalString dateTime(dateTimeProvider.getDateTime()); 59 | dateTimeProvider.sync(dateTime.getCStr()); 60 | 61 | /* Delivery attempt to Amazon SNS. */ 62 | publishInput.setMessage(dateTime); 63 | publishInput.setTargetArn(TARGET_ARN); 64 | PublishOutput publishOutput = snsClient.publish(publishInput, actionError); 65 | Serial.println(publishOutput.getMessageId().getCStr()); 66 | 67 | /* Finish loop and turn off the LED. */ 68 | Serial.println(dateTime.getCStr()); 69 | Serial.println("Finished loop"); 70 | delay(1000); 71 | digitalWrite(led, LOW); 72 | } 73 | -------------------------------------------------------------------------------- /src/common/AWSClient.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWSClient.h 3 | * 4 | * Base classes for services to create an HTTP request. 5 | * 6 | * Created on: Jul 8, 2014 7 | * Author: hoffmaj 8 | */ 9 | 10 | #ifndef AWSCLIENT_H_ 11 | #define AWSCLIENT_H_ 12 | 13 | #include "DeviceIndependentInterfaces.h" 14 | #include "AWSFoundationalTypes.h" 15 | 16 | /* Total number of headers. */ 17 | static const int HEADER_COUNT = 7; 18 | /* Size of the awsDate string. */ 19 | static const int AWS_DATE_LEN = 8; 20 | /* Size of the awsTime string. */ 21 | static const int AWS_TIME_LEN = 6; 22 | /* Size of sha hashes and signatures in hexidecimal. */ 23 | static const int HASH_HEX_LEN = 64; 24 | 25 | /* Base class for an AWS Service Client. Creates http and https request in raw 26 | * http format or as a curl command. */ 27 | class AWSClient { 28 | /* Name of region, eg. "us-east-1" in "kinesis.us-east-1.amazonaws.com". */ 29 | char* awsRegion; 30 | /* Endpoint, eg. "amazonaws.com" in "kinesis.us-east-1.amazonaws.com". */ 31 | char* awsEndpoint; 32 | /* The user's AWS Secret Key for accessing the AWS Resource. */ 33 | char* awsSecKey; 34 | /* The user's AWS Access Key ID for accessing the AWS Resource. */ 35 | char* awsKeyID; 36 | /* GMT date in yyyyMMdd format. */ 37 | char awsDate[AWS_DATE_LEN + 1]; 38 | /* GMT time in HHmmss format. */ 39 | char awsTime[AWS_TIME_LEN + 1]; 40 | /* Number of headers created. */ 41 | int headersCreated; 42 | /* Array of the created http headers. */ 43 | char* headers[HEADER_COUNT]; 44 | /* Array of string lengths of the headers in the "headers" array. */ 45 | int headerLens[HEADER_COUNT]; 46 | /* The payload of the httprequest to be created */ 47 | MinimalString payload; 48 | 49 | /* Add the headers that will be signed to the headers array. Called before 50 | * createStringToSign. */ 51 | void initSignedHeaders(); 52 | /* Create the canonical request and the string to sign as described. Return 53 | * value must be deleted by caller. */ 54 | char* createStringToSign(void); 55 | /* Given the string to sign, create the signature (a 64-char cstring). 56 | * Return value must be deleted by caller. */ 57 | char* createSignature(const char* toSign); 58 | /* Add the headers that will not be signed to the headers array. Called 59 | * after createSignature. */ 60 | void initUnsignedHeaders(const char* signature); 61 | /* Contains all of the work to be done before headersToRequest or 62 | * headersToCurlRequest are called. Takes the payload to be sent and the 63 | * GMT date in yyyyMMddHHmmss format. */ 64 | void createRequestInit(MinimalString &reqPayload); 65 | /* Clean up after headersToRequest or headersToCurlRequest are called. */ 66 | void createRequestCleanup(); 67 | /* Using the headers array, create a raw http request. */ 68 | char* headersToRequest(void); 69 | /* Using the headers array, create a http request in curl command 70 | * format. */ 71 | char* headersToCurlRequest(void); 72 | 73 | protected: 74 | /* Used to keep track of time. */ 75 | IDateTimeProvider* dateTimeProvider; 76 | /* Used to send http to the server. */ 77 | IHttpClient* httpClient; 78 | /* true if https is to be used, false if http is to be used. */ 79 | bool httpS; 80 | /* Name of service, eg. "kinesis" in "kinesis.us-east-1.amazonaws.com". */ 81 | const char* awsService; 82 | /* Content type of payload, eg. "application/x-amz-json-1.1". */ 83 | const char* contentType; 84 | /* The request's target, i.e. "Kinesis_20131202.PutRecord". */ 85 | const char* target; 86 | /* Creates a raw http request, given the payload and current GMT date in 87 | * yyyyMMddHHmmss format. Should be exposed to user by extending class. 88 | * Returns 0 if client is unititialized. */ 89 | char* createRequest(MinimalString &payload); 90 | /* Creates a http request in curl format, given the payload and current GMT 91 | * date in yyyyMMddHHmmss format. Should be exposed to user by extending 92 | * class. Returns 0 if client is unititialized. */ 93 | char* createCurlRequest(MinimalString &payload); 94 | /* Sends http data. Returns http response, or null on error. */ 95 | char* sendData(const char* data); 96 | /* Empty constructor. Must also be initialized with init. */ 97 | AWSClient(); 98 | 99 | public: 100 | /* Setters for values used by createRequest and createCurlRequest. Must 101 | * be set or create[Curl]Request will return null. */ 102 | void setAWSRegion(const char * awsRegion); 103 | void setAWSEndpoint(const char * awsEndpoint); 104 | void setAWSSecretKey(const char * awsSecKey); 105 | void setAWSKeyID(const char * awsKeyID); 106 | void setHttpClient(IHttpClient* httpClient); 107 | void setDateTimeProvider(IDateTimeProvider* dateTimeProvider); 108 | ~AWSClient(void); 109 | }; 110 | 111 | #endif /* AWSCLIENT_H_ */ 112 | -------------------------------------------------------------------------------- /src/common/AWSClient2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AWSClient.cpp 3 | * 4 | * See AWSClient.h for description. 5 | * 6 | */ 7 | 8 | #include "AWSClient2.h" 9 | #include "Utils.h" 10 | #include "DeviceIndependentInterfaces.h" 11 | #include "AWSFoundationalTypes.h" 12 | #include "sha256.h" 13 | #include 14 | #include 15 | #include 16 | 17 | /* Constants string, formats, and lengths. */ 18 | static const char* CANONICAL_FORM_POST_LINE = "POST\n/\n\n"; 19 | static const int CANONICAL_FORM_POST_LINE_LEN = 8; 20 | static const char* HTTPS_REQUEST_POST_LINE = 21 | "POST https://%s.%s.%s/ HTTP/1.1\n"; 22 | static const int HTTPS_REQUEST_POST_LINE_LEN = 28; 23 | static const char* HTTP_REQUEST_POST_LINE = "POST http://%s.%s.%s/ HTTP/1.1\n"; 24 | static const int HTTP_REQUEST_POST_LINE_LEN = 27; 25 | static const char* TO_SIGN_TEMPLATE = 26 | "AWS4-HMAC-SHA256\n%sT%sZ\n%s/%s/%s/aws4_request\n%s"; 27 | static const int TO_SIGN_TEMPLATE_LEN = 36; 28 | static const char* CONTENT_LENGTH_HEADER = "content-length:%d"; 29 | static const int CONTENT_LENGTH_HEADER_LEN = 15; 30 | static const char* HOST_HEADER = "host:%s.%s.%s"; 31 | static const int HOST_HEADER_LEN = 7; 32 | static const char* CONNECTION_HEADER = "Connection:close"; 33 | static const int CONNECTION_HEADER_LEN = 16; 34 | static const char* CONTENT_TYPE_HEADER = "content-type:%s"; 35 | static const int CONTENT_TYPE_HEADER_LEN = 13; 36 | static const char* X_AMZ_DATE_HEADER = "x-amz-date:%sT%sZ"; 37 | static const int X_AMZ_DATE_HEADER_LEN = 13; 38 | static const char* AUTHORIZATION_HEADER = 39 | "Authorization: AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request, SignedHeaders=%s, Signature=%s"; 40 | static const int AUTHORIZATION_HEADER_LEN = 87; 41 | static const char* SIGNED_HEADERS = 42 | "content-length;content-type;host;x-amz-date"; 43 | static const int SIGNED_HEADERS_LEN = 43; 44 | 45 | AWSClient2::AWSClient2() { 46 | /* Null until set in init method. */ 47 | awsRegion = 0; 48 | awsEndpoint = 0; 49 | awsSecKey = 0; 50 | awsKeyID = 0; 51 | httpClient = 0; 52 | dateTimeProvider = 0; 53 | } 54 | 55 | void AWSClient2::setAWSRegion(const char * awsRegion) { 56 | int len = strlen(awsRegion) + 1; 57 | this->awsRegion = new char[len](); 58 | strcpy(this->awsRegion, awsRegion); 59 | } 60 | void AWSClient2::setAWSEndpoint(const char * awsEndpoint) { 61 | int len = strlen(awsEndpoint) + 1; 62 | this->awsEndpoint = new char[len](); 63 | strcpy(this->awsEndpoint, awsEndpoint); 64 | } 65 | void AWSClient2::setAWSSecretKey(const char * awsSecKey) { 66 | int len = strlen(awsSecKey) + 1; 67 | this->awsSecKey = new char[len](); 68 | strcpy(this->awsSecKey, awsSecKey); 69 | } 70 | void AWSClient2::setAWSKeyID(const char * awsKeyID) { 71 | int len = strlen(awsKeyID) + 1; 72 | this->awsKeyID = new char[len](); 73 | strcpy(this->awsKeyID, awsKeyID); 74 | } 75 | void AWSClient2::setHttpClient(IHttpClient* httpClient) { 76 | this->httpClient = httpClient; 77 | } 78 | void AWSClient2::setDateTimeProvider(IDateTimeProvider* dateTimeProvider) { 79 | this->dateTimeProvider = dateTimeProvider; 80 | } 81 | 82 | AWSClient2::~AWSClient2() { 83 | if (awsRegion != 0) 84 | delete[] awsRegion; 85 | if (awsEndpoint != 0) 86 | delete[] awsEndpoint; 87 | if (awsSecKey != 0) 88 | delete[] awsSecKey; 89 | if (awsKeyID != 0) 90 | delete[] awsKeyID; 91 | } 92 | 93 | void AWSClient2::initSignedHeaders() { 94 | /* For each of the formats for unsigned headers, determine the size of the 95 | * formatted string, allocate that much space in the next available element 96 | * in the headers array, create the string, and add it's length to the 97 | * headerLens array. */ 98 | 99 | int contentLen = payload.length(); 100 | int len = CONTENT_LENGTH_HEADER_LEN + digitCount(contentLen); 101 | headers[headersCreated] = new char[len + 1](); 102 | sprintf(headers[headersCreated], CONTENT_LENGTH_HEADER, contentLen); 103 | headerLens[headersCreated++] = len; 104 | 105 | len = CONTENT_TYPE_HEADER_LEN + strlen(contentType); 106 | headers[headersCreated] = new char[len + 1](); 107 | sprintf(headers[headersCreated], CONTENT_TYPE_HEADER, contentType); 108 | headerLens[headersCreated++] = len; 109 | 110 | len = HOST_HEADER_LEN + strlen(awsService) + strlen(awsRegion) 111 | + strlen(awsEndpoint); 112 | headers[headersCreated] = new char[len + 1](); 113 | sprintf(headers[headersCreated], HOST_HEADER, awsService, awsRegion, 114 | awsEndpoint); 115 | headerLens[headersCreated++] = len; 116 | 117 | len = X_AMZ_DATE_HEADER_LEN + AWS_DATE_LEN2 + AWS_TIME_LEN2; 118 | headers[headersCreated] = new char[len + 1](); 119 | sprintf(headers[headersCreated], X_AMZ_DATE_HEADER, awsDate, awsTime); 120 | headerLens[headersCreated++] = len; 121 | } 122 | 123 | char* AWSClient2::createStringToSign(void) { 124 | SHA256* sha256 = new SHA256(); 125 | char* hashed; 126 | /* Calculate length of canonicalForm string. */ 127 | int canonicalFormLen = CANONICAL_FORM_POST_LINE_LEN; 128 | for (int i = 0; i < headersCreated; i++) { 129 | /* +1 for newlines */ 130 | canonicalFormLen += *(headerLens + i) + 1; 131 | } 132 | /* +2 for newlines. */ 133 | canonicalFormLen += SIGNED_HEADERS_LEN + HASH_HEX_LEN2 + 2; 134 | 135 | char* canonicalForm = new char[canonicalFormLen + 1](); 136 | 137 | /* Write the cannonicalForm string. */ 138 | int canonicalFormWritten = 0; 139 | canonicalFormWritten += strlen( 140 | strcpy(canonicalForm + canonicalFormWritten, 141 | CANONICAL_FORM_POST_LINE)); 142 | for (int i = 0; i < headersCreated; i++) { 143 | canonicalFormWritten += sprintf(canonicalForm + canonicalFormWritten, 144 | "%s\n", *(headers + i)); 145 | } 146 | canonicalFormWritten += sprintf(canonicalForm + canonicalFormWritten, 147 | "\n%s\n", SIGNED_HEADERS); 148 | hashed = (*sha256)(payload.getCStr(), payload.length()); 149 | strcpy(canonicalForm + canonicalFormWritten, hashed); 150 | delete[] hashed; 151 | canonicalFormWritten += HASH_HEX_LEN2; 152 | 153 | /* Hash the canonicalForm string. */ 154 | hashed = (*sha256)(canonicalForm, canonicalFormWritten); 155 | delete sha256; 156 | 157 | delete[] canonicalForm; 158 | 159 | /* Determine the size to the string to sign. */ 160 | int toSignLen = TO_SIGN_TEMPLATE_LEN + 2 * AWS_DATE_LEN2 + AWS_TIME_LEN2 161 | + strlen(awsRegion) + strlen(awsService) + HASH_HEX_LEN2; 162 | 163 | /* Create and return the string to sign. */ 164 | char* toSign = new char[toSignLen + 1](); 165 | sprintf(toSign, TO_SIGN_TEMPLATE, awsDate, awsTime, awsDate, awsRegion, 166 | awsService, hashed); 167 | delete[] hashed; 168 | return toSign; 169 | 170 | } 171 | char* AWSClient2::createSignature(const char* toSign) { 172 | 173 | /* Allocate memory for the signature */ 174 | char* signature = new char[HASH_HEX_LEN2 + 1](); 175 | 176 | /* Create the signature key */ 177 | /* + 4 for "AWS4" */ 178 | int keyLen = strlen(awsSecKey) + 4; 179 | char* key = new char[keyLen + 1](); 180 | sprintf(key, "AWS4%s", awsSecKey); 181 | 182 | /* repeatedly apply hmac with the appropriate values. See 183 | * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html 184 | * for algorithm. */ 185 | char* k1 = hmacSha256(key, keyLen, awsDate, strlen(awsDate)); 186 | delete[] key; 187 | char* k2 = hmacSha256(k1, SHA256_DEC_HASH_LEN, awsRegion, 188 | strlen(awsRegion)); 189 | delete[] k1; 190 | char* k3 = hmacSha256(k2, SHA256_DEC_HASH_LEN, awsService, 191 | strlen(awsService)); 192 | delete[] k2; 193 | char* k4 = hmacSha256(k3, SHA256_DEC_HASH_LEN, "aws4_request", 12); 194 | delete[] k3; 195 | char* k5 = hmacSha256(k4, SHA256_DEC_HASH_LEN, toSign, strlen(toSign)); 196 | delete[] k4; 197 | 198 | /* Convert the chars in hash to hex for signature. */ 199 | for (int i = 0; i < SHA256_DEC_HASH_LEN; ++i) { 200 | sprintf(signature + 2 * i, "%02lx", 0xff & (unsigned long) k5[i]); 201 | } 202 | delete[] k5; 203 | return signature; 204 | } 205 | 206 | void AWSClient2::initUnsignedHeaders(const char* signature) { 207 | int len = AUTHORIZATION_HEADER_LEN + strlen(awsKeyID) + AWS_DATE_LEN2 208 | + strlen(awsRegion) + strlen(awsService) + SIGNED_HEADERS_LEN 209 | + HASH_HEX_LEN2; 210 | headers[headersCreated] = new char[len + 1](); 211 | sprintf(headers[headersCreated], AUTHORIZATION_HEADER, awsKeyID, awsDate, 212 | awsRegion, awsService, SIGNED_HEADERS, signature); 213 | headerLens[headersCreated++] = len; 214 | /* 215 | len = CONNECTION_HEADER_LEN; 216 | headers[headersCreated] = new char[len + 1](); 217 | strcpy(headers[headersCreated], CONNECTION_HEADER); 218 | headerLens[headersCreated++] = len; 219 | */ 220 | } 221 | 222 | void AWSClient2::createRequestInit(MinimalString &reqPayload) { 223 | //initialize object-scoped variables 224 | const char* dateTime = dateTimeProvider->getDateTime(); 225 | sprintf(awsDate, "%.8s", dateTime); 226 | sprintf(awsTime, "%.6s", dateTime + 8); 227 | payload = reqPayload; 228 | headersCreated = 0; 229 | 230 | //Create signature and headers 231 | initSignedHeaders(); 232 | char* toSign = createStringToSign(); 233 | char* signature = createSignature(toSign); 234 | delete[] toSign; 235 | initUnsignedHeaders(signature); 236 | delete[] signature; 237 | } 238 | 239 | void AWSClient2::createRequestCleanup() { 240 | /* Free each header */ 241 | for (int i = 0; i < headersCreated; i++) { 242 | delete[] headers[i]; 243 | } 244 | } 245 | 246 | char* AWSClient2::headersToRequest() { 247 | /* Determine whether to use https or http postLine values. */ 248 | int postLineLen = 249 | httpS ? HTTPS_REQUEST_POST_LINE_LEN : HTTP_REQUEST_POST_LINE_LEN; 250 | const char* postLine = 251 | httpS ? HTTPS_REQUEST_POST_LINE : HTTP_REQUEST_POST_LINE; 252 | 253 | /* Calculate length of httpRequest string. */ 254 | int httpRequestLen = postLineLen + strlen(awsService) + strlen(awsRegion) 255 | + strlen(awsEndpoint); 256 | for (int i = 0; i < headersCreated; i++) { 257 | /* +1 for newline. */ 258 | httpRequestLen += *(headerLens + i) + 1; 259 | } 260 | /* +1 for newline. */ 261 | httpRequestLen += payload.length() + 1; 262 | 263 | /* Create and write to the httpRequest string. */ 264 | char* httpRequest = new char[httpRequestLen + 1](); 265 | int httpRequestWritten = 0; 266 | httpRequestWritten += sprintf(httpRequest + httpRequestWritten, postLine, 267 | awsService, awsRegion, awsEndpoint); 268 | for (int i = 0; i < headersCreated; i++) { 269 | httpRequestWritten += sprintf(httpRequest + httpRequestWritten, "%s\n", 270 | *(headers + i)); 271 | } 272 | httpRequestWritten += sprintf(httpRequest + httpRequestWritten, "\n%s", 273 | payload.getCStr()); 274 | 275 | return httpRequest; 276 | } 277 | 278 | char* AWSClient2::createRequest(MinimalString &reqPayload) { 279 | /* Check that all values have been initialized. */ 280 | if (awsRegion == 0 || awsEndpoint == 0 || awsSecKey == 0 || awsKeyID == 0 281 | || httpClient == 0 || dateTimeProvider == 0) 282 | return 0; 283 | 284 | createRequestInit(reqPayload); 285 | char* request = headersToRequest(); 286 | createRequestCleanup(); 287 | return request; 288 | } 289 | 290 | char* AWSClient2::sendData(const char* data) { 291 | char* server = new char[strlen(awsService) + strlen(awsRegion) 292 | + strlen(awsEndpoint) + 4](); 293 | sprintf(server, "%s.%s.%s", awsService, awsRegion, awsEndpoint); 294 | int port = httpS ? 443 : 80; 295 | char* response = httpClient->send(data, server, port); 296 | delete[] server; 297 | return response; 298 | } 299 | -------------------------------------------------------------------------------- /src/common/AWSClient2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWSClient2.h 3 | * 4 | * Base classes for services to create an HTTP request. 5 | * 6 | */ 7 | 8 | #ifndef AWSCLIENT2_H_ 9 | #define AWSCLIENT2_H_ 10 | 11 | #include "DeviceIndependentInterfaces.h" 12 | #include "AWSFoundationalTypes.h" 13 | 14 | /* Total number of headers. */ 15 | static const int HEADER_COUNT2 = 7; 16 | /* Size of the awsDate string. */ 17 | static const int AWS_DATE_LEN2 = 8; 18 | /* Size of the awsTime string. */ 19 | static const int AWS_TIME_LEN2 = 6; 20 | /* Size of sha hashes and signatures in hexidecimal. */ 21 | static const int HASH_HEX_LEN2 = 64; 22 | 23 | /* Base class for an AWS Service Client. Creates http and https request in raw 24 | * http format or as a curl command. */ 25 | class AWSClient2 { 26 | /* Name of region, eg. "us-east-1" in "kinesis.us-east-1.amazonaws.com". */ 27 | char* awsRegion; 28 | /* Endpoint, eg. "amazonaws.com" in "kinesis.us-east-1.amazonaws.com". */ 29 | char* awsEndpoint; 30 | /* The user's AWS Secret Key for accessing the AWS Resource. */ 31 | char* awsSecKey; 32 | /* The user's AWS Access Key ID for accessing the AWS Resource. */ 33 | char* awsKeyID; 34 | /* GMT date in yyyyMMdd format. */ 35 | char awsDate[AWS_DATE_LEN2 + 1]; 36 | /* GMT time in HHmmss format. */ 37 | char awsTime[AWS_TIME_LEN2 + 1]; 38 | /* Number of headers created. */ 39 | int headersCreated; 40 | /* Array of the created http headers. */ 41 | char* headers[HEADER_COUNT2]; 42 | /* Array of string lengths of the headers in the "headers" array. */ 43 | int headerLens[HEADER_COUNT2]; 44 | /* The payload of the httprequest to be created */ 45 | MinimalString payload; 46 | 47 | /* Add the headers that will be signed to the headers array. Called before 48 | * createStringToSign. */ 49 | void initSignedHeaders(); 50 | /* Create the canonical request and the string to sign as described. Return 51 | * value must be deleted by caller. */ 52 | char* createStringToSign(void); 53 | /* Given the string to sign, create the signature (a 64-char cstring). 54 | * Return value must be deleted by caller. */ 55 | char* createSignature(const char* toSign); 56 | /* Add the headers that will not be signed to the headers array. Called 57 | * after createSignature. */ 58 | void initUnsignedHeaders(const char* signature); 59 | /* Contains all of the work to be done before headersToRequest or 60 | * headersToCurlRequest are called. Takes the payload to be sent and the 61 | * GMT date in yyyyMMddHHmmss format. */ 62 | void createRequestInit(MinimalString &reqPayload); 63 | /* Clean up after headersToRequest or headersToCurlRequest are called. */ 64 | void createRequestCleanup(); 65 | /* Using the headers array, create a raw http request. */ 66 | char* headersToRequest(void); 67 | 68 | protected: 69 | /* Used to keep track of time. */ 70 | IDateTimeProvider* dateTimeProvider; 71 | /* Used to send http to the server. */ 72 | IHttpClient* httpClient; 73 | /* true if https is to be used, false if http is to be used. */ 74 | bool httpS; 75 | /* Name of service, eg. "kinesis" in "kinesis.us-east-1.amazonaws.com". */ 76 | const char* awsService; 77 | /* Content type of payload, eg. "application/x-amz-json-1.1". */ 78 | const char* contentType; 79 | /* Creates a raw http request, given the payload and current GMT date in 80 | * yyyyMMddHHmmss format. Should be exposed to user by extending class. 81 | * Returns 0 if client is unititialized. */ 82 | char* createRequest(MinimalString &payload); 83 | /* Sends http data. Returns http response, or null on error. */ 84 | char* sendData(const char* data); 85 | /* Empty constructor. Must also be initialized with init. */ 86 | AWSClient2(); 87 | 88 | public: 89 | /* Setters for values used by createRequest and createCurlRequest. Must 90 | * be set or create[Curl]Request will return null. */ 91 | void setAWSRegion(const char * awsRegion); 92 | void setAWSEndpoint(const char * awsEndpoint); 93 | void setAWSSecretKey(const char * awsSecKey); 94 | void setAWSKeyID(const char * awsKeyID); 95 | void setHttpClient(IHttpClient* httpClient); 96 | void setDateTimeProvider(IDateTimeProvider* dateTimeProvider); 97 | ~AWSClient2(void); 98 | }; 99 | 100 | #endif /* AWSCLIENT2_H_ */ 101 | -------------------------------------------------------------------------------- /src/common/AWSClient4.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * AWSClient.cpp 3 | * 4 | * See AWSClient.h for description. 5 | * See http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html 6 | * 7 | */ 8 | 9 | #include "AWSClient4.h" 10 | #include "Utils.h" 11 | #include "DeviceIndependentInterfaces.h" 12 | #include "AWSFoundationalTypes.h" 13 | #include "sha256.h" 14 | #include 15 | #include 16 | #include 17 | 18 | AWSClient4::AWSClient4() { 19 | /* Null until set in init method. */ 20 | awsRegion = 0; 21 | awsEndpoint = 0; 22 | awsSecKey = 0; 23 | awsKeyID = 0; 24 | httpClient = 0; 25 | dateTimeProvider = 0; 26 | method = 0; 27 | uri = 0; 28 | } 29 | 30 | void AWSClient4::setAWSRegion(const char * awsRegion) { 31 | int len = strlen(awsRegion) + 1; 32 | this->awsRegion = new char[len](); 33 | strcpy(this->awsRegion, awsRegion); 34 | } 35 | void AWSClient4::setAWSEndpoint(const char * awsEndpoint) { 36 | int len = strlen(awsEndpoint) + 1; 37 | this->awsEndpoint = new char[len](); 38 | strcpy(this->awsEndpoint, awsEndpoint); 39 | } 40 | void AWSClient4::setAWSDomain(const char * awsDomain) { 41 | int len = strlen(awsDomain) + 1; 42 | this->awsDomain = new char[len](); 43 | strcpy(this->awsDomain, awsDomain); 44 | } 45 | void AWSClient4::setAWSPath(const char * awsPath) { 46 | int len = strlen(awsPath) + 1; 47 | this->awsPath = new char[len](); 48 | strcpy(this->awsPath, awsPath); 49 | } 50 | void AWSClient4::setAWSSecretKey(const char * awsSecKey) { 51 | int len = strlen(awsSecKey) + 1; 52 | this->awsSecKey = new char[len](); 53 | strcpy(this->awsSecKey, awsSecKey); 54 | } 55 | void AWSClient4::setAWSKeyID(const char * awsKeyID) { 56 | int len = strlen(awsKeyID) + 1; 57 | this->awsKeyID = new char[len](); 58 | strcpy(this->awsKeyID, awsKeyID); 59 | } 60 | void AWSClient4::setHttpClient(IHttpClient* httpClient) { 61 | this->httpClient = httpClient; 62 | } 63 | void AWSClient4::setDateTimeProvider(IDateTimeProvider* dateTimeProvider) { 64 | this->dateTimeProvider = dateTimeProvider; 65 | } 66 | 67 | AWSClient4::~AWSClient4() { 68 | if (awsRegion != 0) 69 | delete[] awsRegion; 70 | if (awsEndpoint != 0) 71 | delete[] awsEndpoint; 72 | if (awsSecKey != 0) 73 | delete[] awsSecKey; 74 | if (awsKeyID != 0) 75 | delete[] awsKeyID; 76 | } 77 | 78 | 79 | char* AWSClient4::createCanonicalHeaders() { 80 | // headers, alphabetically sorted, lowercase, eg: key:value 81 | // content-type:x 82 | // host:host 83 | // x-amz-content-sha256:hash 84 | // x-amz-date:date 85 | char canonical_headers[500] = ""; 86 | sprintf(canonical_headers, "%scontent-type:%s\n", canonical_headers, contentType); 87 | sprintf(canonical_headers, "%shost:%s\n", canonical_headers, awsDomain); 88 | // sprintf(canonical_headers, "%srange:bytes=0-9\n", canonical_headers); // s3 89 | sprintf(canonical_headers, "%sx-amz-content-sha256:%s\n", canonical_headers, payloadHash); 90 | sprintf(canonical_headers, "%sx-amz-date:%sT%sZ\n\n", canonical_headers, awsDate, awsTime); 91 | return canonical_headers; 92 | } 93 | 94 | char* AWSClient4::createRequestHeaders(char* signature) { 95 | char headers[1000] = ""; 96 | sprintf(headers, "%sContent-Type: %s\r\n", headers, contentType); 97 | sprintf(headers, "%sConnection: close\r\n", headers); 98 | sprintf(headers, "%sContent-Length: %d\r\n", headers, strlen(payload.getCStr())); 99 | sprintf(headers, "%sHost: %s\r\n", headers, awsDomain); 100 | sprintf(headers, "%sx-amz-content-sha256: %s\r\n", headers, payloadHash); 101 | sprintf(headers, "%sx-amz-date: %sT%sZ\r\n", headers, awsDate, awsTime); 102 | sprintf(headers, "%sAuthorization: AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request,SignedHeaders=%s,Signature=%s\r\n", headers, awsKeyID, awsDate, awsRegion, awsService, signedHeaders, signature); 103 | return headers; 104 | } 105 | 106 | char* AWSClient4::createStringToSign(char* canonical_request) { 107 | // return canonical_request; 108 | SHA256* sha256 = new SHA256(); 109 | char* hashed = (*sha256)(canonical_request, strlen(canonical_request)); 110 | delete sha256; 111 | // return canonical_request; 112 | 113 | char string_to_sign[700] = ""; 114 | sprintf(string_to_sign, "%sAWS4-HMAC-SHA256\n", string_to_sign); 115 | sprintf(string_to_sign, "%s%sT%sZ\n", string_to_sign, awsDate, awsTime); 116 | sprintf(string_to_sign, "%s%s/%s/%s/aws4_request\n", string_to_sign, awsDate, awsRegion, awsService); 117 | 118 | sprintf(string_to_sign, "%s%s", string_to_sign, hashed); 119 | 120 | return string_to_sign; 121 | } 122 | 123 | char* AWSClient4::createCanonicalRequest() { 124 | char canonical_request[800] = ""; 125 | sprintf(canonical_request, "%s%s\n", canonical_request, method); // VERB 126 | sprintf(canonical_request, "%s%s\n", canonical_request, awsPath); // URI 127 | sprintf(canonical_request, "%s%s\n", canonical_request, queryString); // queryString 128 | 129 | char* headers = createCanonicalHeaders(); 130 | 131 | sprintf(canonical_request, "%s%s", canonical_request, headers); // headers 132 | sprintf(canonical_request, "%s%s\n", canonical_request, signedHeaders); // signed_headers 133 | sprintf(canonical_request, "%s%s", canonical_request, payloadHash); // payload 134 | 135 | return canonical_request; 136 | } 137 | 138 | 139 | char* AWSClient4::createSignature(const char* toSign) { 140 | 141 | /* Allocate memory for the signature */ 142 | char* signature = new char[HASH_HEX_LEN4 + 1](); 143 | 144 | /* Create the signature key */ 145 | /* + 4 for "AWS4" */ 146 | int keyLen = strlen(awsSecKey) + 4; 147 | char* key = new char[keyLen + 1](); 148 | sprintf(key, "AWS4%s", awsSecKey); 149 | 150 | /* repeatedly apply hmac with the appropriate values. See 151 | * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html 152 | * for algorithm. */ 153 | char* k1 = hmacSha256(key, keyLen, awsDate, strlen(awsDate)); 154 | delete[] key; 155 | char* k2 = hmacSha256(k1, SHA256_DEC_HASH_LEN, awsRegion, 156 | strlen(awsRegion)); 157 | delete[] k1; 158 | char* k3 = hmacSha256(k2, SHA256_DEC_HASH_LEN, awsService, 159 | strlen(awsService)); 160 | delete[] k2; 161 | char* k4 = hmacSha256(k3, SHA256_DEC_HASH_LEN, "aws4_request", 12); 162 | delete[] k3; 163 | char* k5 = hmacSha256(k4, SHA256_DEC_HASH_LEN, toSign, strlen(toSign)); 164 | delete[] k4; 165 | 166 | /* Convert the chars in hash to hex for signature. */ 167 | for (int i = 0; i < SHA256_DEC_HASH_LEN; ++i) { 168 | sprintf(signature + 2 * i, "%02lx", 0xff & (unsigned long) k5[i]); 169 | } 170 | delete[] k5; 171 | return signature; 172 | } 173 | 174 | 175 | char* AWSClient4::createRequest(MinimalString &reqPayload) { 176 | /* Check that all values have been initialized. */ 177 | if (awsRegion == 0 || awsEndpoint == 0 || awsSecKey == 0 || awsKeyID == 0 178 | || httpClient == 0 || dateTimeProvider == 0) 179 | return 0; 180 | 181 | // set date and time 182 | // @TODO: find out why sprintf doesn't work 183 | const char* dateTime = dateTimeProvider->getDateTime(); 184 | strncpy(awsDate, dateTime, 8); 185 | awsDate[9] = '\0'; 186 | strncpy(awsTime, dateTime + 8, 6); 187 | awsTime[7] = '\0'; 188 | 189 | SHA256* sha256 = new SHA256(); 190 | payloadHash = (*sha256)(reqPayload.getCStr(), reqPayload.length()); 191 | delete sha256; 192 | 193 | payload = reqPayload; 194 | 195 | // create the canonical request, we need to copy the results 196 | // @TODO: figure out why the reference doesn't work 197 | char *canonical_request_return = createCanonicalRequest(); 198 | char canonical_request[1000]; 199 | strcpy(canonical_request, canonical_request_return); 200 | // return canonical_request; 201 | 202 | // create the signing string, we need to copy the results 203 | // @TODO: figure out why the reference doesn't work 204 | char *return_string_to_sign = createStringToSign(canonical_request); 205 | char string_to_sign[500]; 206 | strcpy(string_to_sign, return_string_to_sign); 207 | 208 | // create the signature 209 | char *signature = createSignature(string_to_sign); 210 | 211 | // create the headers 212 | char *headers = createRequestHeaders(signature); 213 | 214 | // get the host/domain 215 | // char *host = createHost(); 216 | 217 | // create the request with all the vars 218 | char* request = new char[strlen(method) + strlen(awsDomain) + strlen(awsPath) + strlen(headers) + strlen(reqPayload.getCStr()) + 16](); 219 | sprintf(request, "%s %s HTTP/1.1\r\n%s\r\n%s\r\n\r\n", method, awsPath, headers, reqPayload.getCStr()); 220 | 221 | return request; 222 | } 223 | 224 | char* AWSClient4::sendData(const char* data) { 225 | // char* server = createHost(); 226 | int port = httpS ? 443 : 80; 227 | char* response = httpClient->send(data, awsDomain, port); 228 | // delete[] server; 229 | return response; 230 | } 231 | -------------------------------------------------------------------------------- /src/common/AWSClient4.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AWSClient2.h 3 | * 4 | * Base classes for services to create an HTTP request. 5 | * 6 | */ 7 | 8 | #ifndef AWSCLIENT4_H_ 9 | #define AWSCLIENT4_H_ 10 | 11 | #include "DeviceIndependentInterfaces.h" 12 | #include "AWSFoundationalTypes.h" 13 | 14 | /* Total number of headers. */ 15 | static const int HEADER_COUNT4 = 7; 16 | /* Size of the awsDate string. */ 17 | static const int AWS_DATE_LEN4 = 8; 18 | /* Size of the awsTime string. */ 19 | static const int AWS_TIME_LEN4 = 6; 20 | /* Size of sha hashes and signatures in hexidecimal. */ 21 | static const int HASH_HEX_LEN4 = 64; 22 | 23 | /* Base class for an AWS Service Client. Creates http and https request in raw 24 | * http format or as a curl command. */ 25 | class AWSClient4 { 26 | /* Name of region, eg. "us-east-1" in "kinesis.us-east-1.amazonaws.com". */ 27 | char* awsRegion; 28 | /* Endpoint, eg. "amazonaws.com" in "kinesis.us-east-1.amazonaws.com". */ 29 | char* awsEndpoint; 30 | /* Domain, optional, eg. "A2MBBEONHC9LUG.iot.us-east-1.amazonaws.com". */ 31 | char* awsDomain; 32 | /* Path, optional eg. "/things/foobar/shadow", eg for iot-data. */ 33 | char* awsPath; 34 | /* The user's AWS Secret Key for accessing the AWS Resource. */ 35 | char* awsSecKey; 36 | /* The user's AWS Access Key ID for accessing the AWS Resource. */ 37 | char* awsKeyID; 38 | /* GMT date in yyyyMMdd format. */ 39 | char awsDate[AWS_DATE_LEN4 + 1]; 40 | /* GMT time in HHmmss format. */ 41 | char awsTime[AWS_TIME_LEN4 + 1]; 42 | 43 | /* The payload of the httprequest to be created */ 44 | MinimalString payload; 45 | 46 | // /* Add the headers that will be signed to the headers array. Called before 47 | // * createStringToSign. */ 48 | // void initSignedHeaders(); 49 | // /* Create the canonical request and the string to sign as described. Return 50 | // * value must be deleted by caller. */ 51 | char* createStringToSign(char* canonical_request); 52 | // /* Given the string to sign, create the signature (a 64-char cstring). 53 | // * Return value must be deleted by caller. */ 54 | char* createSignature(const char* toSign); 55 | char* createRequestHeaders(char* signature); 56 | // /* Add the headers that will not be signed to the headers array. Called 57 | // * after createSignature. */ 58 | // void initUnsignedHeaders(const char* signature); 59 | // /* Contains all of the work to be done before headersToRequest or 60 | // * headersToCurlRequest are called. Takes the payload to be sent and the 61 | // * GMT date in yyyyMMddHHmmss format. */ 62 | // void createRequestInit(MinimalString &reqPayload); 63 | // /* Clean up after headersToRequest or headersToCurlRequest are called. */ 64 | // void createRequestCleanup(); 65 | // /* Using the headers array, create a raw http request. */ 66 | // char* headersToRequest(void); 67 | 68 | protected: 69 | char* method; 70 | char* uri; 71 | char* queryString; 72 | char* headers; 73 | char* signedHeaders; 74 | char* payloadHash; 75 | 76 | /* Used to keep track of time. */ 77 | IDateTimeProvider* dateTimeProvider; 78 | /* Used to send http to the server. */ 79 | IHttpClient* httpClient; 80 | /* true if https is to be used, false if http is to be used. */ 81 | bool httpS; 82 | /* Name of service, eg. "kinesis" in "kinesis.us-east-1.amazonaws.com". */ 83 | const char* awsService; 84 | /* Content type of payload, eg. "application/x-amz-json-1.1". */ 85 | const char* contentType; 86 | // /* Generates the host based on subdomain, service, etc */ 87 | char* createHost(void); 88 | /* Creates a raw http request, given the payload and current GMT date in 89 | * yyyyMMddHHmmss format. Should be exposed to user by extending class. 90 | * Returns 0 if client is unititialized. */ 91 | char* createRequest(MinimalString &payload); 92 | char* createCanonicalRequest(); 93 | char* createCanonicalHeaders(); 94 | 95 | /* Sends http data. Returns http response, or null on error. */ 96 | char* sendData(const char* data); 97 | /* Empty constructor. Must also be initialized with init. */ 98 | AWSClient4(); 99 | 100 | public: 101 | /* Setters for values used by createRequest and createCurlRequest. Must 102 | * be set or create[Curl]Request will return null. */ 103 | /* Generates the host based on subdomain, service, etc */ 104 | char* createHostString(void); 105 | void setAWSRegion(const char * awsRegion); 106 | void setAWSEndpoint(const char * awsEndpoint); 107 | void setAWSDomain(const char * awsDomain); 108 | void setAWSPath(const char * awsPath); 109 | void setAWSSecretKey(const char * awsSecKey); 110 | void setAWSKeyID(const char * awsKeyID); 111 | void setHttpClient(IHttpClient* httpClient); 112 | void setDateTimeProvider(IDateTimeProvider* dateTimeProvider); 113 | ~AWSClient4(void); 114 | }; 115 | 116 | #endif /* AWSCLIENT4_H_ */ 117 | -------------------------------------------------------------------------------- /src/common/AWSFoundationalTypes.cpp: -------------------------------------------------------------------------------- 1 | #include "AWSFoundationalTypes.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static const char* FALSE_AS_JSON = "\"false\""; 8 | static const char* TRUE_AS_JSON = "\"true\""; 9 | static const int DOUBLE_STR_LEN = 10; 10 | static const int LONG_STR_LEN = 10; 11 | static const int INT_STR_LEN = 10; 12 | 13 | void MinimalString::innerCopy(const MinimalString &toCopy) { 14 | alreadySerialized = toCopy.getAlreadySerialized(); 15 | const char* toCopyCStr = toCopy.getCStr(); 16 | if (toCopyCStr == NULL) { 17 | cStr = NULL; 18 | } else { 19 | cStr = new char[strlen(toCopyCStr) + 1](); 20 | strcpy(cStr, toCopyCStr); 21 | } 22 | } 23 | 24 | void MinimalString::innerDelete() { 25 | if (cStr != NULL) { 26 | delete[] cStr; 27 | } 28 | } 29 | 30 | MinimalString::MinimalString(const char* cStr) { 31 | alreadySerialized = false; 32 | this->cStr = new char[strlen(cStr) + 1](); 33 | strcpy(this->cStr, cStr); 34 | } 35 | 36 | MinimalString::MinimalString(const char* cStr, int len) { 37 | alreadySerialized = false; 38 | this->cStr = new char[len + 1](); 39 | strncpy(this->cStr, cStr, len); 40 | } 41 | 42 | MinimalString::MinimalString() { 43 | alreadySerialized = false; 44 | this->cStr = NULL; 45 | } 46 | 47 | MinimalString::MinimalString(const MinimalString &toCopy) { 48 | innerCopy(toCopy); 49 | } 50 | 51 | const char* MinimalString::getCStr() const { 52 | return cStr; 53 | } 54 | 55 | MinimalString& MinimalString::operator=(const MinimalString &toCopy) { 56 | /* Make sure we are not leaking memory by overwriting a value. */ 57 | innerDelete(); 58 | innerCopy(toCopy); 59 | return *this; 60 | } 61 | 62 | void MinimalString::setAlreadySerialized(bool alreadySerialized) { 63 | this->alreadySerialized = alreadySerialized; 64 | } 65 | 66 | bool MinimalString::getAlreadySerialized() const { 67 | return alreadySerialized; 68 | } 69 | 70 | MinimalString MinimalString::jsonSerialize() const { 71 | if (alreadySerialized) { 72 | return *this; 73 | } 74 | if (cStr == NULL) { 75 | /* Empty quoted string. */ 76 | return MinimalString((char*) "\"\""); 77 | } else { 78 | /* +3 for the 2 quotes and the null termination character. */ 79 | char* json = new char[strlen(cStr) + 3](); 80 | sprintf(json, "\"%s\"", cStr); 81 | MinimalString jsonMinStr(json); 82 | delete json; 83 | return jsonMinStr; 84 | } 85 | } 86 | 87 | bool MinimalString::jsonDeserialize(MinimalString json) { 88 | innerDelete(); 89 | const char* jsonCStr = json.getCStr(); 90 | if (jsonCStr == NULL) { 91 | return false; 92 | } 93 | int jsonLen = json.length(); 94 | /* Only deserialize if the first and last characters are quotes. */ 95 | if ((jsonCStr[0] == '"') && (jsonCStr[jsonLen - 1] == '"')) { 96 | /* -2 for the removed quote, but +1 for the null termination 97 | * character. */ 98 | cStr = new char[jsonLen - 1](); 99 | /* Copy all but the first and last characters */ 100 | strncpy(cStr, jsonCStr + 1, jsonLen - 2); 101 | } else { 102 | return false; 103 | } 104 | return true; 105 | } 106 | 107 | int MinimalString::length() const { 108 | if (cStr == NULL) { 109 | return 0; 110 | } else { 111 | return strlen(cStr); 112 | } 113 | } 114 | 115 | MinimalString::~MinimalString() { 116 | innerDelete(); 117 | } 118 | 119 | SerializableDouble::SerializableDouble(double d) { 120 | this->d = d; 121 | } 122 | SerializableDouble::SerializableDouble() { 123 | } 124 | 125 | double SerializableDouble::getValue() const { 126 | return d; 127 | } 128 | 129 | MinimalString SerializableDouble::jsonSerialize() const { 130 | return jsonSerializeDouble(d); 131 | } 132 | 133 | bool SerializableDouble::jsonDeserialize(MinimalString json) { 134 | d = atof(json.getCStr()); 135 | /* No error checking for this deserialize method, always return success. */ 136 | return true; 137 | } 138 | 139 | MinimalString jsonCommaConcatenate(MinimalList list, 140 | char openChar, char closeChar) { 141 | int arrayLen = list.getLength(); 142 | const MinimalString* array = list.getArray(); 143 | MinimalString* jsonSerializationsArray = new MinimalString[arrayLen](); 144 | /* Count the length of the string to allocate and create array of 145 | * serialized char*s: 146 | * There are arrayLen - 1 separating commas and 1 openChar and 1 closeChar. 147 | * fullSerializationLen = arrayLen - 1 + 2 + (lengths of all 148 | * json-serialized elements) = arraylen + 1 + (lengths of all 149 | * json-serialized elements). */ 150 | int fullSerializationLen = arrayLen + 1; 151 | for (int i = 0; i < arrayLen; i++) { 152 | /* If quote, use teh jsonSerialized string, which simply puts quotes 153 | * around it. Otherwise use the string as is. */ 154 | jsonSerializationsArray[i] = array[i]; 155 | fullSerializationLen += jsonSerializationsArray[i].length(); 156 | } 157 | /* Create the array-syntax serialization of all inner serializations. */ 158 | char* fullSerialization = new char[fullSerializationLen + 1](); 159 | int fullSerializationWritten = 0; 160 | fullSerialization[fullSerializationWritten++] = openChar; 161 | for (int i = 0; i < arrayLen; i++) { 162 | /* Write the inner serialization to the full serialization and delete 163 | * the no longer needed inner serialization. */ 164 | fullSerializationWritten += sprintf( 165 | fullSerialization + fullSerializationWritten, "%s", 166 | jsonSerializationsArray[i].getCStr()); 167 | /* Comma after all but last element. */ 168 | if (i != arrayLen - 1) { 169 | fullSerialization[fullSerializationWritten++] = ','; 170 | } 171 | } 172 | delete[] jsonSerializationsArray; 173 | fullSerialization[fullSerializationWritten++] = closeChar; 174 | MinimalString jsonString(fullSerialization); 175 | delete[] fullSerialization; 176 | return jsonString; 177 | } 178 | 179 | MinimalList jsonCommaSeparate(MinimalString jsonList, 180 | char openChar, char closeChar) { 181 | /* Incemented for every opening bracket and decremented for every closing 182 | * bracket. */ 183 | int bracketLevel = 0; 184 | /* Incemented for every opening brace and decremented for every closing 185 | * brace. */ 186 | int braceLevel = 0; 187 | /* True when looking at characters within quotes. */ 188 | bool inQuotes = false; 189 | int jsonLen = jsonList.length(); 190 | 191 | if ((jsonLen <= 2) || (jsonList.getCStr()[0] != openChar) 192 | || (jsonList.getCStr()[jsonLen - 1] != closeChar)) { 193 | /* Missing open/close character or empty list, return empty list. */ 194 | MinimalList emptyList(0, 0); 195 | return emptyList; 196 | } 197 | 198 | /* Number of elements in the list, determined as the number of 199 | * unquoted/unbraced/unbracketed commas found plus one. */ 200 | int elementCount = 1; 201 | /* This for loop determines elementCount. */ 202 | for (int i = 1; i < jsonLen - 1; i++) { 203 | switch (jsonList.getCStr()[i]) { 204 | case '"': 205 | /* Toggle inQuotes. */ 206 | inQuotes = !inQuotes; 207 | break; 208 | /* increment and decrement for unquoted brackets and braces. */ 209 | case '[': 210 | if (!inQuotes) { 211 | bracketLevel++; 212 | } 213 | break; 214 | case ']': 215 | if (!inQuotes) { 216 | bracketLevel--; 217 | } 218 | break; 219 | case '{': 220 | if (!inQuotes) { 221 | braceLevel++; 222 | } 223 | break; 224 | case '}': 225 | if (!inQuotes) { 226 | braceLevel--; 227 | } 228 | break; 229 | case ',': 230 | /* If we are at the opening level, and unquoted, and have 231 | * encountered a comma, it is a element separator. */ 232 | if ((braceLevel == 0) && (bracketLevel == 0) && !inQuotes) { 233 | elementCount++; 234 | } 235 | } 236 | } 237 | /* Array of elements. */ 238 | MinimalString* elements = new MinimalString[elementCount](); 239 | /* Number of elements created / next index of elements array to write 240 | * to. */ 241 | int elementsCreated = 0; 242 | /* The start index for the current element being coppied. */ 243 | int elementStartIdx = 1; 244 | /* This for loop fills the elements array. */ 245 | for (int i = 1; i < jsonLen - 1; i++) { 246 | switch (jsonList.getCStr()[i]) { 247 | case '"': 248 | /* Toggle inQuotes. */ 249 | inQuotes = !inQuotes; 250 | break; 251 | /* increment and decrement for unquoted brackets and braces. */ 252 | case '[': 253 | if (!inQuotes) { 254 | bracketLevel++; 255 | } 256 | break; 257 | case ']': 258 | if (!inQuotes) { 259 | bracketLevel--; 260 | } 261 | break; 262 | case '{': 263 | if (!inQuotes) { 264 | braceLevel++; 265 | } 266 | break; 267 | case '}': 268 | if (!inQuotes) { 269 | braceLevel--; 270 | } 271 | break; 272 | case ',': 273 | /* If we are at the opening level, and unquoted, and have 274 | * encountered a comma, it is a element separator. */ 275 | if ((braceLevel == 0) && (bracketLevel == 0) && !inQuotes) { 276 | /* Copy over the string from the elementStartIdx (inclusive) to 277 | * i (exclusive). */ 278 | elements[elementsCreated] = MinimalString( 279 | jsonList.getCStr() + elementStartIdx, 280 | i - elementStartIdx); 281 | 282 | elementsCreated++; 283 | /* The start of the next element is the index after the index 284 | * of this comma. */ 285 | elementStartIdx = i + 1; 286 | } 287 | } 288 | } 289 | /* Copy over the last element, which does not end with a comma. */ 290 | elements[elementsCreated] = MinimalString( 291 | jsonList.getCStr() + elementStartIdx, 292 | (jsonList.length() - 1) - elementStartIdx); 293 | 294 | MinimalList elementsList(elements, elementCount); 295 | delete[] elements; 296 | return elementsList; 297 | } 298 | 299 | MinimalString jsonSerializeBool(bool b) { 300 | return b ? MinimalString(TRUE_AS_JSON) : MinimalString(FALSE_AS_JSON); 301 | } 302 | 303 | bool jsonDeserializeBool(MinimalString json) { 304 | return !strcmp("true", json.getCStr()); 305 | } 306 | 307 | int jsonDeserializeEnum(const char** enumLookup, int enumSize, 308 | MinimalString json) { 309 | MinimalString serialized = json.jsonSerialize(); 310 | const char* expected = serialized.getCStr(); 311 | for (int i = 0; i < enumSize; i++) { 312 | if (!strcmp(enumLookup[i], expected)) { 313 | return i; 314 | } 315 | } 316 | return -1; 317 | } 318 | 319 | MinimalString jsonSerializeDouble(double d) { 320 | char buffer[DOUBLE_STR_LEN + 1]; 321 | snprintf(buffer, DOUBLE_STR_LEN, "%f", d); 322 | return MinimalString(buffer); 323 | } 324 | 325 | MinimalString jsonSerializeLong(long l) { 326 | char buffer[LONG_STR_LEN + 1]; 327 | snprintf(buffer, LONG_STR_LEN, "%ld", l); 328 | return MinimalString(buffer); 329 | } 330 | 331 | MinimalString jsonSerializeInt(int i) { 332 | char buffer[INT_STR_LEN + 1]; 333 | snprintf(buffer, INT_STR_LEN, "%d", i); 334 | return MinimalString(buffer); 335 | } 336 | 337 | -------------------------------------------------------------------------------- /src/common/AmazonIOTClient.cpp: -------------------------------------------------------------------------------- 1 | #include "AmazonIOTClient.h" 2 | #include "AWSFoundationalTypes.h" 3 | #include 4 | #include "Utils.h" 5 | 6 | AmazonIOTClient::AmazonIOTClient() : AWSClient4() { 7 | this->awsService = "iotdata"; 8 | this->contentType = "application/json"; 9 | this->signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date"; 10 | this->uri = "/"; 11 | this->queryString = ""; 12 | this->httpS = true; 13 | } 14 | 15 | char* AmazonIOTClient::update_shadow(MinimalString shadow, ActionError& actionError) { 16 | actionError = NONE_ACTIONERROR; 17 | 18 | this->method = "POST"; 19 | char* request = createRequest(shadow); 20 | // return request; 21 | char* response = sendData(request); 22 | return response; 23 | } 24 | -------------------------------------------------------------------------------- /src/common/AmazonIOTClient.h: -------------------------------------------------------------------------------- 1 | #ifndef AMAZONIOTCLIENT_H_ 2 | #define AMAZONIOTCLIENT_H_ 3 | #include "AWSClient4.h" 4 | 5 | 6 | // class Shadow { 7 | // MinimalString shadow; 8 | // void reset(); 9 | // public: 10 | // void setShadow(shadow) const; 11 | // }; 12 | 13 | 14 | 15 | class AmazonIOTClient : public AWSClient4 { 16 | public: 17 | AmazonIOTClient(); 18 | 19 | char* update_shadow(MinimalString shadow, ActionError& actionError); 20 | }; 21 | 22 | #endif /* AMAZONIOTCLIENT_H_ */ 23 | -------------------------------------------------------------------------------- /src/common/AmazonS3Client.cpp: -------------------------------------------------------------------------------- 1 | #include "AmazonS3Client.h" 2 | #include "AWSFoundationalTypes.h" 3 | #include 4 | #include "Utils.h" 5 | 6 | // stub for verifying if the signature actually works, needs to get some more love :) 7 | 8 | AmazonS3Client::AmazonS3Client() : AWSClient4() { 9 | this->awsService = "s3"; 10 | this->signedHeaders = "host;range;x-amz-content-sha256;x-amz-date"; 11 | this->queryString = ""; 12 | this->httpS = true; 13 | } 14 | 15 | char* AmazonS3Client::get(MinimalString uri, ActionError& actionError) { 16 | actionError = NONE_ACTIONERROR; 17 | 18 | this->method = "GET"; 19 | setAWSPath(uri.getCStr()); 20 | MinimalString foo = ""; 21 | char* request = createRequest(foo); 22 | // char* response = sendData(request); 23 | return request; 24 | } 25 | -------------------------------------------------------------------------------- /src/common/AmazonS3Client.h: -------------------------------------------------------------------------------- 1 | #ifndef AMAZONS3CLIENT_H_ 2 | #define AMAZONS3CLIENT_H_ 3 | #include "AWSClient4.h" 4 | 5 | class AmazonS3Client : public AWSClient4 { 6 | public: 7 | AmazonS3Client(); 8 | char* get(MinimalString uri, ActionError& actionError); 9 | }; 10 | 11 | #endif /* AMAZONS3CLIENT_H_ */ 12 | -------------------------------------------------------------------------------- /src/common/AmazonSNSClient.cpp: -------------------------------------------------------------------------------- 1 | #include "AmazonSNSClient.h" 2 | #include "AWSFoundationalTypes.h" 3 | #include 4 | #include "Utils.h" 5 | 6 | static const char* SERVICE = "sns"; 7 | static const char* FORM_TYPE = "application/x-www-form-urlencoded"; 8 | static const char* PAYLOAD_TEMPLATE = "Action=Publish&TargetArn=%s&Message=%s&Version=2010-03-31\n"; 9 | int PAYLOAD_TEMPLATE_LENGTH = 54; 10 | int MESSAGEID_BUFFER_LENGTH = 37; 11 | int EXTRACTED_TIMESTAMP_BUFFER_LENGTH = 17; 12 | int FORMATTED_TIMESTAMP_BUFFER_LENGTH = 15; 13 | 14 | PublishInput::PublishInput() { 15 | reset(); 16 | } 17 | 18 | void PublishInput::reset() { 19 | targetArnBeenSet = false; 20 | messageBeenSet = false; 21 | } 22 | 23 | bool PublishInput::requiredAreSet() const { 24 | return targetArnBeenSet && messageBeenSet; 25 | } 26 | 27 | void PublishInput::setTargetArn(MinimalString targetArn) { 28 | targetArnBeenSet = true; 29 | this->targetArn = targetArn; 30 | } 31 | 32 | void PublishInput::setMessage(MinimalString message) { 33 | messageBeenSet = true; 34 | this->message = message; 35 | } 36 | 37 | MinimalString PublishInput::getTargetArn() const { 38 | return this->targetArn; 39 | } 40 | 41 | MinimalString PublishInput::getMessage() const { 42 | return this->message; 43 | } 44 | 45 | MinimalString PublishInput::serialize() const { 46 | char* payload = new char[PAYLOAD_TEMPLATE_LENGTH + message.length() + targetArn.length() + 1](); 47 | 48 | sprintf(payload, PAYLOAD_TEMPLATE, targetArn.getCStr(), message.getCStr()); 49 | 50 | return MinimalString(payload); 51 | } 52 | 53 | PublishOutput::PublishOutput() { 54 | reset(); 55 | } 56 | 57 | void PublishOutput::reset() { 58 | messageIdBeenSet = false; 59 | 60 | errorType = MinimalString(); 61 | errorMessage = MinimalString(); 62 | } 63 | 64 | void PublishOutput::setMessageId(MinimalString messageId) { 65 | messageIdBeenSet = true; 66 | this->messageId = messageId; 67 | } 68 | 69 | MinimalString PublishOutput::getMessageId() const { 70 | return this->messageId; 71 | } 72 | 73 | MinimalString PublishOutput::getErrorType() const { 74 | return errorType; 75 | } 76 | 77 | MinimalString PublishOutput::getErrorMessage() const { 78 | return errorMessage; 79 | } 80 | 81 | AmazonSNSClient::AmazonSNSClient() : AWSClient2() { 82 | awsService = SERVICE; 83 | } 84 | 85 | PublishOutput AmazonSNSClient::publish(PublishInput publishInput, ActionError& actionError) { 86 | actionError = NONE_ACTIONERROR; 87 | 88 | PublishOutput publishOutput; 89 | 90 | if (!publishInput.requiredAreSet()) { 91 | actionError = MISSING_REQUIRED_ARGS_ACTIONERROR; 92 | return publishOutput; 93 | } 94 | 95 | contentType = FORM_TYPE; 96 | MinimalString payload = publishInput.serialize(); 97 | 98 | char* request = createRequest(payload); 99 | char* response = sendData(request); 100 | delete[] request; 101 | 102 | if (response == NULL) { 103 | actionError = CONNECTION_ACTIONERROR; 104 | return publishOutput; 105 | } 106 | 107 | int httpStatusCode = findHttpStatusCode(response); 108 | 109 | if (httpStatusCode == 200) { 110 | char* msgidIdx = strstr(response, ""); 111 | int msgidPos = msgidIdx - response; 112 | 113 | char* msgid = new char[MESSAGEID_BUFFER_LENGTH](); 114 | strncpy(msgid, response + msgidPos + 11, MESSAGEID_BUFFER_LENGTH - 1); 115 | msgid[37] = '\0'; 116 | 117 | publishOutput.setMessageId(msgid); 118 | return publishOutput; 119 | } 120 | 121 | if (httpStatusCode == 403) { 122 | char* ts = strstr(response, "earlier than "); 123 | int pos = ts - response; 124 | 125 | char* newts = new char[EXTRACTED_TIMESTAMP_BUFFER_LENGTH](); 126 | strncpy(newts, response + pos + 31, EXTRACTED_TIMESTAMP_BUFFER_LENGTH - 1); 127 | newts[16] = '\0'; 128 | 129 | char* time = new char[FORMATTED_TIMESTAMP_BUFFER_LENGTH](); 130 | sprintf(time, "%.8s%.6s", newts, newts + 9); 131 | dateTimeProvider->sync(time); 132 | } 133 | 134 | return publishOutput; 135 | } 136 | -------------------------------------------------------------------------------- /src/common/AmazonSNSClient.h: -------------------------------------------------------------------------------- 1 | #ifndef AMAZONSNSCLIENT_H_ 2 | #define AMAZONSNSCLIENT_H_ 3 | #include "AWSFoundationalTypes.h" 4 | #include "AWSClient2.h" 5 | 6 | class PublishInput { 7 | MinimalString targetArn; 8 | MinimalString message; 9 | bool targetArnBeenSet; 10 | bool messageBeenSet; 11 | void reset(); 12 | public: 13 | PublishInput(); 14 | bool requiredAreSet() const; 15 | void setMessage(MinimalString message); 16 | void setTargetArn(MinimalString targetArn); 17 | MinimalString getMessage() const; 18 | MinimalString getTargetArn() const; 19 | MinimalString serialize() const; 20 | }; 21 | 22 | class PublishOutput { 23 | MinimalString messageId; 24 | bool messageIdBeenSet; 25 | MinimalString errorType; 26 | MinimalString errorMessage; 27 | void reset(); 28 | public: 29 | PublishOutput(); 30 | MinimalString getErrorType() const; 31 | MinimalString getErrorMessage() const; 32 | void setMessageId(MinimalString messageId); 33 | MinimalString getMessageId() const; 34 | }; 35 | 36 | class AmazonSNSClient : public AWSClient2 { 37 | public: 38 | AmazonSNSClient(); 39 | PublishOutput publish(PublishInput input, ActionError& actionError); 40 | }; 41 | 42 | #endif /* AMAZONSNSCLIENT_H_ */ 43 | -------------------------------------------------------------------------------- /src/common/DeviceIndependentInterfaces.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceIndependentInterfaces.h" 2 | 3 | IHttpClient::~IHttpClient() { 4 | } 5 | IDateTimeProvider::~IDateTimeProvider() { 6 | } 7 | 8 | -------------------------------------------------------------------------------- /src/common/DeviceIndependentInterfaces.h: -------------------------------------------------------------------------------- 1 | #ifndef AWSDEVICEINDEPENDENTINTERFACES_H_ 2 | #define AWSDEVICEINDEPENDENTINTERFACES_H_ 3 | 4 | /* HTTPClient Interface. Wraps the functionality of the existing TCPClient, and 5 | * EthernetClient for Spark Core and Intel Galileo Respectively. */ 6 | class IHttpClient { 7 | public: 8 | virtual ~IHttpClient(); 9 | /* Send http request and return the response. */ 10 | virtual char* send(const char *request, const char* serverUrl, 11 | int port) = 0; 12 | /* Returns true if the client uses a curl command, false if the client uses 13 | * raw http/https. */ 14 | virtual bool usesCurl(void) = 0; 15 | }; 16 | 17 | /* Interface for setting and retrieving the current time. Required for sigv4 18 | * signing. */ 19 | class IDateTimeProvider { 20 | public: 21 | virtual ~IDateTimeProvider(); 22 | /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. */ 23 | virtual const char* getDateTime(void) = 0; 24 | /* Return true if the sync function requires the current time as in 25 | * argument. */ 26 | virtual bool syncTakesArg(void) = 0; 27 | /* Called if AWS Service reports in accurate time. Sets the provider to 28 | * current time. If syncTakesArg() returns true, this argument takes the 29 | * current GMT date and time in yyyyMMddHHmmss format. Else, the nput value 30 | * is ignored and may be null. */ 31 | virtual void sync(const char* dateTime) = 0; 32 | }; 33 | 34 | #endif /* AWSDEVICEINDEPENDENTINTERFACES_H_ */ 35 | -------------------------------------------------------------------------------- /src/common/ESP8266AWSImplementations.h: -------------------------------------------------------------------------------- 1 | /Users/svdgraaf/Documents/Arduino/Libraries/aws-sdk-arduino/src/esp8266/ESP8266AWSImplementations.h -------------------------------------------------------------------------------- /src/common/ESP8266AWSImplentations.cpp: -------------------------------------------------------------------------------- 1 | /Users/svdgraaf/Documents/Arduino/Libraries/aws-sdk-arduino/src/esp8266/ESP8266AWSImplentations.cpp -------------------------------------------------------------------------------- /src/common/Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Utils.cpp 3 | * 4 | * See Utils.h for description. 5 | * 6 | * Created on: Jun 30, 2014 7 | * Author: hoffmaj 8 | */ 9 | 10 | #include "Utils.h" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "jsmn.h" 16 | #include "sha256.h" 17 | 18 | /* Constants for base64Encode. */ 19 | static const char ENCODE_CHARS[] = 20 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 21 | static const char PAD_CHAR = '='; 22 | 23 | /* Constants for findHttpStatusCode. */ 24 | static const char HTTP_STATUS_CODE_PREFIX[] = "HTTP/1.1 "; 25 | static const int HTTP_STATUS_CODE_LEN = 3; 26 | 27 | /* Constants for hmacSha256. */ 28 | static const int BLOCK_SIZE = 64; 29 | static const char OPAD = 0x5c; 30 | static const char IPAD = 0x36; 31 | const int SHA256_DEC_HASH_LEN = 32; 32 | 33 | char *base64Encode(const char *toEncode) { 34 | int inLen = strlen(toEncode); 35 | /* For every three input chars there are 4 output chars, plus an extra 4 36 | * output chars for the possible 1 or 2 remaining input chars, plus an 37 | * extra byte for null termination. */ 38 | size_t encodedLen = (((inLen / 3) + (inLen % 3 > 0)) * 4) + 1; 39 | 40 | char *encoded = new char[encodedLen](); 41 | int chunkIdx; 42 | char inChar1; 43 | char inChar2; 44 | char inChar3; 45 | int outIdx1; 46 | int outIdx2; 47 | int outIdx3; 48 | int outIdx4; 49 | 50 | for (chunkIdx = 0; chunkIdx < inLen / 3; chunkIdx++) { 51 | /* This approach of treating each character individually instead of 52 | * containing all bits in a long type allows the encoding to work on 8, 53 | * 16, 32, and 64 bit systems. */ 54 | inChar1 = *toEncode++; 55 | inChar2 = *toEncode++; 56 | inChar3 = *toEncode++; 57 | 58 | outIdx1 = (inChar1 & 0xFC) >> 2; 59 | outIdx2 = ((inChar1 & 0x03) << 4) + ((inChar2 & 0xF0) >> 4); 60 | outIdx3 = ((inChar2 & 0x0F) << 2) + ((inChar3 & 0xC0) >> 6); 61 | outIdx4 = inChar3 & 0x3F; 62 | 63 | encoded[chunkIdx * 4] = ENCODE_CHARS[outIdx1]; 64 | encoded[chunkIdx * 4 + 1] = ENCODE_CHARS[outIdx2]; 65 | encoded[chunkIdx * 4 + 2] = ENCODE_CHARS[outIdx3]; 66 | encoded[chunkIdx * 4 + 3] = ENCODE_CHARS[outIdx4]; 67 | } 68 | 69 | switch (inLen % 3) { 70 | case 1: 71 | /* 1 extra input char -> 2 output chars and 2 padding chars. */ 72 | 73 | inChar1 = *toEncode++; 74 | 75 | outIdx1 = (inChar1 & 0xFC) >> 2; 76 | outIdx2 = (inChar1 & 0x03) << 4; 77 | 78 | encoded[chunkIdx * 4] = ENCODE_CHARS[outIdx1]; 79 | encoded[chunkIdx * 4 + 1] = ENCODE_CHARS[outIdx2]; 80 | encoded[chunkIdx * 4 + 2] = PAD_CHAR; 81 | encoded[chunkIdx * 4 + 3] = PAD_CHAR; 82 | chunkIdx++; 83 | break; 84 | case 2: 85 | /* 2 extra input chars -> 3 output chars and 1 padding char. */ 86 | 87 | inChar1 = *toEncode++; 88 | inChar2 = *toEncode++; 89 | outIdx1 = (inChar1 & 0xFC) >> 2; 90 | outIdx2 = ((inChar1 & 0x03) << 4) + ((inChar2 & 0xF0) >> 4); 91 | outIdx3 = ((inChar2 & 0x0F) << 2); 92 | encoded[chunkIdx * 4] = ENCODE_CHARS[outIdx1]; 93 | encoded[chunkIdx * 4 + 1] = ENCODE_CHARS[outIdx2]; 94 | encoded[chunkIdx * 4 + 2] = ENCODE_CHARS[outIdx3]; 95 | encoded[chunkIdx * 4 + 3] = PAD_CHAR; 96 | chunkIdx++; 97 | break; 98 | } 99 | /* Ensure null termination. */ 100 | encoded[chunkIdx * 4] = 0; 101 | 102 | return encoded; 103 | } 104 | 105 | int digitCount(int i) { 106 | int digits; 107 | for (digits = 0; i != 0; digits++) 108 | i /= 10; 109 | return digits; 110 | } 111 | 112 | char* escapeQuotes(const char* unescaped) { 113 | int unescapedLen = strlen(unescaped); 114 | 115 | /* Count quotes so that the amount of memory to be allocated can be 116 | * determined */ 117 | int quoteCount = 0; 118 | for (int i = 0; i < unescapedLen; i++) { 119 | if (unescaped[i] == '\"') { 120 | quoteCount++; 121 | } 122 | } 123 | 124 | /* Copy ever character over, including a backslash before every quote. */ 125 | char* escaped = new char[unescapedLen + quoteCount + 1](); 126 | int escapedWritten = 0; 127 | for (int i = 0; i < unescapedLen; i++) { 128 | if (unescaped[i] == '\"') { 129 | escaped[escapedWritten] = '\\'; 130 | escapedWritten++; 131 | } 132 | escaped[escapedWritten] = unescaped[i]; 133 | escapedWritten++; 134 | } 135 | return escaped; 136 | } 137 | 138 | bool findJsonStartEnd(const char* str, int* start, int* end) { 139 | /* Ignore everything before the first unquoted bracket and after the 140 | * unquoted bracket matching the first unquoted bracket, eg. headers and 141 | * newlines. */ 142 | int strLen = strlen(str); 143 | /* Incrememented for every unquoted '{' and decremented for every 144 | * unquoted '}' */ 145 | int braceBalance = 0; 146 | /* -1 is invalid start and end indices. */ 147 | int s = -1; 148 | int e = -1; 149 | bool inQuotes = false; 150 | for (int i = 0; i < strLen; i++) { 151 | /* Toggle inQuotes. */ 152 | if (str[i] == '\"') { 153 | inQuotes = !inQuotes; 154 | } 155 | /* Only consider brackets outside of quotes. */ 156 | if (!inQuotes) { 157 | if (str[i] == '{') { 158 | if (s == -1) { 159 | s = i; 160 | } 161 | braceBalance++; 162 | } else if (str[i] == '}') { 163 | braceBalance--; 164 | if (braceBalance == 0) { 165 | e = i; 166 | break; 167 | } 168 | } 169 | } 170 | } 171 | *start = s; 172 | *end = e; 173 | /* If not a full json string no success. */ 174 | if ((s == -1) || (e == -1)) { 175 | return false; 176 | } 177 | return true; 178 | } 179 | 180 | int findHttpStatusCode(const char* str) { 181 | /* If the input is null OR the input is not long enough to contain the 182 | * error code OR the first characters of the input are not 183 | * HTTP_STATUS_CODE_PREFIX, return 0; */ 184 | if (str == NULL 185 | || strlen(str) 186 | < strlen(HTTP_STATUS_CODE_PREFIX) + HTTP_STATUS_CODE_LEN 187 | || strncmp(HTTP_STATUS_CODE_PREFIX, str, 188 | strlen(HTTP_STATUS_CODE_PREFIX))) { 189 | return 0; 190 | } 191 | /* copy the error code string and convert it to an int. */ 192 | char errorCodeStr[HTTP_STATUS_CODE_LEN + 1]; 193 | strncpy(errorCodeStr, str + strlen(HTTP_STATUS_CODE_PREFIX), 194 | HTTP_STATUS_CODE_LEN); 195 | return atoi(errorCodeStr); 196 | } 197 | 198 | int jsonArraySize(const char* jsonArrayStr, int jsonArrayStrLen) { 199 | int elementCount = 0; 200 | bool inQuotes = false; 201 | if (jsonArrayStr[0] != '[' || jsonArrayStr[jsonArrayStrLen - 1] != ']') { 202 | /* Invalid syntax. */ 203 | return -1; 204 | } 205 | for (int i = 1; i < jsonArrayStrLen - 1; i++) { 206 | if (jsonArrayStr[i] == '"' && jsonArrayStr[i - 1] != '\\') { 207 | if (inQuotes) { 208 | elementCount++; 209 | } 210 | inQuotes = !inQuotes; 211 | } 212 | } 213 | return elementCount; 214 | } 215 | 216 | char** jsonArrayToStringArray(int numOfElements, const char* jsonArrayStr, 217 | int jsonArrayStrLen) { 218 | char** strArray = new char*[numOfElements](); 219 | int start = -1; 220 | int elementCount = 0; 221 | bool inQuotes = false; 222 | if (jsonArrayStr[0] != '[' || jsonArrayStr[jsonArrayStrLen - 1] != ']') { 223 | /* Invalid syntax. */ 224 | return 0; 225 | } 226 | for (int i = 1; i < jsonArrayStrLen - 1; i++) { 227 | if (jsonArrayStr[i] == '"' && jsonArrayStr[i - 1] != '\\') { 228 | if (inQuotes) { 229 | /* Delete values and return null if we find more elements than 230 | * expected. */ 231 | if (elementCount == numOfElements) { 232 | for (int j = 0; j < numOfElements; j++) { 233 | delete[] strArray[j]; 234 | } 235 | delete[] strArray; 236 | return 0; 237 | } 238 | 239 | char* str = new char[(i - start) + 1](); 240 | strncpy(str, jsonArrayStr + start, i - start); 241 | strArray[elementCount] = str; 242 | elementCount++; 243 | } else { 244 | start = i + 1; 245 | } 246 | inQuotes = !inQuotes; 247 | } 248 | } 249 | return strArray; 250 | } 251 | 252 | bool isKey(const char * json, int thisEnd, int nextStart) { 253 | /* Check the characters in between the two tokens. */ 254 | for (int i = thisEnd; i < nextStart; i++) { 255 | if (json[i] == ':') { 256 | /* A ':' means the token on the left is a key. */ 257 | return true; 258 | } else if (json[i] == ',') { 259 | /* A ',' means the token on the left is a value. */ 260 | return false; 261 | } 262 | } 263 | return false; 264 | } 265 | 266 | bool isOuterKey(const char * json, int thisEnd, int nextStart) { 267 | /* Check if token is a key at all before checking if it is an inner key. */ 268 | if (!isKey(json, thisEnd, nextStart)) { 269 | return false; 270 | } 271 | /* True when we are in a quoted section of a string. Ignore braces in a 272 | * quoted section. */ 273 | bool inQuote = false; 274 | /* The number of opening braces encountered minus the number of closing 275 | * braces encountered. braceLevel greater than 1 means we are looking at 276 | * inner keys. */ 277 | int braceLevel = 0; 278 | for (int i = 0; i < thisEnd; i++) { 279 | switch (json[i]) { 280 | case '"': 281 | /* Toggle inQuote. */ 282 | inQuote = !inQuote; 283 | break; 284 | case '{': 285 | /* If not in quotes, increase braceLevel. */ 286 | if (!inQuote) { 287 | braceLevel++; 288 | } 289 | break; 290 | case '}': 291 | /* If not in quotes, decrease braceLevel. */ 292 | if (!inQuote) { 293 | braceLevel--; 294 | } 295 | break; 296 | } 297 | } 298 | /* if we are at brace level 1 upon reaching, the key is in the outermost 299 | * json object */ 300 | return (braceLevel == 1); 301 | 302 | } 303 | 304 | char* jsmnGetVal(const char* key, const char* json, jsmntok_t* tokens, 305 | int tokenCount) { 306 | /* Look at all json tokens. */ 307 | for (int i = 0; i < tokenCount - 1; i++) { 308 | /* Check if token is an outer key. */ 309 | if (isOuterKey(json, tokens[i].end, tokens[i + 1].start)) { 310 | int currentKeyLen = tokens[i].end - tokens[i].start; 311 | int valueLen = tokens[i + 1].end - tokens[i + 1].start; 312 | /* Check if the key we are looking at is the key we are looking 313 | * for. */ 314 | if (((int) strlen(key) == currentKeyLen) 315 | && (strncmp(json + tokens[i].start, key, currentKeyLen) == 0)) { 316 | /* Copy and return the value */ 317 | char* value = new char[valueLen + 1](); 318 | strncpy(value, json + tokens[i + 1].start, valueLen); 319 | return value; 320 | } 321 | } 322 | } 323 | /* Key was not found. */ 324 | return NULL; 325 | } 326 | 327 | char* getTimeFromInvalidSignatureMessage(const char* message) { 328 | int messageLen = strlen(message); 329 | /* Iterate through each character in the string. */ 330 | for (int i = 0; i < messageLen; i++) { 331 | /* If an opening parenthesis is found, copy the following 15 332 | * characters, excluding the 9th character which is a 'T'.*/ 333 | if (message[i] == '(') { 334 | char* time = new char[15](); 335 | sprintf(time, "%.8s%.6s", message + i + 1, message + i + 10); 336 | return time; 337 | } 338 | } 339 | return 0; 340 | } 341 | 342 | char* hmacSha256(const char* key, int keyLen, const char* message, 343 | int messageLen) { 344 | SHA256* sha256 = new SHA256(); 345 | /* The corrected key should be BLOCK_SIZE long. */ 346 | char* correctedKey = new char[BLOCK_SIZE + 1](); 347 | /* If the key is greater than BLOCK_SIZE long, copy over its sha256 hash of 348 | * SHA256_DEC_HASH_LEN, leaving 0-padding to fill the entire BLOCK_SIZE. */ 349 | if ((int) keyLen > BLOCK_SIZE) { 350 | sha256->reset(); 351 | sha256->add(key, keyLen); 352 | char* hashedKey = sha256->getHashDec(); 353 | memcpy(correctedKey, hashedKey, SHA256_DEC_HASH_LEN); 354 | delete[] hashedKey; 355 | } 356 | /* if the key is less than BLOCK_SIZE long, simply copy it over, leaving 357 | * 0-padding to fill the entire BLOCK_SIZE. */ 358 | else { 359 | memcpy(correctedKey, key, keyLen); 360 | } 361 | 362 | /* Using an exclusive or with these OPAD and IPAD values to create the 363 | * iPadded and oPadded values specified by the HMAC algorithm. */ 364 | char* oPadded = new char[BLOCK_SIZE + 1](); 365 | char* iPadded = new char[BLOCK_SIZE + 1](); 366 | for (int i = 0; i < BLOCK_SIZE; i++) { 367 | oPadded[i] = correctedKey[i] ^ OPAD; 368 | iPadded[i] = correctedKey[i] ^ IPAD; 369 | } 370 | 371 | delete[] correctedKey; 372 | 373 | /* Create the inner hash with the concatenation of iPadded and message. */ 374 | sha256->reset(); 375 | sha256->add(iPadded, BLOCK_SIZE); 376 | delete[] iPadded; 377 | sha256->add(message, messageLen); 378 | char* innerHash = sha256->getHashDec(); 379 | 380 | /* Create the outer hash with the concatenation of oPadded and 381 | * innerhash. */ 382 | sha256->reset(); 383 | sha256->add(oPadded, BLOCK_SIZE); 384 | delete[] oPadded; 385 | sha256->add(innerHash, SHA256_DEC_HASH_LEN); 386 | delete[] innerHash; 387 | char* final = sha256->getHashDec(); 388 | delete sha256; 389 | return final; 390 | } 391 | 392 | -------------------------------------------------------------------------------- /src/common/Utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Utils.h 3 | * 4 | * Utility functions for the Arduino AWS SDK. 5 | * 6 | * Created on: Jun 30, 2014 7 | * Author: hoffmaj 8 | */ 9 | 10 | #ifndef UTILS_H_ 11 | #define UTILS_H_ 12 | 13 | #include 14 | #include "jsmn.h" 15 | 16 | extern const int SHA256_DEC_HASH_LEN; 17 | 18 | /* Base encode 64 an array of characters. Returned array must be deleted by 19 | * caller. */ 20 | char *base64Encode(const char *inputBuffer); 21 | 22 | /* Determine the number of digits in the base 10 representation a given 23 | * number. */ 24 | int digitCount(int i); 25 | 26 | /* Create a string identical to the given string except with backslashes before 27 | * quotes. Caller must delete the returned string. */ 28 | char* escapeQuotes(const char* unescaped); 29 | 30 | /* Determines the index of the opening and closing of a json string that is 31 | * surrounded by non-json, eg. http headers and newlines. Returns true if json 32 | * was found, false otherwise. */ 33 | bool findJsonStartEnd(const char* str, int* start, int* end); 34 | 35 | /* Find and return the status code of an http response. */ 36 | int findHttpStatusCode(const char* str); 37 | 38 | /* Given a char* of json array syntax ( e.g. ["a", "b", "c"]), count the number 39 | * of elements. */ 40 | int jsonArraySize(const char* jsonArrayStr, int jsonArrayStrLen); 41 | 42 | /* Given a char* of json array syntax ( e.g. ["a", "b", "c"]) and the number of 43 | * elements, create a matching array of char*'s. Caller must delete the created 44 | * array AND each element. */ 45 | char** jsonArrayToStringArray(int numOfElements, const char* jsonArrayStr, 46 | int jsonArrayStrLen); 47 | 48 | /* Determines whether a token is a json key, given the json string, the token's 49 | * end index, and the next token's start index. This includes keys of inner 50 | * json objects, i.e. both 'a' and 'b' would be a key in '{"a":{"b":1}}'. */ 51 | bool isKey(const char * json, int thisEnd, int nextStart); 52 | 53 | /* Determines whether a token is a json key, given the json string, the token's 54 | * end index, and the next token's start index. This does not include keys of 55 | * inner json objects, i.e. 'a' would be a key in '{"a":{"b":1}}', but 'b' 56 | * would not. */ 57 | bool isOuterKey(const char * json, int thisEnd, int nextStart); 58 | 59 | /* Given a json key, get a corresponding key. Takes the key, the json object 60 | * string, and the jsmn tokens. Returns 0 if key does not exist. If key does 61 | * exist, user must free the memory allocated to store the value. */ 62 | char* jsmnGetVal(const char* key, const char* json, jsmntok_t* tokens, 63 | int tokenCount); 64 | 65 | /* Very minimal implementation for finding the current time from the message 66 | * returned by a signature failure. This works with at least Kinesis and 67 | * DynamoDB, in which the time is located within the 15 characters following 68 | * the first opening parenthesis. (e.g. "...(20140721T184435Z ..." )*/ 69 | char* getTimeFromInvalidSignatureMessage(const char* message); 70 | 71 | /* Apply hmac to the key and message. */ 72 | char* hmacSha256(const char* key, int keyLen, const char* message, 73 | int messageLen); 74 | 75 | #endif /* UTILS_H_ */ 76 | -------------------------------------------------------------------------------- /src/common/jsmn.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Serge A. Zaitsev 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #include "jsmn.h" 26 | 27 | /** 28 | * Allocates a fresh unused token from the token pull. 29 | */ 30 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, 31 | size_t num_tokens) { 32 | jsmntok_t *tok; 33 | if (parser->toknext >= num_tokens) { 34 | return NULL; 35 | } 36 | tok = &tokens[parser->toknext++]; 37 | tok->start = tok->end = -1; 38 | tok->size = 0; 39 | #ifdef JSMN_PARENT_LINKS 40 | tok->parent = -1; 41 | #endif 42 | return tok; 43 | } 44 | 45 | /** 46 | * Fills token type and boundaries. 47 | */ 48 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, 49 | int end) { 50 | token->type = type; 51 | token->start = start; 52 | token->end = end; 53 | token->size = 0; 54 | } 55 | 56 | /** 57 | * Fills next available token with JSON primitive. 58 | */ 59 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, 60 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 61 | jsmntok_t *token; 62 | int start; 63 | 64 | start = parser->pos; 65 | 66 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 67 | switch (js[parser->pos]) { 68 | #ifndef JSMN_STRICT 69 | /* In strict mode primitive must be followed by "," or "}" or "]" */ 70 | case ':': 71 | #endif 72 | case '\t': 73 | case '\r': 74 | case '\n': 75 | case ' ': 76 | case ',': 77 | case ']': 78 | case '}': 79 | goto found; 80 | } 81 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { 82 | parser->pos = start; 83 | return JSMN_ERROR_INVAL; 84 | } 85 | } 86 | #ifdef JSMN_STRICT 87 | /* In strict mode primitive must be followed by a comma/object/array */ 88 | parser->pos = start; 89 | return JSMN_ERROR_PART; 90 | #endif 91 | 92 | found: if (tokens == NULL) { 93 | parser->pos--; 94 | return (jsmnerr_t) 0; 95 | } 96 | token = jsmn_alloc_token(parser, tokens, num_tokens); 97 | if (token == NULL) { 98 | parser->pos = start; 99 | return JSMN_ERROR_NOMEM; 100 | } 101 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); 102 | #ifdef JSMN_PARENT_LINKS 103 | token->parent = parser->toksuper; 104 | #endif 105 | parser->pos--; 106 | return (jsmnerr_t) 0; 107 | } 108 | 109 | /** 110 | * Filsl next token with JSON string. 111 | */ 112 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, 113 | size_t len, jsmntok_t *tokens, size_t num_tokens) { 114 | jsmntok_t *token; 115 | 116 | int start = parser->pos; 117 | 118 | parser->pos++; 119 | 120 | /* Skip starting quote */ 121 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 122 | char c = js[parser->pos]; 123 | 124 | /* Quote: end of string */ 125 | if (c == '\"') { 126 | if (tokens == NULL) { 127 | return (jsmnerr_t) 0; 128 | } 129 | token = jsmn_alloc_token(parser, tokens, num_tokens); 130 | if (token == NULL) { 131 | parser->pos = start; 132 | return JSMN_ERROR_NOMEM; 133 | } 134 | jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); 135 | #ifdef JSMN_PARENT_LINKS 136 | token->parent = parser->toksuper; 137 | #endif 138 | return (jsmnerr_t) 0; 139 | } 140 | 141 | /* Backslash: Quoted symbol expected */ 142 | if (c == '\\') { 143 | parser->pos++; 144 | switch (js[parser->pos]) { 145 | /* Allowed escaped symbols */ 146 | case '\"': 147 | case '/': 148 | case '\\': 149 | case 'b': 150 | case 'f': 151 | case 'r': 152 | case 'n': 153 | case 't': 154 | break; 155 | /* Allows escaped symbol \uXXXX */ 156 | case 'u': 157 | parser->pos++; 158 | int i; 159 | for (i = 0; i < 4 && js[parser->pos] != '\0'; i++) { 160 | /* If it isn't a hex character we have an error */ 161 | if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ 162 | (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ 163 | (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ 164 | parser->pos = start; 165 | return JSMN_ERROR_INVAL; 166 | } 167 | parser->pos++; 168 | } 169 | parser->pos--; 170 | break; 171 | /* Unexpected symbol */ 172 | default: 173 | parser->pos = start; 174 | return JSMN_ERROR_INVAL; 175 | } 176 | } 177 | } 178 | parser->pos = start; 179 | return JSMN_ERROR_PART; 180 | } 181 | 182 | /** 183 | * Parse JSON string and fill tokens. 184 | */ 185 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 186 | jsmntok_t *tokens, unsigned int num_tokens) { 187 | jsmnerr_t r; 188 | int i; 189 | jsmntok_t *token; 190 | int count = 0; 191 | 192 | for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { 193 | char c; 194 | jsmntype_t type; 195 | 196 | c = js[parser->pos]; 197 | switch (c) { 198 | case '{': 199 | case '[': 200 | count++; 201 | if (tokens == NULL) { 202 | break; 203 | } 204 | token = jsmn_alloc_token(parser, tokens, num_tokens); 205 | if (token == NULL) 206 | return JSMN_ERROR_NOMEM; 207 | if (parser->toksuper != -1) { 208 | tokens[parser->toksuper].size++; 209 | #ifdef JSMN_PARENT_LINKS 210 | token->parent = parser->toksuper; 211 | #endif 212 | } 213 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); 214 | token->start = parser->pos; 215 | parser->toksuper = parser->toknext - 1; 216 | break; 217 | case '}': 218 | case ']': 219 | if (tokens == NULL) 220 | break; 221 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); 222 | #ifdef JSMN_PARENT_LINKS 223 | if (parser->toknext < 1) { 224 | return JSMN_ERROR_INVAL; 225 | } 226 | token = &tokens[parser->toknext - 1]; 227 | for (;;) { 228 | if (token->start != -1 && token->end == -1) { 229 | if (token->type != type) { 230 | return JSMN_ERROR_INVAL; 231 | } 232 | token->end = parser->pos + 1; 233 | parser->toksuper = token->parent; 234 | break; 235 | } 236 | if (token->parent == -1) { 237 | break; 238 | } 239 | token = &tokens[token->parent]; 240 | } 241 | #else 242 | for (i = parser->toknext - 1; i >= 0; i--) { 243 | token = &tokens[i]; 244 | if (token->start != -1 && token->end == -1) { 245 | if (token->type != type) { 246 | return JSMN_ERROR_INVAL; 247 | } 248 | parser->toksuper = -1; 249 | token->end = parser->pos + 1; 250 | break; 251 | } 252 | } 253 | /* Error if unmatched closing bracket */ 254 | if (i == -1) 255 | return JSMN_ERROR_INVAL; 256 | for (; i >= 0; i--) { 257 | token = &tokens[i]; 258 | if (token->start != -1 && token->end == -1) { 259 | parser->toksuper = i; 260 | break; 261 | } 262 | } 263 | #endif 264 | break; 265 | case '\"': 266 | r = jsmn_parse_string(parser, js, len, tokens, num_tokens); 267 | if (r < 0) 268 | return r; 269 | count++; 270 | if (parser->toksuper != -1 && tokens != NULL) 271 | tokens[parser->toksuper].size++; 272 | break; 273 | case '\t': 274 | case '\r': 275 | case '\n': 276 | case ':': 277 | case ',': 278 | case ' ': 279 | break; 280 | #ifdef JSMN_STRICT 281 | /* In strict mode primitives are: numbers and booleans */ 282 | case '-': 283 | case '0': 284 | case '1': 285 | case '2': 286 | case '3': 287 | case '4': 288 | case '5': 289 | case '6': 290 | case '7': 291 | case '8': 292 | case '9': 293 | case 't': 294 | case 'f': 295 | case 'n': 296 | #else 297 | /* In non-strict mode every unquoted value is a primitive */ 298 | default: 299 | #endif 300 | r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); 301 | if (r < 0) 302 | return r; 303 | count++; 304 | if (parser->toksuper != -1 && tokens != NULL) 305 | tokens[parser->toksuper].size++; 306 | break; 307 | 308 | #ifdef JSMN_STRICT 309 | /* Unexpected char in strict mode */ 310 | default: 311 | return JSMN_ERROR_INVAL; 312 | #endif 313 | } 314 | } 315 | 316 | for (i = parser->toknext - 1; i >= 0; i--) { 317 | /* Unmatched opened object or array */ 318 | if (tokens[i].start != -1 && tokens[i].end == -1) { 319 | return JSMN_ERROR_PART; 320 | } 321 | } 322 | 323 | return (jsmnerr_t) count; 324 | } 325 | 326 | /** 327 | * Creates a new parser based over a given buffer with an array of tokens 328 | * available. 329 | */ 330 | void jsmn_init(jsmn_parser *parser) { 331 | parser->pos = 0; 332 | parser->toknext = 0; 333 | parser->toksuper = -1; 334 | } 335 | 336 | -------------------------------------------------------------------------------- /src/common/jsmn.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2010 Serge A. Zaitsev 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __JSMN_H_ 24 | #define __JSMN_H_ 25 | #include 26 | #define JSMN_STRICT 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * JSON type identifier. Basic types are: 33 | * o Object 34 | * o Array 35 | * o String 36 | * o Other primitive: number, boolean (true/false) or null 37 | */ 38 | typedef enum { 39 | JSMN_PRIMITIVE = 0, JSMN_OBJECT = 1, JSMN_ARRAY = 2, JSMN_STRING = 3 40 | } jsmntype_t; 41 | 42 | typedef enum { 43 | /* Not enough tokens were provided */ 44 | JSMN_ERROR_NOMEM = -1, 45 | /* Invalid character inside JSON string */ 46 | JSMN_ERROR_INVAL = -2, 47 | /* The string is not a full JSON packet, more bytes expected */ 48 | JSMN_ERROR_PART = -3, 49 | } jsmnerr_t; 50 | 51 | /** 52 | * JSON token description. 53 | * @param type type (object, array, string etc.) 54 | * @param start start position in JSON data string 55 | * @param end end position in JSON data string 56 | */ 57 | typedef struct { 58 | jsmntype_t type; 59 | int start; 60 | int end; 61 | int size; 62 | #ifdef JSMN_PARENT_LINKS 63 | int parent; 64 | #endif 65 | } jsmntok_t; 66 | 67 | /** 68 | * JSON parser. Contains an array of token blocks available. Also stores 69 | * the string being parsed now and current position in that string 70 | */ 71 | typedef struct { 72 | unsigned int pos; /* offset in the JSON string */ 73 | unsigned int toknext; /* next token to allocate */ 74 | int toksuper; /* superior token node, e.g parent object or array */ 75 | } jsmn_parser; 76 | 77 | /** 78 | * Create JSON parser over an array of tokens 79 | */ 80 | void jsmn_init(jsmn_parser *parser); 81 | 82 | /** 83 | * Run JSON parser. It parses a JSON data string into and array of tokens, each describing 84 | * a single JSON object. 85 | */ 86 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, 87 | jsmntok_t *tokens, unsigned int num_tokens); 88 | 89 | #ifdef __cplusplus 90 | } 91 | #endif 92 | 93 | #endif /* __JSMN_H_ */ 94 | -------------------------------------------------------------------------------- /src/common/sha256.h: -------------------------------------------------------------------------------- 1 | // ////////////////////////////////////////////////////////// 2 | // sha256.h 3 | // Copyright (c) 2014 Stephan Brumme. All rights reserved. 4 | // see http://create.stephan-brumme.com/disclaimer.html 5 | // 6 | 7 | #pragma once 8 | 9 | //#include "hash.h" 10 | 11 | /* This include statement has been discarded from the original source code. Not 12 | * using string object as it is not necessarilly included on the devices that 13 | * will use this SDK. */ 14 | //#include 15 | // define fixed size integer types 16 | #ifdef _MSC_VER 17 | // Windows 18 | typedef unsigned __int8 uint8_t; 19 | typedef unsigned __int32 uint32_t; 20 | typedef unsigned __int64 uint64_t; 21 | #else 22 | // GCC 23 | #include 24 | #endif 25 | // This line added to original source code. defines size_t. 26 | #include "stddef.h" 27 | 28 | /// compute SHA256 hash 29 | /** Usage: 30 | SHA256 sha256; 31 | std::string myHash = sha256("Hello World"); // std::string 32 | std::string myHash2 = sha256("How are you", 11); // arbitrary data, 11 bytes 33 | 34 | // or in a streaming fashion: 35 | 36 | SHA256 sha256; 37 | while (more data available) 38 | sha256.add(pointer to fresh data, number of new bytes); 39 | std::string myHash3 = sha256.getHash(); 40 | */ 41 | class SHA256 //: public Hash 42 | { 43 | public: 44 | /// same as reset() 45 | SHA256(); 46 | 47 | /// compute SHA256 of a memory block 48 | /* Modified from original source code. Using char* instead of string. */ 49 | char* operator()(const void* data, size_t numBytes); 50 | /// compute SHA256 of a string, excluding final zero 51 | /* Modified from original source code. Using char* instead of string. */ 52 | char* operator()(const char* text); 53 | 54 | /// add arbitrary number of bytes 55 | void add(const void* data, size_t numBytes); 56 | 57 | /// return latest hash as 16 hex characters 58 | /* Modified from original source code. Using char* instead of string. */ 59 | char* getHash(); 60 | 61 | /* This function added to original source. */ 62 | /// return latest hash raw (not as hex) 63 | char* getHashDec(); 64 | 65 | /// restart 66 | void reset(); 67 | 68 | private: 69 | /// process 64 bytes 70 | void processBlock(const void* data); 71 | /// process everything left in the internal buffer 72 | void processBuffer(); 73 | 74 | /// split into 64 byte blocks (=> 512 bits) 75 | enum { 76 | BlockSize = 512 / 8, HashValues = 8 77 | }; 78 | 79 | /// size of processed data in bytes 80 | uint64_t m_numBytes; 81 | /// valid bytes in m_buffer 82 | size_t m_bufferSize; 83 | /// bytes not processed yet 84 | uint8_t m_buffer[BlockSize]; 85 | /// hash, stored as integers 86 | uint32_t m_hash[8]; 87 | }; 88 | -------------------------------------------------------------------------------- /src/edison/EdisonAWSImplementations.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceIndependentInterfaces.h" 2 | #include "EdisonAWSImplementations.h" 3 | /* Arduino.h is Arduino's standard library. Defines the Arduino String 4 | * object and the Arduino delay() procedure. */ 5 | #include 6 | #include 7 | #include 8 | 9 | void printWifiStatus() { 10 | 11 | // print the SSID of the network you're attached to: 12 | Serial.print("SSID: "); 13 | Serial.println(WiFi.SSID()); 14 | 15 | // print your WiFi shield's IP address: 16 | IPAddress ip = WiFi.localIP(); 17 | Serial.print("IP Address: "); 18 | Serial.println(ip); 19 | 20 | // print the received signal strength: 21 | long rssi = WiFi.RSSI(); 22 | Serial.print("signal strength (RSSI):"); 23 | Serial.print(rssi); 24 | Serial.println(" dBm"); 25 | 26 | } 27 | 28 | 29 | bool Edison_Wifi_Setup(char* pSSID, char* pPassword) { 30 | 31 | int tempStatus = WL_IDLE_STATUS; 32 | bool ret_val = true; 33 | enum { 34 | SECS_IN_MS = 7000 35 | }; 36 | if (WiFi.status() == WL_NO_SHIELD) { 37 | Serial.println("WiFi shield not present"); 38 | ret_val = false; 39 | } 40 | 41 | String fv = WiFi.firmwareVersion(); 42 | if (fv != "1.1.0") { 43 | Serial.println("Please upgrade the firmware"); 44 | ret_val = false; 45 | } 46 | while (tempStatus != WL_CONNECTED) { 47 | Serial.print("Attempting to connect to SSID: "); 48 | Serial.println(pSSID); 49 | // Connect to WPA/WPA2 network. Change this line if using open or WEP network: 50 | tempStatus = WiFi.begin(pSSID, pPassword); 51 | // wait 10 seconds for connection: 52 | delay(SECS_IN_MS); 53 | } 54 | 55 | return ret_val; 56 | } 57 | 58 | 59 | int delayTime = 500; 60 | 61 | EdisonHttpClient::EdisonHttpClient() { 62 | } 63 | 64 | char* EdisonHttpClient::send(const char* request, const char* serverUrl, 65 | int port) { 66 | /* Arduino String to build the response with. */ 67 | String responseBuilder = ""; 68 | if (client.connect(serverUrl, port)) { 69 | /* Send the requests */ 70 | client.println(request); 71 | client.println(); 72 | /* Read the request into responseBuilder. */ 73 | delay(delayTime); 74 | while (client.available()) { 75 | char c = client.read(); 76 | responseBuilder.concat(c); 77 | } 78 | client.stop(); 79 | } else { 80 | client.stop(); 81 | /* Error connecting. */ 82 | return 0; 83 | } 84 | /* Copy responseBuilder into char* */ 85 | int len = responseBuilder.length(); 86 | char* response = new char[len + 1](); 87 | responseBuilder.toCharArray(response, len + 1); 88 | return response; 89 | } 90 | 91 | bool EdisonHttpClient::usesCurl() { 92 | /* Does not use curl command. */ 93 | return false; 94 | } 95 | 96 | EdisonHttpCurlClient::EdisonHttpCurlClient() { 97 | } 98 | 99 | char* EdisonHttpCurlClient::send(const char* request, const char* serverUrl, 100 | int port) { 101 | // call curl as a system call, passing 'request' as the system call command 102 | //system (request + " >/dev/ttyGS0"); 103 | char* buffer = new char[1000](); 104 | sprintf(buffer, "%s %s", request, ">/dev/ttyGS0"); 105 | char* response = new char[2 + 1](); 106 | buffer[1000] = '\0'; 107 | system (buffer); 108 | return response; 109 | } 110 | 111 | bool EdisonHttpCurlClient::usesCurl() { 112 | /* Use curl command. */ 113 | return true; 114 | } 115 | 116 | 117 | EdisonDateTimeProvider::EdisonDateTimeProvider() { 118 | /* These are initialized to defaults. The defaults will cause the request 119 | * to fail, but the AWS Services response will contain the correct time 120 | * which will be fed to sync(). */ 121 | year = 2000; 122 | month = 1; 123 | day = 1; 124 | hour = 0; 125 | minute = 0; 126 | second = 0; 127 | /* Never synced so syncTime is 0. */ 128 | syncedTime = 0; 129 | } 130 | 131 | const char* EdisonDateTimeProvider::getDateTime() { 132 | const int secondsPerMinute = 60; 133 | const int secondsPerHour = secondsPerMinute * 60; 134 | const long int secondsPerDay = secondsPerHour * 24; 135 | /* Determine how many seconds have passed since the last sync, break those 136 | * seconds down into days/hours/minutes/seconds, then form the new time 137 | * string. */ 138 | long int newSecond = ((millis() - syncedTime) / 1000) + second 139 | + (secondsPerMinute * minute) + (secondsPerHour * hour) 140 | + (secondsPerDay * day); 141 | int newDay = newSecond / secondsPerDay; 142 | newSecond = newSecond % secondsPerDay; 143 | int newHour = newSecond / secondsPerHour; 144 | newSecond = newSecond % secondsPerHour; 145 | int newMinute = newSecond / secondsPerMinute; 146 | newSecond = newSecond % secondsPerMinute; 147 | 148 | /* Without cumbersome data about how many days a month we cannot factor the 149 | * extra time into more months. AWS Services give different errors that do 150 | * not provide us with the current time if the date is invalid rather than 151 | * innacurate. However, going past the number of days in a month (i.e 152 | * giving 30 for February) is valid, so long as the days does not exceed 153 | * 31. */ 154 | newDay = newDay > 31 ? 31 : newDay; 155 | 156 | /* Copy the values to dateTime and return it. */ 157 | sprintf(dateTime, "%.4d%.2d%.2d%.2d%.2d%.2d", year, month, newDay, newHour, 158 | newMinute, (int) newSecond); 159 | return dateTime; 160 | } 161 | 162 | bool EdisonDateTimeProvider::syncTakesArg(void) { 163 | return true; 164 | } 165 | 166 | void EdisonDateTimeProvider::sync(const char* dateTime) { 167 | /* If given a null dateTime, do nothing. */ 168 | if (dateTime) { 169 | /* Set synced time to the number of milliseconds since the sketch 170 | * started. */ 171 | syncedTime = millis(); 172 | /* Copy each substring into the buffer then convert it to an integer. */ 173 | char buffer[5]; 174 | sprintf(buffer, "%.4s", dateTime); 175 | year = atoi(buffer); 176 | sprintf(buffer, "%.2s", dateTime + 4); 177 | month = atoi(buffer); 178 | sprintf(buffer, "%.2s", dateTime + 6); 179 | day = atoi(buffer); 180 | sprintf(buffer, "%.2s", dateTime + 8); 181 | hour = atoi(buffer); 182 | sprintf(buffer, "%.2s", dateTime + 10); 183 | minute = atoi(buffer); 184 | sprintf(buffer, "%.2s", dateTime + 12); 185 | second = atoi(buffer); 186 | 187 | /* If these values come out as 0, change them to valid AWS date values 188 | * (i.e. cannot have month 0). */ 189 | year = year ? year : 2000; 190 | month = month ? month : 1; 191 | day = day ? day : 1; 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /src/edison/EdisonAWSImplementations.h: -------------------------------------------------------------------------------- 1 | #ifndef EDISONAWSIMPLEMENTATIONS_H_ 2 | #define EDISONAWSIMPLEMENTATIONS_H_ 3 | /* The EthernetClient is defined in this header. */ 4 | #include 5 | #include "DeviceIndependentInterfaces.h" 6 | 7 | /* HttpClient implementation to be used on the Edison device. */ 8 | class EdisonHttpClient: public IHttpClient { 9 | WiFiClient client; 10 | public: 11 | EdisonHttpClient(); 12 | /* Send http request and return the response. */ 13 | char* send(const char *request, const char* serverUrl, int port); 14 | /* Returns false. Client uses raw http/https. */ 15 | bool usesCurl(void); 16 | }; 17 | 18 | /* Experimental HttpClient for Edison that takes a curl command. This class is needed for 19 | * testing purposes */ 20 | class EdisonHttpCurlClient: public IHttpClient { 21 | public: 22 | EdisonHttpCurlClient(); 23 | /* Send http request and return the response.*/ 24 | char* send(const char *request, const char* serverUrl, int port); 25 | /* Returns true. Client uses a curl command. */ 26 | bool usesCurl(void); 27 | }; 28 | 29 | /* IDateTimeProvider implementation to be used on the Edison device. */ 30 | class EdisonDateTimeProvider: public IDateTimeProvider { 31 | /* These values set as default in constructor and updated in sync() */ 32 | int year; 33 | int month; 34 | int day; 35 | int hour; 36 | int minute; 37 | int second; 38 | /* The time in seconds that the EdisonDateTimeProvider was last synced. */ 39 | unsigned long syncedTime; 40 | /* The time as a cstring in yyyyMMddHHmmss format. Is written to within and 41 | * returned by getDateTime(). */ 42 | char dateTime[15]; 43 | public: 44 | EdisonDateTimeProvider(); 45 | /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. On 46 | * first call this will return a default date. sync() must then be called 47 | * with the current date. */ 48 | const char* getDateTime(void); 49 | /* Returns true. Edison requires an argument for sync because the device 50 | * does not intenally handle current time. */ 51 | bool syncTakesArg(void); 52 | /* Synchronizes the EdisonDateTimeProvider's time given the current time 53 | * in yyyyMMddHHmmss format. */ 54 | void sync(const char* dateTime); 55 | }; 56 | 57 | 58 | bool Edison_Wifi_Setup(char* pSSID, char* pPassword); 59 | void printWifiStatus(); 60 | 61 | 62 | #endif /* EDISONAWSIMPLEMENTATIONS_H_ */ 63 | -------------------------------------------------------------------------------- /src/esp8266/ESP8266AWSImplementations.h: -------------------------------------------------------------------------------- 1 | #ifndef AWSESP2866IMPLEMENTATIONS_H_ 2 | #define AWSESP2866IMPLEMENTATIONS_H_ 3 | #include "DeviceIndependentInterfaces.h" 4 | /* application.h is Esp8266's standard library. Define TCPClient. */ 5 | #include 6 | 7 | /* HttpClient implementation to be used on the Esp8266 Core device. */ 8 | class Esp8266HttpClient: public IHttpClient { 9 | WiFiClientSecure sclient; 10 | //TCPClient client; 11 | public: 12 | Esp8266HttpClient(); 13 | /* Send http request and return the response. */ 14 | char* send(const char *request, const char* serverUrl, int port); 15 | /* Returns false. Client uses raw http/https. */ 16 | bool usesCurl(void); 17 | }; 18 | 19 | class Esp8266DateTimeProvider: public IDateTimeProvider { 20 | /* The time as a cstring in yyyyMMddHHmmss format. Is written to within and 21 | * returned by getDateTime(). */ 22 | WiFiClient client; 23 | //char dateTime[15]; 24 | public: 25 | char dateTime[15]; 26 | Esp8266DateTimeProvider(); 27 | /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. */ 28 | const char* getDateTime(void); 29 | /* Returns false because Esp8266 has it's own mechanism for syncing that does 30 | * not require an argument. */ 31 | bool syncTakesArg(void); 32 | /* Synchronizes Esp8266's date and time with Esp8266's servers. The dateTime 33 | * argument is ignored. */ 34 | void sync(const char* dateTime); 35 | }; 36 | 37 | #endif /* AWSESP2866IMPLEMENTATIONS_H_ */ 38 | -------------------------------------------------------------------------------- /src/esp8266/ESP8266AWSImplentations.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | /* application.h is Esp8266's standard library. Defines the Arduino String 3 | * object, the Arduino delay() procedure, and the Esp8266 TCPClient. */ 4 | #include "Esp8266AWSImplementations.h" 5 | #include "DeviceIndependentInterfaces.h" 6 | #include 7 | #include 8 | 9 | int delayTime = 500; 10 | char* updateCurTime(void); 11 | 12 | Esp8266HttpClient::Esp8266HttpClient() { 13 | } 14 | 15 | char* Esp8266HttpClient::send(const char* request, const char* serverUrl, int port) { 16 | 17 | WiFiClientSecure sclient; 18 | Serial.println(serverUrl); 19 | Serial.println(port); 20 | Serial.println(request); 21 | Serial.println(""); 22 | Serial.println(""); 23 | 24 | // 25 | String response = ""; 26 | if (sclient.connect(serverUrl, port)) { 27 | 28 | // Send the request 29 | sclient.print(request); 30 | 31 | // keep reading the response until it's finished 32 | while(sclient.connected()) { 33 | while(sclient.available()){ 34 | char c = sclient.read(); 35 | response.concat(c); 36 | Serial.print('.'); 37 | } 38 | 39 | // disconnect any open connections 40 | sclient.stop(); 41 | } 42 | 43 | } else { 44 | // connection was unsuccessful 45 | sclient.stop(); 46 | return "can't setup SSL connection"; 47 | } 48 | 49 | // convert the string into a char and return 50 | int len = response.length(); 51 | char* response_char = new char[len + 1](); 52 | response.toCharArray(response_char, len + 1); 53 | return response_char; 54 | } 55 | 56 | bool Esp8266HttpClient::usesCurl() { 57 | /* Does not use curl command. */ 58 | return false; 59 | } 60 | 61 | Esp8266DateTimeProvider::Esp8266DateTimeProvider() { 62 | } 63 | 64 | const char* Esp8266DateTimeProvider::getDateTime() { 65 | return updateCurTime(); 66 | } 67 | bool Esp8266DateTimeProvider::syncTakesArg(void) { 68 | return true; 69 | } 70 | 71 | void Esp8266DateTimeProvider::sync(const char* dateTime) { 72 | // should have no need for an implementation 73 | } 74 | 75 | //////////////////////////////////// 76 | // convert month to digits 77 | //////////////////////////////////// 78 | String getMonth(String sM) { 79 | if(sM=="Jan") return "01"; 80 | if(sM=="Feb") return "02"; 81 | if(sM=="Mar") return "03"; 82 | if(sM=="Apr") return "04"; 83 | if(sM=="May") return "05"; 84 | if(sM=="Jun") return "06"; 85 | if(sM=="Jul") return "07"; 86 | if(sM=="Aug") return "08"; 87 | if(sM=="Sep") return "09"; 88 | if(sM=="Oct") return "10"; 89 | if(sM=="Nov") return "11"; 90 | if(sM=="Dec") return "12"; 91 | return "01"; 92 | } 93 | 94 | //////////////////////////////////// 95 | // Scrape UTC Time from server 96 | //////////////////////////////////// 97 | char* updateCurTime(void) { 98 | static int timeout_busy=0; 99 | int ipos; 100 | timeout_busy=0; //reset 101 | 102 | const char* timeServer = "aws.amazon.com"; 103 | 104 | // send a bad header on purpose, so we get a 400 with a DATE: timestamp 105 | const char* timeServerGet = "GET example.com/ HTTP/1.1"; 106 | String utctime; 107 | String GmtDate; 108 | static char dateStamp[20]; 109 | static char chBuf[200]; 110 | char utctimeraw[80]; 111 | char* dpos; 112 | 113 | WiFiClient client; 114 | if (client.connect(timeServer, 80)) { 115 | //Send Request 116 | client.println(timeServerGet); 117 | client.println(); 118 | while((!client.available())&&(timeout_busy++<5000)){ 119 | // Wait until the client sends some data 120 | delay(1); 121 | } 122 | 123 | // kill client if timeout 124 | if(timeout_busy>=5000) { 125 | client.flush(); 126 | client.stop(); 127 | Serial.println("timeout receiving timeserver data\n"); 128 | return dateStamp; 129 | } 130 | 131 | // read the http GET Response 132 | String req2 = client.readString(); 133 | // Serial.println(""); 134 | // Serial.println(""); 135 | // Serial.print(req2); 136 | // Serial.println(""); 137 | // Serial.println(""); 138 | 139 | // close connection 140 | delay(1); 141 | client.flush(); 142 | client.stop(); 143 | 144 | ipos = req2.indexOf("Date:"); 145 | if(ipos>0) { 146 | GmtDate = req2.substring(ipos,ipos+35); 147 | // Serial.println(GmtDate); 148 | utctime = GmtDate.substring(18,22) + getMonth(GmtDate.substring(14,17)) + GmtDate.substring(11,13) + GmtDate.substring(23,25) + GmtDate.substring(26,28) + GmtDate.substring(29,31); 149 | // Serial.println(utctime.substring(0,14)); 150 | utctime.substring(0,14).toCharArray(dateStamp, 20); 151 | } 152 | } 153 | else { 154 | Serial.println("did not connect to timeserver\n"); 155 | } 156 | timeout_busy=0; // reset timeout 157 | return dateStamp; // Return latest or last good dateStamp 158 | } 159 | -------------------------------------------------------------------------------- /src/galileo/GalileoAWSImplementations.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceIndependentInterfaces.h" 2 | #include "GalileoAWSImplementations.h" 3 | /* Arduino.h is Arduino's standard library. Defines the Arduino String 4 | * object and the Arduino delay() procedure. */ 5 | #include 6 | #include 7 | #include 8 | 9 | int delayTime = 500; 10 | 11 | GalileoHttpClient::GalileoHttpClient() { 12 | } 13 | 14 | char* GalileoHttpClient::send(const char* request, const char* serverUrl, 15 | int port) { 16 | /* Arduino String to build the response with. */ 17 | String responseBuilder = ""; 18 | if (client.connect(serverUrl, port)) { 19 | /* Send the requests */ 20 | client.println(request); 21 | client.println(); 22 | /* Read the request into responseBuilder. */ 23 | delay(delayTime); 24 | while (client.available()) { 25 | char c = client.read(); 26 | responseBuilder.concat(c); 27 | } 28 | client.stop(); 29 | } else { 30 | client.stop(); 31 | /* Error connecting. */ 32 | return 0; 33 | } 34 | /* Copy responseBuilder into char* */ 35 | int len = responseBuilder.length(); 36 | char* response = new char[len + 1](); 37 | responseBuilder.toCharArray(response, len + 1); 38 | return response; 39 | } 40 | 41 | bool GalileoHttpClient::usesCurl() { 42 | /* Does not use curl command. */ 43 | return false; 44 | } 45 | 46 | GalileoDateTimeProvider::GalileoDateTimeProvider() { 47 | /* These are initialized to defaults. The defaults will cause the request 48 | * to fail, but the AWS Services response will contain the correct time 49 | * which will be fed to sync(). */ 50 | year = 2000; 51 | month = 1; 52 | day = 1; 53 | hour = 0; 54 | minute = 0; 55 | second = 0; 56 | /* Never synced so syncTime is 0. */ 57 | syncedTime = 0; 58 | } 59 | 60 | const char* GalileoDateTimeProvider::getDateTime() { 61 | const int secondsPerMinute = 60; 62 | const int secondsPerHour = secondsPerMinute * 60; 63 | const long int secondsPerDay = secondsPerHour * 24; 64 | /* Determine how many seconds have passed since the last sync, break those 65 | * seconds down into days/hours/minutes/seconds, then form the new time 66 | * string. */ 67 | long int newSecond = ((millis() - syncedTime) / 1000) + second 68 | + (secondsPerMinute * minute) + (secondsPerHour * hour) 69 | + (secondsPerDay * day); 70 | int newDay = newSecond / secondsPerDay; 71 | newSecond = newSecond % secondsPerDay; 72 | int newHour = newSecond / secondsPerHour; 73 | newSecond = newSecond % secondsPerHour; 74 | int newMinute = newSecond / secondsPerMinute; 75 | newSecond = newSecond % secondsPerMinute; 76 | 77 | /* Without cumbersome data about how many days a month we cannot factor the 78 | * extra time into more months. AWS Services give different errors that do 79 | * not provide us with the current time if the date is invalid rather than 80 | * innacurate. However, going past the number of days in a month (i.e 81 | * giving 30 for February) is valid, so long as the days does not exceed 82 | * 31. */ 83 | newDay = newDay > 31 ? 31 : newDay; 84 | 85 | /* Copy the values to dateTime and return it. */ 86 | sprintf(dateTime, "%.4d%.2d%.2d%.2d%.2d%.2d", year, month, newDay, newHour, 87 | newMinute, (int) newSecond); 88 | return dateTime; 89 | } 90 | 91 | bool GalileoDateTimeProvider::syncTakesArg(void) { 92 | return true; 93 | } 94 | 95 | void GalileoDateTimeProvider::sync(const char* dateTime) { 96 | /* If given a null dateTime, do nothing. */ 97 | if (dateTime) { 98 | /* Set synced time to the number of milliseconds since the sketch 99 | * started. */ 100 | syncedTime = millis(); 101 | /* Copy each substring into the buffer then convert it to an integer. */ 102 | char buffer[5]; 103 | sprintf(buffer, "%.4s", dateTime); 104 | year = atoi(buffer); 105 | sprintf(buffer, "%.2s", dateTime + 4); 106 | month = atoi(buffer); 107 | sprintf(buffer, "%.2s", dateTime + 6); 108 | day = atoi(buffer); 109 | sprintf(buffer, "%.2s", dateTime + 8); 110 | hour = atoi(buffer); 111 | sprintf(buffer, "%.2s", dateTime + 10); 112 | minute = atoi(buffer); 113 | sprintf(buffer, "%.2s", dateTime + 12); 114 | second = atoi(buffer); 115 | 116 | /* If these values come out as 0, change them to valid AWS date values 117 | * (i.e. cannot have month 0). */ 118 | year = year ? year : 2000; 119 | month = month ? month : 1; 120 | day = day ? day : 1; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/galileo/GalileoAWSImplementations.h: -------------------------------------------------------------------------------- 1 | #ifndef GALILEOAWSIMPLEMENTATIONS_H_ 2 | #define GALILEOAWSIMPLEMENTATIONS_H_ 3 | /* The EthernetClient is defined in this header. */ 4 | #include 5 | #include "DeviceIndependentInterfaces.h" 6 | 7 | /* HttpClient implementation to be used on the Galileo device. */ 8 | class GalileoHttpClient: public IHttpClient { 9 | EthernetClient client; 10 | public: 11 | GalileoHttpClient(); 12 | /* Send http request and return the response. */ 13 | char* send(const char *request, const char* serverUrl, int port); 14 | /* Returns false. Client uses raw http/https. */ 15 | bool usesCurl(void); 16 | }; 17 | 18 | /* IDateTimeProvider implementation to be used on the Galileo device. */ 19 | class GalileoDateTimeProvider: public IDateTimeProvider { 20 | /* These values set as default in constructor and updated in sync() */ 21 | int year; 22 | int month; 23 | int day; 24 | int hour; 25 | int minute; 26 | int second; 27 | /* The time in seconds that the GalileoDateTimeProvider was last synced. */ 28 | unsigned long syncedTime; 29 | /* The time as a cstring in yyyyMMddHHmmss format. Is written to within and 30 | * returned by getDateTime(). */ 31 | char dateTime[15]; 32 | public: 33 | GalileoDateTimeProvider(); 34 | /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. On 35 | * first call this will return a default date. sync() must then be called 36 | * with the current date. */ 37 | const char* getDateTime(void); 38 | /* Returns true. Galileo requires an argument for sync because the device 39 | * does not intenally handle current time. */ 40 | bool syncTakesArg(void); 41 | /* Synchronizes the GalileoDateTimeProvider's time given the current time 42 | * in yyyyMMddHHmmss format. */ 43 | void sync(const char* dateTime); 44 | }; 45 | 46 | #endif /* GALILEOAWSIMPLEMENTATIONS_H_ */ 47 | -------------------------------------------------------------------------------- /src/mediatek/MtkAWSImplementations.cpp: -------------------------------------------------------------------------------- 1 | #include "DeviceIndependentInterfaces.h" 2 | #include "MtkAWSImplementations.h" 3 | #include "vmssl.h" 4 | #include "LTask.h" 5 | /* Arduino.h is Arduino's standard library. Defines the Arduino String 6 | * object and the Arduino delay() procedure. */ 7 | #include 8 | #include 9 | #include 10 | 11 | int delayTime = 500; 12 | 13 | MtkHttpClient::MtkHttpClient() { 14 | } 15 | 16 | struct MtkHttpContext 17 | { 18 | const char *request; 19 | const char *serverUrl; 20 | VMINT port; 21 | String response; 22 | VMINT data_sent; 23 | VMINT data_read; 24 | }; 25 | MtkHttpContext *pContext; 26 | 27 | void sslCb(VMINT handle, VMINT event) { 28 | 29 | // This callback is invoked in LinkIt main thread 30 | 31 | VMINT ret; 32 | VMCHAR buf[52] = {0,}; 33 | 34 | #define Serial Serial1 35 | Serial.print("sslCb event="); 36 | Serial.println(event); 37 | 38 | switch(event) { 39 | case VM_SSL_EVT_CONNECTED: 40 | case VM_SSL_EVT_CAN_WRITE: 41 | { 42 | const size_t requestLength = strlen(pContext->request); 43 | 44 | ret = vm_ssl_write(handle, (VMUINT8*)pContext->request + pContext->data_sent, requestLength); 45 | if(ret >= 0) { 46 | pContext->data_sent += ret; 47 | } 48 | break; 49 | } 50 | case VM_SSL_EVT_CAN_READ: 51 | // make sure there is an terminating NULL 52 | ret = vm_ssl_read(handle, (VMUINT8*)buf, sizeof(buf) - 1); 53 | while(ret > 0) { 54 | pContext->response.concat(buf); 55 | memset(buf, 0, sizeof(buf)); 56 | // make sure there is an terminating NULL 57 | ret = vm_ssl_read(handle, (VMUINT8*)buf, sizeof(buf) - 1); 58 | pContext->data_read += ret; 59 | } 60 | 61 | if(ret == VM_TCP_READ_EOF) { 62 | vm_ssl_close(handle); 63 | 64 | // Allow LTask.remoteCall() to return 65 | LTask.post_signal(); 66 | } 67 | break; 68 | case VM_SSL_EVT_PIPE_BROKEN: 69 | case VM_SSL_EVT_HOST_NOT_FOUND: 70 | case VM_SSL_EVT_PIPE_CLOSED: 71 | case VM_SSL_EVT_HANDSHAKE_FAILED: 72 | case VM_SSL_EVT_CERTIFICATE_VALIDATION_FAILED: 73 | vm_ssl_close(handle); 74 | 75 | // Allow LTask.remoteCall() to return 76 | LTask.post_signal(); 77 | break; 78 | default: 79 | break; 80 | } 81 | } 82 | 83 | boolean sendHttps(void* user_data) { 84 | // This function should be executed in LinkIt main thread. 85 | 86 | // Initialize SSL connection 87 | vm_ssl_cntx ssl_cntx = {0}; 88 | ssl_cntx.authmod = VM_SSL_VERIFY_NONE; // Do not limit the encryption type of the server. 89 | ssl_cntx.connection_callback = sslCb; // SSL event handler callback. This will be 90 | ssl_cntx.host = (VMCHAR*)pContext->serverUrl; 91 | ssl_cntx.port = pContext->port; 92 | ssl_cntx.ua = NULL; 93 | vm_ssl_connect(&ssl_cntx); 94 | 95 | // By returning false in this function, 96 | // LTask.remoteCall will block the execution 97 | // of the Arduino thread until LTask.post_signal() is called. 98 | // We shall invoke LTask.post_signal() in 99 | // the event handler callback. 100 | return false; 101 | 102 | } 103 | 104 | char* MtkHttpClient::sendHTTPS(const char *request, const char* serverUrl, int port) 105 | { 106 | #if 1 // for debug input param 107 | Serial.print("Req="); 108 | Serial.println(request); 109 | Serial.print("URL="); 110 | Serial.println(serverUrl); 111 | Serial.print("Port="); 112 | Serial.println(port); 113 | #endif 114 | 115 | // This method is invoked in Arduino thread 116 | // 117 | // Use LTask.remoteCall to invoke the sendHttps() 118 | // function in LinkIt main thread. vm_ssl APIs must be 119 | // executed in LinkIt main thread. 120 | MtkHttpContext context; 121 | context.data_read = context.data_sent = 0; 122 | context.request = request; 123 | context.serverUrl = serverUrl; 124 | context.port = port; 125 | context.response = ""; 126 | pContext = &context; 127 | LTask.remoteCall(sendHttps, &context); 128 | 129 | // Build the response - TODO: when is this response released? 130 | int len = context.response.length(); 131 | char* response = new char[len + 1](); 132 | context.response.toCharArray(response, len + 1); 133 | 134 | #if 1 135 | Serial.println("returned response:"); 136 | Serial.println(response); 137 | #endif 138 | 139 | return response; 140 | } 141 | 142 | char* MtkHttpClient::sendHTTP(const char *request, const char* serverUrl, int port) 143 | { 144 | /* Arduino String to build the response with. */ 145 | String responseBuilder = ""; 146 | if (client.connect(serverUrl, port)) { 147 | /* Send the requests */ 148 | client.println(request); 149 | client.println(); 150 | /* Read the request into responseBuilder. */ 151 | delay(delayTime); 152 | while (client.available()) { 153 | char c = client.read(); 154 | responseBuilder.concat(c); 155 | } 156 | client.stop(); 157 | } else { 158 | client.stop(); 159 | /* Error connecting. */ 160 | return 0; 161 | } 162 | /* Copy responseBuilder into char* */ 163 | int len = responseBuilder.length(); 164 | char* response = new char[len + 1](); 165 | responseBuilder.toCharArray(response, len + 1); 166 | return response; 167 | } 168 | 169 | char* MtkHttpClient::send(const char* request, const char* serverUrl, int port) { 170 | // TODO: probably not the best way to detect protocol... 171 | switch(port) 172 | { 173 | case 80: 174 | return sendHTTP(request, serverUrl, port); 175 | case 443: 176 | return sendHTTPS(request, serverUrl, port); 177 | default: 178 | return sendHTTP(request, serverUrl, port); 179 | } 180 | } 181 | 182 | bool MtkHttpClient::usesCurl() { 183 | /* Does not use curl command. */ 184 | return false; 185 | } 186 | // /* 187 | MtkHttpCurlClient::MtkHttpCurlClient() { 188 | } 189 | 190 | char* MtkHttpCurlClient::send(const char* request, const char* serverUrl, 191 | int port) { 192 | // call curl as a system call, passing 'request' as the system call command 193 | //system (request + " >/dev/ttyGS0"); 194 | char* buffer = new char[1000](); 195 | sprintf(buffer, "%s %s", request, ">/dev/ttyGS0"); 196 | char* response = new char[2 + 1](); 197 | buffer[1000] = '\0'; 198 | system (buffer); 199 | return response; 200 | } 201 | 202 | bool MtkHttpCurlClient::usesCurl() { 203 | // Use curl command 204 | return false; // Mtk board does not support Curl 205 | } 206 | //*/ 207 | 208 | MtkDateTimeProvider::MtkDateTimeProvider() { 209 | /* These are initialized to defaults. The defaults will cause the request 210 | * to fail, but the AWS Services response will contain the correct time 211 | * which will be fed to sync(). */ 212 | 213 | #if 1 214 | year = 2000; 215 | month = 1; 216 | day = 1; 217 | hour = 0; 218 | minute = 0; 219 | second = 0; 220 | /* Never synced so syncTime is 0. */ 221 | syncedTime = 0; 222 | #else 223 | year = 2015; 224 | month = 4; 225 | day = 27; 226 | hour = 9; 227 | minute = 18; 228 | second = 0; 229 | /* Never synced so syncTime is 0. */ 230 | syncedTime = 0; 231 | #endif 232 | } 233 | 234 | const char* MtkDateTimeProvider::getDateTime() { 235 | const int secondsPerMinute = 60; 236 | const int secondsPerHour = secondsPerMinute * 60; 237 | const long int secondsPerDay = secondsPerHour * 24; 238 | /* Determine how many seconds have passed since the last sync, break those 239 | * seconds down into days/hours/minutes/seconds, then form the new time 240 | * string. */ 241 | long int newSecond = ((millis() - syncedTime) / 1000) + second 242 | + (secondsPerMinute * minute) + (secondsPerHour * hour) 243 | + (secondsPerDay * day); 244 | int newDay = newSecond / secondsPerDay; 245 | newSecond = newSecond % secondsPerDay; 246 | int newHour = newSecond / secondsPerHour; 247 | newSecond = newSecond % secondsPerHour; 248 | int newMinute = newSecond / secondsPerMinute; 249 | newSecond = newSecond % secondsPerMinute; 250 | 251 | /* Without cumbersome data about how many days a month we cannot factor the 252 | * extra time into more months. AWS Services give different errors that do 253 | * not provide us with the current time if the date is invalid rather than 254 | * innacurate. However, going past the number of days in a month (i.e 255 | * giving 30 for February) is valid, so long as the days does not exceed 256 | * 31. */ 257 | newDay = newDay > 31 ? 31 : newDay; 258 | 259 | /* Copy the values to dateTime and return it. */ 260 | sprintf(dateTime, "%.4d%.2d%.2d%.2d%.2d%.2d", year, month, newDay, newHour, 261 | newMinute, (int) newSecond); 262 | return dateTime; 263 | } 264 | 265 | bool MtkDateTimeProvider::syncTakesArg(void) { 266 | return true; 267 | } 268 | 269 | void MtkDateTimeProvider::sync(const char* dateTime) { 270 | /* If given a null dateTime, do nothing. */ 271 | if (dateTime) { 272 | /* Set synced time to the number of milliseconds since the sketch 273 | * started. */ 274 | syncedTime = millis(); 275 | /* Copy each substring into the buffer then convert it to an integer. */ 276 | char buffer[5]; 277 | sprintf(buffer, "%.4s", dateTime); 278 | year = atoi(buffer); 279 | sprintf(buffer, "%.2s", dateTime + 4); 280 | month = atoi(buffer); 281 | sprintf(buffer, "%.2s", dateTime + 6); 282 | day = atoi(buffer); 283 | sprintf(buffer, "%.2s", dateTime + 8); 284 | hour = atoi(buffer); 285 | sprintf(buffer, "%.2s", dateTime + 10); 286 | minute = atoi(buffer); 287 | sprintf(buffer, "%.2s", dateTime + 12); 288 | second = atoi(buffer); 289 | 290 | /* If these values come out as 0, change them to valid AWS date values 291 | * (i.e. cannot have month 0). */ 292 | year = year ? year : 2000; 293 | month = month ? month : 1; 294 | day = day ? day : 1; 295 | } 296 | } 297 | 298 | 299 | void printWifiStatus() { 300 | // print the SSID of the network you're attached to: 301 | Serial.print("SSID: "); 302 | Serial.println(LWiFi.SSID()); 303 | // print your LWiFi shield's IP address: 304 | IPAddress ip = LWiFi.localIP(); 305 | Serial.print("IP Address: "); 306 | Serial.println(ip); 307 | // print the received signal strength: 308 | long rssi = LWiFi.RSSI(); 309 | Serial.print("signal strength (RSSI):"); 310 | Serial.print(rssi); 311 | Serial.println(" dBm"); 312 | } 313 | void Mtk_Wifi_Setup(const char* pSSID, const char* pPassword) { 314 | // attempt to connect to Wifi network: 315 | LWiFi.begin(); 316 | while (!LWiFi.connectWPA(pSSID, pPassword)) { 317 | delay(1000); 318 | Serial.println("retry WiFi AP"); 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/mediatek/MtkAWSImplementations.h: -------------------------------------------------------------------------------- 1 | #ifndef MTKAWSIMPLEMENTATIONS_H_ 2 | #define MTKAWSIMPLEMENTATIONS_H_ 3 | /* The EthernetClient is defined in this header. */ 4 | #include 5 | #include 6 | #include 7 | 8 | #include "DeviceIndependentInterfaces.h" 9 | 10 | /* HttpClient implementation to be used on the Mtk device. */ 11 | class MtkHttpClient: public IHttpClient { 12 | LWiFiClient client; 13 | public: 14 | MtkHttpClient(); 15 | /* Send http request and return the response. */ 16 | char* send(const char *request, const char* serverUrl, int port); 17 | /* Returns false. Client uses raw http/https. */ 18 | bool usesCurl(void); 19 | protected: 20 | char* sendHTTP(const char *request, const char* serverUrl, int port); 21 | char* sendHTTPS(const char *request, const char* serverUrl, int port); 22 | }; 23 | 24 | /* Experimental HttpClient for Mtk that takes a curl command. This class is needed for 25 | * testing purposes */ 26 | class MtkHttpCurlClient: public IHttpClient { 27 | public: 28 | MtkHttpCurlClient(); 29 | /* Send http request and return the response.*/ 30 | char* send(const char *request, const char* serverUrl, int port); 31 | /* Returns true. Client uses a curl command. */ 32 | bool usesCurl(void); 33 | }; 34 | 35 | /* IDateTimeProvider implementation to be used on the Mtk device. */ 36 | class MtkDateTimeProvider: public IDateTimeProvider { 37 | /* These values set as default in constructor and updated in sync() */ 38 | int year; 39 | int month; 40 | int day; 41 | int hour; 42 | int minute; 43 | int second; 44 | /* The time in seconds that the MtkDateTimeProvider was last synced. */ 45 | unsigned long syncedTime; 46 | /* The time as a cstring in yyyyMMddHHmmss format. Is written to within and 47 | * returned by getDateTime(). */ 48 | char dateTime[15]; 49 | public: 50 | MtkDateTimeProvider(); 51 | /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. On 52 | * first call this will return a default date. sync() must then be called 53 | * with the current date. */ 54 | const char* getDateTime(void); 55 | /* Returns true. Mtk requires an argument for sync because the device 56 | * does not intenally handle current time. */ 57 | bool syncTakesArg(void); 58 | /* Synchronizes the MtkDateTimeProvider's time given the current time 59 | * in yyyyMMddHHmmss format. */ 60 | void sync(const char* dateTime); 61 | }; 62 | 63 | void Mtk_Wifi_Setup(const char* pSSID, const char* pPassword); 64 | void printWifiStatus(); 65 | 66 | #endif /* MTKAWSIMPLEMENTATIONS_H_ */ 67 | -------------------------------------------------------------------------------- /src/sparkcore/SparkAWSImplementations.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | /* application.h is Spark's standard library. Defines the Arduino String 3 | * object, the Arduino delay() procedure, and the Spark TCPClient. */ 4 | #include 5 | #include "SparkAWSImplementations.h" 6 | #include "DeviceIndependentInterfaces.h" 7 | 8 | int delayTime = 500; 9 | 10 | SparkHttpClient::SparkHttpClient() { 11 | } 12 | 13 | char* SparkHttpClient::send(const char* request, const char* serverUrl, 14 | int port) { 15 | /* Arduino String to build the response with. */ 16 | String responseBuilder = ""; 17 | if (client.connect(serverUrl, port)) { 18 | /* Send the requests */ 19 | client.println(request); 20 | client.println(); 21 | /* Read the request into responseBuilder. */ 22 | delay(delayTime); 23 | while (client.available()) { 24 | char c = client.read(); 25 | responseBuilder.concat(c); 26 | } 27 | client.stop(); 28 | } else { 29 | client.stop(); 30 | /* Error connecting. */ 31 | return 0; 32 | } 33 | /* Copy responseBuilder into char* */ 34 | int len = responseBuilder.length(); 35 | char* response = new char[len + 1](); 36 | responseBuilder.toCharArray(response, len + 1); 37 | return response; 38 | return 0; 39 | } 40 | 41 | bool SparkHttpClient::usesCurl() { 42 | /* Does not use curl command. */ 43 | return false; 44 | } 45 | 46 | SparkDateTimeProvider::SparkDateTimeProvider() { 47 | /* No need to sync, spark sychronizes time on startup. */ 48 | } 49 | 50 | const char* SparkDateTimeProvider::getDateTime() { 51 | /* Spark provides the current time, so all we have to do is format it. */ 52 | sprintf(dateTime, "%.4d%.2d%.2d%.2d%.2d%.2d", Time.year(), Time.month(), 53 | Time.day(), Time.hour(), Time.minute(), Time.second()); 54 | return dateTime; 55 | } 56 | 57 | bool SparkDateTimeProvider::syncTakesArg(void) { 58 | return false; 59 | } 60 | 61 | void SparkDateTimeProvider::sync(const char* dateTime) { 62 | /* Use Spark's servers to synchronize current time. */ 63 | Spark.syncTime(); 64 | } 65 | -------------------------------------------------------------------------------- /src/sparkcore/SparkAWSImplementations.h: -------------------------------------------------------------------------------- 1 | #ifndef AWSSPARKIMPLEMENTATIONS_H_ 2 | #define AWSSPARKIMPLEMENTATIONS_H_ 3 | #include "DeviceIndependentInterfaces.h" 4 | /* application.h is Spark's standard library. Define TCPClient. */ 5 | #include 6 | 7 | /* HttpClient implementation to be used on the Spark Core device. */ 8 | class SparkHttpClient: public IHttpClient { 9 | TCPClient client; 10 | public: 11 | SparkHttpClient(); 12 | /* Send http request and return the response. */ 13 | char* send(const char *request, const char* serverUrl, int port); 14 | /* Returns false. Client uses raw http/https. */ 15 | bool usesCurl(void); 16 | }; 17 | 18 | class SparkDateTimeProvider: public IDateTimeProvider { 19 | /* The time as a cstring in yyyyMMddHHmmss format. Is written to within and 20 | * returned by getDateTime(). */ 21 | char dateTime[15]; 22 | public: 23 | SparkDateTimeProvider(); 24 | /* Retrieve the current GMT date and time in yyyyMMddHHmmss format. */ 25 | const char* getDateTime(void); 26 | /* Returns false because Spark has it's own mechanism for syncing that does 27 | * not require an argument. */ 28 | bool syncTakesArg(void); 29 | /* Synchronizes Spark's date and time with Spark's servers. The dateTime 30 | * argument is ignored. */ 31 | void sync(const char* dateTime); 32 | }; 33 | 34 | #endif /* AWSSPARKIMPLEMENTATIONS_H_ */ 35 | --------------------------------------------------------------------------------