#include <ArduinoJson.h>
#include <esp_log.h>
#define CONFIG_Serial.printfEFAULT_LEVEL_VERBOSE (1)
#define CONFIG_Serial.printfEFAULT_LEVEL (5)
#define CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE (1)
#define CONFIG_LOG_MAXIMUM_LEVEL (5)
#define CONFIG_LOG_COLORS (1)
#define CONFIG_LOG_TIMESTAMP_SOURCE_RTOS (1)
#define JSON_KW_STATUS "status"
#define JSON_KW_MESSAGE "message"
#define JSON_KW_DATA "data"
#define JSON_KW_WORD "word"
#define JSON_KW_WORDS "words"
#define JSON_KW_ELEMENTS "elements"
#define JSON_KW_POWERUP_WORDS "power_up_words"
#define JSON_KW_TARGET_DISTANCE "target_distance"
typedef enum {
API_OK = (0),
API_WIFI_DISCONNECTED,
API_HOST_CONNECT_FAILED,
API_HEADER_TIMEOUT,
API_RESPONSE_TIMEOUT,
API_RESPONSE_PARSE_ERROR,
API_RESPONSE_STATUS_ERROR,
} ApiReturnType_e;
void LocalPrintApiError(ApiReturnType_e ret);
void parse_media_servo(const String& json_data)
{
JsonDocument doc;
DeserializationError error = deserializeJson(doc, json_data);
if (DeserializationError::Ok == error)
{
JsonVariant msgVariant = doc["data"]["messages"];
if ( ! msgVariant.isNull())
{
JsonArray msgArray = msgVariant.as<JsonArray>();
for (JsonVariant v : msgArray) {
JsonVariant mediaVar = v["media"];
JsonVariant servoVar = v["servo_data"];
if ( ! mediaVar.isNull()) {
String mediaType = mediaVar["type"].as<String>();
String mediaUrl = mediaVar["url"].as<String>();
Serial.printf("Media type: %s - Url: %s\n", mediaType.c_str(), mediaUrl.c_str());
} else { Serial.printf("media not found!\n"); }
/* Parse Servo parts */
if ( ! servoVar.isNull())
{
JsonVariant servoParts = servoVar["parts"];
if ( ! servoParts.isNull())
{
JsonArray servoArr = servoParts.as<JsonArray>();
for (JsonVariant v : servoArr) {
String partType = v["part_type"].as<String>();
int rep = v["repetition"].as<int>();
int start_time = v["start_time"].as<int>();
Serial.printf("Type: %s - repetition: %d, start_time: %d\n", partType, rep, start_time);
JsonArray stepArr = v["steps"];
if ( ! stepArr.isNull()) {
for (JsonVariant step_v : stepArr) {
int time = step_v["time"].as<int>();
int angle = step_v["angle"].as<int>();
Serial.printf(" - Time: %d - Angle: %d\n", time, angle);
}
} else { Serial.printf("steps not found!\n"); }
}
} else { Serial.printf("servo parts not found!\n"); }
} else { Serial.printf("servo_data not found!\n"); }
}
} else { Serial.printf("message not found!\n"); }
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Hello, ESP32-S3!");
const char index_html[] PROGMEM = R"rawliteral(
{
"type": "CHAT_RESPONSE",
"data": {
"messages": [
{
"text": "Hôm nay mình cùng học một số món ăn trong tiếng anh nhé.",
"audio": "http://tts-file-mgc-52.hacknao.edu.vn/data/tts_female_linh_v1/4e09f77de9f4746fd555909a7b21665c_3_0.9.mp3",
"emotion": "Smile",
"servo": 1,
"animations": [
{
"cmu": "expression",
"start": 0,
"end": 0.2,
"duration": null,
"animation_id": 0,
"animation": "trigger_mouth_0"
},
{
"cmu": "expression",
"start": 0.2,
"end": 0.55,
"duration": null,
"animation_id": 2,
"animation": "trigger_mouth_2"
},
{
"cmu": "expression",
"start": 0.55,
"end": 1.95,
"duration": null,
"animation_id": 3,
"animation": "trigger_mouth_3"
},
{
"cmu": "expression",
"start": 1.95,
"end": 2.86,
"duration": null,
"animation_id": 2,
"animation": "trigger_mouth_2"
},
{
"cmu": "expression",
"start": 2.86,
"end": 3.13,
"duration": null,
"animation_id": 0,
"animation": "trigger_mouth_0"
},
{
"cmu": "expression",
"start": 3.13,
"end": 3.13,
"duration": null,
"animation_id": null,
"animation": "trigger_idle"
}
],
"media": {
"type": "IMAGE",
"url": "https://sufile.stepup.edu.vn/storage/robot/2.jpg"
},
"servo_data": {
"parts": [
{
"steps": [
{
"time": 300,
"angle": 115
},
{
"time": 300,
"angle": 75
}
],
"part_type": "HEAD",
"repetition": 2,
"start_time": 100
},
{
"steps": [
{
"time": 500,
"angle": 90
}
],
"part_type": "BASE",
"repetition": 1,
"start_time": 300
},
{
"steps": [
{
"time": 500,
"angle": 150
},
{
"time": 2000,
"angle": 150
},
{
"time": 500,
"angle": 60
}
],
"part_type": "LEFT_HAND",
"repetition": 1,
"start_time": 500
},
{
"steps": [
{
"time": 500,
"angle": 150
},
{
"time": 2000,
"angle": 150
},
{
"time": 500,
"angle": 60
}
],
"part_type": "RIGHT_HAND",
"repetition": 1,
"start_time": 400
}
]
}
}
],
"has_next_message": true,
"is_end_conversation": false
}
}
)rawliteral";
String json_data = String(index_html);
parse_media_servo(json_data);
return;
#if 0
ApiReturnType_e ret = API_OK;
String response = "{\"status\":200,\"message\":\"Lấy dữ liệu game word race thành công\",\"data\":{\"words\":[{\"word\":\"musical\",\"elements\":[\"mu\",\"cal\",\"si\"]},{\"word\":\"push\",\"elements\":[\"p\",\"sh\",\"u\"]},{\"word\":\"net\",\"elements\":[\"n\",\"e\",\"t\"]},{\"word\":\"method\",\"elements\":[\"od\",\"th\",\"me\"]},{\"word\":\"toxic\",\"elements\":[\"t\",\"o\",\"xic\"]},{\"word\":\"twin\",\"elements\":[\"w\",\"t\",\"in\"]},{\"word\":\"tedious\",\"elements\":[\"te\",\"ous\",\"di\"]},{\"word\":\"avoid\",\"elements\":[\"a\",\"oid\",\"v\"]},{\"word\":\"raise\",\"elements\":[\"r\",\"a\",\"ise\"]},{\"word\":\"angle\",\"elements\":[\"n\",\"a\",\"gle\"]},{\"word\":\"belt\",\"elements\":[\"e\",\"b\",\"lt\"]},{\"word\":\"arrival\",\"elements\":[\"val\",\"ri\",\"ar\"]},{\"word\":\"drama\",\"elements\":[\"d\",\"ama\",\"r\"]},{\"word\":\"period\",\"elements\":[\"ri\",\"od\",\"pe\"]},{\"word\":\"deserve\",\"elements\":[\"de\",\"rve\",\"se\"]},{\"word\":\"size\",\"elements\":[\"s\",\"i\",\"ze\"]},{\"word\":\"royal\",\"elements\":[\"yal\",\"r\",\"o\"]},{\"word\":\"remind\",\"elements\":[\"re\",\"mi\",\"nd\"]},{\"word\":\"raw\",\"elements\":[\"r\",\"a\",\"w\"]},{\"word\":\"sour\",\"elements\":[\"o\",\"ur\",\"s\"]},{\"word\":\"shave\",\"elements\":[\"ave\",\"s\",\"h\"]},{\"word\":\"style\",\"elements\":[\"yle\",\"t\",\"s\"]},{\"word\":\"terrible\",\"elements\":[\"rr\",\"te\",\"ible\"]},{\"word\":\"manual\",\"elements\":[\"nu\",\"al\",\"ma\"]},{\"word\":\"medium\",\"elements\":[\"me\",\"di\",\"um\"]},{\"word\":\"note\",\"elements\":[\"te\",\"o\",\"n\"]},{\"word\":\"traffic\",\"elements\":[\"fic\",\"af\",\"tr\"]},{\"word\":\"proud\",\"elements\":[\"r\",\"p\",\"oud\"]},{\"word\":\"theater\",\"elements\":[\"ea\",\"th\",\"ter\"]},{\"word\":\"spell\",\"elements\":[\"s\",\"p\",\"ell\"]},{\"word\":\"catch\",\"elements\":[\"a\",\"c\",\"tch\"]},{\"word\":\"stir\",\"elements\":[\"t\",\"s\",\"ir\"]},{\"word\":\"dirty\",\"elements\":[\"rty\",\"d\",\"i\"]},{\"word\":\"assume\",\"elements\":[\"as\",\"su\",\"me\"]},{\"word\":\"brain\",\"elements\":[\"b\",\"ain\",\"r\"]},{\"word\":\"switch\",\"elements\":[\"it\",\"sw\",\"ch\"]},{\"word\":\"seat\",\"elements\":[\"e\",\"at\",\"s\"]},{\"word\":\"port\",\"elements\":[\"o\",\"rt\",\"p\"]},{\"word\":\"doubt\",\"elements\":[\"o\",\"d\",\"ubt\"]},{\"word\":\"mouse\",\"elements\":[\"m\",\"o\",\"use\"]}],\"power_up_words\":[{\"word\":\"ceremony\",\"elements\":[\"mony\",\"ce\",\"re\"]},{\"word\":\"tranquil\",\"elements\":[\"quil\",\"tr\",\"an\"]},{\"word\":\"spring\",\"elements\":[\"ng\",\"ri\",\"sp\"]},{\"word\":\"represent\",\"elements\":[\"rep\",\"ent\",\"res\"]},{\"word\":\"shampoo\",\"elements\":[\"am\",\"sh\",\"poo\"]},{\"word\":\"gorgeous\",\"elements\":[\"rg\",\"go\",\"eous\"]},{\"word\":\"massage\",\"elements\":[\"age\",\"ss\",\"ma\"]},{\"word\":\"frequent\",\"elements\":[\"eq\",\"uent\",\"fr\"]},{\"word\":\"museum\",\"elements\":[\"se\",\"mu\",\"um\"]},{\"word\":\"firework\",\"elements\":[\"work\",\"fi\",\"re\"]}],\"target_distance\":10}}";
JsonDocument doc;
uint16_t words_num = 0;
uint16_t el_num = 0;
DeserializationError error = deserializeJson(doc, response);
if (DeserializationError::Ok == error)
{
/* Validate reponse */
JsonVariant statusCodeVariant = doc[JSON_KW_STATUS];
JsonVariant messageVariant = doc[JSON_KW_MESSAGE];
JsonVariant dataVariant = doc[JSON_KW_DATA];
if (! statusCodeVariant.isNull() && ! messageVariant.isNull() && ! dataVariant.isNull())
{
uint16_t statusCode = statusCodeVariant.as<uint16_t>();
if (200 == statusCode)
{
JsonDocument data_doc = dataVariant;
/* Target distance */
JsonVariant targetVariant = data_doc[JSON_KW_TARGET_DISTANCE];
JsonVariant wordsVariant = data_doc[JSON_KW_WORDS];
JsonVariant powerupVariant = data_doc[JSON_KW_POWERUP_WORDS];
if (! targetVariant.isNull() && ! wordsVariant.isNull() && ! powerupVariant.isNull())
{
Serial.printf("Target distance: %d\n", data_doc[JSON_KW_TARGET_DISTANCE].as<int>());
for (byte i = 0; i < 2; i++)
{
JsonArray word_array = ((i == 0)? wordsVariant.as<JsonArray>() : powerupVariant.as<JsonArray>());
for(JsonVariant v : word_array) {
JsonVariant wordVariant = v[JSON_KW_WORD];
JsonVariant elementvariant = v[JSON_KW_ELEMENTS];
if ( ! wordVariant.isNull() && ! elementvariant.isNull()) {
Serial.printf("\n%s: ", wordVariant.as<String>().c_str());
JsonArray char_array = elementvariant.as<JsonArray>();
el_num = 0;
for (JsonVariant chars : char_array) {
Serial.printf(" %s", chars.as<String>().c_str());
el_num++;
}
if (el_num == 3) {
words_num++;
} else {
Serial.printf(" - Current word is invalid! Number of elements: %d", el_num);
}
} else {
continue;
}
}
}
} else { ret = API_RESPONSE_PARSE_ERROR; }
} else {
Serial.printf("Response status code error (%d)", statusCode);
ret = API_RESPONSE_STATUS_ERROR;
}
} else { ret = API_RESPONSE_PARSE_ERROR; }
} else { ret = API_RESPONSE_PARSE_ERROR; }
LocalPrintApiError(ret);
#endif
}
void loop() {
// put your main code here, to run repeatedly:
delay(10); // this speeds up the simulation
}
void LocalPrintApiError(ApiReturnType_e ret)
{
esp_log_level_set("", ESP_LOG_VERBOSE);
ESP_LOGE("", "This is a error log.");
switch (ret)
{
case API_OK: break;
case API_WIFI_DISCONNECTED:
Serial.printf("API_WIFI_DISCONNECTED"); break;
case API_HOST_CONNECT_FAILED:
Serial.printf("API_HOST_CONNECT_FAILED"); break;
case API_HEADER_TIMEOUT:
Serial.printf("API_HEADER_TIMEOUT"); break;
case API_RESPONSE_TIMEOUT:
Serial.printf("API_RESPONSE_TIMEOUT"); break;
case API_RESPONSE_PARSE_ERROR:
Serial.printf("API_RESPONSE_PARSE_ERROR"); break;
case API_RESPONSE_STATUS_ERROR:
Serial.printf("API_RESPONSE_STATUS_ERROR"); break;
default: break;
}
}