Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 8 | ||||
Software apps and online services | ||||||
| ||||||
| ||||||
| ||||||
| ||||||
|
It is fun to set message by your voice!
Setup Flow- Arduino YUN AWS IoT SDK setup
- Arduino Project
- Wiring
- Lambda
- Skill
Refer to https://github.com/aws/aws-iot-device-sdk-arduino-yun
Everything you need is described. Here is just a summary.
- First, please configure network connection of Arduino YUN, by either by wired or wireless.
- Create AWS IoT Credentials, and copy them to local folder.
- Setup SSH connection to Arduino YUN.
- Download AWS IoT SDK.
- Run setup script.
Download source codes:
- MyMessageBoard.ino
- DotMatrixControl.cpp
- DotMatrixControl.h
- aws_iot_config.h (Change IoT endpoint url with yours)
Add library from https://github.com/nickgammon/MAX7219_Dot_Matrix/tree/master/src
Place them in the library folder.
WiringWire YUN, Dot Matrix and Rotary Angle Sensor as Circuit Design diagram.
Lambda- Go to AWS console, AWS Lambda page.
- Create a new function of Node.js.
- Copy the index.js into the code area
- Change IoT endpoint url with yours and save it.
- Setup trigger from Alexa Skill Kit, access to AWS IoT and CloudWatch logs.
Finally skill
- Go to Amazon developper console.
- Go to Alexa Skill Kit page.
- Add a new skill.
- Enter Name and Invocation Name
- In configuration page, you need to enter Lambda ARN endpoint which created above.
Now you can test this project! Enjoy it!
/*
* Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
#ifndef config_usr_h
#define config_usr_h
// Copy and paste your configuration into this file
//===============================================================
#define AWS_IOT_MQTT_HOST "XXXXXXXXXXXX.iot.us-east-1.amazonaws.com" // your endpoint
#define AWS_IOT_MQTT_PORT 8883 // your port
#define AWS_IOT_CLIENT_ID "My_ClientID" // your client ID
#define AWS_IOT_MY_THING_NAME "My_Board" // your thing name
#define AWS_IOT_ROOT_CA_FILENAME "aws-iot-rootCA.crt" // your root-CA filename
#define AWS_IOT_CERTIFICATE_FILENAME "cert.pem" // your certificate filename
#define AWS_IOT_PRIVATE_KEY_FILENAME "privkey.pem" // your private key filename
//===============================================================
// SDK config, DO NOT modify it
#define AWS_IOT_PATH_PREFIX "../certs/"
#define AWS_IOT_ROOT_CA_PATH AWS_IOT_PATH_PREFIX AWS_IOT_ROOT_CA_FILENAME // use this in config call
#define AWS_IOT_CERTIFICATE_PATH AWS_IOT_PATH_PREFIX AWS_IOT_CERTIFICATE_FILENAME // use this in config call
#define AWS_IOT_PRIVATE_KEY_PATH AWS_IOT_PATH_PREFIX AWS_IOT_PRIVATE_KEY_FILENAME // use this in config call
#endif
#include "DotMatrixControl.h"
DotMatrixControl::DotMatrixControl(int dataPin, int clkPin, int csPin, int _displayCount) {
DATA_PIN = dataPin;
CLK_PIN = clkPin;
CS_PIN = csPin;
displayCount = _displayCount;
if (displayCount <= 0) displayCount = 1;
if (displayCount > MAX_DISPLAY_COUNT) displayCount = MAX_DISPLAY_COUNT;
}
void DotMatrixControl::setup() {
digitalWrite(CS_PIN, HIGH);
pinMode(DATA_PIN, OUTPUT);
pinMode(CLK_PIN, OUTPUT);
pinMode(CS_PIN, OUTPUT);
for (int i = 0; i < sizeof(buffer); i++) buffer[i] = 0x00;
//shutdown all display first
for (int i = 0; i < displayCount; i++) shutdown(i, true);
for (int i = 0; i < displayCount; i++) {
send(i, REG_DISPLAY_TEST, 0);
send(i, REG_SCAN_LIMIT, 7);
send(i, REG_DECODE_MODE, 0);
setIntensity(i, 0);
}
clearDisplay();
//shutdown off
for (int i = 0; i < displayCount; i++) shutdown(i, false);
}
void DotMatrixControl::shutdown(int index, bool isShutdown) {
if ((index < 0) || (index >= displayCount)) return;
send(index, REG_SHUTDOWN, isShutdown ? 0 : 1);
}
//intensity : 0-15
void DotMatrixControl::setIntensity(int intensity) {
if ((intensity < 0) || (intensity >= 16)) return;
for (int x = 0; x < displayCount; x++) send(x, REG_INTENSITY, intensity);
}
void DotMatrixControl::setIntensity(int index, int intensity) {
if ((index < 0) || (index >= displayCount)) return;
if ((intensity < 0) || (intensity >= 16)) return;
send(index, REG_INTENSITY, intensity);
}
void DotMatrixControl::clearDisplay() {
clearBuffer();
transferAllDots();
}
void DotMatrixControl::clearDisplay(int index) {
if ((index < 0) || (index >= displayCount)) return;
clearBuffer(index);
transferAllDots();
}
void DotMatrixControl::clearBuffer() {
for (int x = 0; x < displayCount; x++) clearBuffer(x);
}
void DotMatrixControl::clearBuffer(int index) {
if ((index < 0) || (index >= displayCount)) return;
for (int i = 0; i < 8; i++) buffer[index * 8 + i] = 0;
}
//(0, 0) is right top corner
void DotMatrixControl::setDot(int x, int y, bool value) {
int index = x / 8;
int column = 7 - (x % 8);
int row = y;
setDot(index, row, column, value);
}
void DotMatrixControl::setDot(int index, int row, int column, bool value) {
if ((index < 0) || (index >= displayCount)) return;
if ((row < 0) || (row > 7)) return;
if ((column < 0) || (column > 7)) return;
int offset = index * 8;
byte val = 1 << (7 - column);
if (value) {
buffer[offset + row] = buffer[offset + row] | val;
} else {
val = ~val;
buffer[offset + row] = buffer[offset + row] & val;
}
}
void DotMatrixControl::transferAllDots() {
for (int i = 0; i < 8; i++) {//each line
digitalWrite(CS_PIN, LOW);
for (int x = 0; x < displayCount; x++) {//each device
int offset = ((displayCount - 1) - x) * 8;
shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, REG_DIGIT_0 + i);
shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, buffer[offset + i]);
}
digitalWrite(CS_PIN, HIGH);
}
}
void DotMatrixControl::shiftLeft() {
for (int i = 0; i < 8; i++) {//each line
for (int x = 0; x < displayCount; x++) {//each device
int offset = ((displayCount - 1) - x) * 8;
int mostBit = buffer[offset + i] >> 7;
buffer[offset + i] <<= 1;
if (x > 0) {
int offsetLeftDevice = ((displayCount - 1) - (x - 1)) * 8;
buffer[offsetLeftDevice + i] += mostBit;
}
}
}
}
void DotMatrixControl::send(int index, byte reg, byte value) {
int dataSize = displayCount * 2;
byte sendData[MAX_DISPLAY_COUNT * 2];
memset(sendData, 0, sizeof(sendData));
sendData[index * 2] = reg;
sendData[index * 2 + 1] = value;
digitalWrite(CS_PIN, LOW);
for (int i = 0; i < displayCount; i++) {
shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, sendData[i * 2]); //reg
shiftOut(DATA_PIN, CLK_PIN, MSBFIRST, sendData[i * 2 + 1]); //value
}
digitalWrite(CS_PIN, HIGH);
}
#ifndef DotMatrixControl_h
#define DotMatrixControl_h
#include <avr/pgmspace.h>
#include <Arduino.h>
#define MAX_DISPLAY_COUNT 4
#define REG_NO_OP 0
#define REG_DIGIT_0 1
#define REG_DECODE_MODE 9
#define REG_INTENSITY 10
#define REG_SCAN_LIMIT 11
#define REG_SHUTDOWN 12
#define REG_DISPLAY_TEST 15
class DotMatrixControl {
private:
byte buffer[MAX_DISPLAY_COUNT * 8];
int DATA_PIN;
int CLK_PIN;
int CS_PIN;
int displayCount;
public:
DotMatrixControl(int dataPin, int clkPin, int csPin, int _displayCount);
void setup();
void shutdown(int index, bool isShutdown);
void setIntensity(int intensity);
void setIntensity(int index, int intensity);
void clearDisplay();
void clearDisplay(int index);
void clearBuffer();
void clearBuffer(int index);
void setDot(int x, int y, bool value);
void setDot(int index, int row, int col, bool value);
void transferAllDots();
void shiftLeft();
private:
void send(int index, byte reg, byte value);
};
#endif //DotMatrixControl_h
#include <aws_iot_mqtt.h>
#include <aws_iot_version.h>
#include "aws_iot_config.h"
#include "MAX7219_Dot_Matrix_font.h" //https://github.com/nickgammon/MAX7219_Dot_Matrix/tree/master/src
#include "DotMatrixControl.h"
//Dot Matrix
int displayCount = 4;
DotMatrixControl dmc = DotMatrixControl(12, 11, 10, displayCount);
int delayTime = 0;
int animationStatus = 0;
char currentMessage[100] = "";
int sensorPin = A0;
char skillName[] = "My Message Board";
//AWS IoT
aws_iot_mqtt_client iotClient;
bool isIoTClientReady = false;
char messageReceived[100] = "";
int iotYieldTimerDefault = 1000;//ms
int iotYieldTimer = iotYieldTimerDefault;
//loop
int seqNum = 0;
void cmdCallback(char *message, unsigned int mesLen, Message_status_t messageStatus) {
Serial.println("cmdCallback");
if (messageStatus != STATUS_NORMAL) {
Serial.print("AWS IoT cmdCallback messageStatus : ");
Serial.println(messageStatus);
return;
}
strncpy(messageReceived, message, sizeof(messageReceived));
Serial.println(message);
}
void setupAwsIot() {
//AWS IoT
Serial.println("AWS IoT setup.") ;
int progress = 0;
dmc.setDot(++progress, 0, 1);
dmc.transferAllDots();
if ((iotClient.setup(AWS_IOT_CLIENT_ID)) != 0) {
Serial.println("AWS IoT setup error!") ;
dmc.setDot(progress, 1, 1);
dmc.transferAllDots();
return;
}
Serial.println("AWS IoT setup ok.") ;
dmc.setDot(++progress, 0, 1);
dmc.transferAllDots();
if (iotClient.config(AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT, AWS_IOT_ROOT_CA_PATH, AWS_IOT_PRIVATE_KEY_PATH, AWS_IOT_CERTIFICATE_PATH) != 0) {
Serial.println("AWS IoT config error!");
dmc.setDot(progress, 1, 1);
dmc.transferAllDots();
return;
}
Serial.println("AWS IoT config ok.");
dmc.setDot(++progress, 0, 1);
dmc.transferAllDots();
if (iotClient.connect() != 0) {
Serial.println("AWS IoT connect error!");
dmc.setDot(progress, 1, 1);
dmc.transferAllDots();
return;
}
Serial.println("AWS IoT connect ok.");
dmc.setDot(++progress, 0, 1);
dmc.transferAllDots();
isIoTClientReady = true;
char topicName[] = "cmd";
Serial.print("AWS IoT subscribing to topic:\"");
Serial.print(topicName);
Serial.print("\".\n");
if (iotClient.subscribe(topicName, 1, cmdCallback) != 0) {
Serial.println("AWS IoT subscribe error!");
dmc.setDot(progress, 1, 1);
dmc.transferAllDots();
return;
}
Serial.println("AWS IoT subscribe ok.");
dmc.setDot(++progress, 0, 1);
dmc.transferAllDots();
}
void setup() {
Serial.begin(115200) ;
Serial.println("setup");
randomSeed(analogRead(0));
dmc.setup();
dmc.setIntensity(0); //0-15
Serial.println("DotMatrixControl set up ok.");
dmc.setDot(0, 0, 1);
dmc.transferAllDots();
currentMessage[0] = 0;
messageReceived[0] = 0;
sprintf(currentMessage, "No message. You can set a message by saying \"Alexa, open %s.\"", skillName);
setupAwsIot();
}
void transposeCharData(const char *charDataSrc, char *charDataTrg) {
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
charDataTrg[7 - j] += ((charDataSrc[i] >> (7 - j)) & 0x01) << i;
}
}
}
void alignLeft(const char *charDataSrc, char *charDataTrg) {
int pos = 0;
for (int i = 0; i < 8; i++) {
if (charDataSrc[i] == 0) continue;
charDataTrg[pos++] = charDataSrc[i];
}
while (pos < 8) charDataTrg[pos++] = 0;
}
int getFontWidth(const char *charDataSrc) {
for (int i = 0; i < 8; i++) {
if (charDataSrc[7 - i] != 0) return 7 - i + 1;
}
return 0;
}
void loopScrollMessage() {
int charCount = strlen(currentMessage);
if (animationStatus == 0) dmc.clearBuffer();
if (charCount == 0)return;
if (animationStatus < (8 * charCount)) {
int charIndex = animationStatus / 8;
char c = currentMessage[charIndex];
const byte *charDataSrc = MAX7219_Dot_Matrix_font[c];
byte bufChars1[] = {0, 0, 0, 0, 0, 0, 0, 0};
byte bufChars2[] = {0, 0, 0, 0, 0, 0, 0, 0};
byte bufChars[] = {0, 0, 0, 0, 0, 0, 0, 0};
int currentCharWidth = 0;
//copy char data
for (int i = 0; i < 8; i++) {
bufChars2[i] = pgm_read_byte(charDataSrc + i);
}
alignLeft(bufChars2, bufChars1); //remove white space of left side
currentCharWidth = getFontWidth(bufChars1);
transposeCharData(bufChars1, bufChars);//tranpose
for (int y = 0; y < 8; y++) {
int dot = (bufChars[y] >> (animationStatus % 8)) & 0x01;
dmc.setDot(0, y, dot);
}
int width = currentCharWidth;
if (width < 7) {//skip left side space
if (width == 0) {
//space
if ((animationStatus % 8) == 3) animationStatus += 4;
} else {
if ((animationStatus % 8) == width) animationStatus += (8 - width) - 1;
}
}
}
dmc.transferAllDots();
dmc.shiftLeft();
animationStatus++;
if (animationStatus > (8 * displayCount + (8 * charCount))) {
animationStatus = 0; //loop back to start
}
}
void checkMessageReceive() {
if (messageReceived[0] == 0) return;
Serial.println("message received");
//start new message scroll
strncpy(currentMessage, messageReceived, sizeof(currentMessage));
messageReceived[0] = 0;
animationStatus = 0;
}
void loop() {
checkMessageReceive();
if (--iotYieldTimer < 0) {
if (iotClient.yield() != 0) {
Serial.println("Yield failed!");
}
iotYieldTimer = iotYieldTimerDefault;
}
int sensorValue = analogRead(sensorPin);
int delayValue = 0;
if (sensorValue < 100) {
delayValue = sensorValue / 20;
} else if (sensorValue < 300) {
delayValue = 5 + (sensorValue - 100) / 10;
} else if (sensorValue < 800) {
delayValue = 5 + 29 + (sensorValue - 300);
} else {
//no scroll
return;
}
loopScrollMessage();
delay(delayValue);
iotYieldTimer -= delayValue;
}
'use strict';
/**
* This sample demonstrates a simple skill built with the Amazon Alexa Skills Kit.
* The Intent Schema, Custom Slots, and Sample Utterances for this skill, as well as
* testing instructions are located at http://amzn.to/1LzFrj6
*
* For additional samples, visit the Alexa Skills Kit Getting Started guide at
* http://amzn.to/1LGWsLG
*/
var aws = require('aws-sdk');
var endpoint = 'XXXXXXXXX.iot.us-east-1.amazonaws.com'; //your endpoint
var iotdata = new aws.IotData( { endpoint: endpoint } );
// --------------- Helpers that build all of the responses -----------------------
function buildSpeechletResponse(title, output, repromptText, shouldEndSession) {
return {
outputSpeech: {
type: 'PlainText',
text: output,
},
card: {
type: 'Simple',
title: `${title}`,
content: `${output}`,
},
reprompt: {
outputSpeech: {
type: 'PlainText',
text: repromptText,
},
},
shouldEndSession,
};
}
function buildResponse(sessionAttributes, speechletResponse) {
return {
version: '1.0',
sessionAttributes,
response: speechletResponse,
};
}
// --------------- Functions that control the skill's behavior -----------------------
function getWelcomeResponse(callback) {
// If we wanted to initialize the session to have some attributes we could add those here.
const sessionAttributes = {};
const cardTitle = 'Welcome';
const speechOutput = 'Welcome to My Message Board. ' +
'What message do you want to set?';
// If the user either does not reply to the welcome message or says something that is not
// understood, they will be prompted again with this text.
const repromptText = 'What message do you want to set?';
const shouldEndSession = false;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
function handleSessionEndRequest(callback) {
const cardTitle = '';//'Session Ended';
const speechOutput = 'See you!';
// Setting this to true ends the session and exits the skill.
const shouldEndSession = true;
callback({}, buildSpeechletResponse(cardTitle, speechOutput, null, shouldEndSession));
}
function createFavoriteColorAttributes(favoriteColor) {
return {
favoriteColor,
};
}
/**
* Sets the color in the session and prepares the speech to reply to the user.
*/
function setMessage(intent, session, callback) {
const cardTitle = 'New Massage';//intent.name;
const message = intent.slots.message;
let repromptText = '';
let sessionAttributes = {};
let shouldEndSession = false;
let speechOutput = '';
if (message) {
const messageValue = message.value;
sessionAttributes = createFavoriteColorAttributes(messageValue);
speechOutput = `This message is set: ${messageValue}.`;
repromptText = null;
var params = {
topic: 'cmd', /* required */
payload: messageValue,
qos: 1
};
iotdata.publish(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
}
else{
console.log(data); // successful response
}
shouldEndSession = true;
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
});
} else {
speechOutput = "What message do you want to set?";
repromptText = "What message do you want to set?";
callback(sessionAttributes,
buildSpeechletResponse(cardTitle, speechOutput, repromptText, shouldEndSession));
}
}
// --------------- Events -----------------------
/**
* Called when the session starts.
*/
function onSessionStarted(sessionStartedRequest, session) {
console.log(`onSessionStarted requestId=${sessionStartedRequest.requestId}, sessionId=${session.sessionId}`);
}
/**
* Called when the user launches the skill without specifying what they want.
*/
function onLaunch(launchRequest, session, callback) {
console.log(`onLaunch requestId=${launchRequest.requestId}, sessionId=${session.sessionId}`);
// Dispatch to your skill's launch.
getWelcomeResponse(callback);
}
/**
* Called when the user specifies an intent for this skill.
*/
function onIntent(intentRequest, session, callback) {
console.log(`onIntent requestId=${intentRequest.requestId}, sessionId=${session.sessionId}`);
const intent = intentRequest.intent;
const intentName = intentRequest.intent.name;
// Dispatch to your skill's intent handlers
if (intentName === 'AnyMessage') {
setMessage(intent, session, callback);
} else if (intentName === 'AMAZON.HelpIntent') {
getWelcomeResponse(callback);
} else if (intentName === 'AMAZON.StopIntent' || intentName === 'AMAZON.CancelIntent') {
handleSessionEndRequest(callback);
} else {
throw new Error('Invalid intent');
}
}
/**
* Called when the user ends the session.
* Is not called when the skill returns shouldEndSession=true.
*/
function onSessionEnded(sessionEndedRequest, session) {
console.log(`onSessionEnded requestId=${sessionEndedRequest.requestId}, sessionId=${session.sessionId}`);
// Add cleanup logic here
}
// --------------- Main handler -----------------------
// Route the incoming request based on type (LaunchRequest, IntentRequest,
// etc.) The JSON body of the request is provided in the event parameter.
exports.handler = (event, context, callback) => {
try {
console.log(`event.session.application.applicationId=${event.session.application.applicationId}`);
/**
* Uncomment this if statement and populate with your skill's application ID to
* prevent someone else from configuring a skill that sends requests to this function.
*/
/*
if (event.session.application.applicationId !== 'amzn1.echo-sdk-ams.app.[unique-value-here]') {
callback('Invalid Application ID');
}
*/
if (event.session.new) {
onSessionStarted({ requestId: event.request.requestId }, event.session);
}
if (event.request.type === 'LaunchRequest') {
onLaunch(event.request,
event.session,
(sessionAttributes, speechletResponse) => {
callback(null, buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === 'IntentRequest') {
onIntent(event.request,
event.session,
(sessionAttributes, speechletResponse) => {
callback(null, buildResponse(sessionAttributes, speechletResponse));
});
} else if (event.request.type === 'SessionEndedRequest') {
onSessionEnded(event.request, event.session);
callback();
}
} catch (err) {
callback(err);
}
};
{
"languageModel": {
"types": [
{
"name": "MESSAGE",
"values": [
{
"id": null,
"name": {
"value": "hello",
"synonyms": []
}
},
{
"id": null,
"name": {
"value": "good night",
"synonyms": []
}
},
{
"id": null,
"name": {
"value": "Thank you",
"synonyms": []
}
},
{
"id": null,
"name": {
"value": "I'll be home at 5pm.",
"synonyms": []
}
}
]
}
],
"intents": [
{
"name": "AMAZON.CancelIntent",
"samples": []
},
{
"name": "AMAZON.HelpIntent",
"samples": []
},
{
"name": "AMAZON.StopIntent",
"samples": []
},
{
"name": "AnyMessage",
"samples": [
"{message}"
],
"slots": [
{
"name": "message",
"type": "MESSAGE"
}
]
}
],
"invocationName": "my message board"
}
}
Comments