TD_ICT
Published

Dear Remote Worker

We developed "Remote Kun, " a robot that helps you feel closer to colleagues working remotely.

AdvancedShowcase (no instructions)10 hours202
Dear Remote Worker

Things used in this project

Hardware components

ESP32 Basic Core IoT Development Kit
M5Stack ESP32 Basic Core IoT Development Kit
×1
SG90 Micro-servo motor
SG90 Micro-servo motor
×2
TTP223 touch sensor module
×1

Software apps and online services

PlatformIO IDE
PlatformIO IDE
Visual Studio Code Extension

Story

Read more

Schematics

CircuitDiagram

Code

M5Stack Program(main.cpp)

Arduino
M5Stack Core main (PlatformIO)
#include <M5Unified.h>
#include <WiFi.h>

#include "remotekun-arm.h"
#include "remotekun-communication.h"
#include "remotekun-face.h"
#include "remotekun-patlump.h"
#include "remotekun-touch.h"

// constant ---------------------------------------
// defined by .env
#ifndef WIFI_SSID
#define WIFI_SSID "WIFI_SSID_UNDEFINED"
#endif
#ifndef WIFI_PASSWORD
#define WIFI_PASSWORD "WIFI_PASSWORD_UNDEFINED"
#endif
#ifndef AWS_IOT_ENDPOINT
#define AWS_IOT_ENDPOINT "AWS_IOT_ENDPOINT_UNDEFINED"
#endif
#ifndef DEVICE_NAME
#define DEVICE_NAME "DEVICE_NAME_UNDEFINED"
#endif

const uint8_t PIN_ARM_L = 5; // left arm
const uint8_t PIN_ARM_R = 19; // right arm
const uint8_t PIN_TOUCH = 15; // touch sensor
const uint8_t PIN_LED0 = 16; // patrol lamp
const uint8_t PIN_LED1 = 17;
const uint8_t PIN_LED2 = 21;
const uint8_t PIN_LED3 = 22;
const unsigned long MAIN_LOOP_WAIT_TIME_MS = 10; // main loop wait [msec]
const unsigned long AUTO_TURNOFF_INTERVAL = 10 * 1000; // patrol lamp auto off time
const unsigned long MESSAGE_DISABLE_INTERVAL = 6 * 1000; // emotion continuation time
const unsigned long EMOTION_EXPLOSION_TIME = 5 * 60 * 1000; // 5 * 60 * 1000; // dissatisfaction explosion time = standard 5 minutes [msec]

// global objects --------------------------------------
RemoteKunArm arm(PIN_ARM_L, PIN_ARM_R);
RemoteKunCommunication communication;
RemoteKunFace face;
RemoteKunPatlump patlump(PIN_LED0, PIN_LED1, PIN_LED2, PIN_LED3);
RemoteKunTouch remoteKunTouch(PIN_TOUCH);

// callbacks --------------------------------------

// MQTT topic reception processing
void onEmotionMessage(const String &payload, const size_t size)
{

  // message analysis
  char buf[256];
  payload.toCharArray(buf, 256);
  JsonDocument recievedJson;
  DeserializationError error = deserializeJson(recievedJson, buf);
  if (error)
  {
    Serial.println("onEmotionMessage Deserialization failed!");
    return;
  }

  // ignore message that is received multiple times within a certain period
  if (!communication.isMessageEnabled()) return;

  // face, arms
  if (strcmp(recievedJson["emotion"], "angry") == 0)
  {
    if (face.emotion == Expression::Angry) return;
    face.angry();
    arm.angry();
    communication.updateLastMassageRecievedTime();
  }
  else if (strcmp(recievedJson["emotion"], "disgusted") == 0)
  {
    if (face.emotion == Expression::Angry) return;
    face.angry();
    arm.angry();
    communication.updateLastMassageRecievedTime();
  }
  else if (strcmp(recievedJson["emotion"], "happy") == 0)
  {
    if (face.emotion == Expression::Happy) return;
    face.happy();
    arm.happy();
    communication.updateLastMassageRecievedTime();
  }
  else
  {
    if (face.emotion == Expression::Neutral) return;
    face.neutral();
    arm.neutral();
    communication.updateLastMassageRecievedTime();
  }

}

