Со времени моего первого поста о ESP8266 прошло много времени (см. « Дешевый и простой WiFi с ESP8266 для платы FRDM »). ESP8266 — это новый недорогой ($ 4,50) WiFi-модуль, который облегчает подключение к сети или Интернету. Наконец, на этих выходных я нашел время написать учебное пособие: как реализовать веб-сервер WiFi для модуля WiFi ESP8266 и платы Freescale FRDM-KL25Z:
Контур
В этом уроке я использую плату Freescale FRDM-KL25Z в качестве веб-сервера, используя плату ESP8266 . ESP8266 — это беспроводная WiFi-плата стоимостью менее $ 4,5, которая становится все более популярной в качестве платы IoT. Есть даже способ запустить ESP8266 в автономном режиме (поскольку на этой плате установлен полноценный процессор). Однако это развитие все еще находится в движении и довольно нестабильно. Вместо этого я использую последовательное соединение с ESP8266 вместо этого. При этом любой маленький микроконтроллер может отправлять и получать данные из Интернета: подключите эту плату к микроконтроллеру с напряжением 3,3 В, GND, Tx и Rx, и у вас будет соединение W-LAN!
В этом уроке я использую Eclipse с GNU / GDB с Processor Expert , но с помощью шагов из этого урока вы также сможете использовать любой другой набор инструментов.
Так как в будущем все может измениться с другой прошивкой на ESP8266: у меня установлена прошивка на плате версии 00160901 .
Соединения с платой
Со времени моего первого поста на ESP8266 я почистил проводку. Пины, как показано ниже для ESP8266:
Поскольку ESP8266 может потреблять> 200 мА, я использую преобразователь постоянного тока 5–3,3 В. Я измерял от 70 до 90 мА, так что это (пока) действительно не нужно, но я хотел использовать его для защиты на плате. ESP8266 Rx и Tx подключены к выводам Tx и Rx микроконтроллера. Общее разочарование для модуля ESP8266 — это подключение оставшихся контактов. Что мне помогло, так это подключить CH_PD к 3,3 В и оставить RST , GPIO0 и GPIO2 не подключенными / плавающими.
Протокол связи
Я рекомендую использовать логический анализатор для проверки связи между ESP8266 и микроконтроллером. Мой модуль связывается с 115200, но я вижу сообщения о том, что другие модули (другие прошивки) могут использовать другую скорость передачи данных.
Модуль использует отправку команды AT. Самая простая команда — отправить «AT \ r \ n», и она отвечает «AT \ r \ r \ n \ r \ nOK \ r \ n»:
В этом уроке я использую оболочку командной строки (см. « Оболочку для платы Freedom KL25Z »), чтобы иметь ручной режим для отправки команд модулю. Подробнее об этом позже.
Создание проекта
Вы можете использовать мой проект и исходные файлы, доступные на GitHub (см. Ссылку в конце этой статьи). Или создайте свой собственный проект. Мой проект использует Kinetis Design Studio и для платы FRDM-KL25Z (MKL25Z128VLK4).
Я создал проект для Processor Expert, так как использую несколько его компонентов:
Для проекта у меня есть несколько добавленных файлов:
Со следующими исходными файлами:
- Application.c / .h : запускает программу приложения и веб-сервера
- ESP8266.c / .h : Драйвер для ESP8266
- Events.c / .h : обработчики событий Processor Expert
- main.c : главная точка входа
- Shell.c / .h : интерфейс командной строки
источники
Файлы проекта и исходные файлы доступны на GitHub здесь:
https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL25Z/FRDM-KL25Z_ESP8266
Пожалуйста, проверьте последние исходные файлы на GitHub. На момент написания этой статьи я использовал следующее:
Shell.h является интерфейсом командной оболочки:
01.
/*
02.
* Shell.h
03.
*
04.
* Author: Erich Styger
05.
*/
06.
07.
#ifndef SHELL_H_
08.
#define SHELL_H_
09.
10.
/*!
11.
* \brief Shell parse routine
12.
*/
13.
void
SHELL_Parse(
void
);
14.
15.
/*!
16.
* \brief Shell initialization
17.
*/
18.
void
SHELL_Init(
void
);
19.
20.
#endif
/* SHELL_H_ */
Shell.c реализует прикладную часть оболочки:
01.
/*
02.
* Shell.c
03.
*
04.
* Author: Erich Styger
05.
*/
06.
07.
#include
"Shell.h"
08.
#include
"CLS1.h"
09.
#include
"ESP8266.h"
10.
11.
/* table with shell parser/handler */
12.
static
const
CLS1_ParseCommandCallback CmdParserTable[] =
13.
{
14.
CLS1_ParseCommand,
15.
ESP_ParseCommand,
16.
NULL
/* sentinel */
17.
};
18.
19.
static
unsigned
char
localConsole_buf[
48
];
/* buffer for command line */
20.
21.
void
SHELL_Parse(
void
) {
22.
(
void
)CLS1_ReadAndParseWithCommandTable(localConsole_buf, sizeof(localConsole_buf), CLS1_GetStdio(), CmdParserTable);
23.
}
24.
25.
void
SHELL_Init(
void
) {
26.
localConsole_buf[
0
] =
'\0'
;
/* initialize buffer */
27.
}
ESP8266.h — это интерфейс к модулю WiFi:
001.
/*
002.
* ESP8266.h
003.
*
004.
* Author: Erich Styger
005.
*/
006.
007.
#ifndef ESP8266_H_
008.
#define ESP8266_H_
009.
010.
#include
"CLS1.h"
011.
012.
#define ESP_DEFAULT_TIMEOUT_MS (
100
)
013.
/*!< Default timeout value in milliseconds */
014.
015.
/*!
016.
* \brief Command line parser routine
017.
* \param cmd Pointer to command line string
018.
* \param handled Return value if command has been handled
019.
* \param io Standard Shell I/O handler
020.
* \return Error code, ERR_OK for no failure
021.
*/
022.
uint8_t ESP_ParseCommand(
const
unsigned
char
*cmd, bool *handled,
const
CLS1_StdIOType *io);
023.
024.
/*!
025.
* \brief Send a string to th ESP8266 module
026.
* \param str String to send, "\r\n" will be appended
027.
* \param io Shell I/O handler or NULL if not used
028.
* \return Error code, ERR_OK for no failure
029.
*/
030.
uint8_t ESP_SendStr(
const
uint8_t *str, CLS1_ConstStdIOType *io);
031.
032.
/*!
033.
* \brief Used to send an AT command to the ESP8266 module
034.
* \param cmd Command string to send
035.
* \param rxBuf Buffer for the response, can be NULL
036.
* \param rxBufSize Size of response buffer
037.
* \param expectedTailStr Expected response from the module, can be NULL
038.
* \param msTimeout Timeout time in milliseconds
039.
* \param io Shell I/O handler or NULL if not used
040.
* \return Error code, ERR_OK for no failure
041.
*/
042.
uint8_t ESP_SendATCommand(uint8_t *cmd, uint8_t *rxBuf, size_t rxBufSize, uint8_t *expectedTailStr, uint16_t msTimeout,
const
CLS1_StdIOType *io);
043.
044.
/*!
045.
* \brief Read from the serial line from the module until a sentinel char is received
046.
* \param buf
047.
* \param bufSize
048.
* \param sentinelChar
049.
* \param timeoutMs Timeout time in milliseconds
050.
* \return Error code, ERR_OK for no failure
051.
*/
052.
uint8_t ESP_ReadCharsUntil(uint8_t *buf, size_t bufSize, uint8_t sentinelChar, uint16_t timeoutMs);
053.
054.
/*!
055.
* \brief Sends an AT command to test the connection
056.
* \return Error code, ERR_OK for no failure
057.
*/
058.
uint8_t ESP_TestAT(
void
);
059.
060.
/*!
061.
* \brief Restarts the ESP8266 module
062.
* \param io Shell I/O handler or NULL if not used
063.
* \param timeoutMs Timeout time in milliseconds
064.
* \return Error code, ERR_OK for no failure
065.
*/
066.
uint8_t ESP_Restart(
const
CLS1_StdIOType *io, uint16_t timeoutMs);
067.
068.
/*!
069.
* \brief Set the current mode of the module
070.
* \param mode Where <mode> is 1=Sta, 2=AP or 3=both
071.
* \return Error code, ERR_OK for no failure
072.
*/
073.
uint8_t ESP_SelectMode(uint8_t mode);
074.
075.
/*!
076.
* \Brief returns the firmware version string
077.
* \param fwBuf Buffer for the string
078.
* \param fwBufSize Size of buffer in bytes
079.
* \return Error code, ERR_OK for no failure
080.
*/
081.
uint8_t ESP_GetFirmwareVersionString(uint8_t *fwBuf, size_t fwBufSize);
082.
083.
/*!
084.
* \brief Join an access point.
085.
* \param ssid SSID of access point
086.
* \param pwd Password of access point
087.
* \param nofRetries Number of connection retries
088.
* \param io Shell I/O or NULL if not used
089.
* \return Error code, ERR_OK for no failure
090.
*/
091.
uint8_t ESP_JoinAP(
const
uint8_t *ssid,
const
uint8_t *pwd,
int
nofRetries, CLS1_ConstStdIOType *io);
092.
093.
/*!
094.
* \brief Scans for an IPD message sent by the module
095.
* \param msgBuf Pointer to the message buffer where to store the message
096.
* \param msgBufSize Size of message buffer
097.
* \param ch_id Pointer to where to store the channel/id
098.
* \param size Pointer where to store the size of the message
099.
* \param isGet TRUE if it is a GET message, FALSE for a POST message
100.
* \param timeoutMs Error code, ERR_OK for no failure
101.
* \param io
102.
* \return Error code, ERR_OK for no failure
103.
*/
104.
uint8_t ESP_GetIPD(uint8_t *msgBuf, size_t msgBufSize, uint8_t *ch_id, uint16_t *size, bool *isGet, uint16_t timeoutMs,
const
CLS1_StdIOType *io);
105.
106.
/*!
107.
* \brief Closes a connection
108.
* \param channel Channel ID
109.
* \param io Error code, ERR_OK for no failure
110.
* \param timeoutMs Error code, ERR_OK for no failure
111.
* \return Error code, ERR_OK for no failure
112.
*/
113.
uint8_t ESP_CloseConnection(uint8_t channel,
const
CLS1_StdIOType *io, uint16_t timeoutMs);
114.
115.
/*!
116.
* \brief Used to determine if the web server is running or not.
117.
* \return TRUE if web server has beens started
118.
*/
119.
bool ESP_IsServerOn(
void
);
120.
121.
/*!
122.
* \brief Driver initialization
123.
*/
124.
void
ESP_Init(
void
);
125.
126.
/*!
127.
* \brief Driver de-initialization
128.
*/
129.
void
ESP_Deinit(
void
);
130.
131.
#endif
/* ESP8266_H_ */
А драйвер ESP8266 находится в ESP8266.c, который реализует все функции доступа SPI низкого уровня, функциональную реализацию и интерфейс оболочки командной строки:
001.
/*
002.
* ESP8266.c
003.
*
004.
* Author: Erich Styger
005.
*/
006.
007.
#include
"ESP8266.h"
008.
#include
"Shell.h"
009.
#include
"UTIL1.h"
010.
#include
"CLS1.h"
011.
#include
"AS2.h"
012.
#include
"WAIT1.h"
013.
014.
static
bool ESP_WebServerIsOn = FALSE;
015.
016.
bool ESP_IsServerOn(
void
) {
017.
return
ESP_WebServerIsOn;
018.
}
019.
020.
static
void
Send(unsigned
char
*str) {
021.
while
(*str!=
'\0'
) {
022.
AS2_SendChar(*str);
023.
str++;
024.
}
025.
}
026.
027.
static
void
SkipNewLines(
const
unsigned
char
**p) {
028.
while
(**p==
'\n'
|| **p==
'\r'
) {
029.
(*p)++;
/* skip new lines */
030.
}
031.
}
032.
033.
uint8_t ESP_ReadCharsUntil(uint8_t *buf, size_t bufSize, uint8_t sentinelChar, uint16_t timeoutMs) {
034.
uint8_t ch;
035.
uint8_t res = ERR_OK;
036.
037.
if
(bufSize<=
1
) {
038.
return
ERR_OVERRUN;
/* buffer to small */
039.
}
040.
buf[
0
] =
'\0'
; buf[bufSize-
1
] =
'\0'
;
/* always terminate */
041.
bufSize--;
042.
for
(;;) {
/* breaks */
043.
if
(bufSize==
0
) {
044.
res = ERR_OVERRUN;
045.
break
;
046.
}
047.
if
(AS2_GetCharsInRxBuf()>
0
) {
048.
(
void
)AS2_RecvChar(&ch);
049.
*buf = ch;
050.
buf++;
051.
bufSize--;
052.
if
(ch==sentinelChar) {
053.
*buf =
'\0'
;
/* terminate string */
054.
break
;
/* sentinel found */
055.
}
056.
}
else
{
057.
if
(timeoutMs>
10
) {
058.
WAIT1_WaitOSms(
5
);
059.
timeoutMs -=
5
;
060.
}
else
{
061.
res = ERR_NOTAVAIL;
/* timeout */
062.
break
;
063.
}
064.
}
065.
}
066.
return
res;
067.
}
068.
069.
static
uint8_t RxResponse(unsigned
char
*rxBuf, size_t rxBufLength, unsigned
char
*expectedTail, uint16_t msTimeout) {
070.
unsigned
char
ch;
071.
uint8_t res = ERR_OK;
072.
unsigned
char
*p;
073.
074.
if
(rxBufLength < sizeof(
"x\r\n"
)) {
075.
return
ERR_OVERFLOW;
/* not enough space in buffer */
076.
}
077.
p = rxBuf;
078.
p[
0
] =
'\0'
;
079.
for
(;;) {
/* breaks */
080.
if
(msTimeout ==
0
) {
081.
break
;
/* will decide outside of loop if it is a timeout. */
082.
}
else
if
(rxBufLength ==
0
) {
083.
res = ERR_OVERFLOW;
/* not enough space in buffer */
084.
break
;
085.
}
else
if
(AS2_GetCharsInRxBuf() >
0
) {
086.
#
if
0
087.
if
(AS2_RecvChar(&ch) != ERR_OK) {
088.
res = ERR_RXEMPTY;
089.
break
;
090.
}
091.
#
else
092.
/* might get an overrun OVERRUN_ERR error here? Ignoring error for now */
093.
(
void
)AS2_RecvChar(&ch);
094.
#endif
095.
*p++ = ch;
096.
*p =
'\0'
;
/* always terminate */
097.
rxBufLength--;
098.
}
else
if
(expectedTail!=NULL && expectedTail[
0
]!=
'\0'
099.
&& UTIL1_strtailcmp(rxBuf, expectedTail) ==
0
) {
100.
break
;
/* finished */
101.
}
else
{
102.
WAIT1_WaitOSms(
1
);
103.
msTimeout--;
104.
}
105.
}
/* for */
106.
if
(msTimeout==
0
) {
/* timeout! */
107.
if
(expectedTail[
0
] !=
'\0'
/* timeout, and we expected something: an error for sure */
108.
|| rxBuf[
0
] ==
'\0'
/* timeout, did not know what to expect, but received nothing? There has to be a response. */
109.
)
110.
{
111.
res = ERR_FAULT;
112.
}
113.
}
114.
return
res;
115.
}
116.
117.
uint8_t ESP_SendATCommand(uint8_t *cmd, uint8_t *rxBuf, size_t rxBufSize, uint8_t *expectedTailStr, uint16_t msTimeout,
const
CLS1_StdIOType *io) {
118.
uint16_t snt;
119.
uint8_t res;
120.
121.
if
(rxBuf!=NULL) {
122.
rxBuf[
0
] =
'\0'
;
123.
}
124.
if
(io!=NULL) {
125.
CLS1_SendStr(
"sending>>:\r\n"
, io->stdOut);
126.
CLS1_SendStr(cmd, io->stdOut);
127.
}
128.
if
(AS2_SendBlock(cmd, (uint16_t)UTIL1_strlen((
char
*)cmd), &snt) != ERR_OK) {
129.
return
ERR_FAILED;
130.
}
131.
if
(rxBuf!=NULL) {
132.
res = RxResponse(rxBuf, rxBufSize, expectedTailStr, msTimeout);
133.
if
(io!=NULL) {
134.
CLS1_SendStr(
"received<<:\r\n"
, io->stdOut);
135.
CLS1_SendStr(rxBuf, io->stdOut);
136.
}
137.
}
138.
return
res;
139.
}
140.
141.
uint8_t ESP_TestAT(
void
) {
142.
/* AT */
143.
uint8_t rxBuf[sizeof(
"AT\r\r\n\r\nOK\r\n"
)];
144.
uint8_t res;
145.
146.
res = ESP_SendATCommand(
"AT\r\n"
, rxBuf, sizeof(rxBuf),
"AT\r\r\n\r\nOK\r\n"
, ESP_DEFAULT_TIMEOUT_MS, NULL);
147.
return
res;
148.
}
149.
150.
uint8_t ESP_Restart(
const
CLS1_StdIOType *io, uint16_t timeoutMs) {
151.
/* AT+RST */
152.
uint8_t rxBuf[sizeof(
"AT+RST\r\r\n\r\nOK\r\n"
)];
153.
uint8_t res;
154.
uint8_t buf[
64
];
155.
156.
AS2_ClearRxBuf();
/* clear buffer */
157.
res = ESP_SendATCommand(
"AT+RST\r\n"
, rxBuf, sizeof(rxBuf),
"AT+RST\r\r\n\r\nOK\r\n"
, ESP_DEFAULT_TIMEOUT_MS, io);
158.
if
(res==ERR_OK) {
159.
for
(;;) {
160.
ESP_ReadCharsUntil(buf, sizeof(buf),
'\n'
,
1000
);
161.
if
(io!=NULL) {
162.
CLS1_SendStr(buf, io->stdOut);
163.
}
164.
if
(UTIL1_strncmp(buf,
"ready"
, sizeof(
"ready"
)-
1
)==
0
) {
/* wait until ready message from module */
165.
break
;
/* module has restarted */
166.
}
167.
}
168.
}
169.
AS2_ClearRxBuf();
/* clear buffer */
170.
return
res;
171.
}
172.
173.
uint8_t ESP_CloseConnection(uint8_t channel,
const
CLS1_StdIOType *io, uint16_t timeoutMs) {
174.
/* AT+CIPCLOSE=<channel> */
175.
uint8_t res;
176.
uint8_t cmd[
64
];
177.
178.
UTIL1_strcpy(cmd, sizeof(cmd),
"AT+CIPCLOSE="
);
179.
UTIL1_strcatNum8u(cmd, sizeof(cmd), channel);
180.
UTIL1_strcat(cmd, sizeof(cmd),
"\r\n"
);
181.
res = ESP_SendATCommand(cmd, NULL,
0
,
"Unlink\r\n"
, timeoutMs, io);
182.
return
res;
183.
}
184.
185.
uint8_t ESP_SetNumberOfConnections(uint8_t nof,
const
CLS1_StdIOType *io, uint16_t timeoutMs) {
186.
/* AT+CIPMUX=<nof>, 0: single connection, 1: multiple connections */
187.
uint8_t res;
188.
uint8_t cmd[sizeof(
"AT+CIPMUX=12\r\n"
)];
189.
uint8_t rxBuf[sizeof(
"AT+CIPMUX=12\r\n\r\nOK\r\n"
)+
10
];
190.
191.
if
(nof>
1
) {
/* only 0 and 1 allowed */
192.
if
(io!=NULL) {
193.
CLS1_SendStr(
"Wrong number of connection parameter!\r\n"
, io->stdErr);
194.
}
195.
return
ERR_FAILED;
196.
}
197.
UTIL1_strcpy(cmd, sizeof(cmd),
"AT+CIPMUX="
);
198.
UTIL1_strcatNum8u(cmd, sizeof(cmd), nof);
199.
UTIL1_strcat(cmd, sizeof(cmd),
"\r\n"
);
200.
res = ESP_SendATCommand(cmd, rxBuf, sizeof(rxBuf),
"OK\r\n"
, timeoutMs, io);
201.
return
res;
202.
}
203.
204.
uint8_t ESP_SetServer(bool startIt, uint16_t port,
const
CLS1_StdIOType *io, uint16_t timeoutMs) {
205.
/* AT+CIPSERVER=<en>,<port>, where <en>: 0: stop, 1: start */
206.
uint8_t res;
207.
uint8_t cmd[sizeof(
"AT+CIPSERVER=1,80\r\n\r\nOK\r\n"
)+sizeof(
"no change"
)];
208.
uint8_t rxBuf[sizeof(
"AT+CIPSERVER=1,80\r\n\r\nOK\r\n"
)+sizeof(
"no change"
)];
209.
210.
UTIL1_strcpy(cmd, sizeof(cmd),
"AT+CIPSERVER="
);
211.
if
(startIt) {
212.
UTIL1_strcat(cmd, sizeof(cmd),
"1,"
);
213.
}
else
{
214.
UTIL1_strcat(cmd, sizeof(cmd),
"0,"
);
215.
}
216.
UTIL1_strcatNum16u(cmd, sizeof(cmd), port);
217.
UTIL1_strcat(cmd, sizeof(cmd),
"\r\n"
);
218.
res = ESP_SendATCommand(cmd, rxBuf, sizeof(rxBuf),
"OK\r\n"
, timeoutMs, io);
219.
if
(res!=ERR_OK) {
/* accept "no change" too */
220.
UTIL1_strcpy(cmd, sizeof(cmd),
"AT+CIPSERVER="
);
221.
if
(startIt) {
222.
UTIL1_strcat(cmd, sizeof(cmd),
"1,"
);
223.
}
else
{
224.
UTIL1_strcat(cmd, sizeof(cmd),
"0,"
);
225.
}
226.
UTIL1_strcatNum16u(cmd, sizeof(cmd), port);
227.
UTIL1_strcat(cmd, sizeof(cmd),
"\r\r\nno change\r\n"
);
228.
if
(UTIL1_strcmp(rxBuf, cmd)==
0
) {
229.
res = ERR_OK;
230.
}
231.
}
232.
return
res;
233.
}
234.
235.
uint8_t ESP_SelectMode(uint8_t mode) {
236.
/* AT+CWMODE=<mode>, where <mode> is 1=Sta, 2=AP or 3=both */
237.
uint8_t txBuf[sizeof(
"AT+CWMODE=x\r\n"
)];
238.
uint8_t rxBuf[sizeof(
"AT+CWMODE=x\r\r\nno change\r\n"
)];
239.
uint8_t expected[sizeof(
"AT+CWMODE=x\r\r\nno change\r\n"
)];
240.
uint8_t res;
241.
242.
if
(mode<
1
|| mode>
3
) {
243.
return
ERR_RANGE;
/* only 1, 2 or 3 */
244.
}
245.
UTIL1_strcpy(txBuf, sizeof(txBuf),
"AT+CWMODE="
);
246.
UTIL1_strcatNum16u(txBuf, sizeof(txBuf), mode);
247.
UTIL1_strcat(txBuf, sizeof(txBuf),
"\r\n"
);
248.
UTIL1_strcpy(expected, sizeof(expected),
"AT+CWMODE="
);
249.
UTIL1_strcatNum16u(expected, sizeof(expected), mode);
250.
UTIL1_strcat(expected, sizeof(expected),
"\r\r\n\n"
);
251.
res = ESP_SendATCommand(txBuf, rxBuf, sizeof(rxBuf), expected, ESP_DEFAULT_TIMEOUT_MS, NULL);
252.
if
(res!=ERR_OK) {
253.
/* answer could be as well "AT+CWMODE=x\r\r\nno change\r\n"!! */
254.
UTIL1_strcpy(txBuf, sizeof(txBuf),
"AT+CWMODE="
);
255.
UTIL1_strcatNum16u(txBuf, sizeof(txBuf), mode);
256.
UTIL1_strcat(txBuf, sizeof(txBuf),
"\r\n"
);
257.
UTIL1_strcpy(expected, sizeof(expected),
"AT+CWMODE="
);
258.
UTIL1_strcatNum16u(expected, sizeof(expected), mode);
259.
UTIL1_strcat(expected, sizeof(expected),
"\r\r\nno change\r\n"
);
260.
if
(UTIL1_strcmp(rxBuf, expected)==
0
) {
261.
res = ERR_OK;
262.
}
263.
}
264.
return
res;
265.
}
266.
267.
uint8_t ESP_GetFirmwareVersionString(uint8_t *fwBuf, size_t fwBufSize) {
268.
/* AT+GMR */
269.
uint8_t rxBuf[
32
];
270.
uint8_t res;
271.
const
unsigned
char
*p;
272.
273.
res = ESP_SendATCommand(
"AT+GMR\r\n"
, rxBuf, sizeof(rxBuf),
"\r\n\r\nOK\r\n"
, ESP_DEFAULT_TIMEOUT_MS, NULL);
274.
if
(res!=ERR_OK) {
275.
if
(UTIL1_strtailcmp(rxBuf,
"\r\n\r\nOK\r\n"
)) {
276.
res = ERR_OK;
277.
}
278.
}
279.
if
(res==ERR_OK) {
280.
if
(UTIL1_strncmp(rxBuf,
"AT+GMR\r\r\n"
, sizeof(
"AT+GMR\r\r\n"
)-
1
)==
0
) {
/* check for beginning of response */
281.
UTIL1_strCutTail(rxBuf,
"\r\n\r\nOK\r\n"
);
/* cut tailing response */
282.
p = rxBuf+sizeof(
"AT+GMR\r\r\n"
)-
1
;
/* skip beginning */
283.
UTIL1_strcpy(fwBuf, fwBufSize, p);
/* copy firmware information string */
284.
}
else
{
285.
res = ERR_FAILED;
286.
}
287.
}
288.
if
(res!=ERR_OK) {
289.
UTIL1_strcpy(fwBuf, fwBufSize,
"ERROR"
);
/* default error */
290.
}
291.
return
res;
292.
}
293.
294.
uint8_t ESP_GetIPAddrString(uint8_t *ipBuf, size_t ipBufSize) {
295.
/* AT+CIFSR */
296.
uint8_t rxBuf[
32
];
297.
uint8_t res;
298.
const
unsigned
char
*p;
299.
300.
res = ESP_SendATCommand(
"AT+CIFSR\r\n"
, rxBuf, sizeof(rxBuf), NULL, ESP_DEFAULT_TIMEOUT_MS, NULL);
301.
if
(res!=ERR_OK) {
302.
if
(UTIL1_strtailcmp(rxBuf,
"\r\n"
)) {
303.
res = ERR_OK;
304.
}
305.
}
306.
if
(res==ERR_OK) {
307.
if
(UTIL1_strncmp(rxBuf,
"AT+CIFSR\r\r\n"
, sizeof(
"AT+CIFSR\r\r\n"
)-
1
)==
0
) {
/* check for beginning of response */
308.
UTIL1_strCutTail(rxBuf,
"\r\n"
);
/* cut tailing response */
309.
p = rxBuf+sizeof(
"AT+CIFSR\r\r\n"
)-
1
;
/* skip beginning */
310.
SkipNewLines(&p);
311.
UTIL1_strcpy(ipBuf, ipBufSize, p);
/* copy IP information string */
312.
}
else
{
313.
res = ERR_FAILED;
314.
}
315.
}
316.
if
(res!=ERR_OK) {
317.
UTIL1_strcpy(ipBuf, ipBufSize,
"ERROR"
);
318.
}
319.
return
res;
320.
}
321.
322.
uint8_t ESP_GetModeString(uint8_t *buf, size_t bufSize) {
323.
/* AT+CWMODE? */
324.
uint8_t rxBuf[
32
];
325.
uint8_t res;
326.
const
unsigned
char
*p;
327.
328.
res = ESP_SendATCommand(
"AT+CWMODE?\r\n"
, rxBuf, sizeof(rxBuf),
"\r\n\r\nOK\r\n"
, ESP_DEFAULT_TIMEOUT_MS, NULL);
329.
if
(res==ERR_OK) {
330.
if
(UTIL1_strncmp(rxBuf,
"AT+CWMODE?\r\r\n+CWMODE:"
, sizeof(
"AT+CWMODE?\r\r\n+CWMODE:"
)-
1
)==
0
) {
/* check for beginning of response */
331.
UTIL1_strCutTail(rxBuf,
"\r\n\r\nOK\r\n"
);
/* cut tailing response */
332.
p = rxBuf+sizeof(
"AT+CWMODE?\r\r\n+CWMODE:"
)-
1
;
/* skip beginning */
333.
UTIL1_strcpy(buf, bufSize, p);
/* copy information string */
334.
}
else
{
335.
res = ERR_FAILED;
336.
}
337.
}
338.
if
(res!=ERR_OK) {
339.
UTIL1_strcpy(buf, bufSize,
"ERROR"
);
340.
}
341.
return
res;
342.
}
343.
344.
uint8_t ESP_GetCIPMUXString(uint8_t *cipmuxBuf, size_t cipmuxBufSize) {
345.
/* AT+CIPMUX? */
346.
uint8_t rxBuf[
32
];
347.
uint8_t res;
348.
const
unsigned
char
*p;
349.
350.
res = ESP_SendATCommand(
"AT+CIPMUX?\r\n"
, rxBuf, sizeof(rxBuf),
"\r\n\r\nOK\r\n"
, ESP_DEFAULT_TIMEOUT_MS, NULL);
351.
if
(res==ERR_OK) {
352.
if
(UTIL1_strncmp(rxBuf,
"AT+CIPMUX?\r\r\n+CIPMUX:"
, sizeof(
"AT+CIPMUX?\r\r\n+CIPMUX:"
)-
1
)==
0
) {
/* check for beginning of response */
353.
UTIL1_strCutTail(rxBuf,
"\r\n\r\nOK\r\n"
);
/* cut tailing response */
354.
p = rxBuf+sizeof(
"AT+CIPMUX?\r\r\n+CIPMUX:"
)-
1
;
/* skip beginning */
355.
UTIL1_strcpy(cipmuxBuf, cipmuxBufSize, p);
/* copy IP information string */
356.
}
else
{
357.
res = ERR_FAILED;
358.
}
359.
}
360.
if
(res!=ERR_OK) {
361.
UTIL1_strcpy(cipmuxBuf, cipmuxBufSize,
"ERROR"
);
362.
}
363.
return
res;
364.
}
365.
366.
uint8_t ESP_GetConnectedAPString(uint8_t *apBuf, size_t apBufSize) {
367.
/* AT+CWJAP? */
368.
uint8_t rxBuf[
48
];
369.
uint8_t res;
370.
const
unsigned
char
*p;
371.
372.
res = ESP_SendATCommand(
"AT+CWJAP?\r\n"
, rxBuf, sizeof(rxBuf),
"\r\n\r\nOK\r\n"
, ESP_DEFAULT_TIMEOUT_MS, NULL);
373.
if
(res==ERR_OK) {
374.
if
(UTIL1_strncmp(rxBuf,
"AT+CWJAP?\r\r\n+CWJAP:\""
, sizeof(
"AT+CWJAP?\r\r\n+CWJAP:\""
)-
1
)==
0
) {
/* check for beginning of response */
375.
UTIL1_strCutTail(rxBuf,
"\"\r\n\r\nOK\r\n"
);
/* cut tailing response */
376.
p = rxBuf+sizeof(
"AT+CWJAP?\r\r\n+CWJAP:\""
)-
1
;
/* skip beginning */
377.
UTIL1_strcpy(apBuf, apBufSize, p);
/* copy IP information string */
378.
}
else
{
379.
res = ERR_FAILED;
380.
}
381.
}
382.
if
(res!=ERR_OK) {
383.
UTIL1_strcpy(apBuf, apBufSize,
"ERROR"
);
384.
}
385.
return
res;
386.
387.
}
388.
389.
static
uint8_t JoinAccessPoint(
const
uint8_t *ssid,
const
uint8_t *pwd, CLS1_ConstStdIOType *io) {
390.
/* AT+CWJAP="<ssid>","<pwd>" */
391.
uint8_t txBuf[
48
];
392.
uint8_t rxBuf[
64
];
393.
uint8_t expected[
48
];
394.
395.
UTIL1_strcpy(txBuf, sizeof(txBuf),
"AT+CWJAP=\""
);
396.
UTIL1_strcat(txBuf, sizeof(txBuf), ssid);
397.
UTIL1_strcat(txBuf, sizeof(txBuf),
"\",\""
);
398.
UTIL1_strcat(txBuf, sizeof(txBuf), pwd);
399.
UTIL1_strcat(txBuf, sizeof(txBuf),
"\"\r\n"
);
400.
401.
UTIL1_strcpy(expected, sizeof(expected),
"AT+CWJAP=\""
);
402.
UTIL1_strcat(expected, sizeof(expected), ssid);
403.
UTIL1_strcat(expected, sizeof(expected),
"\",\""
);
404.
UTIL1_strcat(expected, sizeof(expected), pwd);
405.
UTIL1_strcat(expected, sizeof(expected),
"\"\r\r\n\r\nOK\r\n"
);
406.
407.
return
ESP_SendATCommand(txBuf, rxBuf, sizeof(rxBuf), expected, ESP_DEFAULT_TIMEOUT_MS, io);
408.
}
409.
410.
uint8_t ESP_JoinAP(
const
uint8_t *ssid,
const
uint8_t *pwd,
int
nofRetries, CLS1_ConstStdIOType *io) {
411.
uint8_t buf[
32
];
412.
uint8_t res;
413.
414.
do
{
415.
res = JoinAccessPoint(ssid, pwd, io);
416.
if
(res==ERR_OK) {
417.
break
;
418.
}
419.
WAIT1_WaitOSms(
1000
);
420.
nofRetries--;
421.
}
while
(nofRetries>
0
);
422.
return
res;
423.
}
424.
425.
static
uint8_t ReadIntoIPDBuffer(uint8_t *buf, size_t bufSize, uint8_t *p, uint16_t msgSize, uint16_t msTimeout,
const
CLS1_StdIOType *io) {
426.
uint8_t ch;
427.
size_t nofInBuf;
428.
int
timeout;
429.
430.
nofInBuf = p-buf;
431.
bufSize -= nofInBuf;
/* take into account what we already have in buffer */
432.
timeout = msTimeout;
433.
while
(msgSize>
0
&& bufSize>
0
) {
434.
if
(AS2_GetCharsInRxBuf()>
0
) {
435.
(
void
)AS2_RecvChar(&ch);
436.
*p = ch;
437.
if
(io!=NULL) {
/* copy on console */
438.
io->stdOut(ch);
439.
}
440.
p++;
441.
*p =
'\0'
;
/* terminate */
442.
nofInBuf++; msgSize--; bufSize--;
443.
}
else
{
444.
/* check in case we recveive less characters than expected, happens for POST? */
445.
if
(nofInBuf>
6
&& UTIL1_strncmp(&p[-
6
],
"\r\nOK\r\n"
, sizeof(
"\r\nOK\r\n"
)-
1
)==
0
) {
446.
break
;
447.
}
else
{
448.
timeout -=
10
;
449.
WAIT1_WaitOSms(
10
);
450.
if
(timeout<
0
) {
451.
return
ERR_BUSY;
452.
}
453.
}
454.
}
455.
}
456.
return
ERR_OK;
457.
}
458.
459.
uint8_t ESP_GetIPD(uint8_t *msgBuf, size_t msgBufSize, uint8_t *ch_id, uint16_t *size, bool *isGet, uint16_t timeoutMs,
const
CLS1_StdIOType *io) {
460.
/* scan e.g. for
461.
* +IPD,0,404:POST / HTTP/1.1
462.
* and return ch_id (0), size (404)
463.
*/
464.
uint8_t res = ERR_OK;
465.
const
uint8_t *p;
466.
bool isIPD = FALSE;
467.
uint8_t cmd[
24
], rxBuf[
48
];
468.
uint16_t ipdSize;
469.
470.
*ch_id =
0
; *size =
0
; *isGet = FALSE;
/* init */
471.
for
(;;) {
/* breaks */
472.
res = ESP_ReadCharsUntil(msgBuf, msgBufSize,
'\n'
, timeoutMs);
473.
if
(res!=ERR_OK) {
474.
break
;
/* timeout */
475.
}
476.
if
(res==ERR_OK) {
/* line read */
477.
if
(io!=NULL) {
478.
CLS1_SendStr(msgBuf, io->stdOut);
/* copy on console */
479.
}
480.
isIPD = UTIL1_strncmp(msgBuf,
"+IPD,"
, sizeof(
"+IPD,"
)-
1
)==
0
;
481.
if
(isIPD) {
/* start of IPD message */
482.
p = msgBuf+sizeof(
"+IPD,"
)-
1
;
483.
if
(UTIL1_ScanDecimal8uNumber(&p, ch_id)!=ERR_OK) {
484.
if
(io!=NULL) {
485.
CLS1_SendStr(
"ERR: wrong channel?\r\n"
, io->stdErr);
/* error on console */
486.
}
487.
res = ERR_FAILED;
488.
break
;
489.
}
490.
if
(*p!=
','
) {
491.
res = ERR_FAILED;
492.
break
;
493.
}
494.
p++;
/* skip comma */
495.
if
(UTIL1_ScanDecimal16uNumber(&p, size)!=ERR_OK) {
496.
if
(io!=NULL) {
497.
CLS1_SendStr(
"ERR: wrong size?\r\n"
, io->stdErr);
/* error on console */
498.
}
499.
res = ERR_FAILED;
500.
break
;
501.
}
502.
if
(*p!=
':'
) {
503.
res = ERR_FAILED;
504.
break
;
505.
}
506.
ipdSize = p-msgBuf;
/* length of "+IPD,<channel>,<size>" string */
507.
p++;
/* skip ':' */
508.
if
(UTIL1_strncmp(p,
"GET"
, sizeof(
"GET"
)-
1
)==
0
) {
509.
*isGet = TRUE;
510.
}
else
if
(UTIL1_strncmp(p,
"POST"
, sizeof(
"POST"
)-
1
)==
0
) {
511.
*isGet = FALSE;
512.
}
else
{
513.
res = ERR_FAILED;
514.
}
515.
while
(*p!=
'\0'
) {
516.
p++;
/* skip to the end */
517.
}
518.
/* read the rest of the message */
519.
res = ReadIntoIPDBuffer(msgBuf, msgBufSize, (uint8_t*)p, (*size)-ipdSize, ESP_DEFAULT_TIMEOUT_MS, io);
520.
break
;
521.
}
522.
}
523.
}
524.
return
res;
525.
}
526.
527.
uint8_t ESP_StartWebServer(
const
CLS1_StdIOType *io) {
528.
uint8_t buf[
32
];
529.
uint8_t res;
530.
531.
res = ESP_SetNumberOfConnections(
1
, io, ESP_DEFAULT_TIMEOUT_MS);
532.
if
(res!=ERR_OK) {
533.
CLS1_SendStr(
"ERR: failed to set multiple connections.\r\n"
, io->stdErr);
534.
return
res;
535.
}
536.
res = ESP_SetServer(TRUE,
80
, io, ESP_DEFAULT_TIMEOUT_MS);
537.
if
(res!=ERR_OK) {
538.
CLS1_SendStr(
"ERR: failed to set server.\r\n"
, io->stdErr);
539.
return
res;
540.
}
541.
CLS1_SendStr(
"INFO: Web Server started, waiting for connection on "
, io->stdOut);
542.
if
(ESP_GetIPAddrString(buf, sizeof(buf))==ERR_OK) {
543.
CLS1_SendStr(buf, io->stdOut);
544.
CLS1_SendStr(
":80"
, io->stdOut);
545.
}
else
{
546.
CLS1_SendStr(
"(ERROR!)"
, io->stdOut);
547.
}
548.
CLS1_SendStr(
"\r\n"
, io->stdOut);
549.
550.
return
ERR_OK;
551.
}
552.
553.
uint8_t ESP_SendStr(
const
uint8_t *str, CLS1_ConstStdIOType *io) {
554.
uint8_t buf[
32
];
555.
uint8_t rxBuf[
48
];
556.
uint8_t res;
557.
uint16_t timeoutMs;
558.
#define RX_TIMEOUT_MS
3000
559.
AS2_TComData ch;
560.
561.
UTIL1_strcpy(buf, sizeof(buf), str);
562.
UTIL1_strcat(buf, sizeof(buf),
"\r\n"
);
563.
res = ESP_SendATCommand(buf, rxBuf, sizeof(rxBuf), NULL, ESP_DEFAULT_TIMEOUT_MS, io);
564.
timeoutMs =
0
;
565.
while
(timeoutMs<RX_TIMEOUT_MS) {
566.
WAIT1_WaitOSms(
100
);
567.
timeoutMs +=
100
;
568.
while
(AS2_GetCharsInRxBuf()>
0
) {
569.
(
void
)AS2_RecvChar(&ch);
570.
CLS1_SendChar(ch);
571.
}
572.
}
573.
return
ERR_OK;
574.
}
575.
576.
static
uint8_t ESP_PrintHelp(
const
CLS1_StdIOType *io) {
577.
CLS1_SendHelpStr(
"ESP"
,
"ESP8200 commands\r\n"
, io->stdOut);
578.
CLS1_SendHelpStr(
" help|status"
,
"Print help or status information\r\n"
, io->stdOut);
579.
CLS1_SendHelpStr(
" send <str>"
,
"Sends a string to the module\r\n"
, io->stdOut);
580.
CLS1_SendHelpStr(
" test"
,
"Sends a test AT command\r\n"
, io->stdOut);
581.
CLS1_SendHelpStr(
" restart"
,
"Restart module\r\n"
, io->stdOut);
582.
CLS1_SendHelpStr(
" listAP"
,
"List available Access Points\r\n"
, io->stdOut);
583.
CLS1_SendHelpStr(
" connectAP \"ssid\",\"pwd\""
,
"Connect to an Access Point\r\n"
, io->stdOut);
584.
CLS1_SendHelpStr(
" server (start|stop)"
,
"Start or stop web server\r\n"
, io->stdOut);
585.
return
ERR_OK;
586.
}
587.
588.
static
uint8_t ESP_PrintStatus(
const
CLS1_StdIOType *io) {
589.
uint8_t buf[
48
];
590.
591.
CLS1_SendStatusStr(
"ESP8266"
,
"\r\n"
, io->stdOut);
592.
593.
CLS1_SendStatusStr(
" Webserver"
, ESP_WebServerIsOn?
"ON\r\n"
:
"OFF\r\n"
, io->stdOut);
594.
595.
if
(ESP_GetFirmwareVersionString(buf, sizeof(buf)) != ERR_OK) {
596.
UTIL1_strcpy(buf, sizeof(buf),
"FAILED\r\n"
);
597.
}
else
{
598.
UTIL1_strcat(buf, sizeof(buf),
"\r\n"
);
599.
}
600.
CLS1_SendStatusStr(
" AT+GMR"
, buf, io->stdOut);
601.
602.
if
(ESP_GetModeString(buf, sizeof(buf)) != ERR_OK) {
603.
UTIL1_strcpy(buf, sizeof(buf),
"FAILED\r\n"
);
604.
}
else
{
605.
if
(UTIL1_strcmp(buf,
"1"
)==
0
) {
606.
UTIL1_strcat(buf, sizeof(buf),
" (device)"
);
607.
}
else
if
(UTIL1_strcmp(buf,
"2"
)==
0
) {
608.
UTIL1_strcat(buf, sizeof(buf),
" (AP)"
);
609.
}
else
if
(UTIL1_strcmp(buf,
"3"
)==
0
) {
610.
UTIL1_strcat(buf, sizeof(buf),
" (device+AP)"
);
611.
}
else
{
612.
UTIL1_strcat(buf, sizeof(buf),
" (ERROR)"
);
613.
}
614.
UTIL1_strcat(buf, sizeof(buf),
"\r\n"
);
615.
}
616.
CLS1_SendStatusStr(
" AT+CWMODE?"
, buf, io->stdOut);
617.
618.
if
(ESP_GetIPAddrString(buf, sizeof(buf)) != ERR_OK) {
619.
UTIL1_strcpy(buf, sizeof(buf),
"FAILED\r\n"
);
620.
}
else
{
621.
UTIL1_strcat(buf, sizeof(buf),
"\r\n"
);
622.
}
623.
CLS1_SendStatusStr(
" AT+CIFSR"
, buf, io->stdOut);
624.
625.
if
(ESP_GetConnectedAPString(buf, sizeof(buf)) != ERR_OK) {
626.
UTIL1_strcpy(buf, sizeof(buf),
"FAILED\r\n"
);
627.
}
else
{
628.
UTIL1_strcat(buf, sizeof(buf),
"\r\n"
);
629.
}
630.
CLS1_SendStatusStr(
" AT+CWJAP?"
, buf, io->stdOut);
631.
632.
if
(ESP_GetCIPMUXString(buf, sizeof(buf)) != ERR_OK) {
633.
UTIL1_strcpy(buf, sizeof(buf),
"FAILED\r\n"
);
634.
}
else
{
635.
if
(UTIL1_strcmp(buf,
"0"
)==
0
) {
636.
UTIL1_strcat(buf, sizeof(buf),
" (single connection)"
);
637.
}
else
if
(UTIL1_strcmp(buf,
"1"
)==
0
) {
638.
UTIL1_strcat(buf, sizeof(buf),
" (multiple connections)"
);
639.
}
else
{
640.
UTIL1_strcat(buf, sizeof(buf),
" (ERROR)"
);
641.
}
642.
UTIL1_strcat(buf, sizeof(buf),
"\r\n"
);
643.
}
644.
CLS1_SendStatusStr(
" CIPMUX"
, buf, io->stdOut);
645.
return
ERR_OK;
646.
}
647.
648.
uint8_t ESP_ParseCommand(
const
unsigned
char
*cmd, bool *handled,
const
CLS1_StdIOType *io) {
649.
uint32_t val;
650.
uint8_t res;
651.
const
unsigned
char
*p;
652.
uint8_t pwd[
24
], ssid[
24
];
653.
654.
if
(UTIL1_strcmp((
char
*)cmd, CLS1_CMD_HELP)==
0
|| UTIL1_strcmp((
char
*)cmd,
"ESP help"
)==
0
) {
655.
*handled = TRUE;
656.
res = ESP_PrintHelp(io);
657.
}
else
if
(UTIL1_strcmp((
char
*)cmd, CLS1_CMD_STATUS)==
0
|| UTIL1_strcmp((
char
*)cmd,
"ESP status"
)==
0
) {
658.
*handled = TRUE;
659.
res = ESP_PrintStatus(io);
660.
}
else
if
(UTIL1_strncmp((
char
*)cmd,
"ESP send "
, sizeof(
"ESP send "
)-
1
)==
0
) {
661.
*handled = TRUE;
662.
p = cmd+sizeof(
"ESP send "
)-
1
;
663.
664.
(
void
)ESP_SendStr(p, io);
665.
}
else
if
(UTIL1_strcmp((
char
*)cmd,
"ESP test"
)==
0
) {
666.
*handled = TRUE;
667.
if
(ESP_TestAT()!=ERR_OK) {
668.
CLS1_SendStr(
"TEST failed!\r\n"
, io->stdErr);
669.
res = ERR_FAILED;
670.
}
else
{
671.
CLS1_SendStr(
"TEST ok!\r\n"
, io->stdOut);
672.
}
673.
}
else
if
(UTIL1_strcmp((
char
*)cmd,
"ESP listAP"
)==
0
) {
674.
*handled = TRUE;
675.
(
void
)ESP_SendStr(
"AT+CWLAP"
, io);
676.
/* AT + CWLAP
677.
response
678.
+ CWLAP: <ecn>, <ssid>, <rssi> [, <mode>]
679.
OK Or Fails, the return ERROR
680.
<Ecn> 0 OPEN
681.
1 WEP
682.
2 WPA_PSK
683.
3 WPA2_PSK
684.
4 WPA_WPA2_PSK
685.
<Ssid> string parameter, the access point name
686.
<Rssi> signal strength
687.
<Mode> 0: manually connect 1: An automatic connection
688.
*/
689.
return
ERR_OK;
690.
}
else
if
(UTIL1_strncmp((
char
*)cmd,
"ESP connectAP "
, sizeof(
"ESP connectAP "
)-
1
)==
0
) {
691.
*handled = TRUE;
692.
p = cmd+sizeof(
"ESP connectAP "
)-
1
;
693.
ssid[
0
] =
'\0'
; pwd[
0
] =
'\0'
;
694.
res = UTIL1_ScanDoubleQuotedString(&p, ssid, sizeof(ssid));
695.
if
(res==ERR_OK && *p!=
'\0'
&& *p==
','
) {
696.
p++;
/* skip comma */
697.
res = UTIL1_ScanDoubleQuotedString(&p, pwd, sizeof(pwd));
698.
}
else
{
699.
CLS1_SendStr(
"Comma expected between strings!\r\n"
, io->stdErr);
700.
res = ERR_FAILED;
701.
}
702.
if
(res==ERR_OK) {
703.
res = ESP_JoinAP(ssid, pwd,
3
, io);
704.
}
else
{
705.
CLS1_SendStr(
"Wrong command format!\r\n"
, io->stdErr);
706.
res = ERR_FAILED;
707.
}
708.
}
else
if
(UTIL1_strcmp((
char
*)cmd,
"ESP server start"
)==
0
) {
709.
*handled = TRUE;
710.
res = ESP_StartWebServer(io);
711.
ESP_WebServerIsOn = res==ERR_OK;
712.
}
else
if
(UTIL1_strcmp((
char
*)cmd,
"ESP server stop"
)==
0
) {
713.
*handled = TRUE;
714.
ESP_WebServerIsOn = FALSE;
715.
}
else
if
(UTIL1_strcmp((
char
*)cmd,
"ESP restart"
)==
0
) {
716.
*handled = TRUE;
717.
ESP_Restart(io,
2000
);
718.
}
719.
return
res;
720.
}
721.
722.
void
ESP_Deinit(
void
) {
723.
/* nothing to do */
724.
}
725.
726.
void
ESP_Init(
void
) {
727.
AS2_ClearRxBuf();
/* clear buffer */
728.
}
Интерфейс приложения в Application.h довольно короткий :-):
01.
/*
02.
* Application.h
03.
*
04.
* Author: Erich Styger
05.
*/
06.
07.
#ifndef APPLICATION_H_
08.
#define APPLICATION_H_
09.
10.
/*!
11.
* \brief Application main routine
12.
*/
13.
void
APP_Run(
void
);
14.
15.
#endif
/* APPLICATION_H_ */
The main loop of the application is Application.c, along with the application specific web server code.
As the SendWebPage function contains HTML code, I’m posting it here separately:
01.
static
uint8_t SendWebPage(uint8_t ch_id, bool ledIsOn, uint8_t temperature,
const
CLS1_StdIOType *io) {
02.
static
uint8_t http[
1024
];
03.
uint8_t cmd[
24
], rxBuf[
48
], expected[
48
];
04.
uint8_t buf[
16
];
05.
uint8_t res = ERR_OK;
06.
07.
/* construct web page content */
08.
UTIL1_strcpy(http, sizeof(http), (uint8_t*)
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"
);
09.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<html>\r\n<body>\r\n"
);
10.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<title>ESP8266 Web Server</title>\r\n"
);
11.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<h2>Web Server using ESP8266</h2>\r\n"
);
12.
UTIL1_strcat(http, sizeof(http), (uint8_t*)"
13.
<hr>\r\n");
14.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<p><form method=\"POST\"><strong>Temp: <input type=\"text\" size=2 value=\""
);
15.
UTIL1_strcatNum8s(http, sizeof(http), temperature);
16.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"\"> <sup>O</sup>C"
);
17.
if
(ledIsOn) {
18.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<p><input type=\"radio\" name=\"radio\" value=\"0\" >Red LED off"
);
19.
UTIL1_strcat(http, sizeof(http), (uint8_t*)"
20.
<input type=\
"radio\" name=\"radio\" value=\"1\" checked>Red LED on"
);
21.
}
else
{
22.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<p><input type=\"radio\" name=\"radio\" value=\"0\" checked>Red LED off"
);
23.
UTIL1_strcat(http, sizeof(http), (uint8_t*)"
24.
<input type=\
"radio\" name=\"radio\" value=\"1\" >Red LED on"
);
25.
}
26.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"</strong><p><input type=\"submit\"></form></span>"
);
27.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"</body>\r\n</html>\r\n"
);
28.
29.
UTIL1_strcpy(cmd, sizeof(cmd),
"AT+CIPSEND="
);
/* parameters are <ch_id>,<size> */
30.
UTIL1_strcatNum8u(cmd, sizeof(cmd), ch_id);
31.
UTIL1_chcat(cmd, sizeof(cmd),
','
);
32.
UTIL1_strcatNum16u(cmd, sizeof(cmd), UTIL1_strlen(http));
33.
UTIL1_strcpy(expected, sizeof(expected), cmd);
/* we expect the echo of our command */
34.
UTIL1_strcat(expected, sizeof(expected),
"\r\r\n> "
);
/* expect "> " */
35.
UTIL1_strcat(cmd, sizeof(cmd),
"\r\n"
);
36.
res = ESP_SendATCommand(cmd, rxBuf, sizeof(rxBuf), expected, ESP_DEFAULT_TIMEOUT_MS, io);
37.
if
(res!=ERR_OK) {
38.
if
(io!=NULL) {
39.
CLS1_SendStr(
"INFO: TIMEOUT, closing connection!\r\n"
, io->stdOut);
40.
}
41.
}
else
{
42.
if
(io!=NULL) {
43.
CLS1_SendStr(
"INFO: Sending http page...\r\n"
, io->stdOut);
44.
}
45.
UTIL1_strcat(http, sizeof(http),
"\r\n\r\n"
);
/* need to add this to end the command! */
46.
res = ESP_SendATCommand(http, NULL,
0
, NULL, ESP_DEFAULT_TIMEOUT_MS, io);
47.
if
(res!=ERR_OK) {
48.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
49.
}
else
{
50.
for
(;;) {
/* breaks */
51.
res = ESP_ReadCharsUntil(buf, sizeof(buf),
'\n'
,
1000
);
52.
if
(res==ERR_OK) {
/* line read */
53.
if
(io!=NULL) {
54.
CLS1_SendStr(buf, io->stdOut);
/* copy on console */
55.
}
56.
}
57.
if
(UTIL1_strncmp(buf,
"SEND OK\r\n"
, sizeof(
"SEND OK\r\n"
)-
1
)==
0
) {
/* ok from module */
58.
break
;
59.
}
60.
}
61.
}
62.
}
63.
return
res;
64.
}
The rest of Application.c is rather simple:
01.
/*
02.
* Application.c
03.
*
04.
* Author: Erich Styger
05.
*/
06.
#include
"PE_Types.h"
07.
#include
"CLS1.h"
08.
#include
"WAIT1.h"
09.
#include
"Shell.h"
10.
#include
"UTIL1.h"
11.
#include
"ESP8266.h"
12.
#include
"LEDR.h"
13.
#include
"LEDG.h"
14.
#include
"AS2.h"
15.
16.
static
uint8_t APP_EspMsgBuf[
512
];
/* buffer for messages from ESP8266 */
17.
18.
static
void
WebProcess(
void
) {
19.
uint8_t res=ERR_OK;
20.
bool isGet;
21.
uint8_t ch_id=
0
;
22.
uint16_t size=
0
;
23.
const
uint8_t *p;
24.
const
CLS1_StdIOType *io;
25.
26.
if
(ESP_IsServerOn()) {
27.
io = CLS1_GetStdio();
28.
res = ESP_GetIPD(APP_EspMsgBuf, sizeof(APP_EspMsgBuf), &ch_id, &size, &isGet,
1000
, io);
29.
if
(res==ERR_OK) {
30.
if
(isGet) {
/* GET: put web page */
31.
res = SendWebPage(ch_id, LEDR_Get()!=FALSE,
21
/*dummy temperature*/
, io);
32.
if
(res!=ERR_OK && io!=NULL) {
33.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
34.
}
35.
}
else
{
/* POST: received info */
36.
int
pos;
37.
38.
pos = UTIL1_strFind(APP_EspMsgBuf,
"radio="
);
39.
if
(pos!=-
1
) {
/* found */
40.
if
(UTIL1_strncmp(&APP_EspMsgBuf[pos],
"radio=0"
, sizeof(
"radio=0"
)-
1
)) {
41.
LEDR_On();
42.
}
else
if
(UTIL1_strncmp(&APP_EspMsgBuf[pos],
"radio=1"
, sizeof(
"radio=1"
)-
1
)) {
43.
LEDR_Off();
44.
}
45.
}
46.
res = SendWebPage(ch_id, LEDR_Get()!=FALSE,
20
/*dummy temperature*/
, io);
47.
if
(res!=ERR_OK && io!=NULL) {
48.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
49.
}
50.
}
51.
CLS1_SendStr(
"INFO: Closing connection...\r\n"
, io->stdOut);
52.
res = ESP_CloseConnection(ch_id, io, ESP_DEFAULT_TIMEOUT_MS);
53.
}
54.
}
else
{
/* copy messages we receive to console */
55.
while
(AS2_GetCharsInRxBuf()>
0
) {
56.
uint8_t ch;
57.
58.
(
void
)AS2_RecvChar(&ch);
59.
CLS1_SendChar(ch);
60.
}
61.
}
62.
}
63.
64.
void
APP_Run(
void
) {
65.
CLS1_ConstStdIOType *io;
66.
67.
WAIT1_Waitms(
1000
);
/* wait after power-on */
68.
ESP_Init();
69.
SHELL_Init();
70.
io = CLS1_GetStdio();
71.
CLS1_SendStr(
"\r\n------------------------------------------\r\n"
, io->stdOut);
72.
CLS1_SendStr(
"ESP8266 with FRDM-KL25Z\r\n"
, io->stdOut);
73.
CLS1_SendStr(
"------------------------------------------\r\n"
, io->stdOut);
74.
CLS1_PrintPrompt(io);
75.
for
(;;) {
76.
WebProcess();
77.
SHELL_Parse();
78.
WAIT1_Waitms(
10
);
79.
LEDG_Neg();
80.
}
81.
}
In main.c I call the application part:
01.
/* ###################################################################
02.
** Filename : main.c
03.
** Project : FRDM-KL25Z_ESP8266
04.
** Processor : MKL25Z128VLK4
05.
** Version : Driver 01.01
06.
** Compiler : GNU C Compiler
07.
** Date/Time : 2014-10-15, 14:28, # CodeGen: 0
08.
** Abstract :
09.
** Main module.
10.
** This module contains user's application code.
11.
** Settings :
12.
** Contents :
13.
** No public methods
14.
**
15.
** ###################################################################*/
16.
/*!
17.
** @file main.c
18.
** @version 01.01
19.
** @brief
20.
** Main module.
21.
** This module contains user's application code.
22.
*/
23.
/*!
24.
** @addtogroup main_module main module documentation
25.
** @{
26.
*/
27.
/* MODULE main */
28.
29.
/* Including needed modules to compile this module/procedure */
30.
#include
"Cpu.h"
31.
#include
"Events.h"
32.
#include
"WAIT1.h"
33.
#include
"UTIL1.h"
34.
#include
"AS1.h"
35.
#include
"ASerialLdd1.h"
36.
#include
"CLS1.h"
37.
#include
"CS1.h"
38.
#include
"AS2.h"
39.
#include
"ASerialLdd2.h"
40.
#include
"LEDR.h"
41.
#include
"LEDpin1.h"
42.
#include
"BitIoLdd1.h"
43.
#include
"LEDG.h"
44.
#include
"LEDpin2.h"
45.
#include
"BitIoLdd2.h"
46.
#include
"LEDB.h"
47.
#include
"LEDpin3.h"
48.
#include
"BitIoLdd3.h"
49.
/* Including shared modules, which are used for whole project */
50.
#include
"PE_Types.h"
51.
#include
"PE_Error.h"
52.
#include
"PE_Const.h"
53.
#include
"IO_Map.h"
54.
/* User includes (#include below this line is not maintained by Processor Expert) */
55.
#include
"Application.h"
56.
57.
/*lint -save -e970 Disable MISRA rule (6.3) checking. */
58.
int
main(
void
)
59.
/*lint -restore Enable MISRA rule (6.3) checking. */
60.
{
61.
/* Write your local variable definition here */
62.
63.
/*** Processor Expert internal initialization. DON'T REMOVE THIS CODE!!! ***/
64.
PE_low_level_init();
65.
/*** End of Processor Expert internal initialization. ***/
66.
67.
APP_Run();
68.
69.
/*** Don't write any code pass this line, or it will be deleted during code generation. ***/
70.
/*** RTOS startup code. Macro PEX_RTOS_START is defined by the RTOS component. DON'T MODIFY THIS CODE!!! ***/
71.
#ifdef PEX_RTOS_START
72.
PEX_RTOS_START();
/* Startup of the selected RTOS. Macro is defined by the RTOS component. */
73.
#endif
74.
/*** End of RTOS startup code. ***/
75.
/*** Processor Expert end of main routine. DON'T MODIFY THIS CODE!!! ***/
76.
for
(;;){}
77.
/*** Processor Expert end of main routine. DON'T WRITE CODE BELOW!!! ***/
78.
}
/*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/
79.
80.
/* END main */
81.
/*!
82.
** @}
83.
*/
84.
/*
85.
** ###################################################################
86.
**
87.
** This file was created by Processor Expert 10.4 [05.10]
88.
** for the Freescale Kinetis series of microcontrollers.
89.
**
90.
** ###################################################################
91.
*/
Processor Expert Components
In addition, I’m using several Processor Expert component which are available fromSourceForge.
- Wait: Busy waiting component, e.g. to wait for a few milliseconds.
- Utility: string manipulation and utility functions.
- AsynchroSerial (AS1): serial interface to the host for the shell command line interface
- Shell: command line shell implementation
- CriticalSection: for creating critical sections
- AsynchroSerial (AS2): serial interface to the ESP8266 module
- LEDR, LEDG and LEDB: Red, Green and Blue LED on the FRDM-KL25Z board
AS1 is configured as UART connection (over OpenSDA) for the shell:
There are no special settings for the Shell component:
Important are the correct settings to the ESP8266 UART: 115200 baud and using the correct pins on the board connected to the Rx and Tx lines of the ESP8266. I’m using rather large input and output buffers:
The LED components are configured for the pins used on the board: PTB18 for red, PTB19 for green and PTD1 for blue LED.
Sending Commands
The shell implements the command ESP send which I can use to send a string or command to the module:
ESP send <str>
Note that for every command a trailing “\r\n” will be sent.
So instead of using the programmatic way, the shell can be used to ‘manually’ drive a web server, at least most of the part. So I’m using command line commands below to explore how the ESP8266 module works.
Using the Shell
With the project (link to GitHub below), I have a serial connection and command line shell interface to the module. Compile the project and download it to the FRDM-KL25Z board and use a terminal program (I use Termite) to talk with the module.
It power-up, the program shows a greeting message:
With ‘help‘ I get a list of the available commands:
The ‘status‘ command gives a system status:
With this, I’m ready to send commands to the module :-).
Connection Test
To test the connection I send a simple ‘AT’ command
ESP send AT
and the module should respond with
AT\r\r\n\r\nOK\r\n
Module Restart
Sometimes the module gets stuck. What helps is a power-on reset of the module. Another way is to send the
AT+RST
command to reset the module. The module will boot up and print a ‘ready’ message:
Access Point or Device
First I need to configure if the ESP is either a device or an access point. For this, theCWMODE command is used:
AT+CWMODE=
where <mode> is one of:
- 1: ‘Sta’, ESP8266 is a device, it connects to an existing access point
- 2: ‘AP’, ESP8266 is an access point, so other devices can connect to it
- 3: ‘both’. Not really clear to me, but it seems that in this mode the device is in a hybrid mode?
To have the ESP as device so it can connect to an existing access point I use
AT+CWMODE=1
and the module should answer with
AT+CWMODE=1\r\r\n\r\nOK\r\n
or with a ‘no change’:
AT+CWMODE=1\r\r\nno change\r\n
With
AT+CWMODE?
I can ask for the current mode:
List of Access Points
With
AT+CWLAP
I get a list of access points. It reports a list like this:
AT+CWLAP +CWLAP:(0,"",0) +CWLAP:(4,"APforESP",-39) +CWLAP:(4,"iza-97497",-94) OK
❗ I experienced problems with that command in an environment with lots of access points visible. In this case it seems the module hands up. Try first in a place with only a few access points.
For this tutorial I have configured an access point with SSID “APforESP” which shows up in my list.
The list is formatted like this
+ CWLAP: , , [, ]
With following encoding:
<ecn>:
- 0: OPEN
- 1: WPA_PSK
- 2: WPA2_PSK
- 4: WPA_WPA2_PSK
<ssid>: the SSID (string) of the access point.
<rssi>: Signal strength.
<mode>:
- 0: manually connect
- 1: automatic connect
Connecting to Access Point
To connect to an access point I use the command
AT+CWJAP="<ssid>","<pwd>"
Of course replace and with your setup. The module should report back an “OK” message, and you are connected :-).
❗ The module stores the ssid and password. After power-up, the module will automatically reconnect to the Access Point.
IP Address
Once connected I can check the IP address I have been assigned to with
AT+CIFSR
which should give something like
AT+CIFSR 192.168.0.111
So now I know my module IP address :-). With this I can ping my module:
Building a Web Server
Now as we hav a connection, it is time to use it to run a web server :-).What I want to serve a web page which I can use to turn on or off the LEDs on the board.
Number of Connections: CIPMUX
Before I start the server I need to make sure it accepts multiple connections. For this I use the following command:
AT+CIPMUX=1
The parameter is either 0 (single connection), or 1 (multiple connections). For a web server I need to set it up for multiple connections.
The ESP module should respond with
AT+CIPMUX=1\r\n\r\nOK\r\n
:info: To make it clear, I have included the ‘\r’ and ‘\n’ in the responses.
Starting the Server: CIPSERVER
I start the server with
AT+CIPSERVER=1,80
The first parameter is either 0 (close connection) or 1 (open connection), followed by the port. I use here the standard http port (80).
The module should answer with:
AT+CIPSERVER=1,80\r\r\n\r\nOK\r\n
or if it is already running the server with a ‘no change’:
AT+CIPSERVER=1,80\r\r\nno change\r\n
No I have a connection open on my IP address (see above: 192.168.0.111), listening to the port I have specified (80).
Connecting to the Server with Browser
I enter the IP address in a web browser:
http://192.168.0.111:80
For clarity I have specified the standard HTTP port (80). So if you are using a different port, make sure you specify it in the address line.
The browser now sends a GET request to the module, and I will see this from the message printed out from the module:
The ‘Link’ indicates that it has established a link. IPD (IP Data?) is followed by the channel number (this will the one we have to respond to), plus the size of the following data (296 bytes in that case).
As I’m not responding (yet), there will be a timeout (after about 1 minute or so), with an ‘Unlink’ message from the module:
Link +IPD,0,296:GET / HTTP/1.1 Host: 192.168.0.111 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: de,en-US;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive OK Unlink
Sending Data to Server: CIPSEND
Now I need to respond and send data to the browser. For this I need to know the channel number, and this is provided in the IPD message from above, right after the comma:
+IPD,0
To send data, I use the command
AT+CIPSEND=<channel>,<size>
So I connect again with the browser, and I send 5 bytes (“hello”) with:
AT+CIPSEND=0,5
The ESP8266 responds with
AT+CIPSEND=0,5\r\n>
Notice the ‘>’ at the end: this is my signal to send the actual data (“hello” in my case):
hello
The ESP8266 now resonds with a SEND OK:
However, the browser is still busy and spins around. I already thought that I did something wrong, but after the browser run into a timeout (after about one minute), my data is there! ?
Closing Connection: CIPCLOSE
So things *are* working :-). The trick is that I have to close the connection after I have sent the data. There is a CIPCLOSE command I can use:
AT+CIPCLOSE=<channel>
which I can use to close a channel. So I close the connection with
AT+CIPCLOSE=0
and now the browser shows the content right away :-).
Web Server Implementation
So far I have used the module in command line and manual mode. This is great for exploration of the protocol, but for building the web server I need to do this programmatically. For this I run my ‘main’ loop in APP_Run(). After printing a greeting message and initializing the sub modules, it processes the web/module responses, parses the shell command line interfaces and blinks the green (LEDG) LED.
01.
void
APP_Run(
void
) {
02.
CLS1_ConstStdIOType *io;
03.
04.
WAIT1_Waitms(
1000
);
/* wait after power-on */
05.
ESP_Init();
06.
SHELL_Init();
07.
io = CLS1_GetStdio();
08.
CLS1_SendStr(
"\r\n------------------------------------------\r\n"
, io->stdOut);
09.
CLS1_SendStr(
"ESP8266 with FRDM-KL25Z\r\n"
, io->stdOut);
10.
CLS1_SendStr(
"------------------------------------------\r\n"
, io->stdOut);
11.
CLS1_PrintPrompt(io);
12.
for
(;;) {
13.
WebProcess();
14.
SHELL_Parse();
15.
WAIT1_Waitms(
10
);
16.
LEDG_Neg();
17.
}
18.
}
With
ESP server start
I start the web server:
It sends the AT+CIPMUX command followed by the AT+CIPSERVER to start the server, and then listens to the port. Reading and responding messages is done in WebProcess():
01.
static
void
WebProcess(
void
) {
02.
uint8_t res=ERR_OK;
03.
bool isGet;
04.
uint8_t ch_id=
0
;
05.
uint16_t size=
0
;
06.
const
uint8_t *p;
07.
const
CLS1_StdIOType *io;
08.
09.
if
(ESP_IsServerOn()) {
10.
io = CLS1_GetStdio();
11.
res = ESP_GetIPD(APP_EspMsgBuf, sizeof(APP_EspMsgBuf), &ch_id, &size, &isGet,
1000
, io);
12.
if
(res==ERR_OK) {
13.
if
(isGet) {
/* GET: put web page */
14.
res = SendWebPage(ch_id, LEDR_Get()!=FALSE,
21
/*dummy temperature*/
, io);
15.
if
(res!=ERR_OK && io!=NULL) {
16.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
17.
}
18.
}
else
{
/* POST: received info */
19.
int
pos;
20.
21.
pos = UTIL1_strFind(APP_EspMsgBuf,
"radio="
);
22.
if
(pos!=-
1
) {
/* found */
23.
if
(UTIL1_strncmp(&APP_EspMsgBuf[pos],
"radio=0"
, sizeof(
"radio=0"
)-
1
)) {
24.
LEDR_On();
25.
}
else
if
(UTIL1_strncmp(&APP_EspMsgBuf[pos],
"radio=1"
, sizeof(
"radio=1"
)-
1
)) {
26.
LEDR_Off();
27.
}
28.
}
29.
res = SendWebPage(ch_id, LEDR_Get()!=FALSE,
20
/*dummy temperature*/
, io);
30.
if
(res!=ERR_OK && io!=NULL) {
31.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
32.
}
33.
}
34.
CLS1_SendStr(
"INFO: Closing connection...\r\n"
, io->stdOut);
35.
res = ESP_CloseConnection(ch_id, io, ESP_DEFAULT_TIMEOUT_MS);
36.
}
37.
}
else
{
/* copy messages we receive to console */
38.
while
(AS2_GetCharsInRxBuf()>
0
) {
39.
uint8_t ch;
40.
41.
(
void
)AS2_RecvChar(&ch);
42.
CLS1_SendChar(ch);
43.
}
44.
}
45.
}
If the server is not enabled, it simply copies the received messages to the console:
1.
}
else
{
/* copy messages we receive to console */
2.
while
(AS2_GetCharsInRxBuf()>
0
) {
3.
uint8_t ch;
4.
5.
(
void
)AS2_RecvChar(&ch);
6.
CLS1_SendChar(ch);
7.
}
8.
}
Otherwise it scans for an IPD message (ESP_GetIPD()). This function returns the whole message, the channel, the message size and if it is a GET or POST message:
1
|
res = ESP_GetIPD(APP_EspMsgBuf, sizeof(APP_EspMsgBuf), &ch_id, &size, &isGet, 1000, io);
|
If it is a GET message, then it sends a HTML page to the module:
1
|
res = SendWebPage(ch_id, LEDR_Get()!=FALSE, 21 /*dummy temperature*/, io);
|
This web page shows the status of the red LED on the board, a (dummy) temperature value and a button to submit new LED values:
The HTML code for this page is constructed in SendWebPage() and sent withAT+CIPSEND:
01.
static
uint8_t SendWebPage(uint8_t ch_id, bool ledIsOn, uint8_t temperature,
const
CLS1_StdIOType *io) {
02.
static
uint8_t http[
1024
];
03.
uint8_t cmd[
24
], rxBuf[
48
], expected[
48
];
04.
uint8_t buf[
16
];
05.
uint8_t res = ERR_OK;
06.
07.
/* construct web page content */
08.
UTIL1_strcpy(http, sizeof(http), (uint8_t*)
"HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nPragma: no-cache\r\n\r\n"
);
09.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<html>\r\n<body>\r\n"
);
10.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<title>ESP8266 Web Server</title>\r\n"
);
11.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<h2>Web Server using ESP8266</h2>\r\n"
);
12.
UTIL1_strcat(http, sizeof(http), (uint8_t*)"
13.
<hr>\r\n");
14.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<p><form method=\"POST\"><strong>Temp: <input type=\"text\" size=2 value=\""
);
15.
UTIL1_strcatNum8s(http, sizeof(http), temperature);
16.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"\"> <sup>O</sup>C"
);
17.
if
(ledIsOn) {
18.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<p><input type=\"radio\" name=\"radio\" value=\"0\" >Red LED off"
);
19.
UTIL1_strcat(http, sizeof(http), (uint8_t*)"
20.
<input type=\
"radio\" name=\"radio\" value=\"1\" checked>Red LED on"
);
21.
}
else
{
22.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"<p><input type=\"radio\" name=\"radio\" value=\"0\" checked>Red LED off"
);
23.
UTIL1_strcat(http, sizeof(http), (uint8_t*)"
24.
<input type=\
"radio\" name=\"radio\" value=\"1\" >Red LED on"
);
25.
}
26.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"</strong><p><input type=\"submit\"></form></span>"
);
27.
UTIL1_strcat(http, sizeof(http), (uint8_t*)
"</body>\r\n</html>\r\n"
);
28.
29.
UTIL1_strcpy(cmd, sizeof(cmd),
"AT+CIPSEND="
);
/* parameters are <ch_id>,<size> */
30.
UTIL1_strcatNum8u(cmd, sizeof(cmd), ch_id);
31.
UTIL1_chcat(cmd, sizeof(cmd),
','
);
32.
UTIL1_strcatNum16u(cmd, sizeof(cmd), UTIL1_strlen(http));
33.
UTIL1_strcpy(expected, sizeof(expected), cmd);
/* we expect the echo of our command */
34.
UTIL1_strcat(expected, sizeof(expected),
"\r\r\n> "
);
/* expect "> " */
35.
UTIL1_strcat(cmd, sizeof(cmd),
"\r\n"
);
36.
res = ESP_SendATCommand(cmd, rxBuf, sizeof(rxBuf), expected, ESP_DEFAULT_TIMEOUT_MS, io);
37.
if
(res!=ERR_OK) {
38.
if
(io!=NULL) {
39.
CLS1_SendStr(
"INFO: TIMEOUT, closing connection!\r\n"
, io->stdOut);
40.
}
41.
}
else
{
42.
if
(io!=NULL) {
43.
CLS1_SendStr(
"INFO: Sending http page...\r\n"
, io->stdOut);
44.
}
45.
UTIL1_strcat(http, sizeof(http),
"\r\n\r\n"
);
/* need to add this to end the command! */
46.
res = ESP_SendATCommand(http, NULL,
0
, NULL, ESP_DEFAULT_TIMEOUT_MS, io);
47.
if
(res!=ERR_OK) {
48.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
49.
}
else
{
50.
for
(;;) {
/* breaks */
51.
res = ESP_ReadCharsUntil(buf, sizeof(buf),
'\n'
,
1000
);
52.
if
(res==ERR_OK) {
/* line read */
53.
if
(io!=NULL) {
54.
CLS1_SendStr(buf, io->stdOut);
/* copy on console */
55.
}
56.
}
57.
if
(UTIL1_strncmp(buf,
"SEND OK\r\n"
, sizeof(
"SEND OK\r\n"
)-
1
)==
0
) {
/* ok from module */
58.
break
;
59.
}
60.
}
61.
}
62.
}
63.
return
res;
64.
}
In case of a POST message (user has pressed the button), I scan for the radio element string and turn on/off the LED accordingly, and re-submit the new web page:
01.
}
else
{
/* POST: received info */
02.
int
pos;
03.
04.
pos = UTIL1_strFind(APP_EspMsgBuf,
"radio="
);
05.
if
(pos!=-
1
) {
/* found */
06.
if
(UTIL1_strncmp(&APP_EspMsgBuf[pos],
"radio=0"
, sizeof(
"radio=0"
)-
1
)) {
07.
LEDR_On();
08.
}
else
if
(UTIL1_strncmp(&APP_EspMsgBuf[pos],
"radio=1"
, sizeof(
"radio=1"
)-
1
)) {
09.
LEDR_Off();
10.
}
11.
}
12.
res = SendWebPage(ch_id, LEDR_Get()!=FALSE,
20
/*dummy temperature*/
, io);
13.
if
(res!=ERR_OK && io!=NULL) {
14.
CLS1_SendStr(
"Sending page failed!\r\n"
, io->stdErr);
/* copy on console */
15.
}
16.
}
Finally, it closes the connection at the end:
1.
CLS1_SendStr(
"INFO: Closing connection...\r\n"
, io->stdOut);
2.
res = ESP_CloseConnection(ch_id, io, ESP_DEFAULT_TIMEOUT_MS);
With this, I handle GET and POST messages and can toggle the LED on my board ? :-).
Summary
It is amazing what is possible with this tiny and inexpensive ($4.50) WiFi module. The simple AT interface allows small and tiny microprocesors to connect to the internet or the local network. With all the hype around ‘Internet of Things’ this is where things very likely will end up: small nodes connecting in an easy way to the network. The processor on that ESP8266 is probably more powerful than the KL25Z (the specs and data sheets of that ESP8266 are still evolving). Or it is possible to run that module in standalone mode too which is a very interesting approach too, see the links at the end of this article. But still having an UART way to connect to the network is very useful and powerful. Other modules costs multiple times more. I expect that many vendors will come up with similar integrated modules e.g. to combine an ARM processor with the WiFi radio, similar that ESP8266 module. For sure that ESP8266 has a head start and paved the way how WiFi connectivity should work. We all will see what the future brings. Until then, that ESP8266 module is something I can use in many projects :-).
The sources and project files can be found on GitHub:
https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-KL25Z/FRDM-KL25Z_ESP8266
Happy Web-Serving ?
Useful Links:
- http://www.electrodragon.com/w/Wi07c
- http://scargill.wordpress.com/category/esp8266/
- https://github.com/esp8266/esp8266-webserver
- http://www.cse.dmu.ac.uk/~sexton/ESP8266/
- http://defcon-cc.dyndns.org/wiki/ESP8266#Update
- http://www.xess.com/blog/esp8266-resources/