[PYTHON] Spielen Sie, um Slack mithilfe von AWS PaaS über Raspberry Pi3 über Umgebungsdaten von SensorTag zu informieren

Überblick

→ Als Ergebnis haben wir etwas erstellt, das funktioniert, also werde ich es zusammenfassen.

Gesamtbild

SensorTag2Slack.png Die Figur wurde unter www.draw.io erstellt. Super praktisch.

Senden Sie Daten von SensorTag an Raspberry Pi3

Ich habe den SensorTag [CC2650stk] von TI verwendet (http://www.tij.co.jp/tool/jp/cc2650stk). Es verfügt über viele Sensoren wie Beschleunigung, Temperatur, Luftfeuchtigkeit, Beleuchtungsstärke, Kreisel, Geomagnetismus und Druck.

SensorTag Netzteil

Der SensorTag ist grundsätzlich batteriebetrieben. Wenn Sie den Sensor jedoch stark bewegen, ist der Stromverbrauch hoch. Dieses Mal möchte ich alle Sensorinformationen in chronologischer Reihenfolge abrufen. Schließen Sie Debug Board an SensorTag an, stellen Sie über USB eine Verbindung zu RaspberryPi3 her und stellen Sie eine Verbindung her. Die Stromversorgung erfolgte über ein USB-Kabel. Auf diese Weise können Sie lange messen, ohne sich Gedanken über den Akku machen zu müssen.

SensorTag ein- / ausschalten

Ich habe auf diese Seite verwiesen. http://kinokotimes.com/2017/03/07/usb-control-method-by-raspberry-pi/ [BeagleBoneBlackBox_USB Power Control](http://www.si-linux.co.jp/techinfo/index.php?BeagleBoneBlackBox_USB%E9%9B%BB%E6%BA%90%E5%88%B6%E5%BE% A1)

Ich sammle Sensordaten mit der App auf der Raspberry Pi-Seite. Gelegentlich stürzt die App ab und wird neu gestartet. Zu diesem Zeitpunkt wird auch die Stromversorgung von SensorTag für alle Fälle neu gestartet. Unabhängig davon, ob dies angemessen ist oder nicht, wird der Startstatus dadurch einheitlich.

usbrefresh.sh


#! /bin/sh
hub-ctrl -h 0 -P 2 -p 0
sleep 1
hub-ctrl -h 0 -P 2 -p 1

Sammlung mit Bluepy

Ich habe auf diese Seite verwiesen. http://taku-make.blogspot.jp/2015/02/blesensortag.html http://dev.classmethod.jp/hardware/raspberrypi/sensortag-raspberry-pi-2-script/ Es gab verschiedene BLE-bezogene Bibliotheken zum Sammeln von Informationen von Sensor-Tags. bluepy hat am einfachsten funktioniert, also jetzt.

An AWS IoT senden

Der [Beispielcode] des AWS SDK (https://github.com/aws/aws-iot-device-sdk-python/blob/master/samples/basicPubSub/basicPubSub.py) wurde unverändert verwendet. Apache 2.0 Lizenz. Wir haben Änderungen vorgenommen, z. B. das von BLE zu erwerbende Teil und das zu erstellende Teil von JSON. Grundsätzlich denke ich, dass der Code auf der Annahme basiert, dass die Kommunikation mit den X.509-Anmeldeinformationen durchgeführt wird.

sendMQTT.py


'''
/*
 * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
 '''

###-----------------------------------------------------------------------------

from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import sys
import logging
import time
import argparse
from btle import UUID, Peripheral, DefaultDelegate, AssignedNumbers
import struct
import math
from sensortag import SensorTag, KeypressDelegate
import json
from datetime import datetime

###-----------------------------------------------------------------------------

# Custom MQTT message callback
def customCallback(client, userdata, message):
        print("--------------")
	print("Received  : " + message.payload)
	print("from topic: " + message.topic)
	print("--------------\n\n")

###-----------------------------------------------------------------------------

# Read in command-line parameters
parser = argparse.ArgumentParser()

### AWS!
parser.add_argument("-e", "--endpoint", action="store", required=True, dest="host", help="Your AWS IoT custom endpoint")
parser.add_argument("-r", "--rootCA", action="store", dest="rootCAPath", default="root-CA.crt", help="Root CA file path")
parser.add_argument("-c", "--cert", action="store", dest="certificatePath", default="certificate.pem.crt",help="Certificate file path")
parser.add_argument("-k", "--key", action="store", dest="privateKeyPath", default="private.pem.key",help="Private key file path")
parser.add_argument("-w", "--websocket", action="store_true", dest="useWebsocket", default=False,
                    help="Use MQTT over WebSocket")
parser.add_argument("-id", "--clientId", action="store", dest="clientId", default="Raspi_1", help="Targeted client id")
parser.add_argument("-t", "--topic", action="store", dest="topic", default="etl/room", help="Targeted topic")

### SensorTag!
parser.add_argument('-n', action='store', dest='count', default=0,
        type=int, help="Number of times to loop data")
parser.add_argument('-T',action='store', dest="sleeptime", type=float, default=5.0, help='time between polling')
parser.add_argument('-H', action='store', dest="taghost", help='MAC of BT device')
parser.add_argument('--all', action='store_true', default=True)

args = parser.parse_args()
host = args.host
rootCAPath = args.rootCAPath
certificatePath = args.certificatePath
privateKeyPath = args.privateKeyPath
useWebsocket = args.useWebsocket
clientId = args.clientId
topic = args.topic
sleeptime = args.sleeptime
deviceID = args.clientId

###=============================================================================

if args.useWebsocket and args.certificatePath and args.privateKeyPath:
	parser.error("X.509 cert authentication and WebSocket are mutual exclusive. Please pick one.")
	exit(2)

if not args.useWebsocket and (not args.certificatePath or not args.privateKeyPath):
	parser.error("Missing credentials for authentication.")
	exit(2)

###=============================================================================

# Enabling selected sensors
print('Connecting to ' + args.taghost)
tag = SensorTag(args.taghost)
if args.all:
    tag.IRtemperature.enable()
    tag.humidity.enable()
    tag.barometer.enable()
    tag.accelerometer.enable()
    tag.magnetometer.enable()
    tag.gyroscope.enable()
    tag.battery.enable()
    tag.keypress.enable()
    tag.setDelegate(KeypressDelegate())
    tag.lightmeter.enable()
    time.sleep(1.0)

###=============================================================================

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

###=============================================================================

# Init AWSIoTMQTTClient
myAWSIoTMQTTClient = None
if useWebsocket:
	myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId, useWebsocket=True)
	myAWSIoTMQTTClient.configureEndpoint(host, 443)
	myAWSIoTMQTTClient.configureCredentials(rootCAPath)
else:
	myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)
	myAWSIoTMQTTClient.configureEndpoint(host, 8883)
	myAWSIoTMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(0.5)  # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec

# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
#myAWSIoTMQTTClient.subscribe(topic, 1, customCallback)
time.sleep(2)

###=============================================================================

# Publish to the same topic in a loop forever
loopCount = 0
Payload = {}
while True:
    Payload['ID'] = str(deviceID)

    ambient_temp, target_temparature = tag.IRtemperature.read()
    Payload["AmbientTemp"] = ambient_temp
    Payload["TargetTemp"] = target_temparature

    ambient_temp, rel_humidity = tag.humidity.read()
    Payload["Humidity"] = rel_humidity

    ambient_temp, pressure_millibars = tag.barometer.read()
    Payload["Barometer"] = pressure_millibars

    Acc_x, Acc_y, Acc_z = tag.accelerometer.read()
    Payload["AccX"] = Acc_x
    Payload["AccY"] = Acc_y
    Payload["AccZ"] = Acc_z

    magnet_x, magnet_y, magnet_z = tag.magnetometer.read()
    Payload["MagnetX"] = magnet_x
    Payload["MagnetY"] = magnet_y
    Payload["MagnetZ"] = magnet_z

    gyro_x, gyro_y, gyro_z = tag.gyroscope.read()
    Payload["GyroX"] = gyro_x
    Payload["GyroY"] = gyro_y
    Payload["GyroZ"] = gyro_z

    Payload["Light"] = tag.lightmeter.read()
    Payload["Batterys"] = tag.battery.read()

    Payload["Count"] = loopCount
    Payload["Datetime"] = datetime.now().strftime("%Y/%m/%d %H:%M:%S")