// touch detection
void IRAM_ATTR onTouchDetected()
{
  if (!remoteKunTouch.isTouchEnabled()) return;
  communication.requestPublish();
  remoteKunTouch.updateLastTouchedTime();
  face.happy();
  face.speech("Thank you!");
  patlump.off();
}

// emotion explosion
void onEmotionExplosion() {
  patlump.on();
  Serial.println("emotion explosion!");
}

void setup()
{

  Serial.begin(115200);

  M5.begin();

  // Wi-Fi
  Serial.println(WIFI_SSID);
  Serial.println(WIFI_PASSWORD);
  Serial.println("Connecting to WiFi");
  M5.Lcd.printf("Connecting to WiFi(%s)", WIFI_SSID);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    M5.Lcd.print('.');
  }
  Serial.println("Succeessfully connected to WiFi");
  M5.Lcd.printf("Succeessfully connected to WiFi");

  // arms
  arm.begin();
  arm.neutral();

  // MQTT
  communication.begin(AWS_IOT_ENDPOINT, 8883, "RemoteKun-Device1");
  Serial.println("Connecting to AWS IoT...");
  M5.Lcd.printf("Connecting to AWS IoT...");
  bool connectionResult = communication.connect();
  M5.Lcd.printf("MQTT Connection Result: %d\n", connectionResult);
  Serial.printf("MQTT Connection Result: %d\n", connectionResult);
  communication.onEmotionMessage = onEmotionMessage;
  communication.messageDisableInterval = MESSAGE_DISABLE_INTERVAL;
  communication.subscribe();

  // face
  face.onEmotionExplosion = onEmotionExplosion;
  face.emotionPeriod = MESSAGE_DISABLE_INTERVAL;
  face.emotionExplosionTime = EMOTION_EXPLOSION_TIME;
  face.autoRecovery = false;
  face.begin();

  // touch
  remoteKunTouch.begin(onTouchDetected);

  // patlump
  patlump.activeLevel = LOW;
  patlump.inactiveLevel = HIGH;
  patlump.autoTurnOffInterval = AUTO_TURNOFF_INTERVAL;
  patlump.begin();

}

void loop()
{

  // MQTT
  communication.action();

  // arms
  arm.action();

  // face
  face.action();

  // patlump
  patlump.action();

  // adjustment
  delay(MAIN_LOOP_WAIT_TIME_MS);
}

M5Stack Program(arm)

Arduino
Arm control module(PlatformIO)
/*
arm control

- SG90
- 50Hz -> 20msec
- -90deg: 0.5msec = 6.4 / 256
- 0deg: 1.45msec = 18.56 / 256
- 90deg: 2.4msec = 30.72 / 256

action(): Queues are executed sequentially according to their scheduled execution times

When methods executed, the action is added to the queue.
angry()
disgusted()
happy()

*/
#include <M5Unified.h>

class RemoteKunArm
{
public:
    static const uint8_t MAX_ACTIONS = 16;
    double PWM_FREQUENCY = 50;
    double PWM_RESOLUTION = 8;
    uint8_t RIGHT_ARM_CHANNEL = 0;
    uint8_t LEFT_ARM_CHANNEL = 1;

    RemoteKunArm(uint8_t pin_right_arm, uint8_t pin_left_arm)
    {
        _pin_right_arm = pin_right_arm;
        _pin_left_arm = pin_left_arm;
        _pwm_resolution_pulse = pow(2, PWM_RESOLUTION);
    }
    void begin()
    {
        ledcSetup(0, PWM_FREQUENCY, PWM_RESOLUTION);
        ledcSetup(1, PWM_FREQUENCY, PWM_RESOLUTION);
        ledcAttachPin(_pin_right_arm, RIGHT_ARM_CHANNEL);
        ledcAttachPin(_pin_left_arm, LEFT_ARM_CHANNEL);
    }

    void angry()
    {
        unsigned long now = millis();
        uint8_t action_num = 5;
        int angles[action_num] = {90, -90, 90, -90, 0};

        for (uint8_t i = 0; i < action_num; i++)
        {
            _right_arm_action_queue[i] = angles[i];
            _left_arm_action_queue[i] = -angles[i];
            _action_timeout_queue[i] = now + i * 500;
        }
        _action_index = 0;
        _action_count = action_num;
    }

