How We Added Surge Pricing Capabilities to our IoT eScooter with AWS Lambda and SORACOM Funk

Written by aahuang | Published 2019/10/31
Tech Story Tags: transportation | raspberry-pi | python | business-ideas | latest-test-stories | iot-top-story | internet-of-things | iot

TLDR We’ll use Amazon Web Services’ serverless platform, AWS Lambda, to create Lambda functions. Lambda lets you focus on writing code rather than managing servers and costs. We use MongoDB: a No.J database to store scooters’ data and payment transaction information. We also need a way to calculate prices in realtime using SORACOM Funk. We can offload this time and power-intensive logic onto Lambda using Lambda. It essentially streamlines the process of passing data from the eScooter-mounted Raspberry Pi and a serverless compute instance like Lambda to the cloud.via the TL;DR App

We’ll assume that you’ve completed the steps from the first part of our eScooter series already. For a broader overview of this project, check out the first article here.
So you’ve set up your IoT eScooter with SORACOM Air in part one, sent scooter data to the cloud with SORACOM Harvest in part two, and remotely managed your scooter with SORACOM Napter in part three. So far, we’ve only been collecting data and managing our devices. In this fourth and final article of the series, we’ll link everything together with an Android app and add surge pricing capabilities to our IoT eScooter with SORACOM Funk.
With a basic surge pricing model, we can somewhat accurately reflect the current scooter supply and demand in the area and adapt our scooters' prices accordingly. With our model, a scooter’s price per minute will rise if more scooters are already in use around it.
Although we created a way to send data from our Raspberry Pi in the last article, we still need a way to calculate prices in realtime. Normally this would require a lot of processing power on the Raspberry Pi, but we can offload this time and power-intensive logic onto Lambda using SORACOM Funk. It essentially streamlines the process of passing data from the eScooter-mounted Raspberry Pi and a serverless compute instance like AWS Lambda. Instead of spending our time on setting up more functionality on Lambda, we’ll focus on setting up Funk, which is much easier. 
We’ll be using Amazon Web Services’ (AWS) serverless platform, AWS Lambda. We can use it to create Lambda functions, which scale automatically as our userbase grows. What this means for you is that you only pay for the compute time you actually use, so you don’t need to think about server usage beforehand. Basically, Lambda lets you focus on writing code rather than managing servers and costs.
To get started with Lambda, make an AWS account if you don’t already have one. We’ll be using Node.js with the Serverless platform for our Lambda functions, but do note that you can also run other languages with Lambda.
Since we’re using Node.js in this article, make sure that you have it installed on your computer beforehand. If you don’t, follow this link for Node.js. You can then verify your installation with
$ node -v
which will print out your currently installed version of Node.js.


Create a DocumentDB Cluster

We’ll first set up a database instance to store our scooters’ data so that our app can access it later. We’re going to use MongoDB: a NoSQL database to store scooter data and payment transaction information.
To connect to MongoDB, we’ll use AWS’s dedicated NoSQL service: DocumentDB. You can access it from your AWS console here.
Under DocumentDB on the console, click Create. Now give your DocumentDB cluster a unique identifier. Change the number of instances to what you feel fit. We set it to 1 because fewer instances means less cost. For authentication, come up with a username and password. This will be used to connect to the DocumentDB cluster manually and programmatically. Finally, click Create Cluster.
After AWS finishes creating your cluster, you will be presented with a page of commands like
$ mongo --ssl --host scooter-db.[redacted].us-east-1.docdb.amazonaws.com:27017 --sslCAFile rds-combined-ca-bundle.pem --username scooteradmin --password <insertYourPassword>
and 
mongodb://scooteradmin:<insertYourPassword>@scooter-db.[redacted].us-east-1.docdb.amazonaws.com:27017/?ssl=true&ssl_ca_certs=rds-combined-ca-bundle.pem&replicaSet=rs0
In this page, under Security Groups, click Default. In the tabs below, click Inbound and Edit to match this
This will ensure that your Lambda functions will be able to connect to DocumentDB. 
Optionally, you can verify your DocumentDB setup by creating an AWS EC2 instance under the same region as the DocumentDB cluster with the MongoDB client installed and run the
mongo
command from earlier. You’ll need to download DocumentDB’s public key for the command to work. Get it with this command:
$ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem
After you’ve completed this step, your MongoDB database should be set up for storing your scooters’ records.