#    print("try to send!")
    myAWSIoTMQTTClient.publish(topic, json.dumps(Payload), 1)
#    print("end!")
    loopCount += 1
    time.sleep(sleeptime)

Überwachung von Leben und Tod von Python-Programmen

Das obige Programm schlägt manchmal fehl. Es kann sinnvoll sein, ernsthaft zu debuckeln, Es ist eine Raspeltorte und es ist Python, also habe ich beschlossen, dass es fallen würde. Führen Sie einmal pro Minute ein Skript wie das folgende mit cron aus. Möglicherweise möchten Sie sendMQTT.py jedes Mal beenden und mit nur cron starten. Es ist auch schwer, Maikai Python zu starten. Es gibt auch einen Teil von Python, der Wiederholungen zählt. Ich weiß auch, dass es ungefähr 10.000 Mal funktioniert, ohne zu fallen, also versucht cron, Leben und Tod zu überwachen.

Übrigens mit crontab -e

sendSensorData.sh


#!/bin/sh

ps | grep python
if [ "$?" -eq 0 ]
then
  logger "python is exist.  exit..."
  exit 0
else

  logger "start reset sequence..."
  sudo usbrefresh.sh
  sleep 3

  cd /home/pi/deviceSDK
  python ./sendMQTT.py -T 59 -H "AA:BB:CC:DD:EE:FF" -e awsiotnoarn.iot.ap-northeast-1.amazonaws.com  >/dev/null 2>&1 & 

  cd
  exit 0
fi

Genauigkeit für jeden Zyklus

Es gibt einen Teil des obigen Skripts, der -T 59 sagt. Dies bedeutet, 59 Sekunden in einer Endlosschleife im Python-Skript zu schlafen. Es gibt geringfügige Schwankungen wie die Zeit zum Erfassen von Daten mit BLE und die Kommunikationszeit mit AWS. Es kann möglich sein, ein Echtzeit-Betriebssystem zu verwenden und jede Minute einen Interrupt usw. einzufügen. Da es aus Raspetorte, Raspbian Jessie und Python besteht, wird beurteilt, dass es unmöglich ist, diesen Punkt zu erreichen, und es ist teilbar. Sie können tatsächlich Zeitreihendaten abrufen, jedoch nicht genau jede Minute. Die Daten zeigen, dass es einige Sekunden Jitter gibt.

AWS PaaS Die AWS-Seite wird ohne Server erstellt. Zum Lernen.

AWS IoT Ich habe auf diese Seite verwiesen. http://qiita.com/nsedo/items/c6f33c7cadea7023403f Der Rest ist immer noch das AWS SDK. MQTT erstellt und wirft nur JSON, sodass ich mir keine Gedanken über die Verwendung von AWS IoT machen musste. Es empfängt die JSON-Daten und legt sie unverändert in DynamoDB ab.

DynamoDB

DB zum Speichern von Zeitreihendaten

Schlüssel ist ID-Name und Zeitinformation. Ich habe irgendwo gehört, dass diese Methode beim Erstellen von Zeitreihendaten mit IoT gut ist. Andere sind wie das Anordnen der erfassten Daten. Bei der Analyse von Zeitreihendaten ordnen wir nur unstrukturierte Daten in der DB an. Wie Sie sehen können, gibt es einen Ort, an dem ich einen JSON erstellt und ihn so geworfen habe, wie er ist.

DB zum Speichern des Status

Der Schlüssel ist nur der ID-Name. Wert ist --Data: Der zuletzt empfangene JSON wie er ist --isNight: Geben Sie an, ob das Licht eingeschaltet ist oder nicht ist. Dieses Mal beschäftigen wir uns nur mit Änderungen der Lichtinformationen, also halten wir nur diesen Zustand. Um die Lambda-Seite zustandslos zu machen, richtet sich der Staat an DynamoDB.

Nur für den Fall, wenn Sie es in JSON schreiben, werden es solche Daten sein. Für die Zeitreihen-DB werden nur die folgenden Datenelemente ausgerichtet.

room.json


{
  "Data": {
    "AccX": {
      "N": "0.915283203125"
    },
    "AccY": {
      "N": "-0.129150390625"
    },
    "AccZ": {
      "N": "0.48974609375"
    },
    "AmbientTemp": {
      "N": "31.78125"
    },
    "Barometer": {
      "N": "1006.03"
    },
    "Batterys": {
      "N": "100"
    },
    "Count": {
      "N": "261"
    },
    "Datetime": {
      "S": "2017/09/06 13:31:35"
    },
    "GyroX": {
      "N": "-4.0740966796875"
    },
    "GyroY": {
      "N": "1.85394287109375"
    },
    "GyroZ": {
      "N": "1.983642578125"
    },
    "Humidity": {
      "N": "40.95458984375"
    },
    "ID": {
      "S": "Raspi_1"
    },
    "Light": {
      "N": "57.88"
    },
    "MagnetX": {
      "N": "38.83418803418803"
    },
    "MagnetY": {
      "N": "-19.34212454212454"
    },
    "MagnetZ": {
      "N": "-17.842735042735043"
    },
    "TargetTemp": {
      "N": "23.78125"
    }
  },
  "ID": "Raspi_1",
  "isNight": "0"
}

Lambda Es ist peinlich, weil es viele Orte gibt, die nicht richtig geschrieben sind. .. .. Der Teil von Exception und der Inhalt kommen zu Slack als einem Ort, an dem ich keine Schlussfolgerung ziehen kann, während ich mir Sorgen mache. Ich bin mir nicht sicher, was ich in einem solchen Fall tun soll. Ich möchte nicht wiederholt ausgeführt werden, wenn eine Ausnahme in Lambda auftritt, oder ich möchte Slack nicht wiederholt einen Fehler senden, also habe ich mich gefragt, was ich tun soll, aber ich habe es zu einer zukünftigen Aufgabe gemacht. Ich kenne auch die Spezifikationen von Lambda nicht.

Es ist auch fast angemessen, einen Raumtisch zu bauen, der den Zustand des Raumes festhält. Schlüssel: Rasppie ID, hier "Raspi_1" Daten: Alle Daten von SensorTag, Kita Licht: Wird der aktuelle Status als "Ein" oder "Aus" bestimmt? Es ist ein Design.

slackpost Hier empfängt Lambda die Daten aus dem dynamoDB-Stream. Holen Sie sich Light-Informationen aus der ETLRoom-Tabelle und vergleichen Sie sie mit der aktuellen Es bestimmt, ob das Licht ein- oder ausgeschaltet ist, und lässt es nach, wenn es sich ändert.

slackpost.py


# coding : utf-8
import json
import datetime
import requests
import boto3

LIGHT_TAG='Light'

#===============================================================================
# Slack Incomming Webhooks URL
SLACL_POST_URL = "https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ"

# Post to Slack
def PostSlack(message, icon=':ghost:', username='ETLBot', channel="#room"):
    
    Dict_Payload = {
    "text": message,
    "username": username,
    "icon_emoji": icon,
    "channel": channel,
    }
    return requests.post(SLACL_POST_URL, data=json.dumps(Dict_Payload))

#-------------------------------------------------------------------------------
    
def Check_LightChanges(new, old, IsNight):

    Change2Night = None
    Change2Morining = None
    print("new:" , new, ", old:", old, ", IsNight:", IsNight)
    
    if (IsNight=='1') and (new > (old + 50)):
        Change2Morining = True
        IsNight = '0'
    elif (IsNight=='0') and (new < 10):
        Change2Night = True
        IsNight = '1'
    
    # Down -> UP
    if Change2Morining:
        message = ":smiley: Light is Turned On. Good Morning! :smiley:"
        icon = ":smiley:"
    # UP -> Down
    elif Change2Night:
        message = ":ghost: Light is Turned Down. Good Bye! :ghost:"
        icon = ":ghost:"
    else:
        return IsNight

    PostSlack(message, icon=icon)
    return IsNight
#-------------------------------------------------------------------------------