    void happy()
    {
        unsigned long t = millis() - 800;
        uint8_t action_num = 6;
        int angles[action_num] = {90, 0, 90, 0, 90, 0};

        for (uint8_t i = 0; i < action_num; i++)
        {
            _right_arm_action_queue[i] = angles[i];
            _left_arm_action_queue[i] = -angles[i];
            if (i % 2 == 0) {
                t += 800;
            } else {
                t += 400;
            }
            _action_timeout_queue[i] = t;
        }
        _action_index = 0;
        _action_count = action_num;
    }

    void disgusted()
    {
        angry();
    }

    void arbitrary(int right_arm_angless[], int left_arm_angles[], int timeouts[], int action_num)
    {
        unsigned long now = millis();
        for (uint8_t i = 0; i < action_num; i++)
        {
            _right_arm_action_queue[i] = right_arm_angless[i];
            _left_arm_action_queue[i] = left_arm_angles[i];
            _action_timeout_queue[i] = now + i * timeouts[i];
        }
        _action_index = 0;
        _action_count = action_num;
    }

    void neutral()
    {
        unsigned long now = millis();
        _right_arm_action_queue[0] = 0;
        _left_arm_action_queue[0] = 0;
        _action_timeout_queue[0] = now;
        _action_index = 0;
        _action_count = 1;
    }

    void action()
    {
        if (_action_index >= _action_count)
        {
            return;
        }
        if (millis() >= _action_timeout_queue[_action_index])
        {
            ledcWrite(RIGHT_ARM_CHANNEL, _angle2pulse(_right_arm_action_queue[_action_index]));
            ledcWrite(LEFT_ARM_CHANNEL, _angle2pulse(_left_arm_action_queue[_action_index]));
            _action_index++;
        }
    }

private:
    uint8_t _pin_right_arm = 19;
    uint8_t _pin_left_arm = 26;
    uint32_t _pwm_resolution_pulse = 256;
    uint32_t _right_arm_action_queue[MAX_ACTIONS] = {0}; // right arm angle queue
    uint32_t _left_arm_action_queue[MAX_ACTIONS] = {0};  // left arm angle queue
    uint32_t _action_timeout_queue[MAX_ACTIONS] = {0};   // scheduled execution time queue
    uint8_t _action_index = 0;                  // queue index
    uint8_t _action_count = 0;                  // queue length

    // convert angle [deg] to pulse width
    // (1.9 / 180.0 * (angle + 90) + 0.5) * _pwm_resolution_pulse
    uint32_t _angle2pulse(int angle)
    {
        return (uint32_t)((0.0105555555 * (angle + 90) + 0.5) / 20.0 * _pwm_resolution_pulse);
    }

};

M5Stack Program(communication)

Arduino
MQTT communication with AWS IoT(PlatformIO)
/*

MQTT communication with AWS IoT

Define the content of the environment variables by concatenating all lines except for the BEGIN CERTIFICATE and END CERTIFICATE lines into a single line
AWS_CERT_CA_CONTENT
AWS_CERT_PRIVATE_CONTENT
AWS_CERT_CRT_CONTENT

Topics
/RemoteKun/worker/{device_name}/emotion
/RemoteKun/device/{device_name}/slack

*/

#include <WiFiClientSecure.h>
#include <MQTTPubSubClient.h>
#include <ArduinoJson.h>

//  ---------------------------------------

#ifndef AWS_CERT_CA_CONTENT
#define AWS_CERT_CA_CONTENT "AWS_CERT_CA_UNDEFINED"
#endif
#ifndef AWS_CERT_PRIVATE_CONTENT
#define AWS_CERT_PRIVATE_CONTENT "AWS_CERT_PRIVATE_UNDEFINED"
#endif
#ifndef AWS_CERT_CRT_CONTENT
#define AWS_CERT_CRT_CONTENT "AWS_CERT_CRT_UNDEFINED"
#endif

const char AWS_CERT_CA[] =
    "-----BEGIN CERTIFICATE-----\n" 
    AWS_CERT_CA_CONTENT
    "-----END CERTIFICATE-----\n";

const char AWS_CERT_PRIVATE[] =
    "-----BEGIN RSA PRIVATE KEY-----\n" 
    AWS_CERT_PRIVATE_CONTENT
    "-----END RSA PRIVATE KEY-----\n";

