├── LICENSE ├── README.md ├── aws_button.png ├── aws_button_template.json ├── fs └── init.js └── mos.yml /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Cesanta Software Limited 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS IoT button example 2 | 3 | This is an Internet Button reference project: when a button on the device 4 | is pressed, a cloud backend gets a notification and performs an action. 5 | In this particular case, AWS Lambda function sends an email to the specific 6 | email address. But, again, the action could be anything. 7 | 8 | [![aws iot button](https://img.youtube.com/vi/yZ8VAxJ2XpA/0.jpg)](https://www.youtube.com/watch?v=yZ8VAxJ2XpA) 9 | 10 | ## Prerequisites 11 | 12 | - Hardware: ESP8266 NodeMCU 13 | - Amazon AWS account 14 | - Amazon's `aws` management tool (see https://aws.amazon.com/cli) 15 | - `mos` management tool installed 16 | (see [mos installation guide](https://mongoose-os.com/software.html)) 17 | 18 | ## Architecture 19 | 20 |

21 | 22 |

23 | 24 | The data flow is as follows: 25 | 26 | - User presses the button 27 | - Device sends a message to the MQTT topic `DEVICE_ID/button_pressed` 28 | - AWS IoT receives the message and calls AWS Lambda Function 29 | - AWS Lambda Function publishes a message to the AWS SNS (Simple Notification Service) 30 | - AWS SNS notifies subscribers: in this case, just sends a message to a single email address 31 | - User receives the email 32 | 33 | ## Build instructions 34 | 35 | 1. Follow the [Cloud side setup](https://mongoose-os.com/aws-iot-starter-kit/#cloud) instructions to setup AWS CLI utility and your AWS credentials 36 | 2. Follow the [Device setup](https://mongoose-os.com/aws-iot-starter-kit/#dev) instructions to setup your device and provision it to the AWS IoT 37 | 3. Download [this repository as a zip file](https://github.com/mongoose-os-apps/aws-iot-button/archive/master.zip) and extract this app on your computer 38 | 4. Exit any running `mos.exe` process 39 | 5. Open a command prompt (on Windows) or terminal (on Mac/Linux) and go to the extracted app. 40 | 6. You should be able to see the `mos.yml` file by running `dir mos.yml` command (on Windows) or `ls -l mos.yml` (on Mac/Linux) 41 | 7. Find out your device ID 42 | ``` 43 | mos config-get device.id 44 | ``` 45 | On Windows, here and further, you might need to 46 | specify the full path to the `mos.exe` binary: 47 | 48 | ``` 49 | c:\path\to\mos.exe config-get device.id 50 | ``` 51 | 8. Run the following command to create AWS Cloud Formation stack. 52 | Change `$DEVICE_ID` to your actual device ID, and `$MY_EMAIL` to your email: 53 | 54 | ``` 55 | aws cloudformation create-stack --stack-name my-internet-button --parameters ParameterKey=TopicName,ParameterValue=$DEVICE_ID/button_pressed ParameterKey=SubscriptionEmail,ParameterValue=$MY_EMAIL --capabilities CAPABILITY_IAM --template-body file://aws_button_template.json 56 | ``` 57 | 58 | 9. Wait until the stack creation is completed (it may take a few minutes). 59 | Alternatively, you can use the web UI to check the status and read event 60 | details: https://console.aws.amazon.com/cloudformation/home 61 | 62 | 10. During the stack creation, AWS will send a Subscription Confirmation email, 63 | so check your email and confirm the subscription by following a link. 64 | 65 | 11. Run the following command to ensure that the stack creation is complete: 66 | ``` 67 | aws cloudformation wait stack-create-complete --stack-name my-internet-button 68 | ``` 69 | 70 | 11. Copy the `fs/init.js` file to your device: 71 | ``` 72 | mos put fs/init.js 73 | ``` 74 | 75 | 12. Attach to the device to see the device logs 76 | ``` 77 | mos console 78 | ``` 79 | 13. Reboot your device by pressing a reboot button 80 | 13. When the device is connected to the AWS IoT, push the "flash" 81 | button on your device. 82 | In the device's console, you'll see a message like this: 83 | 84 | ``` 85 | Published: yes topic: esp8266_DA84C1/button_pressed message: {"free_ram":26824,"total_ram":44520} 86 | ``` 87 | 88 | Now, check your email. It'll contain a new message: 89 | 90 | ``` 91 | Button pressed: esp8266_DA84C1/button_pressed 92 | ``` 93 | 94 | 14. Now you can go to your AWS dashboard and play with your stack. 95 | For example, you may add more subscriptions to the SNS: other than 96 | sending emails, it can also call some URL, send SMS, etc. And, of course, 97 | you can modify your lambda function to do whatever you want in response to 98 | the button press. 99 | 100 | -------------------------------------------------------------------------------- /aws_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mongoose-os-apps/aws-iot-button/69df36586e3437203e0e968817651da8925788e5/aws_button.png -------------------------------------------------------------------------------- /aws_button_template.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "Mongoose OS Internet Button Template", 4 | "Parameters": { 5 | "TopicName": { 6 | "Description": "IoT Topic Name", 7 | "Type": "String", 8 | "AllowedPattern": "[a-zA-Z0-9_/#+]*", 9 | "MinLength": "1", 10 | "MaxLength": "2048", 11 | "ConstraintDescription": "must contain only alphanumberic characters and underscores" 12 | }, 13 | "SubscriptionEmail": { 14 | "Description": "Subscription Email", 15 | "Type": "String", 16 | "AllowedPattern": ".+", 17 | "MinLength": "1", 18 | "MaxLength": "2048" 19 | } 20 | }, 21 | "Resources": { 22 | "mySNSTopic": { 23 | "Type": "AWS::SNS::Topic", 24 | "Properties": { 25 | "Subscription": [ 26 | { 27 | "Protocol": "email", 28 | "Endpoint": {"Ref": "SubscriptionEmail"} 29 | } 30 | ] 31 | } 32 | }, 33 | "myLambda": { 34 | "Type": "AWS::Lambda::Function", 35 | "Properties": { 36 | "Code": { 37 | "ZipFile": { 38 | "Fn::Join": ["\n", [ 39 | "var AWS = require('aws-sdk');", 40 | "", 41 | "exports.handler = (event, context, callback) => {", 42 | {"Fn::Join": ["", [" var message = 'Button pressed: ", { "Ref": "TopicName" }, "';"]]}, 43 | " var sns = new AWS.SNS();", 44 | " sns.publish({", 45 | {"Fn::Join": ["", [" TopicArn: '", { "Ref": "mySNSTopic" }, "',"]]}, 46 | " Message: JSON.stringify(message)", 47 | " }, function(err, data) {", 48 | " if(err) {", 49 | " console.error('error publishing to SNS');", 50 | " context.fail(err);", 51 | " } else {", 52 | " console.info('message published to SNS');", 53 | " context.succeed();", 54 | " }", 55 | " });", 56 | "};", 57 | "" 58 | ]] 59 | } 60 | }, 61 | "Handler": "index.handler", 62 | "Runtime": "nodejs4.3", 63 | "Role": {"Fn::GetAtt": ["myLambdaRoleWhichAllowsCallingSNS", "Arn"]} 64 | } 65 | }, 66 | "myLambdaRoleWhichAllowsCallingSNS": { 67 | "Type": "AWS::IAM::Role", 68 | "Properties": { 69 | "AssumeRolePolicyDocument": { 70 | "Version" : "2012-10-17", 71 | "Statement": [ { 72 | "Effect": "Allow", 73 | "Principal": { 74 | "Service": [ "lambda.amazonaws.com" ] 75 | }, 76 | "Action": [ "sts:AssumeRole" ] 77 | } ] 78 | }, 79 | "Path": "/", 80 | "Policies": [ 81 | { 82 | "PolicyName": "allow-call-sns", 83 | "PolicyDocument": { 84 | "Version" : "2012-10-17", 85 | "Statement": [ { 86 | "Effect": "Allow", 87 | "Action": "sns:Publish", 88 | "Resource": { "Ref": "mySNSTopic" } 89 | } ] 90 | } 91 | } 92 | ] 93 | } 94 | }, 95 | "myTopicRule": { 96 | "Type": "AWS::IoT::TopicRule", 97 | "Properties": { 98 | "TopicRulePayload": { 99 | "RuleDisabled": "false", 100 | "Sql": { 101 | "Fn::Join" : [ "", ["SELECT * FROM '", { "Ref": "TopicName" }, "'"] ] 102 | }, 103 | "Actions": [ 104 | { 105 | "Lambda": { 106 | "FunctionArn": {"Fn::GetAtt": ["myLambda", "Arn"]} 107 | } 108 | } 109 | ] 110 | } 111 | } 112 | }, 113 | "myPermissionForTopicRuleToInvokeLambda": { 114 | "Type": "AWS::Lambda::Permission", 115 | "Properties": { 116 | "Action": "lambda:InvokeFunction", 117 | "FunctionName": {"Fn::GetAtt": ["myLambda", "Arn"]}, 118 | "Principal": "iot.amazonaws.com", 119 | "SourceAccount": {"Ref": "AWS::AccountId"}, 120 | "SourceArn": { 121 | "Fn::Join": [ 122 | "", [ 123 | "arn:aws:iot:", 124 | { "Ref": "AWS::Region" }, 125 | ":", 126 | { "Ref": "AWS::AccountId" }, 127 | ":rule/", 128 | { "Ref": "myTopicRule" } 129 | ] 130 | ] 131 | } 132 | } 133 | } 134 | }, 135 | "Outputs": { 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /fs/init.js: -------------------------------------------------------------------------------- 1 | // This example demonstrates how to react on a button press 2 | // by sending a message to AWS IoT. 3 | // 4 | // See README.md for details. 5 | // 6 | // Load Mongoose OS API 7 | load('api_gpio.js'); 8 | load('api_mqtt.js'); 9 | load('api_sys.js'); 10 | load('api_config.js'); 11 | 12 | let pin = 0; // GPIO 0 is typically a 'Flash' button 13 | GPIO.set_button_handler(pin, GPIO.PULL_UP, GPIO.INT_EDGE_NEG, 50, function(x) { 14 | let topic = Cfg.get('device.id') + '/button_pressed'; 15 | let message = JSON.stringify({ 16 | total_ram: Sys.total_ram(), 17 | free_ram: Sys.free_ram() 18 | }); 19 | let ok = MQTT.pub(topic, message, 1); 20 | print('Published:', ok ? 'yes' : 'no', 'topic:', topic, 'message:', message); 21 | }, true); 22 | 23 | print('Flash button is configured on GPIO pin ', pin); 24 | print('Press the flash button now!'); 25 | -------------------------------------------------------------------------------- /mos.yml: -------------------------------------------------------------------------------- 1 | author: mongoose-os 2 | description: Internet button on AWS IoT 3 | # arch: PLATFORM 4 | version: 1.0 5 | manifest_version: 2017-05-18 6 | 7 | libs_version: ${mos.version} 8 | modules_version: ${mos.version} 9 | mongoose_os_version: ${mos.version} 10 | 11 | tags: 12 | - js 13 | - c 14 | - cloud 15 | - aws 16 | 17 | filesystem: 18 | - fs 19 | 20 | libs: 21 | # common mgos libs 22 | - origin: https://github.com/mongoose-os-libs/boards 23 | - origin: https://github.com/mongoose-os-libs/ca-bundle 24 | - origin: https://github.com/mongoose-os-libs/i2c 25 | - origin: https://github.com/mongoose-os-libs/rpc-service-config 26 | - origin: https://github.com/mongoose-os-libs/rpc-service-fs 27 | - origin: https://github.com/mongoose-os-libs/rpc-uart 28 | - origin: https://github.com/mongoose-os-libs/spi 29 | 30 | # libs necessary for the current app 31 | - origin: https://github.com/mongoose-os-libs/aws 32 | - origin: https://github.com/mongoose-os-libs/mjs 33 | - origin: https://github.com/mongoose-os-libs/wifi 34 | --------------------------------------------------------------------------------