table = None
def update_table(data):
    ID = data['ID']['S']
    
    # Access to ETLRoom Table
    global table
    if not table:
        table = boto3.resource('dynamodb').Table('ETLRoom')
    response = table.get_item(Key={'ID': ID})
    if response:
        item = response['Item']
        #PostSlack(json.dumps(item))
        light = round(float(item['Data'][LIGHT_TAG]['N']))
        IsNight = item['IsNight']
    else:
        light = 0

    IsNight = Check_LightChanges(round(float(data[LIGHT_TAG]['N'])), light, IsNight)
    
    # Update Room Table
    response = table.put_item(
    Item={
          "ID": ID,
          "Data" : data,
          "IsNight": IsNight
        }
    )

    return 0

#-------------------------------------------------------------------------------

def lambda_handler(event, context):
    
    try:
        for record in event['Records']:
            dynamodb = record['dynamodb']
            keys = dynamodb['Keys']
            data = dynamodb['NewImage']
        
        # Keys are "ID" and "Datetime".
        id = keys['ID']['S']
        datetime = keys['Datetime']['S']
        print("ID:", id, "/ Date:", datetime)
        
        update_table(data)
    except Exception as e:
        import traceback
        message = traceback.format_exc()
        print(message)
        PostSlack('Meets Exception!\n' + message)
        raise e
        
    return 0

#===============================================================================

getroomenv

getroomenv.py


# coding : utf-8
import json
import requests
import boto3

# Slack Incomming Webhooks URL
SLACL_POST_URL = "https://hooks.slack.com/services/XXXXX/YYYYY/ZZZZZ"

#===============================================================================

def MakeStr(data, key, round_n):
    return str(round(float(data[key]['N']), round_n))

table = None
def GetRoomEnv(id, isAll):
    
    global table
    if not table:
        table = boto3.resource('dynamodb').Table('ETLRoom')

    response = table.get_item(
        Key={
            'ID': id
        }
    )
    
    data = response['Item']['Data']
    light = MakeStr(data, 'Light', 1)
    temp = MakeStr(data, 'TargetTemp', 1)
    humid = MakeStr(data, 'Humidity', 1)
    balo = MakeStr(data, 'Barometer', 1)
    time = data['Datetime']['S']
    
    message = "" \
        + "Die aktuelle Temperatur ist" + temp + "Grad, Luftfeuchtigkeit"+ humid + "Grad.\n" \
        + "Helligkeit" + light + "In Lux ist der Druck"+ balo + "Es ist hPa.\n" \
        + "(" + time + "Gemessen an:bar_chart:)"
        
    #Wenn es ein ALL-Argument gibt, wenn es ein Argument gibt, sichern Sie alle Daten
    if isAll:
        message = ""
        for d in data:
            s = str(d)
            v = data[s]
            if "N" in v:
                message += s + ":" + v["N"] + "\n"
            else:
                message += s + ":" + v["S"] + "\n"

    return message
    
#-------------------------------------------------------------------------------

# POST to Slack
def PostSlack(message):
    
    Dict_Payload = {
    "text": message,
    "username": 'NowBot',
    "icon_emoji": ":clock3:",
    "channel": '#room',
    }
    return requests.post(SLACL_POST_URL, data=json.dumps(Dict_Payload))

#-------------------------------------------------------------------------------
def lambda_handler(event, context):
    
    isAll = False
    try:
        tri1 = 'text='
        tri2 = 'trigger_word='
        body = event['body']
        tag = body[body.find(tri1)+len(tri1):body.find(tri2)-1]
        taglist = tag.split("+")
        for word in taglist:
            if "ALL" in word:
                isAll = True
    except:
        pass
    
    try:
        message = GetRoomEnv('Raspi_1', isAll)
    except Exception as e:
        import traceback
        message = traceback.format_exc()
        print(message)
        PostSlack('Meets Exception!\n' + message)
        raise e
        
    PostSlack(message)
    return 0

#===============================================================================

API Gateway

Slack Eingehende WebHooks und ausgehende WebHooks wurden zu benutzerdefinierten Integrationen hinzugefügt. Jetzt können Sie die Benutzeroberfläche verwenden. Super praktisch.

