JSON یک روش برای انتقال دیتا در صفحات وب و برنامه های بک اند می باشد . در JSON هر شی و یا پارامتری در قالب یک رشته متنی منتقل می شود . JSON کلیه مقادیر را که شامل داده ها و رکورد ها است به صورت یک رشته پشت سر هم در می آورد . در تصویر زیر نمونه آن نشان داده شده است :
می توان رشته های JSON را به شی های جاوا اسکریپت برگرداند . در JSON بر اساس قواعد خاصی داده های چیده می شودند . این قواعد تعیین می کنند که هر دابل کوتیشن های ، کروشه و… کجا به کار می روند . به طور کلی از JSON برای انتقال داده از وب سرور به کلاینت استفاده می شود . بسیاری از API ها در پاسخ رشته های JSON را باز می گردانند . لذا تجزیه و تحلیل این رشته ها در آردوینو بسیار کاربردی است . به طور مثال می خواهید از طریق یک API مقدار زمان و تاریخ را محاسبه نماید . در پاسخ API یک رشته JSON را بر می گرداند که شامل مقادیر زمان و تاریخ است . شما برای اینکه بتوانید از این رشته مقادیری را که می خواهید جدا کنید نیاز به توابعی برای تشحیص کاراکتری هایی مانند دابل کوتیشن و… دارید . اما خوشبختانه کتابخانه JSON آردوینو این کار را به راحتی انجام می دهد . شما با استفاده از این کتابخانه می توانید فایل های JSON را به متغییر هایی برگردانید و یا متغییر های مختلف را به صورت فایل های JSON در آوردید . در این آموزش به نحوه استفاده از این کتابخانه خواهیم پرداخت .
برای درک بهتر کاربرد JSON می توانید API زیر را در مرورگر خود سرچ کنید و نتایج را ببینید :
https://api.keybit.ir/time
کتابخانه JSON بسیار کامل است و تفاوتی ندارد از اترنت استفاده می کنید یا از برد های ESP8266 ، می توانید از این کتابخانه استفاده کنید .
کتابخانه JSON را می توانید از لینک زیر دانلود کنید :
https://github.com/bblanchon/ArduinoJson/archive/6.x.zip
پس از دانلود کتابخانه فوق آن را نصب کرده و سپس شیلد اترنتر W5100 را بر روی برد آردوینوی خورد قرار دهید و آن را از طریق یک کابل شبکه به مودم خود متصل کتید :
از یک مثال شروع خواهیم کرد . در برنامه ابتدا باید کتابخانه را فراخوانی کنید :
#include <ArduinoJson.h>
فرض کنید یک زشته JSON به صورت زیر داریم :
{"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
رشته فوق شامل یک نوع از جنس sensor با مدل gps و همچنین پارامتر time است که مقادیر آن ها نیز درج شده است . می خواهیم این رشته JSON را دیکود کنیم و ان را به صورت متغییر هایی با عنوان نوع سنسور ، زمان و همچنین مقادیر آن ها را در این دو متعییر قرار دهیم تا در برنامه عملکرد های لازم را بر روی این متغییر ها انجام دهیم . در قدم اول نیاز به یک بافر خواهیم داشت که رشته JSON را در آن قرار دهیم . این بافر می تواند استاتیک با طول ثابت و یا به صورت داینامیک با طول متغییر باشد . برای این کار به صورت زیر عمل خواهیم نمود :
StaticJsonBuffer<200> jsonBuffer;
کلمه کلیدی در اینجا StaticJsonBuffer می باشد . مقدار 200 نیز ظرفیت بافر است (200 کاراکتر ) و jsonBuffer نامی است که ما برای بافر انتخاب کرده ایم ( می تواند هر اسم دلخواه دیگری باشد ).
قدم دوم رشته های JSON را در یک آرایه کاراکتری قرار داده و سپس آن را در بافر میریزیم . در این مثال چون رشته های JSON را خودمان در برنامه وارد کرده این این کار را انجام یم دهیم . رد برنامه های کاربردی که MCU رشته را از سرور دریافت می کند آن را مستقیما در بافر قرار خواهیم داد و نیازی به آرایه کاراکتری نمی باشد :
char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
سپس با استفاده از کد زیر رشته را در بافر قرار می دهیم :
JsonObject& root = jsonBuffer.parseObject(json);
در دستور فوق json همان آرایه کاراکتری که ساختیم می باشد . تحلیل ساختار فوق برای مبتدیان ممکن است دشوار به نظر برسد لذا تنها ساختار کلی این دستور را بدانید کافیست (در این دستور از رفرنس برای شی ساخته شده استفاده شده است ) .
پس قرار دادن json در بافر ، باید صحت این کار چک شودتا از قرار کرفتن رشته های json در بافر اطمینان حاصل کنیم . برای این کار می توانید از دستورات شرطی استفاده کنید :
if(!root.success()) { Serial.println("parseObject() failed"); return false; }
حال به راحتی از طریق شی root می توانید مقادیر را از رشته json جدا کرده و آن را داخل متغییر ها بریزید :
const char* sensor = root["sensor"]; long time = root["time"]; double latitude = root["data"][0]; double longitude = root["data"][1];
بعد از آشنایی با عملکرد کتابخاه json به سراغ یک مثال عملی خواهیم رفت . در اینجا قصد داریم مقادیر مربوط به دمای هوا ، میزان بارش و… را از وب سایت Openweathermap دریافت نمایم . این وب سایت خدمات هواشناسی را برای کاربرد های کشاورزی ، پژوهشی و… ارائه می دهد . یکی از مزیت های این وب سایت API های رایگان آن است . می توانید در این وب سایت ثبت نام کرده تا API Key هایی در اختیار شما قرار دهد . اگر API های این سایت را از طریق متد GET یا POST از سرور درخواست کنید ، مقادیر هواشناسی را درقالب JSON برای شما ارسال می کند . در زیر API این سایت نشان داده شده است :
http://api.openweathermap.org/data/2.5/weather?q={your city},{your country code}&APPID={your API Key}
در ساختار این API سه پارامتر مهم وجود دارد که باید آن ها را واید کنید تا سرور جواب درست را برای شما ارسال کند در غیر این صورت خطا خواهد گرفت :
1-your city- بجای این قسمت اسم شهرمورد نظر خود را وارد کنید مثلا tehran
2-your country در این قسمت کد اختصاصی کشور خود را وارد کنید مانند IR
3-your key در این قسمت API KEY خود را وارد کنید . API Key را پس از ثبتنام در سایت Openweathermap.org می توانید دریافت کنید .
مثال :
http://api.openweathermap.org/data/2.5/weather?q=Porto,PT&APPID=801d2603e9f2e1c70e042e4------
در مثال فوق API Key به دلیل محرمانه بودن کامل نوشته نشده است و چند کاراکتر آخر آن به صورت ‘-‘ نشان داده شده .
حال که در سایت Openweathermap.org ثبتنام کردید ، پس از دریافت API Key ، یک بار API را در مروگر خود سرچ کنید تا از درست بودن مراحل فوق مطما شوید
حال کد های زیر را بر روی برد آردوینو خود آپلود کنید :
/* * Rui Santos * Complete Project Details http://randomnerdtutorials.com * Based on the Arduino Ethernet Web Client Example * and on the sketch "Sample Arduino Json Web Client" of the Arduino JSON library by Benoit Blanchon (bblanchon.github.io/ArduinoJson) */ #include <ArduinoJson.h> #include <Ethernet.h> #include <SPI.h> EthernetClient client; // Name address for Open Weather Map API const char* server = "api.openweathermap.org"; // Replace with your unique URL resource const char* resource = "REPLACE_WITH_YOUR_URL_RESOURCE"; // How your resource variable should look like, but with your own COUNTRY CODE, CITY and API KEY (that API KEY below is just an example): //const char* resource = "/data/2.5/weather?q=Tehran,ir&appid=در این قسمت api key خود را وارد کنید"; const unsigned long HTTP_TIMEOUT = 10000; // max respone time from server const size_t MAX_CONTENT_SIZE = 512; // max size of the HTTP response byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // The type of data that we want to extract from the page struct clientData { char temp[8]; char humidity[8]; }; // ARDUINO entry point #1: runs once when you press reset or power the board void setup() { Serial.begin(9600); while (!Serial) { ; // wait for serial port to initialize } Serial.println("Serial ready"); if(!Ethernet.begin(mac)) { Serial.println("Failed to configure Ethernet"); return; } Serial.println("Ethernet ready"); delay(1000); } // ARDUINO entry point #2: runs over and over again forever void loop() { if(connect(server)) { if(sendRequest(server, resource) && skipResponseHeaders()) { clientData clientData; if(readReponseContent(&clientData)) { printclientData(&clientData); } } } disconnect(); wait(); } // Open connection to the HTTP server bool connect(const char* hostName) { Serial.print("Connect to "); Serial.println(hostName); bool ok = client.connect(hostName, 80); Serial.println(ok ? "Connected" : "Connection Failed!"); return ok; } // Send the HTTP GET request to the server bool sendRequest(const char* host, const char* resource) { Serial.print("GET "); Serial.println(resource); client.print("GET "); client.print(resource); client.println(" HTTP/1.1"); client.print("Host: "); client.println(host); client.println("Connection: close"); client.println(); return true; } // Skip HTTP headers so that we are at the beginning of the response's body bool skipResponseHeaders() { // HTTP headers end with an empty line char endOfHeaders[] = "\r\n\r\n"; client.setTimeout(HTTP_TIMEOUT); bool ok = client.find(endOfHeaders); if (!ok) { Serial.println("No response or invalid response!"); } return ok; } // Parse the JSON from the input string and extract the interesting values // Here is the JSON we need to parse /*{ "coord": { "lon": -8.61, "lat": 41.15 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "base": "stations", "main": { "temp": 296.15, "pressure": 1020, "humidity": 69, "temp_min": 296.15, "temp_max": 296.15 }, "visibility": 10000, "wind": { "speed": 4.6, "deg": 320 }, "clouds": { "all": 0 }, "dt": 1499869800, "sys": { "type": 1, "id": 5959, "message": 0.0022, "country": "PT", "sunrise": 1499836380, "sunset": 1499890019 }, "id": 2735943, "name": "Porto", "cod": 200 }*/ bool readReponseContent(struct clientData* clientData) { // Compute optimal size of the JSON buffer according to what we need to parse. // See https://bblanchon.github.io/ArduinoJson/assistant/ const size_t bufferSize = JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(1) + 2*JSON_OBJECT_SIZE(2) + JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(12) + 390; DynamicJsonBuffer jsonBuffer(bufferSize); JsonObject& root = jsonBuffer.parseObject(client); if (!root.success()) { Serial.println("JSON parsing failed!"); return false; } // Here were copy the strings we're interested in using to your struct data strcpy(clientData->temp, root["main"]["temp"]); strcpy(clientData->humidity, root["main"]["humidity"]); // It's not mandatory to make a copy, you could just use the pointers // Since, they are pointing inside the "content" buffer, so you need to make // sure it's still in memory when you read the string return true; } // Print the data extracted from the JSON void printclientData(const struct clientData* clientData) { Serial.print("Temp = "); Serial.println(clientData->temp); Serial.print("Humidity = "); Serial.println(clientData->humidity); } // Close the connection with the HTTP server void disconnect() { Serial.println("Disconnect"); client.stop(); } // Pause for a 1 minute void wait() { Serial.println("Wait 60 seconds"); delay(60000); }
پس از پروگرام کردن آردوینو وارد محیط Serial monitor شوید تا نتایج را ببینید .
استفاده از کتابخانه json و دیکود کردن فایل های json قابلیت های زیادی دارد . می توانید بسیاری از پردازش ها را به صورت ابری انجام دهید و نیازی به درگیر کردن MCU نباشد .