Help us understand the problem. What is going on with this article?

M5Stick-CでJsonをPOSTする

M5Stick-Cを使って、HTTP POSTします。当然、BodyにJSONを入れて、JSONが返ってくるやつです。
今回は、Arduino IDEを使います。
いろんな人が使っているので情報がたくさんありますが、JsonをPOSTするまで複数の情報を集めないと実現できなかったので、ここでまとめておきます。

使うもの
・M5Stack-C
  ESP系CPUであれば大丈夫なのですが、M5Stack-CについているLCDを使っているのでそこだけ代替してください。
・Arduino IDE
  https://docs.m5stack.com/#/ja/quick_start/m5stickc/m5stickc_quick_start
 上記ページを参考に、ボードマネージャとライブラリマネージャよりM5StickCをインストールしておきます。

インストールが必要なライブラリ
・ArduinoJson
  https://arduinojson.org/v6/doc/
 JSONを簡単に扱えるようになります。Arduino IDEのライブラリマネージャからインストールしておいて下さい。

本投稿の最後に、M5Stick-Cについているジャイロ、バッテリー容量、GroveのBME680の環境センサ、NTP(時刻)、Ambientへのアップロードもろもろを組み込んだサンプルソースも載せておきます。

WiFiアクセスポイントに接続する

ESP系であれば、標準のライブラリでできます。setup()で準備します。

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
  Serial.println(WiFi.localIP());

POSTするJsonを作成する

JSONの生成には、ArduinoJsonを使いました。
まずは定義。

const int capacity = JSON_OBJECT_SIZE(2);
StaticJsonDocument<capacity> json_request;

メモリの省エネを意識しているようで、あらかじめセットする予定の項目の数を指定する必要があります。JSON_OBJECT_SIZEの部分です。セットする項目が配列だったり、階層化されていたりする場合の計算方法もあるので、ArduinoJsonのHPを参考にしてください。
今回は2個の項目を設定します。

Jsonの項目の設定はこんな感じ。

  json_request["counter"] = counter;
  json_request["tick"] = tick;

最後に、JSON文字列化(JSON.stringifyのことです)します。
出力先にバッファー(buffer)を用意しました。

char buffer[255];
serializeJson(json_request, buffer, sizeof(buffer));

JsonをPOSTする

POSTには、HTTPClientを使いました。

以下の感じです。

  HTTPClient http;
  http.begin(host);
  http.addHeader("Content-Type", "application/json");
  int status_code = http.POST((uint8_t*)buffer, strlen(buffer));
  if( status_code == 200 ){
    Stream* resp = http.getStreamPtr();

    DynamicJsonDocument json_response(255);
    deserializeJson(json_response, *resp);

    serializeJson(json_response, Serial);
    Serial.println("");
  }
  http.end();

http.POSTで、JSON文字列を載せてPOSTしています。

結果は、http.getStreamPtr()で取得します。
WiFiClient* が返ってくるのですが、Streamを継承しています。

POSTのレスポンスは、application/jsonであることを期待して、パースしています。
以下の部分で、JSON文字列をパースしています。

    DynamicJsonDocument json_response(255);
    deserializeJson(json_response, *resp);

(もちろん、DynamicJsonDocumentではなくStaticJsonDocumentを使っても構いません。)

ソースコード

以上をまとめたソースコードは以下になります。

#include <M5StickC.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

const char *ssid = "【WiFiアクセスポイントのSSID】";
const char *password = "【WiFiアクセスポイントのパスワード】";

const int capacity = JSON_OBJECT_SIZE(2);
StaticJsonDocument<capacity> json_request;
char buffer[255];

const char *host = "【POST接続するエンドポイント】";

unsigned long counter = 0;
unsigned long tick = 0;