const char AWS_CERT_CRT[] =
    "-----BEGIN CERTIFICATE-----\n" 
    AWS_CERT_CRT_CONTENT
    "-----END CERTIFICATE-----\n";

class RemoteKunCommunication
{
public:

    unsigned int timeout = 3;
    unsigned long messageDisableInterval = 10000; // Message reception disable period [msec]

    WiFiClientSecure client; // SSL client
    MQTTPubSubClient mqtt;   // MQTT client
    std::function<void(const String& payload, const size_t size)> onEmotionMessage;

    void begin(const char *host, uint16_t port, const char *device_name)
    {
        _host = host;
        _port = port;
        _device_name = device_name;
        mqtt.begin(client);
    }

    bool connect()
    {
        // Connect to AWS IoT endpoint
        Serial.print("connecting to host...");
        client.stop();
        // connect to aws endpoint with certificates and keys
        client.setCACert(AWS_CERT_CA);
        client.setCertificate(AWS_CERT_CRT);
        client.setPrivateKey(AWS_CERT_PRIVATE);
        while (!client.connect(_host, _port))
        {
            Serial.print(".");
            delay(1000);
        }
        Serial.println(" OK!");

        // Connect to AWS MQTT broker
        Serial.print("connecting to aws mqtt broker...");
        mqtt.disconnect();
        unsigned int t = 0;
        while (!mqtt.connect("RemoteKun-Device1"))
        {
            Serial.print(".");
            delay(1000);
            t++;
            if (t > timeout)
            {
                Serial.println("timeout");
                return false;
            }
            mqtt.update();
        }
        Serial.println(" OK!");
        return true;
    }

    void subscribe()
    {
        char TOPIC_ACTION[64] = "";
        sprintf(TOPIC_ACTION, "/RemoteKun/worker/%s/emotion", _device_name);
        mqtt.subscribe(TOPIC_ACTION, onEmotionMessage);
    }

    // MQTT publish request
    void requestPublish() {
        _publishRequest = true;
    }

    void action() {
        mqtt.update();
        if (!_publishRequest) return;
        publish();
    }

    // Publish
    // Do not call directly from interrupt handlers (doing so may cause WDT timeout and restart)
    void publish()
    {
        _publishRequest = false;
        JsonDocument doc;
        doc["message"] = "hello from RemoteKun!";
        char buffer[128];
        serializeJson(doc, buffer);
        char TOPIC_SLACK[128] = "";
        sprintf(TOPIC_SLACK, "/RemoteKun/device/%s/slack", _device_name);
        mqtt.publish(TOPIC_SLACK, buffer);
    }

    void updateLastMassageRecievedTime() {
        _lastRecievedTime = millis();
    }

    bool isMessageEnabled() {
        unsigned long now = millis();
        if (now - _lastRecievedTime > messageDisableInterval) {
            return true;
        } else {
            Serial.printf("Message DISable\n"); // 
            return false;
        }
    }

private:
    const char *_host;
    uint16_t _port = 8883;
    const char *_device_name;
    bool _publishRequest = false;
    unsigned long _lastRecievedTime = 0;
};

M5Stack Program(face)

Arduino
face controller(PlatformIO)
/*
face control
*/

#include <M5Unified.h>
#include <Avatar.h>

using namespace m5avatar;

class RemoteKunFace {
public:

    const uint8_t EMOTION_NEUTRAL = 0;
    m5avatar::Expression emotion = Expression::Neutral;
    m5avatar::Avatar avatar;
    unsigned int emotionPeriod = 10000; // Emotion duration [msec]
    unsigned int speechPeriod = 10000; // Speech bubble display duration [msec]
    unsigned long emotionExplosionTime = 1000 * 10; // Discontent explosion threshold [msec]
    std::function<void(void)> onEmotionExplosion; // Discontent explosion callback
    bool autoRecovery = false; // Emotion auto-recovery enable flag

    void begin() {
        avatar.init();
        avatar.setMouthOpenRatio(0);
        avatar.setSpeechFont(&fonts::lgfxJapanMinchoP_12);
    }

    void happy() {
        _newEmotion = Expression::Happy;
    }

    void angry() {
        _newEmotion = Expression::Angry;
    }

    void neutral() {
        _newEmotion = Expression::Neutral;
    }

    void sleepy() {
        _newEmotion = Expression::Sleepy;
    }

    void speech(String text) {
        _newText = text;
        _speechRequestStatus = 1;
    }

    void action() {
        // Emotion change ----------------------------------------------
        if (_newEmotion != emotion) {
            emotion = _newEmotion;
            _emotionStartTime = millis();
            avatar.setExpression(emotion);
            _emotionExplosionStatus = 0;
            Serial.printf("set emotion: %d\n", emotion);
        }
        // Automatically revert expression
        if (autoRecovery && 
            emotion != Expression::Neutral &&
             _emotionStartTime + emotionPeriod < millis()) {
            neutral();
            Serial.printf("auto emotion recovered to neutral\n");
        }
        // Emotion explosion ----------------------------------------------
        if (_emotionExplosionStatus == 0 && 
            emotion == Expression::Angry && 
            _emotionStartTime + emotionExplosionTime < millis()) {
            onEmotionExplosion();
            _emotionExplosionStatus = 1;
        }
        // Speech bubble ----------------------------------------------
        if (_speechRequestStatus == 1) {
            avatar.setSpeechText(_newText.c_str());
            avatar.setMouthOpenRatio(0.7);
            Serial.printf("remotekun-face: setSpeechText\n");
            _speechRequestStatus = 2;
            _speechStartTime = millis();
        }
        // Speech bubble revert
        if (_speechRequestStatus == 2 && _speechStartTime + speechPeriod < millis()) {
            avatar.setSpeechText("");
            avatar.setMouthOpenRatio(0);
            neutral();
            _speechRequestStatus = 0;
        }
    }

private:

    m5avatar::Expression _newEmotion = Expression::Neutral;
    String _newText = "";
    uint8_t _speechRequestStatus = 0;
    uint8_t _emotionExplosionStatus = 0;
    unsigned long _emotionStartTime = 0;
    unsigned long _speechStartTime = 0;

};

M5Stack Program(Rotating red light)

Arduino
Rotating red light controller(PlatformIO)
/*
Rotating red lights control
*/

class RemoteKunPatlump
{
public:
    uint8_t activeLevel = HIGH;
    uint8_t inactiveLevel = LOW;
    unsigned long interval = 75; // LED switching period [msec]
    unsigned long autoTurnOffInterval = 10000; // Auto-off time [msec]

    RemoteKunPatlump(
        uint8_t pin_led0,
        uint8_t pin_led1,
        uint8_t pin_led2,
        uint8_t pin_led3)
    {
        _pin_leds[0] = pin_led0;
        _pin_leds[1] = pin_led1;
        _pin_leds[2] = pin_led2;
        _pin_leds[3] = pin_led3;
    }

    void begin()
    {
        pinMode(_pin_leds[0], OUTPUT);
        pinMode(_pin_leds[1], OUTPUT);
        pinMode(_pin_leds[2], OUTPUT);
        pinMode(_pin_leds[3], OUTPUT);
        action();
    }

    void off()
    {
        _on = false;
    }

    void on()
    {
        _on = true;
        _lastTurnOnTime = millis();
    }

    void action()
    {

        unsigned long now = millis();

        // Wait for switching period
        if (now < _lastSwitchedTime + interval) return;

        // Auto-off after a certain period
        if (now > _lastTurnOnTime + autoTurnOffInterval) {
            off();
        }

        for (int k = 0; k < 4; k++)
        {

            if (k == _flashIndex)
            {
                if (_on)
                {
                    digitalWrite(_pin_leds[k], activeLevel);
                }
                else
                {
                    digitalWrite(_pin_leds[k], inactiveLevel);
                }
            }
            else
            {
                digitalWrite(_pin_leds[k], inactiveLevel);
            }
        }

        _lastSwitchedTime = now;
        _flashIndex = (_flashIndex + 1) % 4;
    }

private:
    uint8_t _pin_leds[4] = {0, 1, 2, 3};
    unsigned int _flashIndex = 0;
    unsigned long _lastSwitchedTime = 0;
    unsigned long _lastTurnOnTime = 0;
    bool _on = false;
};

Code

XML
<mxfile host="65bd71144e">
    <diagram id="FylvCg6JfPMyBLLnnH1h" name="Device">
        <mxGraphModel dx="1101" dy="866" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
            <root>
                <mxCell id="0"/>
                <mxCell id="1" parent="0"/>
                <mxCell id="2" value="M5Stack&lt;div&gt;Basic V2.7&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
                    <mxGeometry x="294" y="100" width="116" height="300" as="geometry"/>
                </mxCell>
                <mxCell id="3" value="&lt;div&gt;Servo Motor&lt;/div&gt;SG-90" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
                    <mxGeometry x="520" y="180" width="120" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="4" value="&lt;div&gt;Servo Motor&lt;/div&gt;SG-90" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
                    <mxGeometry x="520" y="260" width="120" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="5" value="rotating red light" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
                    <mxGeometry x="520" y="100" width="120" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="6" value="touch sensor" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
                    <mxGeometry x="520" y="340" width="120" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="7" value="" style="endArrow=classic;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" target="3" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="410" y="210" as="sourcePoint"/>
                        <mxPoint x="490" y="250" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="8" value="" style="endArrow=classic;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="410" y="289.8" as="sourcePoint"/>
                        <mxPoint x="520" y="289.8" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="9" value="" style="endArrow=classic;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="410" y="129.57999999999998" as="sourcePoint"/>
                        <mxPoint x="520" y="129.57999999999998" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="10" value="" style="endArrow=classic;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="6" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="440" y="240" as="sourcePoint"/>
                        <mxPoint x="410" y="370" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="12" value="&lt;p&gt;&lt;br&gt;&lt;/p&gt;" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#434445;aspect=fixed;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;align=center;outlineConnect=0;shape=mxgraph.vvd.wi_fi;rotation=-135;" parent="1" vertex="1">
                    <mxGeometry x="200" y="230" width="50" height="50" as="geometry"/>
                </mxCell>
                <mxCell id="13" value="Wi-Fi" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
                    <mxGeometry x="200" y="290" width="60" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="15" value="AWS" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;" parent="1" vertex="1">
                    <mxGeometry x="40" y="215" width="120" height="80" as="geometry"/>
                </mxCell>
            </root>
        </mxGraphModel>
    </diagram>
    <diagram id="b_0gra4JIXNoi4Pa5X-z" name="AWS">
        <mxGraphModel dx="1745" dy="722" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
            <root>
                <mxCell id="0"/>
                <mxCell id="1" parent="0"/>
                <mxCell id="XMrbSRp4rwc0Vd3_QZxJ-1" value="" style="group" parent="1" vertex="1" connectable="0">
                    <mxGeometry x="-40" y="230" width="820" height="510" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-1" value="API Gateway" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#E7157B;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.api_gateway;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="200" y="50" width="60" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-4" value="AWS IoT" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.aws_iot;fillColor=#5294CF;gradientColor=none;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="460" y="190" width="50" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-5" value="Lambda Function" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.lambda;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="362" y="50" width="60" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-6" value="Lambda Function" style="sketch=0;points=[[0,0,0],[0.25,0,0],[0.5,0,0],[0.75,0,0],[1,0,0],[0,1,0],[0.25,1,0],[0.5,1,0],[0.75,1,0],[1,1,0],[0,0.25,0],[0,0.5,0],[0,0.75,0],[1,0.25,0],[1,0.5,0],[1,0.75,0]];outlineConnect=0;fontColor=#232F3E;fillColor=#ED7100;strokeColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.lambda;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="540" y="50" width="60" height="60" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-7" value="Slack" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="700" y="40" width="120" height="80" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-9" value="" style="image;html=1;image=img/lib/clip_art/computers/Laptop_128x128.png;flipH=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry y="40" width="80" height="80" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-11" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="465" y="370" width="40" height="40" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-12" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="460" y="410" width="50" height="50" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-13" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="465" y="460" width="20" height="20" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-14" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="485" y="460" width="20" height="20" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-15" value="" style="rounded=1;whiteSpace=wrap;html=1;arcSize=0;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="510" y="420" width="10" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-16" value="" style="rounded=1;whiteSpace=wrap;html=1;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="450" y="420" width="10" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-17" value="" style="whiteSpace=wrap;html=1;aspect=fixed;labelBackgroundColor=#4F4F4F;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="470" y="375" width="30" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-18" value="RemoteKun" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="455" y="480" width="60" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-22" value="" style="endArrow=classic;html=1;labelBorderColor=#3333FF;fillColor=#0050ef;strokeColor=#001DBC;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="459.52" y="290" as="sourcePoint"/>
                        <mxPoint x="459.52" y="350" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-23" value="" style="endArrow=classic;html=1;fillColor=#d80073;strokeColor=#A50040;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="509.52" y="350" as="sourcePoint"/>
                        <mxPoint x="509.52" y="290" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-25" value="" style="endArrow=classic;html=1;labelBorderColor=#3333FF;fillColor=#0050ef;strokeColor=#001DBC;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="400" y="150" as="sourcePoint"/>
                        <mxPoint x="440" y="210" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-26" value="" style="endArrow=classic;html=1;labelBorderColor=#3333FF;fillColor=#0050ef;strokeColor=#001DBC;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="280" y="80" as="sourcePoint"/>
                        <mxPoint x="340" y="80" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-27" value="" style="endArrow=classic;html=1;fillColor=#d80073;strokeColor=#A50040;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="610" y="79.57999999999998" as="sourcePoint"/>
                        <mxPoint x="690" y="79.57999999999998" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-28" value="" style="endArrow=classic;html=1;labelBorderColor=#3333FF;fillColor=#0050ef;strokeColor=#001DBC;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="110" y="79.5" as="sourcePoint"/>
                        <mxPoint x="170" y="79.5" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-29" value="Emotion Message" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="90" y="90" width="60" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-32" value="AWS Cloud" style="points=[[0,0],[0.25,0],[0.5,0],[0.75,0],[1,0],[1,0.25],[1,0.5],[1,0.75],[1,1],[0.75,1],[0.5,1],[0.25,1],[0,1],[0,0.75],[0,0.5],[0,0.25]];outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;container=1;pointerEvents=0;collapsible=0;recursiveResize=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_aws_cloud_alt;strokeColor=#232F3E;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#232F3E;dashed=0;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="150" width="510" height="310" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-24" value="" style="endArrow=classic;html=1;fillColor=#d80073;strokeColor=#A50040;" parent="ILtzoP_ymIQ9zSnd1lK3-32" edge="1">
                    <mxGeometry width="50" height="50" relative="1" as="geometry">
                        <mxPoint x="370" y="210" as="sourcePoint"/>
                        <mxPoint x="410" y="150" as="targetPoint"/>
                    </mxGeometry>
                </mxCell>
                <mxCell id="m7r5OI7vA8Q7iABK5DND-1" value="HTTP" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="ILtzoP_ymIQ9zSnd1lK3-32">
                    <mxGeometry x="-30" y="50" width="60" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="ILtzoP_ymIQ9zSnd1lK3-31" value="Slack Message" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="XMrbSRp4rwc0Vd3_QZxJ-1" vertex="1">
                    <mxGeometry x="520" y="320" width="60" height="30" as="geometry"/>
                </mxCell>
                <mxCell id="m7r5OI7vA8Q7iABK5DND-4" value="MQTT" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="XMrbSRp4rwc0Vd3_QZxJ-1">
                    <mxGeometry x="455" y="310" width="60" height="30" as="geometry"/>
                </mxCell>
            </root>
        </mxGraphModel>
    </diagram>
</mxfile>

M5Stack Program(touch sensor)

Arduino
touch sensor controller(PlatformIO)
/*
touch sensor control
*/
#include <M5Unified.h>

class RemoteKunTouch {
public:

    unsigned long touchDisableInterval = 5000; // Touch disable period [msec]

    RemoteKunTouch(uint8_t pin_touch) {
        _pin_touch = pin_touch;
    }

    void begin(void (*onTouchDetected) ()) {
        pinMode(_pin_touch, INPUT);
        attachInterrupt(_pin_touch, onTouchDetected, FALLING);
    }

    void updateLastTouchedTime() {
        _lastTouchedTime = millis();
    }

    bool isTouchEnabled() {
        unsigned long now = millis();
        if (now - _lastTouchedTime > touchDisableInterval) {
            return true;
        } else {
            return false;
        }
    }

    uint8_t isTouching() {
        return digitalRead(_pin_touch);
    }

private:
    uint8_t _pin_touch;
    unsigned long _lastTouchedTime = 0;

};

Credits

TD_ICT
2 projects • 0 followers
We are a team developing IoT devices and related products in Japan.

Comments