Create Lambda Functions

Now that we have a database on AWS, we can begin working on the Lambda function. It will be responsible for the functionality of what we have on AWS, including creating/updating database entries, interacting with SORACOM Funk, and serving data to our app. 
We’ll begin by installing
serverless
, which is a Node.js tool that helps us deploy serverless functions quickly. To install
serverless
, run
$ npm install -g serverless
To verify your installation, run 
$ serverless
If you get an error, you may have to restart your terminal window or rerun the installation command. 
Now link the tool to AWS. Go to IAM (Identity & Access Management) from your AWS Dashboard.
Click Users and Add User. Name the user something like “serverless-admin” and enable programmatic access.
Hit next and Attach existing policies directly. Check
AdministratorAccess
and click Create User.
Now, it should show your new user’s API credentials.
With these credentials, enter this in your terminal with your Access ID replacing
{access_id}
and API Secret replacing
{secret_key}
$ serverless config credentials --provider aws --key {access_id} --secret {secret_key}
Great! Your serverless-cli is all setup! Download the serverless project with
$ git clone https://github.com/DevinMui/soracom-escooter-server
Switch into the project directory
$ cd soracom-escooter-server
Install the dependencies
$ npm install
Install the DocumentDB public key
$ wget https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem

Connecting to Stripe to Securely Handle Payments

We’ll use Stripe to collect payments from our customers. Stripe gives us a simple-to-use yet secure payment API, which we can use in our server for a small fee. 
First, create a Stripe account if you haven’t done so already. In your Stripe dashboard, under Developers, click API keys. Copy your Secret key. You can also use your test key by flipping the switch to view test data, but doing so will prevent Stripe from charging your users’ credit cards until you disable test mode.
This secret key essentially acts as a password to calling Stripe’s API through code. Make sure to keep it in a safe place and never share it, because anyone who has access to this key could get access to your payments through Stripe. If this happens, you should request a new key through the same API keys page.
Once you have your secret API key, you can start using Stripe! Keep the key on hand, because we’ll need to add it to our Lambda function in the next step.

Configure
serverless.yml

Although we already have the serverless code for the Lambda function written for you already, we’ll need to create an additional file
serverless.yml
before we deploy our code to AWS. This file will store vital settings and keys (i.e. the Stripe API key). Because of how important this file is, you should keep it hidden from the public (which is why it is not included in the GitHub repository).
Create a new file
env.yml
. This file will hold all our environmental variables, which can be accessed through the Lambda function’s code. Add the following lines into the file, replacing
{stripe_secret_key}
with your Stripe API key and
{mongo_url}
with the MongoDB URL from the steps above. Be sure to follow the format of the code below exactly or AWS might reject the file
default:
  STRIPE_SECRET_KEY: {stripe_secret_key}
  MONGO_URL: {mongo_url}/test?ssl=true&replicaSet=rs0%
Now that we have that file configured, deploy the application with
$ serverless deploy
Now if you try to access the
getScooters
function, you may notice the request will timeout. This is because Lambda is not set under the same Virtual Private Cloud (VPC) as DocumentDB and therefore has no access to DocumentDB.
Go to your AWS Dashboard. Click Services. Under the Compute section, click Lambda.
Click on one of your scooter Lambda functions. Scroll down until you see the Network panel. Under the VPC selector, click the one associated with your DocumentDB cluster. Under Subnets and Security Groups, copy two of each ID (e.g. subnet-37fca853 and sg-163e825e).
Now update your
serverless.yml
file to update the VPC settings to use the correct subnet IDs and security group IDs. Under line 10, replace the security group IDs listed with your security group IDs. Under line 13, replace the subnet IDs with your subnet IDs. 
vpc:
    securityGroupIds:
      - # your first security group id
      - # your second security group id
    subnetIds:
      - # your first subnet id
      - # your second subnet id
      - # your third subnet id
  iamRoleStatements:
Redeploy your Lambda functions.
$ serverless deploy
Open the link associated to the
getScooters
function. If the server responds with a
[]
, it worked! Your functions are now connected to DocumentDB.

Set up NAT

Unfortunately, by setting up DocumentDB, Lambda won’t be able to connect to the outside Internet anymore. However, the solution is trivial to implement. We just need to change our NAT and routing settings.
Create a new NAT Gateway pointing to the public subnet.
Create a Routing Table pointing
0.0.0.0/0
to the Internet Gateway
Find the public subnet and under Actions, click Edit route table association
Create two new subnets. It may be useful to label these two as private subnets. Create another Routing Table. This time point
0.0.0.0/0
to the NAT Gateway we created earlier.
On both private subnets, edit the route associations to use this new routing table. 
For more information on how this works or for troubleshooting, take a look at AWS’s official documentation/support.

Funk Up Your Pricing Model

To make our operation more profitable, we can add surge pricing to our fleet to reflect the current scooter supply and demand in the area. This way, a scooter’s price per minute will rise if there are other scooters in use around its area. We can easily achieve this using SORACOM Funk which streamlines the connection between our cellular IoT device and AWS Lambda.
We’ll have to create another IAM user in AWS to grant Soracom access to our function first. Like before, open the IAM dashboard and add a new user. Enter a name for the user like
soracom-funk
and check Programmatic access. 
In the next screen, click Attach existing policies directly and click the Create policy button. Under the JSON tab, copy paste the following.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "VisualEditor0",
      "Effect": "Allow",
      "Action": "lambda:InvokeFunction",
      "Resource": "<Lambda function ARN of the createOrUpdateScooter function>"
    }
  ]
}
Make a name for the policy and click the Create policy button. Go back to the Add user screen, find the policy you just created, and click its checkbox. Click the Next buttons until the user is created. It is imperative that you do not close this window. If you do, you will have to restart recreating the IAM user process as the secret key is only viewable immediately after the creation of a new IAM user.
As with all Soracom products, click on the group you want to enable SORACOM Funk. Find the SORACOM Funk panel and turn Funk on. Change the service the
AWS Lambda
, content type to
JSON
, and the function ARN to the Lambda function ARN of the
createOrUpdateScooter
function.
For the credentials, you’ll have to click
Add new credentials
. Change the
Credentials set ID
to a label you can identify. From the IAM user information page, paste in the AWS Access key ID and Secret access key and click Register
With those few steps, SORACOM Funk has now been set up. When you make an HTTP request to
http://funk.soracom.io
, this request will now be forwarded instead to your Lambda function. This forwarding is pretty powerful and allows developers to offload processor/battery intensive operations from IoT devices to AWS Lambda instead. For this article, we’ll offload the algorithm to calculate the surge price of the scooter from the Raspberry Pi to the cloud. If you take a look at the
createOrUpdateScooter
function, you can see that the algorithm may take a while to run depending on how many scooters are active and how many scooters are within the current scooter’s location, making this a realistic example of how SORACOM Funk can reduce computational load on cellular devices.
Next, we’ll want to set up our cellular device to connect to SORACOM Funk. SSH into your Raspberry Pi. Clone the repository if you have not been following along with our previous articles.
$ git clone https://github.com/DevinMui/soracom-escooter-device
Switch into the project directory.
$ cd soracom-escooter-device
Install the necessary Python modules.
$ pip install -r requirements.txt
On line 7 of
funk.py
, update the
mac
variable to reflect your scooter’s Bluetooth MAC address. Finally, start the program.
$ python server.py
Your scooter will now send data over to the SORACOM Funk endpoints every five seconds! To test this, you can create a front end solution to call the other Lambda functions to lock/unlock the scooter OR you can use our React Native app below to test it out. You should see the location of the scooter visualized on the map.

Run the App

If you don’t want to spend more time creating a new app for your user, you can simply use our example app. It currently runs only on Android and relies on React Native. As a result, you’ll need to have Android Studio setup for React Native (which you can do by following the official React Native documentation here). There’s also a small setup process for the app that you’ll need to complete if you’re building it for your own machine. 
First clone the repository
$ git clone https://github.com/aaronkh/soracom-escooter-app.git
and install the required modules with 
$ npm install
at the root of the cloned repository.
Since this project relies on Google Maps, we’ll need a Google Maps API key. You should follow the official documentation for doing so here. Like the Stripe key, keep this key hidden from the public. You can do so by hiding it in your
gradle.properties
file. This file is found under
/Users/{Username}/.gradle
on both Mac and Windows. If it doesn’t already exist create one named
gradle.properties
. Add
GMAPS_SDK_KEY="{YOUR_API_KEY}"
into the file, replacing
{YOUR_API_KEY}
with your own API key. You can optionally restrict access to only allow your app in your Google Cloud console, but we’ll skip that for now. 
Next we’ll set up a connection to Lambda through the app. To do so, simply add the base URL to your Lambda function as an environment variable named
API_URL
. Do so with 
$ export API_URL={YOUR_URL}
on Mac/Linux or 
> setx API_URL "{YOUR_URL}"
on Windows, again replacing
{YOUR_URL}
with your Lambda URL.
If you forgot the URL or lost it, simply run
$ serverless deploy
and it should print the invocation URL to your console. 
Lastly, we’ll need to add our Stripe API key to the app. Open up the API keys page on Stripe and copy the publishable key under the Standard Keys table. Make sure that you are using the publishable key and not the secret key. This publishable key will be visible to users who download the app, but it’s useless without your secret key, which is hidden to your users in Lambda’s
env.yml
file. Create an environment variable named
STRIPE_KEY
with 
$ export STRIPE_KEY={YOUR_KEY}
on Mac/Linux or 
> setx STRIPE_KEY "{YOUR_KEY}"
on Windows, again replacing
{YOUR_KEY}
with your Stripe key.
Finally, we can install the app onto an Android device by running
$ react-native run-android
at the root of the repository.

Conclusion

After the long setup process for AWS Lambda, you should now be able to access both the state and pricing of every electric scooter in your fleet. Combined with the steps in our previous articles, you should also be able to lock and unlock your scooter from anywhere in the world and see if any scooters need recharging. We even have a user-facing Android app to help users locate electric scooters around them and pay for their rides. Plus, with our surge pricing model and AWS’s auto-scaling Lambda functions on the cloud, your electric scooters’ prices will adapt to your platform’s demand, guaranteeing increased revenues as your user base grows. Now we have all the tools needed to kickstart your new IoT eScooter startup!
In this four-part series, we’ve explored how to build a cellular connected IoT eScooter using Soracom’s services in depth. We learned how to connect devices to the cloud through a cellular connection with SORACOM Air, how to collect and visualize scooter data with SORACOM Harvest and Lagoon, how to remotely connect and troubleshoot your scooter with SORACOM Napter, and how to offload computationally complex logic from our device to the cloud through SORACOM Funk
All in all, creating an IoT eScooter was much easier than we’d imagined. We expected to spend more hours reading documentation and writing code to connect our electric scooter to the cloud. However, we found that the development time was greatly reduced because of the Soracom platform’s simplicity. Most of the Soracom integrations took only minutes to set up and only required us to add a single HTTP request into our code.
We’ve only scratched the surface of what the SORACOM platform is capable of. They offer dozens of other easy-to-use products and services, and I’m sure you makers can come up with new and unique ways to leverage the SORACOM platform to build your next game-changing IoT project.

Written by aahuang | Software Engineer and Student @ UC Berkeley
Published by HackerNoon on 2019/10/31