-
-
Save takeru/c1505539a3621898d1318d0ba15c51ca to your computer and use it in GitHub Desktop.
Automatic ventilation system for comfortable sleep
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, {useEffect} from "react"; | |
import Amplify, {Auth} from 'aws-amplify'; | |
import awsconfig from './aws-exports'; | |
import {withAuthenticator} from "@aws-amplify/ui-react"; | |
import { PubSub } from 'aws-amplify'; | |
import { AWSIoTProvider } from '@aws-amplify/pubsub'; | |
Amplify.addPluggable(new AWSIoTProvider({ | |
aws_pubsub_region: 'ap-northeast-1', | |
aws_pubsub_endpoint: 'wss://a15o79dfbws0i-ats.iot.ap-northeast-1.amazonaws.com/mqtt', | |
})); | |
Amplify.configure(awsconfig); | |
Auth.currentCredentials().then((info) => { | |
const identityId = info.identityId; | |
console.log("identityId=", identityId); | |
}); | |
// const sub_all = PubSub.subscribe('#').subscribe({ | |
// next: (data) => { | |
// console.log("MQTT data=", data); | |
// }, | |
// error: error => console.error(error), | |
// close: () => console.log('Done'), | |
// }); | |
function sub(cb){ | |
const values = {}; | |
const change = (reported)=>{ | |
if(reported.temperature){ | |
values.temperature = reported.temperature; | |
values.humidity = reported.humidity; | |
} | |
if(reported.usbCurrent){ | |
values.fanSpeed = reported.fanSpeed; | |
values.usbCurrent = reported.usbCurrent; | |
values.ledBrightness = reported.ledBrightness; | |
} | |
cb(JSON.parse(JSON.stringify(values))); | |
}; | |
const sub1 = PubSub.subscribe('$aws/things/012395fb0a29199a01/shadow/update/documents').subscribe({ | |
next: (data) => { | |
const reported = data.value.current.state.reported; | |
console.log("update: temperature=", reported.temperature, "humidity=", reported.humidity); | |
change(reported); | |
}, | |
error: error => console.error(error), | |
close: () => console.log('Done'), | |
}); | |
const sub2 = PubSub.subscribe('$aws/things/M5StickC-003/shadow/update/documents').subscribe({ | |
next: (data) => { | |
const reported = data.value.current.state.reported; | |
console.log("update: fanSpeed=", reported.fanSpeed, "usbCurrent=", reported.usbCurrent, "ledBrightness=", reported.ledBrightness); | |
change(reported); | |
}, | |
error: error => console.error(error), | |
close: () => console.log('Done'), | |
}); | |
const sub3 = PubSub.subscribe('$aws/things/012395fb0a29199a01/shadow/get/accepted').subscribe({ | |
next: (data) => { | |
const reported = data.value.state.reported; | |
console.log("get: temperature=", reported.temperature, "humidity=", reported.humidity); | |
change(reported); | |
}, | |
error: error => console.error(error), | |
close: () => console.log('Done'), | |
}); | |
const sub4 = PubSub.subscribe('$aws/things/M5StickC-003/shadow/get/accepted').subscribe({ | |
next: (data) => { | |
const reported = data.value.state.reported; | |
console.log("get: fanSpeed=", reported.fanSpeed, "usbCurrent=", reported.usbCurrent, "ledBrightness=", reported.ledBrightness); | |
change(reported); | |
}, | |
error: error => console.error(error), | |
close: () => console.log('Done'), | |
}); | |
setTimeout(()=>{ | |
PubSub.publish('$aws/things/012395fb0a29199a01/shadow/get', ""); | |
PubSub.publish('$aws/things/M5StickC-003/shadow/get', ""); | |
}, 1000); | |
return ()=>{ | |
console.log("unsubscribe1"); | |
sub1.unsubscribe(); | |
sub2.unsubscribe(); | |
sub3.unsubscribe(); | |
sub4.unsubscribe(); | |
console.log("unsubscribe2"); | |
}; | |
} | |
function App() { | |
const [currentUserName, setCurrentUserName] = React.useState(""); | |
const [values, setValues] = React.useState({}); | |
useEffect(() => { | |
const init = async() => { | |
const currentUser = await Auth.currentAuthenticatedUser(); | |
setCurrentUserName(currentUser.username); | |
} | |
init() | |
}, []); | |
useEffect(() => { | |
return sub((values)=>{ | |
setValues(values); | |
}); | |
}, []); | |
const signOut = async() => { | |
try { | |
await Auth.signOut(); | |
} catch (error) { | |
console.log('error signing out: ', error); | |
} | |
document.location.reload(); | |
} | |
const setLedBrightness = async (b) => { | |
const msg = {state:{desired:{ledBrightness:b}}}; | |
await PubSub.publish('$aws/things/M5StickC-003/shadow/update', msg); | |
} | |
const setFanSpeed = async (s) => { | |
const msg = {state:{desired:{fanSpeed:s}}}; | |
console.log(JSON.stringify(msg)); | |
await PubSub.publish('$aws/things/M5StickC-003/shadow/update', msg); | |
} | |
return ( | |
<div> | |
<h1>username={currentUserName}</h1> | |
<button onClick={signOut}>signOut</button> | |
<ul> | |
<li>temperature={values.temperature}</li> | |
<li>humidity={values.humidity}</li> | |
<li>fanSpeed={values.fanSpeed}</li> | |
<li>usbCurrent={values.usbCurrent}</li> | |
<li>ledBrightness={values.ledBrightness}</li> | |
</ul> | |
<div> | |
<button onClick={()=>{ setLedBrightness(1); }}>LED ON</button> | |
<button onClick={()=>{ setLedBrightness(0); }}>LED OFF</button> | |
</div> | |
<div> | |
<button onClick={()=>{ setFanSpeed(1); }}>FAN ON</button> | |
<button onClick={()=>{ setFanSpeed(0); }}>FAN OFF</button> | |
</div> | |
</div> | |
); | |
} | |
export default withAuthenticator(App); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* AWS IoT EduKit - Core2 for AWS IoT EduKit | |
* Smart Thermostat v1.2.2 | |
* main.c | |
* | |
* Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD | |
* | |
* 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. | |
*/ | |
/** | |
* @file main.c | |
* @brief simple MQTT publish, subscribe, and device shadows for use with AWS IoT EduKit reference hardware. | |
* | |
* This example takes the parameters from the build configuration and establishes a connection to AWS IoT Core over MQTT. | |
* | |
* Some configuration is required. Visit https://edukit.workshop.aws | |
* | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <ctype.h> | |
#include <unistd.h> | |
#include <limits.h> | |
#include <string.h> | |
#include <math.h> | |
#include "freertos/FreeRTOS.h" | |
#include "freertos/task.h" | |
#include "freertos/semphr.h" | |
#include "freertos/event_groups.h" | |
#include "esp_log.h" | |
#include "aws_iot_config.h" | |
#include "aws_iot_log.h" | |
#include "aws_iot_version.h" | |
#include "aws_iot_mqtt_client_interface.h" | |
#include "aws_iot_shadow_interface.h" | |
#include "core2forAWS.h" | |
#include "wifi.h" | |
#include "ui.h" | |
#include "switchbot_meter.h" | |
static const char *TAG = "MAIN"; | |
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200 | |
/* CA Root certificate */ | |
extern const uint8_t aws_root_ca_pem_start[] asm("_binary_aws_root_ca_pem_start"); | |
extern const uint8_t aws_root_ca_pem_end[] asm("_binary_aws_root_ca_pem_end"); | |
/* Default MQTT HOST URL is pulled from the aws_iot_config.h */ | |
char HostAddress[255] = AWS_IOT_MQTT_HOST; | |
/* Default MQTT port is pulled from the aws_iot_config.h */ | |
uint32_t port = AWS_IOT_MQTT_PORT; | |
void iot_subscribe_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, | |
IoT_Publish_Message_Params *params, void *pData) { | |
ESP_LOGI(TAG, "Subscribe callback"); | |
ESP_LOGI(TAG, "%.*s\t%.*s", topicNameLen, topicName, (int) params->payloadLen, (char *)params->payload); | |
} | |
void disconnect_callback_handler(AWS_IoT_Client *pClient, void *data) { | |
ESP_LOGW(TAG, "MQTT Disconnect"); | |
ui_textarea_add("Disconnected from AWS IoT Core...", NULL, 0); | |
IoT_Error_t rc = FAILURE; | |
if(NULL == pClient) { | |
return; | |
} | |
if(aws_iot_is_autoreconnect_enabled(pClient)) { | |
ESP_LOGI(TAG, "Auto Reconnect is enabled, Reconnecting attempt will start now"); | |
} else { | |
ESP_LOGW(TAG, "Auto Reconnect not enabled. Starting manual reconnect..."); | |
rc = aws_iot_mqtt_attempt_reconnect(pClient); | |
if(NETWORK_RECONNECTED == rc) { | |
ESP_LOGW(TAG, "Manual Reconnect Successful"); | |
} else { | |
ESP_LOGW(TAG, "Manual Reconnect Failed - %d", rc); | |
} | |
} | |
} | |
static bool shadowUpdateInProgress; | |
static uint32_t shadowUpdateNextMs = UINT32_MAX; | |
static uint32_t shadowUpdateAcceptedMs = UINT32_MAX; | |
static uint32_t switchbotMeterDataReceivedMs = UINT32_MAX; | |
uint32_t millis(){ | |
return pdTICKS_TO_MS(xTaskGetTickCount()); | |
} | |
void ShadowUpdateStatusCallback(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, | |
const char *pReceivedJsonDocument, void *pContextData) { | |
IOT_UNUSED(pThingName); | |
IOT_UNUSED(action); | |
IOT_UNUSED(pReceivedJsonDocument); | |
IOT_UNUSED(pContextData); | |
shadowUpdateInProgress = false; | |
char* msg = ""; | |
if(SHADOW_ACK_TIMEOUT == status) { | |
msg = "Update timed out"; | |
ESP_LOGW(TAG, "%s", msg); | |
shadowUpdateNextMs = millis() + 10*1000; | |
} else if(SHADOW_ACK_REJECTED == status) { | |
msg = "Update rejected"; | |
ESP_LOGW(TAG, "%s", msg); | |
shadowUpdateNextMs = millis() + 10*1000; | |
} else if(SHADOW_ACK_ACCEPTED == status) { | |
msg = "Update accepted"; | |
ESP_LOGI(TAG, "%s", msg); | |
shadowUpdateAcceptedMs = millis(); | |
} | |
char tmp[50]; | |
snprintf(tmp, sizeof(tmp), "[%d] %s\n", millis()/1000, msg); | |
ui_textarea_add(tmp, NULL, 0); | |
} | |
void ledColor_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) { | |
IOT_UNUSED(pJsonString); | |
IOT_UNUSED(JsonStringDataLen); | |
char * ledColor = (char *) (pContext->pData); | |
if(pContext != NULL) { | |
ESP_LOGI(TAG, "Delta - ledColor changed to %s", ledColor); | |
} | |
if(strcmp(ledColor, "red") == 0) { | |
ESP_LOGI(TAG, "setting side LEDs to red"); | |
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_LEFT, 0xFF0000); | |
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_RIGHT, 0xFF0000); | |
Core2ForAWS_Sk6812_Show(); | |
} else if(strcmp(ledColor, "blue") == 0) { | |
ESP_LOGI(TAG, "setting side LEDs to blue"); | |
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_LEFT, 0x0000FF); | |
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_RIGHT, 0x0000FF); | |
Core2ForAWS_Sk6812_Show(); | |
}else if(strlen(ledColor) == 12){ | |
ESP_LOGI(TAG, "setting side LEDs to %s", ledColor); | |
char s[7]; | |
strncpy(s, ledColor, 6); | |
long left = strtol(s, NULL, 16); | |
strncpy(s, ledColor+6, 6); | |
long right = strtol(s, NULL, 16); | |
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_LEFT, left); | |
Core2ForAWS_Sk6812_SetSideColor(SK6812_SIDE_RIGHT, right); | |
Core2ForAWS_Sk6812_Show(); | |
} else { | |
ESP_LOGI(TAG, "clearing side LEDs"); | |
Core2ForAWS_Sk6812_Clear(); | |
Core2ForAWS_Sk6812_Show(); | |
} | |
} | |
void boolValue2_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) { | |
IOT_UNUSED(pJsonString); | |
IOT_UNUSED(JsonStringDataLen); | |
if(pContext != NULL) { | |
bool* pBoolValue2 = (bool*)(pContext->pData); | |
ESP_LOGI(TAG, "Delta - boolValue2 state changed to %d", *pBoolValue2); | |
} | |
} | |
char ledColor[13] = ""; | |
bool boolValue2 = false; | |
float temperature = 0.0f; | |
int32_t humidity = 0; | |
int32_t battery = 0; | |
float core2_battery_voltage = 0.0f; | |
// helper function for working with audio data | |
long map(long x, long in_min, long in_max, long out_min, long out_max) { | |
long divisor = (in_max - in_min); | |
if(divisor == 0){ | |
return -1; //AVR returns -1, SAM returns 0 | |
} | |
return (x - in_min) * (out_max - out_min) / divisor + out_min; | |
} | |
void aws_iot_task(void *param) { | |
IoT_Error_t rc = FAILURE; | |
char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER]; | |
size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]); | |
jsonStruct_t ledColor_Actuator; | |
ledColor_Actuator.cb = ledColor_Callback; | |
ledColor_Actuator.pKey = "ledColor"; | |
ledColor_Actuator.pData = &ledColor; | |
ledColor_Actuator.type = SHADOW_JSON_STRING; | |
ledColor_Actuator.dataLength = sizeof(ledColor); | |
jsonStruct_t boolValue2_Actuator; | |
boolValue2_Actuator.cb = boolValue2_Callback; | |
boolValue2_Actuator.pKey = "boolValue2"; | |
boolValue2_Actuator.pData = &boolValue2; | |
boolValue2_Actuator.type = SHADOW_JSON_BOOL; | |
boolValue2_Actuator.dataLength = sizeof(bool); | |
jsonStruct_t temperature_Handler; | |
temperature_Handler.cb = NULL; | |
temperature_Handler.pKey = "temperature"; | |
temperature_Handler.pData = &temperature; | |
temperature_Handler.type = SHADOW_JSON_FLOAT; | |
temperature_Handler.dataLength = sizeof(float); | |
jsonStruct_t humidity_Handler; | |
humidity_Handler.cb = NULL; | |
humidity_Handler.pKey = "humidity"; | |
humidity_Handler.pData = &humidity; | |
humidity_Handler.type = SHADOW_JSON_INT32; | |
humidity_Handler.dataLength = sizeof(int32_t); | |
jsonStruct_t battery_Handler; | |
battery_Handler.cb = NULL; | |
battery_Handler.pKey = "battery"; | |
battery_Handler.pData = &battery; | |
battery_Handler.type = SHADOW_JSON_INT32; | |
battery_Handler.dataLength = sizeof(int32_t); | |
jsonStruct_t core2_battery_voltage_Handler; | |
core2_battery_voltage_Handler.cb = NULL; | |
core2_battery_voltage_Handler.pKey = "c2vbat"; // "core2_battery_voltage" --> "Update timed out" with long key name. | |
core2_battery_voltage_Handler.pData = &core2_battery_voltage; | |
core2_battery_voltage_Handler.type = SHADOW_JSON_FLOAT; | |
core2_battery_voltage_Handler.dataLength = sizeof(float); | |
ESP_LOGI(TAG, "AWS IoT SDK Version %d.%d.%d-%s", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); | |
// initialize the mqtt client | |
AWS_IoT_Client iotCoreClient; | |
ShadowInitParameters_t sp = ShadowInitParametersDefault; | |
sp.pHost = HostAddress; | |
sp.port = port; | |
sp.enableAutoReconnect = false; | |
sp.disconnectHandler = disconnect_callback_handler; | |
sp.pRootCA = (const char *)aws_root_ca_pem_start; | |
sp.pClientCRT = "#"; | |
sp.pClientKey = "#0"; | |
#define CLIENT_ID_LEN (ATCA_SERIAL_NUM_SIZE * 2) | |
char *client_id = malloc(CLIENT_ID_LEN + 1); | |
ATCA_STATUS ret = Atecc608_GetSerialString(client_id); | |
if (ret != ATCA_SUCCESS){ | |
ESP_LOGE(TAG, "Failed to get device serial from secure element. Error: %i", ret); | |
abort(); | |
} | |
ui_textarea_add("\nDevice client Id:\n>> %s <<\n", client_id, CLIENT_ID_LEN); | |
ESP_LOGI(TAG, "Device client Id: >> %s <<", client_id); | |
/* Wait for WiFI to show as connected */ | |
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, | |
false, true, portMAX_DELAY); | |
ESP_LOGI(TAG, "Shadow Init"); | |
rc = aws_iot_shadow_init(&iotCoreClient, &sp); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_init returned error %d, aborting...", rc); | |
abort(); | |
} | |
ShadowConnectParameters_t scp = ShadowConnectParametersDefault; | |
scp.pMyThingName = client_id; | |
scp.pMqttClientId = client_id; | |
scp.mqttClientIdLen = CLIENT_ID_LEN; | |
ESP_LOGI(TAG, "Shadow Connect"); | |
rc = aws_iot_shadow_connect(&iotCoreClient, &scp); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_connect returned error %d, aborting...", rc); | |
abort(); | |
} | |
ui_textarea_add("\nConnected to AWS IoT Core and pub/sub to the device shadow state\n", NULL, 0); | |
/* | |
* Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h | |
* #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL | |
* #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL | |
*/ | |
rc = aws_iot_shadow_set_autoreconnect_status(&iotCoreClient, true); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "Unable to set Auto Reconnect to true - %d, aborting...", rc); | |
abort(); | |
} | |
// register delta callback for ledColor | |
rc = aws_iot_shadow_register_delta(&iotCoreClient, &ledColor_Actuator); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "Shadow Register Delta Error"); | |
} | |
// register delta callback for boolValue2 | |
rc = aws_iot_shadow_register_delta(&iotCoreClient, &boolValue2_Actuator); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "Shadow Register Delta Error"); | |
} | |
// loop and publish changes | |
while(NETWORK_ATTEMPTING_RECONNECT == rc || NETWORK_RECONNECTED == rc || SUCCESS == rc) { | |
rc = aws_iot_shadow_yield(&iotCoreClient, 200); | |
if(SUCCESS != rc){ | |
ESP_LOGE(TAG, "aws_iot_shadow_yield(A) failed. rc=%d", rc); | |
} | |
if(NETWORK_ATTEMPTING_RECONNECT == rc || shadowUpdateInProgress) { | |
rc = aws_iot_shadow_yield(&iotCoreClient, 1000); | |
if(SUCCESS != rc){ | |
ESP_LOGE(TAG, "aws_iot_shadow_yield(B) failed. rc=%d", rc); | |
} | |
// If the client is attempting to reconnect, or already waiting on a shadow update, | |
// we will skip the rest of the loop. | |
continue; | |
} | |
// START get sensor readings | |
// MPU6886_GetTempData(&temperature); | |
// END get sensor readings | |
// ESP_LOGI(TAG, "*****************************************************************************************"); | |
// ESP_LOGI(TAG, "On Device: ledColor %s", ledColor); | |
// ESP_LOGI(TAG, "On Device: boolValue2 %s", boolValue2 ? "true" : "false"); | |
// ESP_LOGI(TAG, "On Device: temperature %f", temperature); | |
// ESP_LOGI(TAG, "On Device: humidity %d", humidity); | |
// ESP_LOGI(TAG, "On Device: battery %d", battery); | |
int32_t timer = shadowUpdateNextMs - millis(); | |
ESP_LOGI(TAG, "timer=%.3f", timer/1000.0); | |
if(timer <= 0){ | |
while(true){ | |
rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer); | |
if(SUCCESS != rc){ | |
ESP_LOGE(TAG, "aws_iot_shadow_init_json_document failed. rc=%d", rc); | |
break; | |
} | |
core2_battery_voltage = Core2ForAWS_PMU_GetBatVolt(); | |
rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, 6, | |
&ledColor_Actuator, | |
&boolValue2_Actuator, | |
&temperature_Handler, | |
&humidity_Handler, | |
&battery_Handler, | |
&core2_battery_voltage_Handler | |
); | |
if(SUCCESS != rc){ | |
ESP_LOGE(TAG, "aws_iot_shadow_add_reported failed. rc=%d", rc); | |
break; | |
} | |
rc = aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer); | |
if(SUCCESS != rc){ | |
ESP_LOGE(TAG, "aws_iot_finalize_json_document failed. rc=%d", rc); | |
break; | |
} | |
ESP_LOGI(TAG, "Update Shadow: %s", JsonDocumentBuffer); | |
rc = aws_iot_shadow_update(&iotCoreClient, client_id, JsonDocumentBuffer, | |
ShadowUpdateStatusCallback, NULL, 4, true); | |
if(SUCCESS != rc){ | |
ESP_LOGE(TAG, "aws_iot_shadow_update failed. rc=%d", rc); | |
break; | |
} | |
char tmp[50]; | |
snprintf(tmp, sizeof(tmp), "[%d] %4.1fC/%d%% %d%% %5.3fV\n", millis()/1000, temperature, humidity, battery, core2_battery_voltage); | |
ui_textarea_add(tmp, NULL, 0); | |
shadowUpdateInProgress = true; | |
shadowUpdateNextMs = millis() + 5*60*1000; | |
break; | |
} | |
} | |
// ESP_LOGI(TAG, "*****************************************************************************************"); | |
//ESP_LOGI(TAG, "Stack remaining for task '%s' is %d bytes", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); | |
vTaskDelay(pdMS_TO_TICKS(1000)); | |
// aws_iot_shadow_yield(&iotCoreClient, 1000); | |
if(6*60*1000 < millis() - shadowUpdateAcceptedMs){ | |
abort(); | |
} | |
if(2*60*1000 < millis() - switchbotMeterDataReceivedMs){ | |
abort(); | |
} | |
} | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "An error occurred in the loop %d", rc); | |
} | |
ESP_LOGI(TAG, "Disconnecting"); | |
rc = aws_iot_shadow_disconnect(&iotCoreClient); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "Disconnect error %d", rc); | |
} | |
vTaskDelay(pdMS_TO_TICKS(1000)); | |
abort(); | |
vTaskDelete(NULL); | |
} | |
void _switchbot_meter_callback(float _temperature, int8_t _humidity, int8_t _battery){ | |
ESP_LOGI(TAG, "_switchbot_meter_callback temperature=%4.1f humidity=%d battery=%d", | |
_temperature, _humidity, _battery | |
); | |
if(temperature != _temperature || humidity != _humidity || battery != _battery){ | |
shadowUpdateNextMs = millis() + 1*1000; | |
} | |
temperature = _temperature; | |
humidity = _humidity; | |
battery = _battery; | |
switchbotMeterDataReceivedMs = millis(); | |
} | |
void app_main() | |
{ | |
esp_log_level_set("*", ESP_LOG_DEBUG); | |
esp_log_level_set("NimBLEScan", ESP_LOG_WARN); | |
Core2ForAWS_Init(); | |
// Core2ForAWS_Display_SetBrightness(30); | |
Core2ForAWS_LED_Enable(0); | |
for(int i=0; i<5; i++){ | |
Core2ForAWS_Sk6812_SetColor(i+0, i%2==0 ? 0x040000 : 0x000000); | |
Core2ForAWS_Sk6812_SetColor(i+5, i%2==0 ? 0x040000 : 0x000000); | |
} | |
Core2ForAWS_Sk6812_Show(); | |
ui_init(); | |
initialise_wifi(); | |
shadowUpdateAcceptedMs = millis(); | |
xTaskCreatePinnedToCore(&aws_iot_task, "aws_iot_task", 4096*2, NULL, 5, NULL, 1); | |
/* Wait for WiFI to show as connected */ | |
xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, | |
false, true, portMAX_DELAY); | |
init_ble(&_switchbot_meter_callback); | |
switchbotMeterDataReceivedMs = millis(); | |
// xTaskCreate(ble_task, "ble_task", 5000, NULL, 1, NULL); | |
xTaskCreatePinnedToCore(&ble_task, "ble_task", 4096, NULL, 5, NULL, 1); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <sys/types.h> | |
#include <NimBLEDevice.h> | |
#include "switchbot_meter.h" | |
static const char *TAG_BLE = "ble"; | |
BLEUUID serviceUUID = BLEUUID("cba20d00-224d-11e6-9fb8-0002a5d5c51b"); | |
BLEUUID serviceDataUUID = BLEUUID("00000d00-0000-1000-8000-00805f9b34fb"); | |
//BLEAddress addr01 = BLEAddress("ee:33:dc:9c:72:61"); // meter1 | |
BLEAddress addr02 = BLEAddress("d6:09:18:89:b9:56"); // meter2 | |
//BLEAddress addr03 = BLEAddress("ff:fb:c4:aa:4f:f5"); // meter3 | |
class MyAdvertisedDeviceCallbacks: public BLEAdvertisedDeviceCallbacks { | |
switchbot_meter_callback_t callback = NULL; | |
public: | |
MyAdvertisedDeviceCallbacks(switchbot_meter_callback_t cb){ | |
callback = cb; | |
} | |
void onResult(BLEAdvertisedDevice* advertisedDevice) { | |
//ESP_LOGI(TAG_BLE, "Advertised Device: %s", advertisedDevice->toString().c_str()); | |
if(advertisedDevice->getAddress().equals(addr02)){ | |
// OK | |
}else{ | |
return; | |
} | |
if(!advertisedDevice->haveServiceUUID()) return; | |
if(!advertisedDevice->getServiceUUID().equals(serviceUUID)) return; | |
//ESP_LOGI(TAG_BLE, "SwitchBot Meter: %s", advertisedDevice->toString().c_str()); | |
if(!advertisedDevice->haveServiceData()) return; | |
//printf("ServiceDataUUID=%s\n", advertisedDevice->getServiceDataUUID().toString().c_str()); | |
std::string s = advertisedDevice->getServiceData(); | |
//printf("ServiceData len=%d [", s.length()); | |
//for(int i=0; i<s.length(); i++){ | |
// printf("%02X ", s.c_str()[i]); | |
//} | |
//printf("]\n"); | |
if(!advertisedDevice->getServiceDataUUID().equals(serviceDataUUID)) return; | |
const char* servicedata = s.c_str(); | |
int8_t battery = servicedata[2] & 0b01111111; | |
bool isTemperatureAboveFreezing = servicedata[4] & 0b10000000; | |
float temperature = ( servicedata[3] & 0b00001111 ) / 10.0 + ( servicedata[4] & 0b01111111 ); | |
if(!isTemperatureAboveFreezing){ | |
temperature = -temperature; | |
} | |
int8_t humidity = servicedata[5] & 0b01111111; | |
// bool isEncrypted = ( servicedata[0] & 0b10000000 ) >> 7; | |
// bool isDualStateMode = ( servicedata[1] & 0b10000000 ) >> 7; | |
// bool isStatusOff = ( servicedata[1] & 0b01000000 ) >> 6; | |
// bool isTemperatureHighAlert = ( servicedata[3] & 0b10000000 ) >> 7; | |
// bool isTemperatureLowAlert = ( servicedata[3] & 0b01000000 ) >> 6; | |
// bool isHumidityHighAlert = ( servicedata[3] & 0b00100000 ) >> 5; | |
// bool isHumidityLowAlert = ( servicedata[3] & 0b00010000 ) >> 4; | |
// bool isTemperatureUnitF = ( servicedata[5] & 0b10000000 ) >> 7; | |
// ESP_LOGI(TAG_BLE, "----"); | |
// ESP_LOGI(TAG_BLE, "address: %s", advertisedDevice->getAddress().toString().c_str()); | |
// ESP_LOGI(TAG_BLE, "battery: %d", battery); | |
// ESP_LOGI(TAG_BLE, "temperature: %.1f", temperature); | |
// ESP_LOGI(TAG_BLE, "humidity: %d", humidity); | |
// ESP_LOGI(TAG_BLE, "isEncrypted: %d", isEncrypted); | |
// ESP_LOGI(TAG_BLE, "isDualStateMode: %d", isDualStateMode); | |
// ESP_LOGI(TAG_BLE, "isStatusOff: %d", isStatusOff); | |
// ESP_LOGI(TAG_BLE, "isTemperatureHighAlert: %d", isTemperatureHighAlert); | |
// ESP_LOGI(TAG_BLE, "isTemperatureLowAlert: %d", isTemperatureLowAlert); | |
// ESP_LOGI(TAG_BLE, "isHumidityHighAlert: %d", isHumidityHighAlert); | |
// ESP_LOGI(TAG_BLE, "isHumidityLowAlert: %d", isHumidityLowAlert); | |
// ESP_LOGI(TAG_BLE, "isTemperatureUnitF: %d", isTemperatureUnitF); | |
// ESP_LOGI(TAG_BLE, "----"); | |
ESP_LOGI(TAG_BLE, "SwitchBotMeter: address=%s battery=%d temperature=%.1f humidity=%d", | |
advertisedDevice->getAddress().toString().c_str(), | |
battery, | |
temperature, | |
humidity | |
); | |
if(callback){ | |
(*callback)(temperature, humidity, battery); | |
} | |
} | |
}; | |
BLEScan* pBLEScan = NULL; | |
int scanDurationSeconds = 5; | |
void init_ble(switchbot_meter_callback_t cb){ | |
BLEDevice::init(""); | |
pBLEScan = BLEDevice::getScan(); //create new scan | |
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks(cb)); | |
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster | |
pBLEScan->setInterval(100); | |
pBLEScan->setWindow(99); // less or equal setInterval value | |
} | |
void scanCompleteCB(NimBLEScanResults results){ | |
ESP_LOGI(TAG_BLE, "scan complate. count=%d", results.getCount()); | |
pBLEScan->clearResults(); | |
} | |
void ble_task(void * parameter){ | |
for(;;) { | |
bool ok = pBLEScan->start(scanDurationSeconds, scanCompleteCB, false); | |
if(ok){ | |
ESP_LOGI(TAG_BLE, "scan begin"); | |
} | |
while(pBLEScan->isScanning()){ | |
//ESP_LOGI(TAG_BLE, "scanning..."); | |
vTaskDelay(1000/portTICK_PERIOD_MS); | |
} | |
ESP_LOGI(TAG_BLE, "scan end"); | |
//ESP_LOGI(TAG_BLE, "Stack remaining for task '%s' is %d bytes", pcTaskGetTaskName(NULL), uxTaskGetStackHighWaterMark(NULL)); | |
vTaskDelay(1000/portTICK_PERIOD_MS); | |
} | |
vTaskDelete(NULL); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <sys/types.h> | |
#ifdef __cplusplus | |
extern "C" { | |
#endif /* __cplusplus */ | |
typedef void (* switchbot_meter_callback_t)(float temperature, int8_t humidity, int8_t battery); | |
void init_ble(switchbot_meter_callback_t cb); | |
void ble_task(void * parameter); | |
#ifdef __cplusplus | |
} | |
#endif /* __cplusplus */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* AWS IoT EduKit - Core2 for AWS IoT EduKit | |
* Smart Thermostat v1.2.2 | |
* ui.c | |
* | |
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of | |
* this software and associated documentation files (the "Software"), to deal in | |
* the Software without restriction, including without limitation the rights to | |
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
* the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
*/ | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "freertos/FreeRTOS.h" | |
#include "freertos/semphr.h" | |
#include "esp_log.h" | |
#include "core2forAWS.h" | |
#include "ui.h" | |
#define MAX_TEXTAREA_LENGTH 1024 | |
static lv_obj_t *active_screen; | |
static lv_obj_t *out_txtarea; | |
static lv_obj_t *wifi_label; | |
static char *TAG = "UI"; | |
static void ui_textarea_prune(size_t new_text_length){ | |
const char * current_text = lv_textarea_get_text(out_txtarea); | |
size_t current_text_len = strlen(current_text); | |
if(current_text_len + new_text_length >= MAX_TEXTAREA_LENGTH){ | |
for(int i = 0; i < new_text_length; i++){ | |
lv_textarea_set_cursor_pos(out_txtarea, 0); | |
lv_textarea_del_char_forward(out_txtarea); | |
} | |
lv_textarea_set_cursor_pos(out_txtarea, LV_TEXTAREA_CURSOR_LAST); | |
} | |
} | |
void ui_textarea_add(char *baseTxt, char *param, size_t paramLen) { | |
if( baseTxt != NULL ){ | |
xSemaphoreTake(xGuiSemaphore, portMAX_DELAY); | |
if (param != NULL && paramLen != 0){ | |
size_t baseTxtLen = strlen(baseTxt); | |
size_t bufLen = baseTxtLen + paramLen; | |
char buf[(int) bufLen]; | |
sprintf(buf, baseTxt, param); | |
ui_textarea_prune(strlen(buf)); | |
lv_textarea_add_text(out_txtarea, buf); | |
} | |
else{ | |
ui_textarea_prune(strlen(baseTxt)); | |
lv_textarea_add_text(out_txtarea, baseTxt); | |
} | |
xSemaphoreGive(xGuiSemaphore); | |
} | |
else{ | |
ESP_LOGE(TAG, "Textarea baseTxt is NULL!"); | |
} | |
} | |
void ui_wifi_label_update(bool state){ | |
xSemaphoreTake(xGuiSemaphore, portMAX_DELAY); | |
if (state == false) { | |
lv_label_set_text(wifi_label, LV_SYMBOL_WIFI); | |
} | |
else{ | |
char buffer[25]; | |
sprintf (buffer, "#0000ff %s #", LV_SYMBOL_WIFI); | |
lv_label_set_text(wifi_label, buffer); | |
} | |
xSemaphoreGive(xGuiSemaphore); | |
} | |
void ui_init() { | |
xSemaphoreTake(xGuiSemaphore, portMAX_DELAY); | |
active_screen = lv_scr_act(); | |
wifi_label = lv_label_create(active_screen, NULL); | |
lv_obj_align(wifi_label,NULL,LV_ALIGN_IN_TOP_RIGHT, 0, 6); | |
lv_label_set_text(wifi_label, LV_SYMBOL_WIFI); | |
lv_label_set_recolor(wifi_label, true); | |
out_txtarea = lv_textarea_create(active_screen, NULL); | |
lv_obj_set_size(out_txtarea, 300, 180); | |
lv_obj_align(out_txtarea, NULL, LV_ALIGN_IN_BOTTOM_MID, 0, -12); | |
lv_textarea_set_max_length(out_txtarea, MAX_TEXTAREA_LENGTH); | |
lv_textarea_set_text_sel(out_txtarea, false); | |
lv_textarea_set_cursor_hidden(out_txtarea, true); | |
lv_textarea_set_text(out_txtarea, "Starting!\n"); | |
xSemaphoreGive(xGuiSemaphore); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <Wire.h> | |
uint8_t Wire1_REG_Read(uint8_t device, uint8_t reg){ | |
Wire1.beginTransmission(device); | |
Wire1.write(reg); | |
Wire1.endTransmission(); | |
Wire1.requestFrom(device, (uint8_t)1); | |
uint8_t buf = Wire1.read(); | |
return buf; | |
} | |
void Wire1_REG_Write(uint8_t device, uint8_t reg, uint8_t val){ | |
Wire1.beginTransmission(device); | |
Wire1.write(reg); | |
Wire1.write(val); | |
Wire1.endTransmission(); | |
} | |
void output5v_ctrl(bool on) | |
{ | |
uint8_t v0 = Wire1_REG_Read(0x34, 0x12); | |
uint8_t v = v0 & 0b10111111; | |
if(on){ | |
v |= 0b01000000; | |
} | |
if(v!=v0){ | |
Wire1_REG_Write(0x34, 0x12, v); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <M5StickC.h> | |
#include "AXP192.h" | |
#ifdef ARDUINO_ARCH_ESP32 | |
#include "esp32-hal-log.h" | |
#endif | |
#include <WiFi.h> | |
#include <WiFiMulti.h> | |
#include "time.h" | |
#include "shadow.h" | |
#include "m5stickc_5v.h" | |
#include "secrets.h" | |
void sync_rtc_with_ntp(); | |
WiFiMulti wifiMulti; | |
#define PIN_LED GPIO_NUM_10 | |
#define PWMCH_LED 0 | |
void setLedBrightness(int16_t power){ | |
int32_t duty = 256 - power; | |
if(duty<0) duty = 0; | |
if(256<duty) duty = 256; | |
ledcWrite(PWMCH_LED, duty); // off=0 max=256 | |
} | |
void setup() { | |
esp_log_level_set("*", ESP_LOG_DEBUG); | |
//esp_log_level_set("aws_iot", ESP_LOG_NONE); | |
Serial.begin(115200); | |
Serial.flush(); | |
delay(50); | |
Serial.print("M5StickC initializing..."); | |
M5.Axp.begin(true, true, false, false, false, false); | |
Serial.println("OK"); | |
M5.Rtc.begin(); | |
M5.Axp.EnableCoulombcounter(); | |
wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD); | |
pinMode(PIN_LED, OUTPUT); | |
ledcSetup(PWMCH_LED, 12000, 8); | |
ledcAttachPin(PIN_LED, PWMCH_LED); | |
setLedBrightness(0); | |
} | |
class Every{ | |
unsigned long intervalMs = 1000; | |
unsigned long nextMs = 0; | |
public: | |
Every(unsigned long interval, unsigned long delay){ | |
this->intervalMs = interval; | |
this->nextMs = millis() + delay; | |
} | |
bool alarm(){ | |
if(this->nextMs <= millis()){ | |
this->nextMs = millis() + this->intervalMs; | |
return true; | |
} | |
return false; | |
} | |
}; | |
String mac_string(const uint8_t *mac) | |
{ | |
char macStr[18] = { 0 }; | |
sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); | |
return String(macStr); | |
} | |
void loop(){ | |
if(M5.Axp.GetBtnPress() == 0x02) | |
{ | |
esp_restart(); | |
} | |
M5.update(); | |
output5v_ctrl(1 <= fanSpeed); | |
delay(1); | |
if(WiFi.status() != WL_CONNECTED){ | |
uint8_t ret = wifiMulti.run(30 * 1000); | |
printf("wifiMulti.run: ret=%d\n", ret); | |
if(ret == WL_CONNECTED) { | |
uint8_t mac[6]; | |
WiFi.macAddress(mac); | |
printf("CONNECTED SSID=%s RSSI=%d localIP=%s mac=%s\n", WiFi.SSID().c_str(), WiFi.RSSI(), WiFi.localIP().toString().c_str(), mac_string(mac).c_str()); | |
sync_rtc_with_ntp(); | |
}else{ | |
delay(5000); | |
} | |
return; | |
} | |
static Every* every_a = new Every(60*1000, 10*1000); | |
bool update = false; | |
if(every_a->alarm()){ update = true; } | |
shadow_loop(update, 1); | |
/* | |
static Every* every_b = new Every(10*1000, 0); | |
if(every_b->alarm()){ | |
RTC_TimeTypeDef t; | |
RTC_DateTypeDef d; | |
M5.Rtc.GetTime(&t); | |
M5.Rtc.GetData(&d); | |
printf("RTC: %04d-%02d-%02d %02d:%02d:%02d\n", d.Year, d.Month, d.Date, t.Hours, t.Minutes, t.Seconds); | |
printf("AXP Temp: %.1fC \n", M5.Axp.GetTempInAXP192()); | |
printf("Bat:\n V: %.3fv I: %.3fma\n", M5.Axp.GetBatVoltage(), M5.Axp.GetBatCurrent()); | |
printf("USB:\n V: %.3fv I: %.3fma\n", M5.Axp.GetVBusVoltage(), M5.Axp.GetVBusCurrent()); | |
printf("5V-In:\n V: %.3fv I: %.3fma\n", M5.Axp.GetVinVoltage(), M5.Axp.GetVinCurrent()); | |
printf("Bat power %.3fmw\n", M5.Axp.GetBatPower()); | |
printf("\n"); | |
} | |
*/ | |
static Every* every_c = new Every(100, 0); | |
if(every_c->alarm()){ | |
setLedBrightness(ledBrightness); | |
} | |
const int usbCurrentHistorySize = 10; | |
static float usbCurrentHistory[usbCurrentHistorySize]; | |
static uint32_t usbCurrentHistoryIndex = 0; | |
static Every* every_d = new Every(500, 0); | |
if(every_d->alarm()){ | |
usbCurrentHistory[usbCurrentHistoryIndex] = M5.Axp.GetVBusCurrent(); | |
usbCurrentHistoryIndex = (usbCurrentHistoryIndex + 1) % usbCurrentHistorySize; | |
if(usbCurrentHistoryIndex == 0 || 0<=usbCurrent){ | |
float sum = 0.0; | |
for(int i=0; i<usbCurrentHistorySize; i++){ | |
sum += usbCurrentHistory[i]; | |
} | |
usbCurrent = sum / usbCurrentHistorySize; | |
printf("usbCurrent=%6.2f\n", usbCurrent); | |
} | |
} | |
} | |
void sync_rtc_with_ntp() | |
{ | |
const char* ntpServer = "ntp.jst.mfeed.ad.jp"; | |
configTime(9 * 3600, 0, ntpServer); | |
struct tm timeInfo; | |
if (getLocalTime(&timeInfo)) { | |
RTC_TimeTypeDef t; | |
t.Hours = timeInfo.tm_hour; | |
t.Minutes = timeInfo.tm_min; | |
t.Seconds = timeInfo.tm_sec; | |
M5.Rtc.SetTime(&t); | |
RTC_DateTypeDef d; | |
d.WeekDay = timeInfo.tm_wday; | |
d.Month = timeInfo.tm_mon + 1; | |
d.Date = timeInfo.tm_mday; | |
d.Year = timeInfo.tm_year + 1900; | |
M5.Rtc.SetData(&d); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define WIFI_SSID "my-ssid" | |
#define WIFI_PASSWORD "my-passed" | |
#define AWSIOT_THING_NAME "M5StickC-thing-name" | |
#define AWSIOT_MQTT_CLIENT_ID "clientid-m5stickc-xxx" | |
#define AWSIOT_MQTT_HOST "xxxxxxxx-ats.iot.xxxxx.amazonaws.com" | |
#define AWSIOT_MQTT_PORT 8883 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string.h> | |
#include <stdint.h> | |
#include "aws_iot_config.h" | |
#include "aws_iot_log.h" | |
#include "aws_iot_version.h" | |
#include "aws_iot_mqtt_client_interface.h" | |
#include "aws_iot_shadow_interface.h" | |
#include "secrets.h" | |
#define MAX_LENGTH_OF_UPDATE_JSON_BUFFER 200 | |
static const char *TAG = "shadow"; | |
extern const uint8_t aws_root_ca_pem_start[] asm("_binary_aws_root_ca_pem_start"); | |
extern const uint8_t aws_root_ca_pem_end[] asm("_binary_aws_root_ca_pem_end"); | |
extern const uint8_t certificate_pem_crt_start[] asm("_binary_certificate_pem_crt_start"); | |
extern const uint8_t certificate_pem_crt_end[] asm("_binary_certificate_pem_crt_end"); | |
extern const uint8_t private_pem_key_start[] asm("_binary_private_pem_key_start"); | |
extern const uint8_t private_pem_key_end[] asm("_binary_private_pem_key_end"); | |
int8_t fanSpeed = 0; | |
float temperature = 0.0; | |
int16_t ledBrightness = 0; | |
float usbCurrent = 0.0; | |
bool deltaFlag = false; | |
static bool shadowUpdateInProgress; | |
enum { | |
INACTIVE, | |
ACTIVE, | |
ERROR | |
} status; | |
void _cb_aws_iot_shadow_update(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, | |
const char *pReceivedJsonDocument, void *pContextData) { | |
IOT_UNUSED(pThingName); | |
IOT_UNUSED(action); | |
IOT_UNUSED(pReceivedJsonDocument); | |
IOT_UNUSED(pContextData); | |
shadowUpdateInProgress = false; | |
deltaFlag = false; | |
if(SHADOW_ACK_TIMEOUT == status) { | |
ESP_LOGE(TAG, "Update timed out"); | |
} else if(SHADOW_ACK_REJECTED == status) { | |
ESP_LOGE(TAG, "Update rejected"); | |
} else if(SHADOW_ACK_ACCEPTED == status) { | |
ESP_LOGI(TAG, "Update accepted"); | |
} | |
} | |
void fanSpeedActuate_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) { | |
IOT_UNUSED(pJsonString); | |
IOT_UNUSED(JsonStringDataLen); | |
if(pContext != NULL) { | |
ESP_LOGI(TAG, "Delta - fanSpeed changed to %d", *(int8_t *) (pContext->pData)); | |
deltaFlag = true; | |
} | |
} | |
void ledBrightnessActuate_Callback(const char *pJsonString, uint32_t JsonStringDataLen, jsonStruct_t *pContext) { | |
IOT_UNUSED(pJsonString); | |
IOT_UNUSED(JsonStringDataLen); | |
if(pContext != NULL) { | |
ESP_LOGI(TAG, "Delta - ledBrightness changed to %d", *(int16_t *) (pContext->pData)); | |
deltaFlag = true; | |
} | |
} | |
jsonStruct_t fanSpeedActuator; | |
jsonStruct_t ledBrightnessActuator; | |
jsonStruct_t temperatureHandler; | |
jsonStruct_t usbCurrentHandler; | |
AWS_IoT_Client mqttClient; | |
bool _shadow_init(void) | |
{ | |
IoT_Error_t rc = FAILURE; | |
fanSpeedActuator.cb = fanSpeedActuate_Callback; | |
fanSpeedActuator.pData = &fanSpeed; | |
fanSpeedActuator.pKey = "fanSpeed"; | |
fanSpeedActuator.type = SHADOW_JSON_INT8; | |
fanSpeedActuator.dataLength = sizeof(int8_t); | |
ledBrightnessActuator.cb = ledBrightnessActuate_Callback; | |
ledBrightnessActuator.pData = &ledBrightness; | |
ledBrightnessActuator.pKey = "ledBrightness"; | |
ledBrightnessActuator.type = SHADOW_JSON_INT16; | |
ledBrightnessActuator.dataLength = sizeof(int16_t); | |
temperatureHandler.cb = NULL; | |
temperatureHandler.pKey = "temperature"; | |
temperatureHandler.pData = &temperature; | |
temperatureHandler.type = SHADOW_JSON_FLOAT; | |
temperatureHandler.dataLength = sizeof(float); | |
usbCurrentHandler.cb = NULL; | |
usbCurrentHandler.pKey = "usbCurrent"; | |
usbCurrentHandler.pData = &usbCurrent; | |
usbCurrentHandler.type = SHADOW_JSON_FLOAT; | |
usbCurrentHandler.dataLength = sizeof(float); | |
ESP_LOGI(TAG, "AWS IoT SDK Version %d.%d.%d-%s", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); | |
ShadowInitParameters_t sp = ShadowInitParametersDefault; | |
sp.pHost = AWSIOT_MQTT_HOST; | |
sp.port = AWSIOT_MQTT_PORT; | |
sp.pClientCRT = (const char *)certificate_pem_crt_start; | |
sp.pClientKey = (const char *)private_pem_key_start; | |
sp.pRootCA = (const char *)aws_root_ca_pem_start; | |
sp.enableAutoReconnect = false; | |
sp.disconnectHandler = NULL; | |
rc = aws_iot_shadow_init(&mqttClient, &sp); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_init failed. rc=%d", rc); | |
return false; | |
} | |
ShadowConnectParameters_t scp = ShadowConnectParametersDefault; | |
scp.pMyThingName = AWSIOT_THING_NAME; | |
scp.pMqttClientId = AWSIOT_MQTT_CLIENT_ID; | |
scp.mqttClientIdLen = (uint16_t) strlen(AWSIOT_MQTT_CLIENT_ID); | |
rc = aws_iot_shadow_connect(&mqttClient, &scp); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_connect failed. rc=%d", rc); | |
return false; | |
} | |
rc = aws_iot_shadow_set_autoreconnect_status(&mqttClient, true); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_set_autoreconnect_status failed. rc=%d", rc); | |
return false; | |
} | |
rc = aws_iot_shadow_register_delta(&mqttClient, &fanSpeedActuator); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_register_delta fanSpeedActuator failed. rc=%d", rc); | |
return false; | |
} | |
rc = aws_iot_shadow_register_delta(&mqttClient, &ledBrightnessActuator); | |
if(SUCCESS != rc) { | |
ESP_LOGE(TAG, "aws_iot_shadow_register_delta ledBrightness failed. rc=%d", rc); | |
return false; | |
} | |
return true; | |
} | |
bool _shadow_deinit(void) | |
{ | |
IoT_Error_t rc = FAILURE; | |
rc = aws_iot_shadow_disconnect(&mqttClient); | |
if(SUCCESS != rc){ ESP_LOGE(TAG, "error: aws_iot_shadow_disconnect. rc=%d", rc); return false; } | |
rc = aws_iot_shadow_free(&mqttClient); | |
if(SUCCESS != rc){ ESP_LOGE(TAG, "error: aws_iot_shadow_free. rc=%d", rc); return false; } | |
return true; | |
} | |
bool _shadow_tick(bool update, uint32_t timeout) | |
{ | |
IoT_Error_t rc = aws_iot_shadow_yield(&mqttClient, timeout); | |
if(NETWORK_ATTEMPTING_RECONNECT == rc) { | |
return true; | |
} | |
if(NETWORK_RECONNECTED == rc || SUCCESS == rc){ | |
// OK | |
}else{ | |
ESP_LOGE(TAG, "error: aws_iot_shadow_yield. rc=%d", rc); | |
return false; | |
} | |
if(deltaFlag){ update = true; } | |
if(update){ | |
if(shadowUpdateInProgress){ | |
return true; | |
} | |
char JsonDocumentBuffer[MAX_LENGTH_OF_UPDATE_JSON_BUFFER]; | |
size_t sizeOfJsonDocumentBuffer = sizeof(JsonDocumentBuffer) / sizeof(JsonDocumentBuffer[0]); | |
rc = aws_iot_shadow_init_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer); | |
if(SUCCESS != rc){ ESP_LOGE(TAG, "error: aws_iot_shadow_init_json_document. rc=%d", rc); return false; } | |
rc = aws_iot_shadow_add_reported(JsonDocumentBuffer, sizeOfJsonDocumentBuffer, | |
4, | |
&temperatureHandler, | |
&fanSpeedActuator, | |
&ledBrightnessActuator, | |
&usbCurrentHandler | |
); | |
if(SUCCESS != rc){ ESP_LOGE(TAG, "error: aws_iot_shadow_add_reported. rc=%d", rc); return false; } | |
rc = aws_iot_finalize_json_document(JsonDocumentBuffer, sizeOfJsonDocumentBuffer); | |
if(SUCCESS != rc){ ESP_LOGE(TAG, "error: aws_iot_finalize_json_document. rc=%d", rc); return false; } | |
ESP_LOGI(TAG, "aws_iot_shadow_update: %s", JsonDocumentBuffer); | |
rc = aws_iot_shadow_update(&mqttClient, AWSIOT_THING_NAME, JsonDocumentBuffer, | |
_cb_aws_iot_shadow_update, NULL, 4, true); | |
if(SUCCESS != rc){ ESP_LOGE(TAG, "error: aws_iot_shadow_update. rc=%d", rc); return false; } | |
shadowUpdateInProgress = true; | |
} | |
return true; | |
} | |
bool shadow_loop(bool update, uint32_t timeout) | |
{ | |
switch (status) | |
{ | |
case INACTIVE: { | |
bool ok = _shadow_init(); | |
ESP_LOGI(TAG, "_shadow_init: %d", ok); | |
if(ok){ | |
status = ACTIVE; | |
} | |
break; | |
} | |
case ACTIVE: | |
if(!_shadow_tick(update, timeout)){ | |
if(_shadow_deinit()){ | |
status = INACTIVE; | |
}else{ | |
status = ERROR; | |
} | |
} | |
break; | |
case ERROR: | |
return false; | |
break; | |
default: | |
break; | |
} | |
return true; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <stdint.h> | |
bool shadow_loop(bool update, uint32_t timeout); | |
extern int8_t fanSpeed; | |
extern float temperature; | |
extern int16_t ledBrightness; | |
extern float usbCurrent; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const AWS = require('aws-sdk'); | |
AWS.config.update({region: 'ap-northeast-1'}); | |
const docClient = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'}); | |
const iotData = new AWS.IotData({endpoint: "a15o79dfbws0i-ats.iot.ap-northeast-1.amazonaws.com"}); | |
exports.handler = async (event) => { | |
const timestamp_begin = (new Date()).getTime() - (1 * 60 * 1000); | |
const params = { | |
TableName: 'awsiot_shadow_values', | |
KeyConditionExpression: 'client_id = :client_id and aws_timestamp >= :timestamp_begin', | |
ExpressionAttributeValues: { | |
':client_id': "M5StickC-003", | |
':timestamp_begin': timestamp_begin | |
}, | |
ScanIndexForward: false, | |
Limit: 1 | |
}; | |
const queryResult = await docClient.query(params).promise(); | |
if(queryResult.Items.length == 0){ | |
return {statusCode: 200, body: {result: "error", error: "no items."}}; | |
} | |
const item = queryResult.Items[0]; | |
console.log("item = ", item); | |
let fanSpeed = 0; | |
let ledColor = "000010" + "000010"; | |
if(30.0 <= item.state.reported.temperature){ | |
fanSpeed = 80; | |
ledColor = "201000" + "201000"; | |
} | |
const updateResult = await updateFanSpeed(fanSpeed, ledColor); | |
return { | |
statusCode: 200, | |
body: {result: "success", updateResult: updateResult} | |
}; | |
}; | |
function updateFanSpeed(fanSpeed, ledColor){ | |
const p1 = iotData.updateThingShadow({ | |
thingName: "M5StickC-003", | |
payload: JSON.stringify({ | |
state: { | |
desired: { | |
fanSpeed: fanSpeed | |
} | |
} | |
}) | |
}).promise(); | |
const p2 = iotData.updateThingShadow({ | |
thingName: "012395fb0a29199a01", | |
payload: JSON.stringify({ | |
state: { | |
desired: { | |
ledColor: ledColor | |
} | |
} | |
}) | |
}).promise(); | |
return Promise.all([p1,p2]); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment