Мы продолжаем ставить эксперименты над нашим подопытным. Так как имеем доступ к любому TCP серверу, если знаем его адрес и порт, то пора немного усложнить задачу.
HTTP и немного теории
Что есть HTTP можно прочитать во многих источниках, но в целом это протокол, а значит стандарт, передачи данных в интернете по средствам TCP, что нас полностью устраивает.
Из всей этой теории нам следует узнать как строятся HTTP запросы. Разбираться будем с двумя основными GET и POST.
GET запрос.
Его мы будем использовать для получения каких-либо данных. Имеет следующий формат:
GET / HTTP HTTP/1.1 \r\n
Host: [IP адрес и порт]/ \r\n\r\n
Обращаем внимания на \r\n это перевод каретки на новую строку с самого начала, от прошлого печатных машинок нам не уйти. Также в GET запросе можно передавать данные. тогда у нас будет что-то из серии: GET /?key=value.
POST запрос.
Если GET мы просим нам вернуть какие-то данные, то POST отдает эти данные в своем теле.
POST /HTTP/1.1 \r\n
Host: http://[IP адрес и порт]/ \r\n
User-Agent: wtfcontrolsengineer \r\n
Accept: text/html \r\n
Content-Type: application/json \r\n
Content-Length: [Длина данных] \r\n\r\n"
[ТЕЛО ЗАПРОСА]\r\n"
И этого вполне хватает. Если разбирать то, чего мы не знаем: User-Agent - это тот объект, что запрашивает, считайте, что имя браузера; Accept - данные которые разрешены в ответ, вряд ли мы захотим получить видео; Content-Type - показывает то, что у нас в теле запроса, исходя из примера выше это будет что-то формата JSON, Content-Length - длина тела запроса.
Ну и в тело запроса мы помещаем то, что нам требуется.
Отправка запросов
И вот тут начинается самое интересное. Дальше мы будем программировать наш Arduino. У меня это Arduino UNO3 так что поехали.
#include <SoftwareSerial.h> // Программный порт для общения с DT-06
#include <LiquidCrystal.h>// LCD дисплей. он просто у меня есть
#include <ArduinoJson.h>// Работа с json
Теперь проходим инициализацию
SoftwareSerial mySerial(2, 3);
#define WIFI_SERIAL mySerial
const int rs = 8, en = 9, d4 = 4, d5 = 5, d6 = 6, d7 = 7;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
void setup() {
/* Инициализация нашего дисплея
Задержка тут нужна чтобы просто прочитать надпись на дисплее*/
lcd.begin(16, 2);
lcd.print("Test");
delay(1000);
/*Устанавливаем скорость порта*/
Serial.begin(9600);
while (!Serial) {
// ждём, пока не откроется монитор последовательного порта
// для того, чтобы отследить все события в программе
}
lcd.clear();
lcd.print("Serial init OK");//Выводим на экран
Serial.print("Serial init OK\r\n");//Выводим в монитор
delay(1000);
/*Инициализируем наш программный последовательный порт*/
WIFI_SERIAL.begin(9600);
lcd.clear();
lcd.print("WiFi init OK");
}
Далее мы напишем функцию, которая будет организовывать отправку нашего запроса. Первый на очереди GET.
int auth(String *apiKey, LiquidCrystal *lcd1, SoftwareSerial *serial)
{
lcd1->clear();
lcd1->println("Start Auth");
serial->println("GET /?key="+*apiKey+" HTTP/1.1\r\nHost: http://192.168.1.242:88/ \r\n" );
return 0;
}
Во входных параметрах функции apiKey - какой-то ключ, я просто в эту переменную записал различные значения. serial - программный последовательный порт.
Сам запрос: "GET /?key="+*apiKey+" HTTP/1.1\r\n Host http://192.168.1.242:88/ \r\n"
http://192.168.1.242:88/ - мой локальный сервер, который и будет обрабатывать этот запрос
/?key= - параметр, который мы передаем в запросе, равен apiKey
И это все что нам потребуется для проверки.
ВАЖНО!!! Передаем непосредственно строку. Не массив символов, а строку.
Дальше используем эту функцию, где нам будет удобно.
Как видим на сервер пришел запрос. IP адрес запроса - 192.168.1.33, что является адресом модуля DT-06. И видно что у нас был GET запрос где в поле key было значение этого ключа. Сервер нам должен отправить ответ с этим же ключом.
Строка HTTP/1.1 200 OK говорит, что запрос прошел успешно и в теле ответа мы можем увидеть наш ключ.
Теперь пойдем дальше.
POST запрос и JSON
Отправка POST запроса примерно осуществляется также, но нам требуется сформировать массив данных, которые мы отправим. Дополняем нашу функцию setup()
/*Задаем размерность нашего файла. Он будет матрицей 2*2
const int capacity = JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(2);
/*Инициализация JSON документа doc*/
StaticJsonDocument<capacity> doc;
/*Заполняем наш массив данными*/
JsonObject obj1 = doc.createNestedObject();
obj1["key"] = "a1";
obj1["value"] = analogRead(A1);
JsonObject obj2 = doc.createNestedObject();
obj2["key"] = "a2";
obj2["value"] = analogRead(A2);
/*Записываем длину нашего документа*/
postlength = measureJson(doc);
/*Производим сериализацию файла doc в строку hhjjj*/
serializeJson(doc, hhjjj);
Serial.println(hhjjj);
В данном фрагменте кода мы создаем JSON файл, заполняем его данными и превращаем в строку для дальнейшей передачи, плюс узнаем длину данных, которые будут в теле функции.
Теперь слегка переписываем функцию отправки, которая у меня называется auth()
int auth(LiquidCrystal *lcd1, SoftwareSerial *serial, int *postlength, String *body)
{
lcd1->clear();
lcd1->println("Start Auth");
serial->println("POST /senddata/ HTTP/1.1 \r\n Host: http://192.168.1.242:88/\r\n User-Agent: Sudalintest \r\n Accept: text/html \r\n Content-Type: application/json \r\n Content-Length: "+String(*postlength)+"\r\n\r\n" + *body+"\r\n" );
return 0;
}
Теперь мы передаем в функцию длину и тело запроса. И формируем сам пост запрос.
Смотрим что выходит.
Сформировали данные. Теперь отправляем их не сервер.
Вот он выводит название ключа и его значение, а ниже полностью всю JSON строку.
И нам приходит замечательная посылочка, что все ок. Теперь можно наладить общение через HTTP и поиграться с удаленным управлением.
Если если есть вопросы, то задавайте в комментариях или пишите в мессенджер. Всем спасибо.