void setup() {
  M5.begin();

  M5.Axp.ScreenBreath(9);
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextSize(2);

  M5.Lcd.println("[M5StickC]");
  delay(1000);

  M5.Lcd.println("start Serial");
  Serial.begin(9600);
  while (!Serial);
  delay(100);

  M5.Lcd.println("start WiFi");
  Serial.print("start Wifi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("");
  M5.Lcd.println("Connected");
}

void loop() {
  counter++;
  tick = millis();

  json_request["counter"] = counter;
  json_request["tick"] = tick;

  serializeJson(json_request, Serial);
  Serial.println("");

  serializeJson(json_request, buffer, sizeof(buffer));

  HTTPClient http;
  http.begin(host);
  http.addHeader("Content-Type", "application/json");
  int status_code = http.POST((uint8_t*)buffer, strlen(buffer));
  Serial.printf("status_code=%d\r\n", status_code);
  if( status_code == 200 ){
    Stream* resp = http.getStreamPtr();

    DynamicJsonDocument json_response(255);
    deserializeJson(json_response, *resp);

    serializeJson(json_response, Serial);
    Serial.println("");
  }
  http.end();

  delay(5000);
}

以下の部分は、各自の環境に合わせて変更してください。

【WiFiアクセスポイントのSSID】
【WiFiアクセスポイントのパスワード】
【POST接続するエンドポイント】

(参考)サンプルアプリ

ちょうど手元に、GroveのBME680があったので、つないでみました。

Grove - Temperature Humidity Pressure Gas Sensor(BME680)
 http://wiki.seeedstudio.com/Grove-Temperature_Humidity_Pressure_Gas_Sensor_BME680/

また、せっかくなので、Ambientを使わせていただいて、可視化してみました。

Ambient
 https://ambidata.io/

Arduino IDEに以下をインストールしておいて下さい。
 https://github.com/AmbientDataInc/Ambient_ESP8266_lib
 https://github.com/Seeed-Studio/Seeed_BME680

ソースコードは以下の通りです。

#include <M5StickC.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiUdp.h>
#include <NTPClient.h>

#define BME6800

#ifdef BME6800

#include "seeed_bme680.h"

#include "Ambient.h"

WiFiClient client;
Ambient ambient;
unsigned int channelId = AmbientのチャネルID;
const char* writeKey = "【Ambientのライトキー】";

#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10

#define IIC_ADDR  uint8_t(0x76)

/**  NOTE!!!!!!!!!!!!  Select the communication protocol correctly **/

Seeed_BME680 bme680(IIC_ADDR); /* IIC PROTOCOL */
//Seeed_BME680 bme680;             /* SPI PROTOCOL */
//Seeed_BME680 bme680(BME_CS, BME_MOSI, BME_MISO,  BME_SCK);/*SPI PROTOCOL*/

#define MAX_COUNT_ENV   5

#endif

const char *ssid = "【WiFiアクセスポイントのSSID】";
const char *password = "【WiFiアクセスポイントのパスワード】";

bool ntp_updated = false;
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, 9 * 60 * 60);

#define TIME_INTARVAL   1000
unsigned long next_time = TIME_INTARVAL;
unsigned long prev_time = 0;

#define MAX_COUNT_VBAT   10
unsigned long count = 0;
#define CHECK_COUNT(c,m)  (c != 0 && count < (m))

enum{
  MODE_GYRO = 0,
#ifdef BME6800
  MODE_ENV,
#endif
  MODE_VBAT,
  MODE_TIME,

  NUM_OF_MODE
};
unsigned char mode = MODE_GYRO;

void setup() {
  M5.begin();
  M5.IMU.Init();

  M5.Axp.ScreenBreath(9);
  M5.Lcd.setRotation(3);
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextSize(2);

  M5.Lcd.println("[M5StickC]");
  delay(1000);

  M5.Lcd.println("start Serial");
  Serial.begin(9600);
  while (!Serial);
  delay(100);

  M5.Lcd.println("start WiFi");
  Serial.print("start Wifi");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("");

  timeClient.begin();

#ifdef BME6800
  M5.Lcd.println("start BME680");
  while (!bme680.init()){
    Serial.println("bme680 init failed ! can't find device!");
    delay(10000);
  }

  ambient.begin(channelId, writeKey, &client);
#endif
}

