kopia lustrzana https://github.com/helium/longfi-arduino
AWS IoT Demo (#18)
rodzic
d48d6df55f
commit
335d98170f
|
@ -0,0 +1,74 @@
|
||||||
|
# ST B-L072Z-LRWAN1 - Button to AWS Example
|
||||||
|
|
||||||
|
This is an example code for building an AWS IoT application that sends you text messages using Twilio.
|
||||||
|
|
||||||
|
You'll need:
|
||||||
|
* ST-B-L072Z-LRWAN1
|
||||||
|
* An AWS account
|
||||||
|
* A Twilio account
|
||||||
|
|
||||||
|
You need to load this sketch onto the ST-B-L072Z-LRWAN1. The sketch will send a heartbeat every 15 seconds and will asyncronously send an alert whenever the button is pressed.
|
||||||
|
|
||||||
|
Once you confirm that the device is connecting and sending data via Helium Console, you'll want to setup an integration to AWS IoT.
|
||||||
|
|
||||||
|
Once you do that, you want to create a Lambda function using the Python file here: lambda_button_text.py
|
||||||
|
|
||||||
|
You'll need to set environmental variables to make it work:
|
||||||
|
* TWILIO_AUTH_TOKEN: get this from your Twilio dashboard
|
||||||
|
* TWILIO_ACCOUNT_SID: get this from your Twilio dashboard
|
||||||
|
* TWILIO_ASSIGNED_NUMBER: get this from your Twilio dashboard
|
||||||
|
* USER_NUMBER: this is the cell phone number which will get text message alerts when the button is pressed
|
||||||
|
|
||||||
|
Once you save the lambda function, you will simply need to connect the lambda function to an AWS IoT trigger. Use the rule query statement: SELECT * FROM "helium"
|
||||||
|
|
||||||
|
Now you're done! Press the button and you should get a text message.
|
||||||
|
|
||||||
|
## Required Arduino Libraries
|
||||||
|
|
||||||
|
From the Arduino IDE, open the Library Manager (Sketch->Include Library->Manage Libraries). In the search box, type the library name below and install the latest version.
|
||||||
|
|
||||||
|
[MCCI Arduino LoRaWAN Library](https://github.com/mcci-catena/arduino-lmic)
|
||||||
|
|
||||||
|
## Required Arduino Board Support
|
||||||
|
|
||||||
|
### B-L072Z-LRWAN1 - ST STM32L0 Discovery kit
|
||||||
|
Install board support package, find instructions [here](https://github.com/stm32duino/Arduino_Core_STM32#getting-started).
|
||||||
|
|
||||||
|
Arduino IDE:
|
||||||
|
1. Select Tools -> Board: -> Discovery
|
||||||
|
2. Select Tools -> Board part number: -> Discovery L072Z-LRWAN1
|
||||||
|
|
||||||
|
## Required Hardware
|
||||||
|
|
||||||
|
### B-L072Z-LRWAN1 - ST STM32L0 Discovery kit
|
||||||
|
|
||||||
|
[B-L072Z-LRWAN1 Product Page](https://www.st.com/en/evaluation-tools/b-l072z-lrwan1.html)
|
||||||
|
[B-L072Z-LRWAN1 User Manual](https://www.st.com/content/ccc/resource/technical/document/user_manual/group0/ac/62/15/c7/60/ac/4e/9c/DM00329995/files/DM00329995.pdf/jcr:content/translations/en.DM00329995.pdf)
|
||||||
|
|
||||||
|
## Programming (Uploading Method):
|
||||||
|
|
||||||
|
#### STM32CubeProgrammer(SWD)
|
||||||
|
Will use onboard ST-Link(Flasher/Debugger) to upload sketch.
|
||||||
|
Download and Install required utility from ST [here](https://www.st.com/en/development-tools/stm32cubeprog.html).
|
||||||
|
|
||||||
|
Arduino IDE:
|
||||||
|
Select Tools -> Upload Method -> STM32CubeProgrammer(SWD)
|
||||||
|
|
||||||
|
### PlatformIO Support
|
||||||
|
|
||||||
|
The PlatformIO Board file for this board is currently using the incorrect OpenOCD (Upload/Debug)
|
||||||
|
script for the microcontroller on this board. We are in the process of pushing a fix upstream. When
|
||||||
|
uploading or debugging, hold the reset button down right until the upload or debug process initiates
|
||||||
|
it's routine in communicating with the board, this seems to aleviate the issue for right now.
|
||||||
|
|
||||||
|
`platformio.ini`
|
||||||
|
```
|
||||||
|
[env:disco_l072cz_lrwan1]
|
||||||
|
platform = ststm32
|
||||||
|
board = disco_l072cz_lrwan1
|
||||||
|
framework = arduino
|
||||||
|
|
||||||
|
lib_deps =
|
||||||
|
MCCI LoRaWAN LMIC library
|
||||||
|
CayenneLPP
|
||||||
|
```
|
|
@ -0,0 +1,27 @@
|
||||||
|
import base64
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from botocore.vendored import requests
|
||||||
|
|
||||||
|
TWILIO_ACCOUNT_SID = os.environ.get("TWILIO_ACCOUNT_SID")
|
||||||
|
TWILIO_AUTH_TOKEN = os.environ.get("TWILIO_AUTH_TOKEN")
|
||||||
|
TWILIO_SMS_URL = "https://api.twilio.com/2010-04-01/Accounts/" + TWILIO_ACCOUNT_SID + "/Messages.json"
|
||||||
|
TWILIO_ASSIGNED_NUMBER = os.environ.get("TWILIO_ASSIGNED_NUMBER")
|
||||||
|
USER_NUMBER = os.environ.get("USER_NUMBER")
|
||||||
|
|
||||||
|
def lambda_handler(event, context):
|
||||||
|
|
||||||
|
byte_arr = list(base64.b64decode(event['payload']))
|
||||||
|
|
||||||
|
if byte_arr[0] == 1 :
|
||||||
|
to_number =USER_NUMBER
|
||||||
|
from_number = TWILIO_ASSIGNED_NUMBER
|
||||||
|
body = "Button pressed!"
|
||||||
|
data = {"To": to_number, "From": from_number, "Body": body}
|
||||||
|
|
||||||
|
response = requests.post(TWILIO_SMS_URL, data = data, auth=(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN))
|
||||||
|
print(response.text)
|
||||||
|
return "Button pushed - Message Sent"
|
||||||
|
else:
|
||||||
|
print("Heartbeat")
|
||||||
|
return "Heartbeat"
|
|
@ -0,0 +1,236 @@
|
||||||
|
#include <lmic.h>
|
||||||
|
#include <hal/hal.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
|
||||||
|
|
||||||
|
// This should also be in little endian format
|
||||||
|
static const u1_t PROGMEM DEVEUI[8]= { FILL_ME_IN };
|
||||||
|
void os_getDevEui (u1_t* buf) { memcpy_P(buf, DEVEUI, 8);}
|
||||||
|
|
||||||
|
// This should also be in little endian format
|
||||||
|
static const u1_t PROGMEM APPEUI[8]= { FILL_ME_IN };
|
||||||
|
void os_getArtEui (u1_t* buf) { memcpy_P(buf, APPEUI, 8);}
|
||||||
|
|
||||||
|
// This key should be in big endian format
|
||||||
|
static const u1_t PROGMEM APPKEY[16] = { FILL_ME_IN };
|
||||||
|
void os_getDevKey (u1_t* buf) { memcpy_P(buf, APPKEY, 16);}
|
||||||
|
|
||||||
|
static uint8_t mydata[] = {0};
|
||||||
|
static osjob_t sendjob;
|
||||||
|
|
||||||
|
// Schedule TX every this many seconds (might become longer due to duty
|
||||||
|
// cycle limitations).
|
||||||
|
const unsigned TX_INTERVAL = 15;
|
||||||
|
|
||||||
|
// Pin mapping
|
||||||
|
#include "arduino_lmic_hal_boards.h"
|
||||||
|
// Pin mapping Discovery
|
||||||
|
const lmic_pinmap lmic_pins = *Arduino_LMIC::GetPinmap_Disco_L072cz_Lrwan1();
|
||||||
|
|
||||||
|
void printHex2(unsigned v) {
|
||||||
|
v &= 0xff;
|
||||||
|
if (v < 16)
|
||||||
|
Serial.print('0');
|
||||||
|
Serial.print(v, HEX);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onEvent (ev_t ev) {
|
||||||
|
Serial.print(os_getTime());
|
||||||
|
Serial.print(": ");
|
||||||
|
switch(ev) {
|
||||||
|
case EV_SCAN_TIMEOUT:
|
||||||
|
Serial.println(F("EV_SCAN_TIMEOUT"));
|
||||||
|
break;
|
||||||
|
case EV_BEACON_FOUND:
|
||||||
|
Serial.println(F("EV_BEACON_FOUND"));
|
||||||
|
break;
|
||||||
|
case EV_BEACON_MISSED:
|
||||||
|
Serial.println(F("EV_BEACON_MISSED"));
|
||||||
|
break;
|
||||||
|
case EV_BEACON_TRACKED:
|
||||||
|
Serial.println(F("EV_BEACON_TRACKED"));
|
||||||
|
break;
|
||||||
|
case EV_JOINING:
|
||||||
|
Serial.println(F("EV_JOINING"));
|
||||||
|
break;
|
||||||
|
case EV_JOINED:
|
||||||
|
Serial.println(F("EV_JOINED"));
|
||||||
|
{
|
||||||
|
u4_t netid = 0;
|
||||||
|
devaddr_t devaddr = 0;
|
||||||
|
u1_t nwkKey[16];
|
||||||
|
u1_t artKey[16];
|
||||||
|
LMIC_getSessionKeys(&netid, &devaddr, nwkKey, artKey);
|
||||||
|
Serial.print("netid: ");
|
||||||
|
Serial.println(netid, DEC);
|
||||||
|
Serial.print("devaddr: ");
|
||||||
|
Serial.println(devaddr, HEX);
|
||||||
|
Serial.print("AppSKey: ");
|
||||||
|
for (size_t i=0; i<sizeof(artKey); ++i) {
|
||||||
|
if (i != 0)
|
||||||
|
Serial.print("-");
|
||||||
|
printHex2(artKey[i]);
|
||||||
|
}
|
||||||
|
Serial.println("");
|
||||||
|
Serial.print("NwkSKey: ");
|
||||||
|
for (size_t i=0; i<sizeof(nwkKey); ++i) {
|
||||||
|
if (i != 0)
|
||||||
|
Serial.print("-");
|
||||||
|
printHex2(nwkKey[i]);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
// Disable link check validation (automatically enabled
|
||||||
|
// during join, but because slow data rates change max TX
|
||||||
|
// size, we don't use it in this example.
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
|| This event is defined but not used in the code. No
|
||||||
|
|| point in wasting codespace on it.
|
||||||
|
||
|
||||||
|
|| case EV_RFU1:
|
||||||
|
|| Serial.println(F("EV_RFU1"));
|
||||||
|
|| break;
|
||||||
|
*/
|
||||||
|
case EV_JOIN_FAILED:
|
||||||
|
Serial.println(F("EV_JOIN_FAILED"));
|
||||||
|
break;
|
||||||
|
case EV_REJOIN_FAILED:
|
||||||
|
Serial.println(F("EV_REJOIN_FAILED"));
|
||||||
|
break;
|
||||||
|
break;
|
||||||
|
case EV_TXCOMPLETE:
|
||||||
|
Serial.println(F("EV_TXCOMPLETE (includes waiting for RX windows)"));
|
||||||
|
if (LMIC.txrxFlags & TXRX_ACK)
|
||||||
|
Serial.println(F("Received ack"));
|
||||||
|
if (LMIC.dataLen) {
|
||||||
|
Serial.println(F("Received "));
|
||||||
|
Serial.println(LMIC.dataLen);
|
||||||
|
Serial.println(F(" bytes of payload"));
|
||||||
|
}
|
||||||
|
// Schedule next transmission
|
||||||
|
os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case EV_LOST_TSYNC:
|
||||||
|
Serial.println(F("EV_LOST_TSYNC"));
|
||||||
|
break;
|
||||||
|
case EV_RESET:
|
||||||
|
Serial.println(F("EV_RESET"));
|
||||||
|
break;
|
||||||
|
case EV_RXCOMPLETE:
|
||||||
|
// data received in ping slot
|
||||||
|
Serial.println(F("EV_RXCOMPLETE"));
|
||||||
|
break;
|
||||||
|
case EV_LINK_DEAD:
|
||||||
|
Serial.println(F("EV_LINK_DEAD"));
|
||||||
|
break;
|
||||||
|
case EV_LINK_ALIVE:
|
||||||
|
Serial.println(F("EV_LINK_ALIVE"));
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
|| This event is defined but not used in the code. No
|
||||||
|
|| point in wasting codespace on it.
|
||||||
|
||
|
||||||
|
|| case EV_SCAN_FOUND:
|
||||||
|
|| Serial.println(F("EV_SCAN_FOUND"));
|
||||||
|
|| break;
|
||||||
|
*/
|
||||||
|
case EV_TXSTART:
|
||||||
|
Serial.println(F("EV_TXSTART"));
|
||||||
|
break;
|
||||||
|
case EV_TXCANCELED:
|
||||||
|
Serial.println(F("EV_TXCANCELED"));
|
||||||
|
break;
|
||||||
|
case EV_RXSTART:
|
||||||
|
/* do not print anything -- it wrecks timing */
|
||||||
|
break;
|
||||||
|
case EV_JOIN_TXCOMPLETE:
|
||||||
|
Serial.println(F("EV_JOIN_TXCOMPLETE: no JoinAccept"));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Serial.print(F("Unknown event: "));
|
||||||
|
Serial.println((unsigned) ev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_send(osjob_t* j){
|
||||||
|
// Check if there is not a current TX/RX job running
|
||||||
|
if (LMIC.opmode & OP_TXRXPEND) {
|
||||||
|
Serial.println(F("OP_TXRXPEND, not sending update packet"));
|
||||||
|
} else {
|
||||||
|
mydata[0] = 0;
|
||||||
|
// Prepare upstream data transmission at the next possible time.
|
||||||
|
LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
|
||||||
|
Serial.println(F("Packet queued: Update Send"));
|
||||||
|
}
|
||||||
|
// Next TX is scheduled after TX_COMPLETE event.
|
||||||
|
}
|
||||||
|
|
||||||
|
bool async_send(){
|
||||||
|
// Check if there is not a current TX/RX job running
|
||||||
|
if (LMIC.opmode & OP_TXRXPEND) {
|
||||||
|
Serial.println(F("OP_TXRXPEND, not sending async send yet"));
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
mydata[0] = 1;
|
||||||
|
// Prepare upstream data transmission at the next possible time.
|
||||||
|
LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
|
||||||
|
Serial.println(F("Packet queued: Async Send"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Next TX is scheduled after TX_COMPLETE event.
|
||||||
|
}
|
||||||
|
|
||||||
|
static volatile bool button_pushed;
|
||||||
|
|
||||||
|
void push_button_ISR(){
|
||||||
|
button_pushed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(9600);
|
||||||
|
Serial.println("Starting");
|
||||||
|
|
||||||
|
SPI.setMOSI(RADIO_MOSI_PORT);
|
||||||
|
SPI.setMISO(RADIO_MISO_PORT);
|
||||||
|
SPI.setSCLK(RADIO_SCLK_PORT);
|
||||||
|
SPI.setSSEL(RADIO_NSS_PORT);
|
||||||
|
|
||||||
|
// Configure User Button
|
||||||
|
pinMode(USER_BTN, INPUT_PULLUP);
|
||||||
|
attachInterrupt(digitalPinToInterrupt(USER_BTN), push_button_ISR, HIGH);
|
||||||
|
|
||||||
|
Serial.println("Setup Complete");
|
||||||
|
|
||||||
|
// LMIC init
|
||||||
|
os_init();
|
||||||
|
// Reset the MAC state. Session and pending data transfers will be discarded.
|
||||||
|
LMIC_reset();
|
||||||
|
|
||||||
|
// allow much more clock error than the X/1000 default. See:
|
||||||
|
// https://github.com/mcci-catena/arduino-lorawan/issues/74#issuecomment-462171974
|
||||||
|
// https://github.com/mcci-catena/arduino-lmic/commit/42da75b56#diff-16d75524a9920f5d043fe731a27cf85aL633
|
||||||
|
// the X/1000 means an error rate of 0.1%; the above issue discusses using values up to 10%.
|
||||||
|
// so, values from 10 (10% error, the most lax) to 1000 (0.1% error, the most strict) can be used.
|
||||||
|
LMIC_setClockError(1 * MAX_CLOCK_ERROR / 40);
|
||||||
|
|
||||||
|
LMIC_setLinkCheckMode(0);
|
||||||
|
LMIC_setDrTxpow(DR_SF7,14);
|
||||||
|
LMIC_selectSubBand(6);
|
||||||
|
|
||||||
|
// Start job (sending automatically starts OTAA too)
|
||||||
|
do_send(&sendjob);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
if(button_pushed) {
|
||||||
|
button_pushed = async_send();
|
||||||
|
}
|
||||||
|
os_runloop_once();
|
||||||
|
}
|
Ładowanie…
Reference in New Issue