Incoming WebHooks https://hooks.slack.com/services/XXXXXXX/YYYYYYY/ZZZZZZ Generieren Sie die URL für die Veröffentlichung in Slack und legen Sie sie fest. Alles was Sie tun müssen, ist Post von Lambda. Sie können erkennen, ob es ein- oder ausgeschaltet ist. Das Folgende ist ein Beispiel.

<img width = "556" alt = "Screenshot 2017-09-05 21.07.36.png " src = "https://qiita-image-store.s3.amazonaws.com/0/171122/6fd49e45-4271" -642d-f5a4-045833411c15.png ">

Wenn wir uns das ansehen, können wir Folgendes sehen.

Outgoing WebHooks Trigger Word Das Auslösewort ist "jetzt". Senden Sie "jetzt" und NowBot teilt Ihnen den Status des Raums mit.

<img width = "389" alt = "Screenshot 2017-09-05 20.07.48.png " src = "https://qiita-image-store.s3.amazonaws.com/0/171122/94beb1e4-359a" -45f4-a6bb-01d02354fd63.png ">

Wenn Sie "now ALL" senden, werden alle Daten in der Datenbank gespeichert. Zum Debuggen.

<img width = "437" alt = "Screenshot 2017-09-05 20.11.08.png " src = "https://qiita-image-store.s3.amazonaws.com/0/171122/20954556-c401" -142f-e816-d6a2ade8f29b.png ">

Ich habe darüber nachgedacht, den Wortlaut nach "jetzt" in natürlicher Sprache (wie Amazon Polly) zu verarbeiten und zurückzugeben, wenn es einen verwandten Parameter gibt (wie z. B. Temperatur), aber das wird die nächste Gelegenheit sein. .. ..

URL https://XXXXXX.execute-api.ap-northeast-1.amazonaws.com/prod/getroomenv Ist eingestellt. Es wurde mit AWS API Gateway erstellt, dahinter Lambda. Das getroomenv-Skript wird ausgeführt.

Impressionen

Es dauerte ungefähr 3 Tage, um sich den ganzen Weg zu bewegen Danach habe ich ungefähr zwei Tage lang aufgefrischt und die Situation beobachtet. Ich brauche etwas mehr Kouo für DynamoDB und Lambda. Es war eine gute Studie. Die Verwendung von Slack für die Benutzeroberfläche war eine gute Antwort. Wirklich praktisch.

Da ich Zeitreihendaten gesammelt habe, Verwenden Sie diese Option, um die periodische Fluktuationskomponente zu erfassen Wenn Sie Slack benachrichtigen können, wenn ungewöhnliche Bewegungen auftreten, Ich denke, es ist gut als nächste Entwicklung. Legen Sie fest, ob es normal ist, dass Menschen samstags zur Arbeit kommen. .. ..

Recommended Posts

Spielen Sie, um Slack mithilfe von AWS PaaS über Raspberry Pi3 über Umgebungsdaten von SensorTag zu informieren
Senden Sie Daten von Raspberry Pi mit AWS IOT
So erhalten Sie die Temperatur vom SwitchBot-Thermo-Hygrometer mit Himbeer-Pi
Ich habe die Daten von Raspberry Pi an GCP gesendet (kostenlos)
Benachrichtigen Sie LINE über die Körpertemperatur vom BLE-Thermometer mit Raspeltorte Nr. 1
Benachrichtigen Sie LINE über die Körpertemperatur vom BLE-Thermometer mit Raspeltorte Nr. 2
Ausgabe von Raspberry Pi an Line
Erstellen eines LINE BOT, um zusätzliche AtCoder-Wettbewerbe mit AWS zu benachrichtigen
Ich habe versucht, Slack über das Update von Redmine zu informieren
Automatischer Start von Raspberry Pi-Programmen mit Systemd
CSV-Ausgabe von Impulsdaten mit Raspberry Pi (CSV-Ausgabe)
Verbinden Sie Ihren Raspberry Pi mit Blynk mit Ihrem Smartphone
[AWS] Migrieren Sie Daten von DynamoDB nach Aurora MySQL
Memo zur Migration von Djangos DB von SQLite3 nach MySQL auf Docker auf Raspberry Pi 4B
Versuchen Sie, Videos und Websites von Raspeye mit CATT in Chromecast oder Nest Hub zu übertragen