void loop() {
  M5.update();

  if( !ntp_updated ){
    Serial.println("NTP updating");
    ntp_updated = timeClient.update();
    if( ntp_updated ){
      Serial.println("NTP updated");
    }
  }

  if( M5.BtnA.wasReleased() ){
    mode = (mode + 1) % NUM_OF_MODE;
    count = 0;
  }

  if( count > 0 ){
    unsigned long now = millis();
    if( ( ( next_time > prev_time ) && ( prev_time < now && now <= next_time ) ) ||
        ( ( next_time < prev_time ) && ( prev_time < now || now <= next_time ) ) )
      return;

    prev_time = next_time;
    next_time += TIME_INTARVAL;

    count++;
  }

  if( mode == MODE_GYRO ){
    int16_t accX, accY, accZ;
    int16_t gyroX, gyroY, gyroZ;
    M5.IMU.getGyroAdc(&gyroX, &gyroY, &gyroZ);
    M5.IMU.getAccelAdc(&accX, &accY, &accZ);

    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.setTextSize(1);

    M5.Lcd.printf("gyrX: %.2f\n", ((float)gyroX) * M5.IMU.gRes);
    M5.Lcd.printf("gyrY: %.2f\n", ((float)gyroY) * M5.IMU.gRes);
    M5.Lcd.printf("gyrZ: %.2f\n", ((float)gyroZ) * M5.IMU.gRes);
    M5.Lcd.printf("accX: %.2f\n", ((float)accX) * M5.IMU.gRes);
    M5.Lcd.printf("accY: %.2f\n", ((float)accY) * M5.IMU.gRes);
    M5.Lcd.printf("accZ: %.2f\n", ((float)accZ) * M5.IMU.gRes);
  }else
#ifdef BME6800
  if( mode == MODE_ENV ){
    if(CHECK_COUNT(count, MAX_COUNT_ENV))
      return;

    if (bme680.read_sensor_data()){
      Serial.println("Failed to perform reading :(");
      return;
    }

    float tmp, hum, pres, gas;

    tmp = bme680.sensor_result_value.temperature;
    hum = bme680.sensor_result_value.humidity;
    pres = bme680.sensor_result_value.pressure / 100.0;
    gas = bme680.sensor_result_value.gas / 1000.0;

    Serial.printf("tmp: %2.1fC\r\n", tmp);
    Serial.printf("hum: %2.1f%%\r\n", hum);
    Serial.printf("prs: %3.0fhPa\r\n", pres);
    Serial.printf("gas: %2.0fKohm\r\n", gas);
    Serial.println("");

    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.setTextSize(2);
    M5.Lcd.printf("tmp: %2.1fC\n", tmp);
    M5.Lcd.printf("hum: %2.1f%%\n", hum);
    M5.Lcd.printf("prs: %3.0fhPa\n", pres);
    M5.Lcd.printf("gas: %2.0fKohm\n", gas);

    ambient.set(1, String(tmp).c_str());
    ambient.set(2, String(hum).c_str());
    ambient.set(3, String(pres).c_str());
    ambient.set(4, String(gas).c_str());
    ambient.send();
  }else
#endif  
  if( mode == MODE_VBAT ){
    if(CHECK_COUNT(count, MAX_COUNT_VBAT))
      return;

    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);

    double vbat = M5.Axp.GetVbatData() * 1.1 / 1000;
    M5.Lcd.setTextSize(3);
    M5.Lcd.printf("v_bat:\r\n%4.2f", vbat);
  }else if( mode == MODE_TIME ){
    String time_str = timeClient.getFormattedTime();

    M5.Lcd.fillScreen(BLACK);
    M5.Lcd.setCursor(0, 0);
    M5.Lcd.setTextSize(3);

    M5.Lcd.println(time_str);
  }

  count = 1;
}

以下の部分は、各自の環境に合わせて変更してください。

【WiFiアクセスポイントのSSID】
【WiFiアクセスポイントのパスワード】
【POST接続するエンドポイント】
【AmbientのチャネルID】
【Ambientのライトキー】

5秒ごとに、AmbientにBME680から取得した環境情報を上げています。
Ambientのページを見てみると、こんな感じで可視化されました。

image.png

以上

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした