Arduino からの ESP8266 Wi-Fi モジュールの基本操作
時 エスプレッシフ 最初のモジュールを市場に投入 無線LAN 統合された ESP8266 Y·エル ファームウェア AT コマンドを使用してそれを処理する方法について、私たちユーザーが興味を持っていたのは、それをアセンブリに統合することでした。 マイクロコントローラー そして問題は(かつての)闇を知ることに帰着した ESP8266 ATコマンドテーブル、栄養補給のニーズ、または ESP8266ファームウェアアップデート.
その後、プログラムを作成するための代替手段がすぐに登場しました。 ESP8266 およびモジュールの実装 無線LAN 非常に異なるフォーマットであるため、別の懸念が生じました。 どの ESP8266 Wi-Fi モジュールを選択するか さまざまなアンテナ (外部アンテナを含む) の範囲、またはアセンブリへのこれらの新しいモジュールの物理的な統合に応じて異なります。
確かに、これらすべての変化により、最も基本的な側面、つまり企業の最も基本的な管理に重点が置かれていない可能性があります。 ESP8266 Wi-Fi モジュール。 でも 極性.es の使用に関する情報を見つけることができます。 ESP8266 の動作を一般的な方法で説明することを目的としたアプリケーションがいくつかあります。 ESP8266 Wi-Fi モジュール 特に記事での AT コマンドの使用 ESP8266 Wi-Fi モジュールを使用して Arduino から HTTP クエリを作成するライブラリ、読者の感想によると、 ESP8266 独自の実装を実行します。
を操作するための基本的な操作について話し合います。 ESP8266 そして、一般的な解決策を提案することは、いくつかの非常に異なる部分からなる目的です。記事の内容を理解するために、次の索引がガイドとして役立ちます。
- シリアルポートを介してコンピュータからESP8266 Wi-Fiモジュールを制御します
- esptoolでファームウェアを更新する
- モジュールに命令を送信する
- ESP8266からデータを受信する
- コンテンツ内のテキストを検索して応答を分析する
- 応答を受信するまでの待ち時間を制限する
- 複数のATコマンドで定義された複雑なオペレーションを実行する
シリアルポートを介してコンピュータからESP8266 Wi-Fiモジュールを制御します
お皿から Arduinoの そしてあなたの IDE の動作を監視することが可能です。 ESP8266 Wi-Fi モジュール、送信します ESP8266 AT コマンド 答えを見てください。ただし、ターミナル タイプのアプリケーションを使用してコンピュータから実行する方がはるかに便利です。
どの板かによる Arduinoの 使用すると、ハードウェア シリアル ポートが XNUMX つしか使用できない場合があり、送受信に少し不便が加わります。コンピュータや一部のマザーボードからのシリアル通信アプリケーションでは、通信速度の変更がより快適になります。 Arduinoの (および状況によっては) シリアル通信の高速度、特に最新バージョンのデフォルト速度である 115200 ボーはサポートされていません。 ファームウェア.
上の 監視に使用するプログラム ESP8266 シリアルポートを使用する、ニーズや好みに応じて選択できるものがたくさんあります。最近はクラシックをよく使うようになりました キュートコム (上のスクリーンショットにあるもの) 特定のことを繰り返すのが私にとって非常に快適だからです。 ESP8266 Wi-Fi モジュール AT の注文 プロジェクトのテスト中。
シリアル コンソールとして機能するプログラムについては、いくつかの推奨事項がここですでに示されています。たとえば、次のような話をするとき、 コンピューターから UART シリアル デバイスを制御するための PuTTY. PUTTY優れたアプリケーションであることに加えて、ほとんどのデスクトップ オペレーティング システムで利用できます。さらに、 PUTTY シリアル ポートと インターネット プロトコル ファミリ (TCP/IP)、上で動作するものを含む TLSは、設定とその使用に慣れるまでに費やした (わずかな) 時間を十分に返済してくれる一般的なツールになります。
シリアル通信ソフトウェアに加えて、 接続する ESP8266 Wi-Fi モジュール ポートへ USB パソコンにもコンバーターが必要です USB シリーズへ TTL。ソフトウェアの場合と同様に、いくつかのバージョンがあり、それらはポートを変換するためにのみ使用されます。 USB シリアルポート上で TTL (XNUMX ユーロから入手可能) から、さまざまなプロトコルをエミュレートできるもの (たとえば、 SPI o I2C).
シリアル コンソールとして機能するプログラムと同様に、コンピュータと通信するためのハードウェア USB 論理回路(だけではありません)を使用して、 ESP8266) マイクロ制御アプリケーション開発者の作業では一般的なツールとなるため、できるだけ早くツールボックスに入れて作業する価値があります。 ESP8266 Wi-Fi モジュール 手に入れる絶好のチャンスです。
コンバーター USB a UART TTL を使用する回路の動作を監視するためにも使用できます。 ESP8266これを行うには、監視したい出力を、高速ダイオード ( 1N4148、たとえば)と抵抗(たとえば 2K2)を互いに並列に接続します。このようなセットアップは、ハードウェア シリアル スニファーのように機能します。
上の画像のスニッファーは確かに初歩的なものではありますが (特に、次のような機能はありません) バッファ) でアセンブリの動作を監視するには十分です。 Arduinoの Y·エル ESP8266.
以前のスキームからスニファーを削除すると、 接続方法を示す回路図 ESP8266 Wi-Fi モジュール 皿に Arduinoの。 3V3 での給電に加えて、集積回路のリセット ピンとアクティベーション ピンをハイ レベル (イネーブル) に接続する必要があります。もちろん、一方の RX ピンはもう一方の TX ピンに接続する必要があります。
前の図を簡略化するために、プレートが表されています Arduinoの 3V3 で電力が供給されており、シリアル ポートの電圧も 3V3 であると想定されます。を使用する場合は、 マイクロコントローラー 損傷しないように、シリアル ポートの信号レベル (通常は 5 V) を変更する必要があります。 ESP8266、 使う レベルコンバータ 以下の図のようなものです。この回路は、多くの市販の既製モジュール実装で頻繁に見られます。
ESP8266ファームウェアをアップデートする
ラス ESP8266 AT コマンド、その終了、モジュールのデフォルト速度... のバージョンによって異なります。 ESP8266ファームウェア。すべてのモジュールで同じバージョンを使用し、可能であれば最新バージョンであることを確認することが最善です。
残念ながら、ほとんどの ESP8266 Wi-Fi モジュール モデル 4Mbit しかないため、最新バージョンをインストールできません。インストールできるファームウェアの最新 (公式) バージョン ESP8266 Wi-Fi モジュール 4 Mbit (ほとんど) のバージョンは 0.9.4 で、これにはバージョン 0.2 が含まれます。 ESP8266 AT コマンド.
要約すると、ファームウェアを更新するには以下が必要です。
-
対応するファームウェアバージョンをダウンロードする。 ザ 4 M ビットのメモリを備えたモジュールの最新 (公式) バージョン。Github の Espressif フォルダーにあります。。 で エスプレシフのウェブサイト ファームウェアの最新バージョンをダウンロードできますが、ファームウェアがインストールされているモジュールに十分なメモリがあることを確認することが非常に重要です。
-
最新バージョンのファームウェアインストールツールをダウンロードします。。 私のお気に入りは エスプツール に書かれているのは Python , そのため、どのプラットフォームでも動作します。ダウンロードするだけでなく、次の方法でインストールすることもできます。
pip install esptool
(opip2
opython -m pip
…)。もちろん、 エスプレッシフ 独自のツールも提供していますが、現在は Windows でのみ利用可能です。 -
ダウンロードしたファイルを準備する;それらをアクセス可能なフォルダーに解凍し、必要に応じてツールを実行可能にします。 エスプツール、私の場合、 GNU / Linuxの、と
chmod +x esptool
-
コンバータを使用してモジュールをコンピュータに接続します USB UART TTL 3V3で動作します または、5 V で動作する場合はレベルコンバータを使用します。電源に加えて、コンバータの TX を RX に接続する必要があります。 USB UART TTL、RX から TX、GPIO0 を低レベル (GND) に、おそらく GPIO2 を高レベルにします (私のテストでは、低レベルでの接続と切断の両方で機能しました)。モジュールに GPIO15 接続が空いている場合 (ESP-12 で発生するように)、ロー レベルに接続する必要があります。通常、動作中はハイレベルになるRESETは、録音を開始する前に接続してデバイスをリセットする必要がある場合があるため、未接続のままにするか、抵抗(たとえば10K)を使用してハイレベルに接続することができます。低いレベルまで。
モジュールの電源を入れるとアップデートできるようになりますが、 接続エラーが表示された場合はリセットが必要となります RESET をローレベルで一瞬接続し、その後更新プロセスのためにオンエアのまま (接続せずに) 残します。
モジュールには、 半アンペア消費のピーク (一部のユーザーによると最大 600 mA) そのため、特にファームウェアを更新する場合は、この消費量に対応できる電源を使用することが重要です。 -
ツールを実行してファームウェアを更新する。私の場合、ステップ 3 のツールとファームウェアのドキュメントを同じフォルダーに保存したので、コンソールから実行します。
cd ~/Datos/firmwareESP8266
(ツールとファームウェアを含むフォルダーに変更)./esptool.py --baud 115200 --port /dev/ttyUSB0 write_flash \
0x00000 ./boot_v1.1.bin \
0x01000 ./user1.bin \
0x7C000 ./esp_init_data_default.bin \
0x7E000 ./blank.bin
--baud
の速度を設定します ESP8266 (私の場合は 115200 ボー)--port
接続先のシリアル ポート (私の場合、エミュレートされた最初の USB)。ファームウェアを構成するさまざまなドキュメントが失われますwrite_flash
先頭にアドレスがあり、更新ペイロードを含む user1.bin ドキュメントが続きます。
ESP8266 Wi-Fi モジュールにコマンドを送信します
を制御するには、 ESP8266 コンピュータから始める必要があります アプリを構成する そのためには、①コンバータが接続されているポートを選択するだけで十分です。 USB UART TTL、 何かのようなもの /dev/USB0
GNU/Linux および同様のもの、またはそのようなもの COM6
Windowsの場合、②速度を選択します。 ESP8266、おそらく 115200 ボー、③ パリティまたはハンドシェイクなしで 8 データ ビットと XNUMX ストップ ビットを設定、④ 行末に応じて設定 ファームウェア、ほとんどの場合 CR+LF。
アプリケーションが設定されると (または、必要に応じて保存され、選択されると)、 接続を開く (上記の例のスクリーンショットでは、それぞれ「デバイスを開く」と「開く」 キュートコム y PUTTY)そして、への注文の送信を開始できます ESP8266.
でわかるように、 ESP8266 ATコマンドテーブル、アクティブ化、非アクティブ化、値の設定、およびその参照の形式は非常に予測可能ですが、一般に、それらをすべて覚えるのは簡単ではなく、おそらく参照するには手元に用意する必要があります。
の方法 enviar ATの注文 al ESP8266 Wi-Fi モジュール から Arduinoの とても簡単です: ① 通信を設定します Serial.begin(115200);
(または、複数のハードウェア シリアル ポートを備えたボードでは Serial1、Serial2…)、② 形式を使用してコマンドを送信します。 Serial.print(orden+"\r\n");
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#define PUERTO_SERIE Serial // Objeto serie que corresponde a puerto serie hardware al que está conectado el módulo wifi ESP8266
#define VELOCIDAD_ESP8266 115200 // Velocidad, en baudios, a la que está configurado el ESP8266
#define IDENTIFICADOR_WIFI “polaridad.es” // SSID (Service Set Identifier)
#define CLAVE_WIFI “54lLij1RiTn3MEd3v41C” // Clave del punto de acceso wifi al que se conecta el ESP8266
void setup()
{
PUERTO_SERIE.begin(VELOCIDAD_ESP8266);
PUERTO_SERIE.print
(
“AT+CWJAP=\””+
String(IDENTIFICADOR_WIFI)+
“\”,\””+
String(CLAVE_WIFI)+
“\”\r\n”
);
}
void loop()
{
}
|
上の例は、 ESP8266 Wi-Fi モジュール AT の注文 から Arduinoの。この場合に図示されているのは、 AT+CWJAP
、アクセス ポイントに接続するために使用されます。このコマンドは引数としてアクセス ポイント識別子 (SSID) とキーを両方引用符で囲み、オブジェクトになります。 Srtring
エスケープ コード (\"
)。注文を完了するには、次を使用します \r\n
に相当する CR
y LF
.
シリアル ポートは常に次のように識別されるわけではないことに注意してください。 Serial
(特定のプレートでは、 Serial1
, Serial2
…) 使用されるポート オブジェクトは、マクロに割り当てることによって定義されています。 PUERTO_SERIE
。使用されているボードの種類を検出すると、シリアル ポートの選択に少しのインテリジェンスが追加される可能性があります。後で、タイプを確認する方法について説明します。 Arduinoの。残りの定義は、定数値の繰り返し (および間違い) を避け、変更を容易にするために、定数値に「名前」を付けることができる通常の定義です。
上記の例では、 ESP8266 Wi-Fi モジュール 示されたアクセスポイントに接続していますが、以前にすでに接続されていましたか?接続は機能しましたか?これを知るには、何が起こっているのかを「聞く」必要があります。 ESP8266
ESP8266 Wi-Fiモジュールからデータを受信します
上で説明したデータ スニファをコンピュータに接続すると、どのような情報が得られるかを確認できます。 Arduinoの に送信しました ESP8266 そして彼の反応。から読む Arduinoの その中の情報を処理するには、次の方法で検出する必要があります。 Serial.available()
データが到着したかどうか、到着した場合はそれをロードします Serial.read()
。次の例は、からの応答を読み取る方法を示しています。 AT+CWJAP?
、アクセス ポイントへの接続があるかどうかを報告します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#define PUERTO_ESP8266 Serial // Objeto serie que corresponde a puerto serie hardware al que está conectado el módulo wifi ESP8266
#define VELOCIDAD_ESP8266 115200 // Velocidad, en baudios, a la que está configurado el ESP8266
char letra_recibida;
void setup()
{
PUERTO_ESP8266.begin(VELOCIDAD_ESP8266);
PUERTO_ESP8266.print(“AT+CWJAP?\r\n”);
}
void loop()
{
while(PUERTO_ESP8266.available())
{
letra_recibida=PUERTO_ESP8266.read();
}
}
|
お皿の上のように Arduinoの宇野 (その他の場合でも) シリアル モニターを開くとプログラムがリセットされ、シリアル コンソールで確認するために使用できます。 Arduinoの あなたが送信する情報 ESP8266 以下の画像のスクリーンショットが示すように。
ESP8266 Wi-Fi モジュールから送信された応答を分析する
届く情報を読み取る方法はすでに見てきました。 Arduinoの から ESP8266。あなたが対処しなければならない問題は、いつ到着し始めるか、到着するまでにどれくらい時間がかかるか、どのくらいの長さになるかがわからないことです...そして、担当者からの応答を待つのはあまり効率的ではありません。 ESP8266 をさせずに受信されます マイクロコントローラー その間に他のタスクを実行します。
この状況に対処する簡単な方法は次のとおりです。 受け取ったデータを反復して具体的な答えを探す これにより、たとえば、受信したテキストの検索を続行するかどうか、およびメッセージから到着した情報に基づいてどのようなアクションを実行するかを決定するインジケーター (フラグまたはブール変数) がアクティブになります。 ESP8266. 応答が届くまでの間 マイクロコントローラー 他のタスクに専念できるたとえば、センサーからデータを受信して処理します。
ESP8266から受信した情報内のテキストを検索します。
からのテキストを検索するには、 ESP8266 かもしれない 受け取った各手紙と、探しているメッセージに対応する手紙を比較します。。比較する文字を指すカウンター (またはポインター) を使用する必要があります。から到着したキャラクターの場合、 ESP8266 メッセージ内で検査されているものと同じである場合、カウンタは進み、異なる場合は初期化されます。
最後に到達したことを知るために、検索されたメッセージの次の文字が参照されます。この文字はゼロになります (\0
) または、メッセージの長さが保存されているため、カウンターと比較することで、比較が終了したかどうかを確認できるため、 ESP8266 Wi-Fi モジュール 必要なメッセージを送信しました。
次の例では、次のコマンドを使用します AT+CWLAP
これにより、アクセス ポイントのリストが返され、その中で「wifi Polaridad.es」という名前のものが検索されます。最後の文字がゼロであることを検証することを選択しましたが、 バッファ 検索されたテキストを保存するだけで、その長さはわかっています。また、そのような数の正しい文字を受信したかどうかをチェックすることもできます。とともに LED製品 ピン 2 に接続すると、予期したテキストが見つかったことが報告されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWLAP\r\n” // Buscar los puntos de acceso wifi disponibles. (Dependiendo de la versión del firmware) Las órdenes terminan en CR+LF
#define MENSAJE_BUSCADO “wifi polaridad.es” // Ver si está disponible el punto de acceso llamado “wifi polaridad.es”
#define LONGITUD_MENSAJE 18 // Se necesita guardar, al menos, 17 letras y el terminador \0
#define PIN_LED_ENCONTRADO 2 // Pin al que se conecta el LED que informa de que se ha encontrado el texto (el punto de acceso buscado está disponible)
#include <string.h> // strncpy
boolean esperando=true;
char buffer_mensaje;
char mensaje[LONGITUD_MENSAJE];
byte posicion_mensaje=0;
void setup()
{
pinMode(PIN_LED_ENCONTRADO,OUTPUT);
digitalWrite(PIN_LED_ENCONTRADO,LOW); // Apagar el LED (por ahora no se ha encontrado el mensaje)
strncpy(mensaje,MENSAJE_BUSCADO,LONGITUD_MENSAJE); // sizeof(mensaje)
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (consultar los puntos de acceso disponibles) al módulo wifi ESP8266
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
if(buffer_mensaje==mensaje[posicion_mensaje]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje++; // Pasar a la siguiente letra del mensaje
if(mensaje[posicion_mensaje]==0) // ¿Ha terminado de analizarse todo el mensaje? (la última letra de la cadena de texto es \0)
{
esperando=false; // Si se ha terminado de analizar con éxito todo el mensaje ya no se está esperando
digitalWrite(PIN_LED_ENCONTRADO,HIGH); // Encender el LED para indicar que se ha encontrado el texto buscado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje=0; // …empezar desde la primera letra del texto buscado
}
}
}
}
|
前の例のコードでは、次の方法も見ることができます。 ボードの種類に応じてシリアル ポートを選択します Arduinoの 使用済み。この例では、プロジェクトに XNUMX 種類のボードがあることを前提としています。 Arduinoの宇野、 Arduino メガ 2560 と Arduinoのレオナルド。一緒に作業する場合は、 Arduinoの宇野 それは使われるでしょう Serial
それ以外の場合 Serial1
.
皿を使って作業する場合 Arduinoのレオナルド 同じ方法を使用してプログラムを停止し、コンソール (コンソールに関連付けられたシリアル ポート) を待つことができます。 Serial
) 利用可能です。
1
2
3
4
5
6
7
8
9
10
11
|
#ifdef ARDUINO_AVR_LEONARDO
#define SERIE Serial1
#define ESPERA_CONSOLA while(!Serial){} /* Esperar a la consola */
#else
#define ESPERA_CONSOLA /* Si no es un Arduino Leonardo no hace falta esperar a la consola */
#ifdef ARDUINO_AVR_MEGA2560
#define SERIE Serial1
#else // En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí
#define SERIE Serial
#endif
#endif
|
ESP8266 応答内のさまざまなテキストを検索します
前の例のコードは、送信された情報内のテキストを検索するために使用されます。 ESP8266 ただし、応答には操作に応じて異なる情報が含まれる場合があります。次の例の簡単なケースから始めるとします。 MCU ESP8266 es OK
操作が正しく実行された場合と、 ERROR
それ以外の場合は、注文と同様に AT+CWJAP?
、これは、 ESP8266 Wi-Fi モジュール すでにアクセスポイントに接続されています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWJAP?\r\n” // Verificar que está conectado a un punto de acceso
#define CANTIDAD_MENSAJES 2 // Se distingue entre dos mensajes: acierto y error que se identificarán con true y false
#define MENSAJE_ACIERTO “OK”
#define MENSAJE_ERROR “ERROR”
#define LONGITUD_MENSAJE 6 // Se necesita guardar, al menos, 5 letras y el terminador \0 (Un pequeño desperdicio al hacer una matriz en la que todos los elementos ocupan como el mayor. Es admisible porque son muy pocos elementos y así no se complica este ejemplo inicial)
#define PIN_LED_ACIERTO 2 // Pin al que se conecta el LED que informa del acierto
#define PIN_LED_ERROR 3 // Pin al que se conecta el LED que informa del error
#include <string.h> // strncpy
boolean esperando=true; // Todavía no se ha encontrado el final del mensaje
char buffer_mensaje; // Para almacenar la última letra cargada desde el ESP8266
byte led_estado[CANTIDAD_MENSAJES]; // Un LED (pin) para cada estado
char mensaje[CANTIDAD_MENSAJES][LONGITUD_MENSAJE]; // Mensajes de acierto y error
byte posicion_mensaje[CANTIDAD_MENSAJES]; // Un contador de posición para cada mensaje
void setup()
{
led_estado[true]=PIN_LED_ACIERTO;
led_estado[false]=PIN_LED_ERROR;
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
pinMode(led_estado[numero_mensaje],OUTPUT); // Establecer el pin del LED
digitalWrite(led_estado[numero_mensaje],LOW); // Apagar el LED
posicion_mensaje[numero_mensaje]=0; // Inicializar a cero el número de letra a analizar de cada mensaje
}
strncpy(mensaje[true],MENSAJE_ACIERTO,LONGITUD_MENSAJE); // Preparar el mensaje de acierto
strncpy(mensaje[false],MENSAJE_ERROR,LONGITUD_MENSAJE); // Preparar el mensaje de error
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (verificar si existe conexión a un punto de acceso) al módulo wifi ESP8266
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
if(buffer_mensaje==mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje[numero_mensaje]++; // Pasar a la siguiente letra del mensaje
if(mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]==0) // ¿Ha terminado de analizarse todo el mensaje actual? (la última letra de la cadena de texto es \0)
{
esperando=false; // Si se ha encontrado algún mensaje y ya no se está esperando
digitalWrite(led_estado[numero_mensaje],HIGH); // Encender el LED correspondiente al mensaje encontrado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje[numero_mensaje]=0; // …empezar desde la primera letra del texto buscado
}
}
}
}
}
|
同じメソッドのこの新しい実装では、複数の考えられるメッセージと一致するものを検索するため、メッセージから受信した応答に応じてさまざまなアクションを選択できます。 ESP8266をオンにするだけです。 LED製品 対応しています。
応答を受け取るまでにかかる時間を制限する
これまでのところ、関連する問題については言及されていません。 操作が失敗したとみなされるまでの最大待機時間 (タイムアウト)。何らかの理由で、 ESP8266 Wi-Fi モジュール、アクセス ポイントを備えたモジュール、インターネットを備えたアクセス ポイント、またはたとえば仮想サーバーが利用できない場合、プログラムはある時点でブロックされて無期限に待機する可能性があるため、そのような状況に対する応答を明確にする必要があります。最大待機時間はアプリケーション全体に対して設定できます。通常、その場合はより「寛大」になります。または、操作ごとに個別の待機時間をプログラムすることもできます。
(少なくとも) 一定の時間が経過したことを確認するには 通常、アカウントが開始された瞬間の「時間」が現在の「時間」から減算され、その差が望ましい制限よりも大きいことが検証されます。。この「時間」はリアルタイムである必要はありません。通常は、開始から経過した間隔に対応します。 MCU 時間を数え始めます。重要なのは絶対時間ではなく経過時間であるため、これはプログラムには影響しません。
通常、特定の間隔が経過したかどうかを確認するには、次のタイプの式が使用されます。
1
|
(unsigned long)(millis()–milisegundos_al_empezar)>intervalo_de_tiempo
|
可変 milisegundos_al_empezar
の値が含まれています millis()
実行中の特定の瞬間の時間を計測するため、その名前が「クロノメーター」という言葉を指すことは珍しいことではありません。変数 intervalo_de_tiempo
前の式を true にする最大ミリ秒数が含まれます。つまり、タイムアウトを表します。通常、これは定数 (またはマクロ) であり、前述の場合と同様に、名前に「TIMEOUT」という単語が含まれることがよくあります。非常に短い間隔で作業する場合は、次のように使用できます。 micros()
代わりに millis()
(ミリ秒ではなくマイクロ秒)ただし、一般的ではなく、精度もはるかに低くなります。
1
|
(unsigned long)(millis()–cronometro)>TIMEOUT
|
長整数 Arduinoの (unsigned long
) は 4 バイト (32 ビット) を占めるため、表現できる最大値は 4294967295 (2 から始まるため、32 の XNUMX 乗マイナス XNUMX) になります。皿の上に Arduinoの 継続的に実行している間、ミリ秒カウンターは約 50 日ごとにリセット (ゼロに戻ります) します。符号なしデータ型で減算する場合、同じ動作が再現される (カウンターを反転する) ため、タイムアウトを無期限に制御することが可能です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define ORDEN “AT+CWJAP?\r\n” // Verificar que está conectado a un punto de acceso
#define CANTIDAD_MENSAJES 2 // Se distingue entre dos mensajes: acierto y error que se identificarán con true y false
#define MENSAJE_ACIERTO “OK”
#define MENSAJE_ERROR “ERROR”
#define LONGITUD_MENSAJE 6 // Se necesita guardar, al menos, 5 letras y el terminador \0 (Un pequeño desperdicio al hacer una matriz en la que todos los elementos ocupan como el mayor. Es admisible porque son muy pocos elementos y así no se complica este ejemplo inicial)
#define PIN_LED_ACIERTO 2 // Pin al que se conecta el LED que informa del acierto
#define PIN_LED_ERROR 3 // Pin al que se conecta el LED que informa del error
#define TIMEOUT 5000 // Espera 5 segundos la respuesta del ESP8266 (y su análisis) antes de desistir
#include <string.h> // strncpy
boolean esperando=true; // Todavía no se ha encontrado el final del mensaje
boolean encontrado=false; // Salvo que se encuentre el punto de acceso se considera que la operación ha fracasado
char buffer_mensaje; // Para almacenar la última letra cargada desde el ESP8266
byte led_estado[CANTIDAD_MENSAJES]; // Un LED (pin) para cada estado
char mensaje[CANTIDAD_MENSAJES][LONGITUD_MENSAJE]; // Mensajes de acierto y error
byte posicion_mensaje[CANTIDAD_MENSAJES]; // Un contador de posición para cada mensaje
unsigned long cronometro;
void setup()
{
led_estado[true]=PIN_LED_ACIERTO;
led_estado[false]=PIN_LED_ERROR;
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
pinMode(led_estado[numero_mensaje],OUTPUT); // Establecer el pin del LED
digitalWrite(led_estado[numero_mensaje],LOW); // Apagar el LED
posicion_mensaje[numero_mensaje]=0; // Inicializar a cero el número de letra a analizar de cada mensaje
}
strncpy(mensaje[true],MENSAJE_ACIERTO,LONGITUD_MENSAJE); // Preparar el mensaje de acierto
strncpy(mensaje[false],MENSAJE_ERROR,LONGITUD_MENSAJE); // Preparar el mensaje de error
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
SERIE.print(ORDEN); // Enviar la orden (verificar si existe conexión a un punto de acceso) al módulo wifi ESP8266
cronometro=millis();
}
void loop()
{
if(esperando) // Buscará infinitamente hasta que llegue el texto esperado (y si no existe el punto de acceso nunca llegará ¡Es un ejemplo!)
{
while(SERIE.available()) // Si ha llegado algún dato por el puerto serie…
{
buffer_mensaje=SERIE.read(); // …almacenarlo en el buffer
for(byte numero_mensaje=0;numero_mensaje<CANTIDAD_MENSAJES;numero_mensaje++)
{
if(buffer_mensaje==mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
posicion_mensaje[numero_mensaje]++; // Pasar a la siguiente letra del mensaje
if(mensaje[numero_mensaje][posicion_mensaje[numero_mensaje]]==0) // ¿Ha terminado de analizarse todo el mensaje actual? (la última letra de la cadena de texto es \0)
{
encontrado=numero_mensaje; // (numero_mensaje!=0) Hay conexión con el punto de acceso
esperando=false; // Si se ha encontrado algún mensaje y ya no se está esperando
digitalWrite(led_estado[numero_mensaje],HIGH); // Encender el LED correspondiente al mensaje encontrado
}
}
else // Si la letra que ha llegado por el puerto serie no corresponde con la buscada del mensaje…
{
posicion_mensaje[numero_mensaje]=0; // …empezar desde la primera letra del texto buscado
}
}
}
if((unsigned long)(millis()–cronometro)>TIMEOUT&&!encontrado) // Se ha superado el tiempo de espera y no hay conexión (se ha verificado que no hay o no ha llegado respuesta)
{
digitalWrite(PIN_LED_ERROR,HIGH); // Si ha superado el tiempo de espera encender el LED de error
esperando=false;
}
}
}
|
上記のコードは、 タイムアウト制限の非常に基本的な実装 前の例に関してマークされた行を組み込んでいます。タイムアウト検証は、から到着したデータを処理した後に実行されるため、 ESP8266 Wi-Fi モジュールの場合、受信に課せられた待ち時間より長くかかっても、操作は成功したと見なされます。
複数のATコマンドで定義された複雑なオペレーションを実行する
を利用するアプリケーションの目的の参照例を取得するには、 ESP8266 Wi-Fi モジュール、そうだと仮定してください Web サービスを通じてアクセスされるデータベースに情報を保存する 温度を追跡するため。次のコードは、一定の時間間隔ごとにアナログ入力に接続されたセンサーを読み取り、平均値を計算し、より長い時間間隔の後にそれを Web サーバーに送信します (スタイル IoT)を通して リクエスト HTTP (投稿、取得…).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de -550 mV a +1500 mV | de 0 V a 2.050 V | analogRead*5.0/1023.0*100-55.0 -> analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
void setup()
{
Serial.begin(9600);
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
// Aquí iría la parte del código que graba la temperatura. Para verificar el funcionamiento, en este ejemplo simplemente se muestra en la consola
Serial.println(“\nTemperatura media “+String(temperatura,DEC)+” °C (“+String((float)millis()/1000.0,DEC)+” s)\n”);
}
}
|
この温度記録の例では、Web サーバーに XNUMX 分ごとにアクセスします。可用性は特に高くありませんが、この提案は機能すると予想されますが、より高い記録頻度が必要な場合は、他のリソースを実装する必要があります。 データバッファ 送信を待っています、サーバーが参加できるときに複数を送信し、サーバーが利用できないときのためにそれらを保存します。データを記録する必要がある頻度がさらに高い場合は、プロトコルの代替として他のタイプのプロトコルを提案する必要があります。 HTTP または交換することさえ TCP によって UDP 一部のデータを失っても、ほとんどのデータを必要な速度で送信できるようになります。
温度を送信するために実行されるタスクを構成する操作は次のとおりです。
- Wi-Fiモジュールをリセットする
- 現在のアクセス ポイントから切断します (デフォルトの接続が存在する場合)
- 設定を行います。例では、接続モード(シンプル)と Wi-Fi 通信での役割(ステーション)を設定する必要があると仮定します。
- アクセスポイントに接続する
- 接続が正しいことを確認します (実際には、これがエントリ ポイントです) 接続がない場合は、プロセスを最初から開始します
- サーバーに接続する
- リクエストを送信する HTTP 保存されるデータと一緒に
操作の順序は (操作は同じですが) 必ずしもこのとおりである必要はなく、各ステップでいくつかの操作が必要になる場合があります。 ESP8266 AT コマンドたとえば、上記の構成には次の XNUMX つが必要です。 AT+CIPMUX=0
y AT+CWMODE=1
.
ESP8266 での操作を表すデータ構造
前の例では、非常に基本的な方法ではありますが、問題に対する一般的な解決策がすでに提案されています。 考えられる応答とそれぞれの場合に実行する必要があるアクションを保存するデータ構造を使用する;アクションを送信し、応答を待ち、応答の意味に従って続行します。それぞれの複雑な操作には複数の操作が必要となるため、 ESP8266 AT コマンド、データ構造は、後続または前の操作と他の操作をリンクする必要があり、その操作は、オブジェクトの応答に応じてそれぞれの場合に実行する必要があります。 ESP8266.
前の例では、メッセージは、 ESP8266 そしてそれは成功またはエラーとして解釈されました。受け取ったすべてのテキストの受信 (および分析) に加えて、 一般的な最小限度にするために、メッセージの完了にも注意することをお勧めします。 言い換えれば、 ESP8266 Wi-Fi モジュール 新たな注文を受けるため。このようにして、たとえば「Wi-Fi 利用可能」と呼ぶことができる状態への変化は、アクセス ポイントの名前を受信し、テキストを受信することになる可能性があります。 ERROR
またはテキスト OK
ということになります ESP8266 応答が完了したので、次の応答を送信できます ESP8266へのATコマンド.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
// inicializar_operaciones.h
// 0 Reiniciar el módulo wifi ESP8266
operacion[REINICIAR_ESP8266]=“AT+RST”;
mensaje[REINICIAR_ESP8266][FALLO]=mensaje_fallo;
mensaje[REINICIAR_ESP8266][ACIERTO]=“ready\r\n”;
mensaje[REINICIAR_ESP8266][LITERAL]=mensaje_vacio;
siguiente_operacion[REINICIAR_ESP8266][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[REINICIAR_ESP8266][ACIERTO]=DESCONECTAR_WIFI;
siguiente_operacion[REINICIAR_ESP8266][LITERAL]=DESCONECTAR_WIFI;
configuracion[REINICIAR_ESP8266]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[REINICIAR_ESP8266]=10000;
// 1 Desconectar del punto de acceso por defecto (si fuera el caso)
operacion[DESCONECTAR_WIFI]=“AT+CWQAP”;
mensaje[DESCONECTAR_WIFI][FALLO]=mensaje_fallo;
mensaje[DESCONECTAR_WIFI][ACIERTO]=mensaje_acierto;
mensaje[DESCONECTAR_WIFI][LITERAL]=mensaje_vacio;
siguiente_operacion[DESCONECTAR_WIFI][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[DESCONECTAR_WIFI][ACIERTO]=MODO_ESTACION;
siguiente_operacion[DESCONECTAR_WIFI][LITERAL]=MODO_ESTACION;
configuracion[DESCONECTAR_WIFI]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[DESCONECTAR_WIFI]=2500;
// 2 Establecer el modo de estación (no punto de acceso)
operacion[MODO_ESTACION]=“AT+CWMODE=1”;
mensaje[MODO_ESTACION][FALLO]=mensaje_fallo;
mensaje[MODO_ESTACION][ACIERTO]=mensaje_acierto;
mensaje[MODO_ESTACION][LITERAL]=mensaje_vacio;
siguiente_operacion[MODO_ESTACION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[MODO_ESTACION][ACIERTO]=MODO_SIMPLE;
siguiente_operacion[MODO_ESTACION][LITERAL]=MODO_SIMPLE;
configuracion[MODO_ESTACION]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[MODO_ESTACION]=2500;
// 3 Establecer el modo de conexión simple
operacion[MODO_SIMPLE]=“AT+CIPMUX=0”;
mensaje[MODO_SIMPLE][FALLO]=mensaje_fallo;
mensaje[MODO_SIMPLE][ACIERTO]=mensaje_acierto;
mensaje[MODO_SIMPLE][LITERAL]=mensaje_vacio;
siguiente_operacion[MODO_SIMPLE][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[MODO_SIMPLE][ACIERTO]=CONECTAR_WIFI;
siguiente_operacion[MODO_SIMPLE][LITERAL]=CONECTAR_WIFI;
configuracion[MODO_SIMPLE]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[MODO_SIMPLE]=2500;
// 4 Conectar al punto de acceso
operacion[CONECTAR_WIFI]=“AT+CWJAP=\”polaridad.es\”,\”54lLij1RiTn3MEd3v41C\””;
mensaje[CONECTAR_WIFI][FALLO]=mensaje_fallo;
mensaje[CONECTAR_WIFI][ACIERTO]=mensaje_acierto;
mensaje[CONECTAR_WIFI][LITERAL]=mensaje_vacio;
siguiente_operacion[CONECTAR_WIFI][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[CONECTAR_WIFI][ACIERTO]=VERIFICAR_CONEXION;
siguiente_operacion[CONECTAR_WIFI][LITERAL]=VERIFICAR_CONEXION;
configuracion[CONECTAR_WIFI]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[CONECTAR_WIFI]=20000;
// 5 Verificar si hay conexión
operacion[VERIFICAR_CONEXION]=“AT+CIPSTATUS”;
mensaje[VERIFICAR_CONEXION][FALLO]=mensaje_fallo;
mensaje[VERIFICAR_CONEXION][ACIERTO]=mensaje_acierto;
mensaje[VERIFICAR_CONEXION][LITERAL]=“STATUS:5”;
siguiente_operacion[VERIFICAR_CONEXION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[VERIFICAR_CONEXION][ACIERTO]=REINICIAR_ESP8266;
siguiente_operacion[VERIFICAR_CONEXION][LITERAL]=CONECTAR_SERVIDOR;
configuracion[VERIFICAR_CONEXION]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[VERIFICAR_CONEXION]=5000;
// 6 Conectar al servidor
operacion[CONECTAR_SERVIDOR]=“AT+CIPSTART=\”TCP\”,\”servidoriot.com\”,80″;
mensaje[CONECTAR_SERVIDOR][FALLO]=mensaje_fallo;
mensaje[CONECTAR_SERVIDOR][ACIERTO]=mensaje_acierto;
mensaje[CONECTAR_SERVIDOR][LITERAL]=“CONNECT”;
siguiente_operacion[CONECTAR_SERVIDOR][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[CONECTAR_SERVIDOR][ACIERTO]=INFORMAR_CANTIDAD;
siguiente_operacion[CONECTAR_SERVIDOR][LITERAL]=INFORMAR_CANTIDAD;
configuracion[CONECTAR_SERVIDOR]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[CONECTAR_SERVIDOR]=15000;
// 7 Avisar de la cantidad de datos que se envían
operacion[INFORMAR_CANTIDAD]=“AT+CIPSEND=”;
mensaje[INFORMAR_CANTIDAD][FALLO]=mensaje_vacio;
mensaje[INFORMAR_CANTIDAD][ACIERTO]=mensaje_acierto;
mensaje[INFORMAR_CANTIDAD][LITERAL]=mensaje_vacio;
siguiente_operacion[INFORMAR_CANTIDAD][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[INFORMAR_CANTIDAD][ACIERTO]=ENVIAR_CANTIDAD;
siguiente_operacion[INFORMAR_CANTIDAD][LITERAL]=ENVIAR_CANTIDAD;
configuracion[INFORMAR_CANTIDAD]=NO_ESPERAR_RESPUESTA;
timeout[INFORMAR_CANTIDAD]=1000;
// 8 Enviar cantidad
//operacion[ENVIAR_CANTIDAD]=”123″; // Definido para cada envío
mensaje[ENVIAR_CANTIDAD][FALLO]=sin_enlace;
mensaje[ENVIAR_CANTIDAD][ACIERTO]=“>”;
mensaje[ENVIAR_CANTIDAD][LITERAL]=mensaje_vacio;
siguiente_operacion[ENVIAR_CANTIDAD][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_CANTIDAD][ACIERTO]=ENVIAR_PREFIJO_PETICION;
siguiente_operacion[ENVIAR_CANTIDAD][LITERAL]=ENVIAR_PREFIJO_PETICION;
configuracion[ENVIAR_CANTIDAD]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[ENVIAR_CANTIDAD]=5000;
// 9 Enviar el prefijo de la petición
operacion[ENVIAR_PREFIJO_PETICION]=“GET /frigo03/almacenar_temperatura.php?temperatura=”;
mensaje[ENVIAR_PREFIJO_PETICION][FALLO]=mensaje_vacio;
mensaje[ENVIAR_PREFIJO_PETICION][ACIERTO]=mensaje_vacio;
mensaje[ENVIAR_PREFIJO_PETICION][LITERAL]=mensaje_vacio;
siguiente_operacion[ENVIAR_PREFIJO_PETICION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_PREFIJO_PETICION][ACIERTO]=ENVIAR_DATOS;
siguiente_operacion[ENVIAR_PREFIJO_PETICION][LITERAL]=ENVIAR_DATOS;
configuracion[ENVIAR_PREFIJO_PETICION]=NO_ESPERAR_RESPUESTA;
timeout[ENVIAR_PREFIJO_PETICION]=10000;
// 10 Enviar la temperatura
//operacion[ENVIAR_DATOS]=”+000.00″; // Definido para cada envío
mensaje[ENVIAR_DATOS][FALLO]=mensaje_vacio;
mensaje[ENVIAR_DATOS][ACIERTO]=mensaje_vacio;
mensaje[ENVIAR_DATOS][LITERAL]=mensaje_vacio;
siguiente_operacion[ENVIAR_DATOS][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_DATOS][ACIERTO]=ENVIAR_SUFIJO_PETICION;
siguiente_operacion[ENVIAR_DATOS][LITERAL]=ENVIAR_SUFIJO_PETICION;
configuracion[ENVIAR_DATOS]=NO_ESPERAR_RESPUESTA;
timeout[ENVIAR_DATOS]=5000;
// 11 Enviar el sufijo de la petición
operacion[ENVIAR_SUFIJO_PETICION]=” HTTP/1.1\r\nHost: www.servidoriot.com\r\nUser-Agent: ESP8266\r\nConnection: close\r\n\r\n”;
mensaje[ENVIAR_SUFIJO_PETICION][FALLO]=sin_enlace;
mensaje[ENVIAR_SUFIJO_PETICION][ACIERTO]=“CLOSED\r\n\r\nOK\r\n”;
mensaje[ENVIAR_SUFIJO_PETICION][LITERAL]=mensaje_vacio; // “SEND OK”
siguiente_operacion[ENVIAR_SUFIJO_PETICION][FALLO]=REINICIAR_ESP8266;
siguiente_operacion[ENVIAR_SUFIJO_PETICION][ACIERTO]=VERIFICAR_CONEXION;
siguiente_operacion[ENVIAR_SUFIJO_PETICION][LITERAL]=VERIFICAR_CONEXION;
configuracion[ENVIAR_SUFIJO_PETICION]=ESPERAR_RESPUESTA|ACIERTO_TERMINA|FALLO_TERMINA;
timeout[ENVIAR_SUFIJO_PETICION]=20000;
|
上記のコードではベクトル (operacion
) 完全なタスクを形成する連続操作のテキストを保存します。 XNUMX次元配列が使用されます(mensaje
) 分析された XNUMX つの応答。上で説明したように、応答の正誤を表すメッセージに加えて、応答の終了を表すメッセージを探す必要があります。すべての操作で同じ数の可能な答えが得られるわけではありません。応答が少ない場合は、分析に必要なサイクル数を最小限に抑える空のメッセージを使用できます (それでも、これは最適な方法ではありません)。論理的には、すべての可能性がすべてではない場合でも、すべての動作可能性を含めるために、求められる応答の最小数 (この例では XNUMX つ) が必要になります。
可能な答えについて話すとき、この例は、任意の形式のデータをサーバーから受信する場合にはあまり役に立たないことがすでにわかります。 ESP8266 Wi-Fi モジュール、しかし問題は、 で使用するコンテキストでは、 マイクロコントローラー それは普通ではありません。最も一般的なのは、接続されているセンサーによって収集されたデータを送信したり、制御するアクチュエーターをどうするかに関する情報を受信したりすることです。非常に貴重な情報であり、非常に正確に予測できます。
前のデータ構造では、分析される可能な応答を表現するために行われるのと同じように、それぞれの場合に実行する必要がある操作を決定するために XNUMX 次元行列も使用されます (siguiente_operacion
)。具体的には、次の XNUMX 種類のメッセージに応答することを選択しました。 ① 任意のテキスト (LITERAL
) Wi-Fi アクセスポイントとサーバーに接続されているかどうかを確認するため、② プロセスのエラーを検出するためのテキスト (FALLO
) および ③ 操作が正常に完了したことを示すテキスト (ACIERTO
).
最後に、諦めるまでの最大待機時間を設定するベクトルがさらに XNUMX つあります (timeout
) を指定して (configuracion
) 応答を待たずに操作が終了した場合 (ESPERAR_RESPUESTA
)と通信終了を示すメッセージが表示されます。この最後のベクトルは、メモリを節約する方法の例を示しており、構成バイトのビットと連携してさまざまな状態を示します。
最初の ESP8266 AT コマンド データ構造の場合、常に応答が期待されます。応答は、成功メッセージまたはエラー メッセージである可能性があります。エラーが発生すると、モジュールが再起動されて再び開始され、メッセージが操作が正しいことを示している場合は、次の操作に進みます。
サーバーに接続すると、パターンが変わります。この場合、①送信するデータパケットの長さを送信し、②リクエストを作成する必要があります。 HTTP 固定テキストに、サーバーに保存するために送信される (温度の) 値を加えたもの。このデータの作成は出荷ごとに行われ、XNUMX回(長さ通知)またはXNUMX回(リクエスト送信)に分ける必要があります。 HTTP)で ESP8266AT注文。操作が分割された最後の部分のみが応答を待ちます。
この場合、問題なく動作します (モジュールがビジーであることを警告する可能性があります) が、データの長さが長くなると、データ ブロックをより小さな部分に分割する必要があり、待機の実装が必要になる場合もあります。温度の読み取りとともに行われ、データを埋めることなくモジュールにデータを送信する時間を与えます。 バッファ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define REINICIAR_ESP8266 0 // Índice del vector de operaciones que representa la orden de reinicio del módulo wifi ESP8266
#define DESCONECTAR_WIFI 1
#define MODO_ESTACION 2
#define MODO_SIMPLE 3
#define CONECTAR_WIFI 4
#define VERIFICAR_CONEXION 5
#define CONECTAR_SERVIDOR 6
#define INFORMAR_CANTIDAD 7
#define ENVIAR_CANTIDAD 8
#define ENVIAR_PREFIJO_PETICION 9
#define ENVIAR_DATOS 10
#define ENVIAR_SUFIJO_PETICION 11
#define CANTIDAD_OPERACIONES 12 // Cantidad de operaciones que forma el cuerpo de la aplicación
#define FALLO 0 // Índice del vector de respuestas que representa el mensaje de error
#define FALLO_TERMINA 0B00000001 // 1<<FALLO
#define ACIERTO 1
#define ACIERTO_TERMINA 0B00000010 // 1<<ACIERTO
#define LITERAL 2
#define LITERAL_TERMINA 0B00000100 // 1<<LITERAL
#define CANTIDAD_RESPUESTAS 3 // Cantidad de posibles respuestas que se buscan en el texto recibido desde el ESP8266
#define ESPERAR_RESPUESTA 0B00001000 // 1<<CANTIDAD_RESPUESTAS
#define NO_ESPERAR_RESPUESTA 0B00000000
#define ENVIAR_OPERACION esperando_respuesta=true;SERIE.print(operacion[operacion_actual]);if(configuracion[operacion_actual]&ESPERAR_RESPUESTA){SERIE.print(“\r\n”);}for(unsigned char numero_respuesta=0;numero_respuesta<CANTIDAD_RESPUESTAS;numero_respuesta++){numero_caracter[numero_respuesta]=0;}cronometro_esp8266=millis();
|
すでに説明した他のマクロとともに、上記のコード例は、応答を待つかどうか、および該当する場合は応答が終了したことを示すメッセージを指定するためのさまざまな状態がどのように定義されるかを示しています。
コード内のさまざまなポイントで操作が送信されます (平均温度を送信するとき、操作の待機時間を超えたとき、現在の操作が正常に完了したときなど) が、その方法は次のとおりです。グローバルに確立されており、マクロが定義されています ENVIAR_OPERACION
配送に関わるステップをグループ化します。
1
2
3
4
5
6
7
8
9
10
11
12
|
// La macro ENVIAR_OPERACION corresponde con las operaciones:
esperando_respuesta=true;
SERIE.print(operacion[operacion_actual]);
if(configuracion[operacion_actual]&ESPERAR_RESPUESTA)
{
SERIE.print(“\r\n”);
}
for(unsigned char numero_respuesta=0;numero_respuesta<CANTIDAD_RESPUESTAS;numero_respuesta++)
{
numero_caracter[numero_respuesta]=0;
}
cronometro_esp8266=millis();
|
以下は例のメインプログラムのコードです。最も外部のタスクは、平均を計算するために温度をサンプリングするタスクであり、一定期間ごとに、 ESP8266 Wi-Fi モジュール。各操作が送信されると、応答が分析されて、次の操作がどれであるか、または情報を送信するタスクが完了したかどうかが判断されます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
#include “ESP8266_operacion_compleja_varias_ordenes_AT.h”
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de 0 V a 2.050 V | de -550 mV a +1500 mV | analogRead*5.0/1023.0*100-55.0 analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
char *mensaje_fallo=“ERROR\r\n”;
char *mensaje_acierto=“OK\r\n”;
char *sin_enlace=“link is not\r\n”;
char *mensaje_vacio=“\f”;
char *operacion[CANTIDAD_OPERACIONES]; // Matriz de punteros a constantes de caracteres con las operaciones que se envían al ESP8266 (no solo órdenes AT, aunque seguro que algunas son órdenes AT)
char *mensaje[CANTIDAD_OPERACIONES][CANTIDAD_RESPUESTAS]; // Mensajes de respuesta
unsigned char siguiente_operacion[CANTIDAD_OPERACIONES][CANTIDAD_RESPUESTAS];
unsigned char configuracion[CANTIDAD_OPERACIONES];
unsigned int timeout[CANTIDAD_OPERACIONES];
unsigned char numero_caracter[CANTIDAD_RESPUESTAS];
unsigned int longitud_peticion;
char texto_longitud_peticion[4]; // 3 caracteres para almacenar la longitud de la petición en formato texto
char valor_enviado[9]; // signo + 4 enteros + punto + 2 decimales + \0 = 9
unsigned long cronometro_esp8266;
unsigned char operacion_actual; // Número de operación que se está procesando
unsigned char proxima_operacion; // Siguiente operación que se procesará cuando termine la actual
char lectura_serie;
boolean grabando_datos=false;
boolean esperando_respuesta;
void setup()
{
#include “inicializar_operaciones.h”
longitud_peticion=strlen(operacion[ENVIAR_PREFIJO_PETICION])+strlen(operacion[ENVIAR_SUFIJO_PETICION]);
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
//delay(8000); // En fase de pruebas se puede introducir un tiempo de espera para conectar una consola/sniffer
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if(grabando_datos)
{
if((unsigned long)(millis()–cronometro_esp8266)>timeout[operacion_actual]) // Si se ha superado el tiempo de espera máximo
{
operacion_actual=siguiente_operacion[operacion_actual][FALLO]; // Pasar a la operación correspondiente al error
ENVIAR_OPERACION
}
else // Si no se ha superado el tiempo de espera máximo
{
if(configuracion[operacion_actual]&ESPERAR_RESPUESTA) // Si la siguiente operación depende de la respuesta a la actual desde el ESP8266 hay que leer la información que llegue desde el puerto serie
{
while(SERIE.available())
{
lectura_serie=SERIE.read();
for(unsigned char numero_respuesta=0;numero_respuesta<CANTIDAD_RESPUESTAS;numero_respuesta++) // Comparar la letra cargada desde el puerto serie con la correspondiente de los mensajes disponibles
{
if(lectura_serie==mensaje[operacion_actual][numero_respuesta][numero_caracter[numero_respuesta]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
numero_caracter[numero_respuesta]++; // Como el carácter coincide, se puede comparar con el siguiente lo próximo que llegue por el puerto serie
if(mensaje[operacion_actual][numero_respuesta][numero_caracter[numero_respuesta]]==0) // Si el carácter que toca es \0 es que se ha terminado de analizar el mensaje
{
if(esperando_respuesta) // Todavía no se ha encontrado un mensaje que determine la siguiente operación
{
proxima_operacion=siguiente_operacion[operacion_actual][numero_respuesta]; // La próxima operación que habrá que procesar será la que indique el mensaje encontrado para la operación actual
esperando_respuesta=false; // Ya se ha encontrado un mensaje que determina la siguiente operación
}
if(configuracion[operacion_actual]&(1<<numero_respuesta)) // Si el mensaje encontrado es uno de los que terminan la operación…
{
if(operacion_actual+1==CANTIDAD_OPERACIONES) // Se ha completado la última operación de la tarea compleja
{
grabando_datos=false; // Se ha terminado la tarea compleja (grabar datos en el servidor)
}
else // No es la última operación de la tarea compleja, hay que seguir realizando otras operaciones
{
operacion_actual=proxima_operacion; // Ejecutar la siguiente operación
ENVIAR_OPERACION
}
}
}
}
else // Si la letra recibida no es igual que la correspondiente del mensaje
{
numero_caracter[numero_respuesta]=0; // Empezar a comparar desde la primera letra del mensaje
}
}
}
}
else // Si no hay que esperar datos desde el puerto serie
{
operacion_actual=siguiente_operacion[operacion_actual][ACIERTO];
ENVIAR_OPERACION
}
}
}
else
{
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
dtostrf(media_temperaturas,4,2,valor_enviado); // snprintf(valor_enviado,9,”%+3.2f”,media_temperaturas); // printf y derivadas no funcionan en AVR
snprintf(texto_longitud_peticion,4,“%d”,longitud_peticion+strlen(valor_enviado));
grabando_datos=true;
operacion_actual=VERIFICAR_CONEXION;
operacion[ENVIAR_DATOS]=valor_enviado;
operacion[ENVIAR_CANTIDAD]=texto_longitud_peticion;
ENVIAR_OPERACION
}
}
}
|
論理的には、前のコードでいくつかの最適化アクションを実行できますが、これは、どのように最適化するかを理解するための例です。 ESP8266 一般的に、いくつかの側面に注目する価値があります。最初の側面はデータ構造です。論理的なことは次のようです プログラミング言語のデータ構造を使用します (struct
) 処理中の情報を表す: ESP8266 AT コマンド そして分析されるメッセージ。
構造体を使用します (struct
) サンプル配列 (配列に基づく) の代わりにデータを保存することは簡単であり、より洗練されたコードになる可能性がありますが、結果の改善を意味するものではありません。の使用によってもたらされる真の代替案 struct
以下で説明するように実装することです。 「内部」データを含む構造体の変数長 彼らが参照しているものです。この方法では、たとえば、操作で分析するために固定数の応答を用意する必要がなくなります。
このアプローチは、ソリューションを実装する最良の方法であることを示唆していますが、欠点は、 動的メモリ割り当てを使用します。これは、 マイクロコントローラー 実行時に使用されるメモリの量を慎重に測定する必要がありますコンパイラはこれについて警告することがほとんどできず、プログラムの実行に致命的な結果をもたらすメモリ (またはスタック) を使い果たす可能性がある程度あるためです。
コードを最適化する際に、大量のテキストを使用するこのタイプのプログラムでは、次のことを覚えておくと興味深いでしょう。 メモリスペースを節約できる SRAM テキスト文字列をプログラムメモリに保存する (フラッシュ) マクロを使用して F()
。次のスクリーンショットでは、通常のテキストの使用とマクロの使用による、別のプログラムと動的メモリ分散を確認できます。 F()
.
から届く情報に応じて実行されるアクションに関しては、 ESP8266 Wi-Fi モジュールコードからのメッセージをチェックし、受信した内容に応じていずれかを実行する代わりに、このデータ構造に保存できます。 ステータスインジケーターの代わりに各タスクを実行する関数へのポインター (フラグ) は、アプリケーションが管理する責任がある特定の状態 (たとえば、メイン ループ内) を警告します。
以下は、リクエストのデータを格納する構造体の例です。 ESP8266 (データ型 operacion_esp8266
) とその応答 (データ型 respuesta_esp8266
).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
unsigned char timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
respuesta_esp8266 *respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
unsigned char posicion_mensaje; // Posición (letra) que se está comparando con la recibida desde el ESP8266
//boolean estado; // El estado se representa por un valor booleano (por ejemplo ¿se ha encontrado ya esta respuesta? para no seguir buscándola)
//unsigned char *estado; // El estado se representa con un texto (de longitud variable)
unsigned char estado; // El estado se establece con 8 banderas, una por bit
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 comprobar_conexion;
respuesta_esp8266 respuesta_OK;
respuesta_esp8266 respuesta_ERROR;
|
操作を表す構造体 (サーバーに送信されるデータ) ESP8266 Wi-Fi モジュール) 応答が定義される構造と、操作の構造に対する応答の構造を指します。 最初に両方を宣言する必要があります、新しいデータ型を定義してから、その内容を定義します。
前の例では、それを含むプログラムが、 ステータスインジケータこれは、その値によって示される XNUMX つまたは他の操作の実行を担当するコードからアクセスできる変数に対応する必要があります。の応答の場合 ESP8266 特定のテキストが分析されると、状態は対応する応答の構造を示す値になります。
前に述べたように、ステータスインジケーターを置き換えるか補完する別の選択肢は次のとおりです。 参照構造体に関数を格納する (ポインタ) からの応答で特定のテキストが見つかったときに呼び出されます。 ESP8266 Wi-Fi モジュール.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
unsigned char timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
respuesta_esp8266 *respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
unsigned char posicion_mensaje; // Posición (letra) que se está comparando con la recibida desde el ESP8266
//boolean estado; // El estado se representa por un valor booleano (por ejemplo ¿se ha encontrado ya esta respuesta? para no seguir buscándola)
//unsigned char *estado; // El estado se representa con un texto (de longitud variable)
unsigned char estado; // El estado se establece con 8 banderas, una por bit
float (*accion)(unsigned char,unsigned char); // Puntero a la función que se llama si se encuentra la respuesta
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 comprobar_conexion;
respuesta_esp8266 respuesta_OK;
respuesta_esp8266 respuesta_ERROR;
|
前の例では、からの応答を処理するために使用されるデータ構造に追加されています。 ESP8266 Wi-Fi モジュール 型のデータを返す (想定される) 関数へのポインタ float
(アナログ読み取り値の重み付けされた値である可能性があります) であり、引数として XNUMX バイトが提供されます (XNUMX バイト unsigned char
これは、アナログ入力が読み取られるピンであり、仮想統合の ENABLE をアクティブにするピンである可能性があります)。
開発中 MCU大規模システムの開発スタイルとは対照的に、アセンブリを制御するアプリケーションの (グローバル) 動作を定義するときにグローバル変数を使用することはそれほど珍しいことではありません。そのため、このタイプの定義を見つけることは特に珍しいことではありません。パラメータを持たず、値を返さない関数として、次のようなものです。 void (*accion)();
このデータ表現方法を使用する場合は、次のようにします。 struct
可変長データの場合は、動的にメモリを割り当てる必要があります。 malloc()
(o new()
、オブジェクトが使用されている場合)、割り当てられたメモリ量をパラメータとして使用し、予約されているメモリ領域の先頭へのポインタを返します。と sizeof()
保存されている型に使用される要素の数を掛けると、必要なメモリの量を求めることができます。これを使用した例と使用しない例を以下のスクリーンショットに示します。 malloc()
;最初のケースではプログラムが使用するメモリに注意してください。この関数を含むライブラリをロードする必要があります。
での操作の場合、 ESP8266 Wi-Fi モジュール プログラムの実行を通じて変化するため、使用されていないメモリを解放する必要があります。 free()
(o delete()
、オブジェクトの場合)。コンパイラ (GCC) はメモリの分割を避けるためにプログラムを最適化しますが、静的に割り当てられたメモリを使用する場合ほどパフォーマンスは最適化されません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
error_operacion.mensaje=“ERROR\r\n”;
error_operacion.termina_operacion=true;
error_operacion.operacion=&reiniciar_esp8266;
error_enlace.mensaje=“link is not\r\n”;
error_enlace.termina_operacion=true;
error_enlace.operacion=&reiniciar_esp8266;
reiniciar_esp8266_correcto.mensaje=“ready\r\n”;
reiniciar_esp8266_correcto.termina_operacion=true;
reiniciar_esp8266_correcto.operacion=&desconectar_wifi;
desconectar_wifi_correcto.mensaje=mensaje_acierto;
desconectar_wifi_correcto.termina_operacion=true;
desconectar_wifi_correcto.operacion=&establecer_modo_estacion;
establecer_modo_estacion_correcto.mensaje=mensaje_acierto;
establecer_modo_estacion_correcto.termina_operacion=true;
establecer_modo_estacion_correcto.operacion=&establecer_modo_simple;
establecer_modo_simple_correcto.mensaje=mensaje_acierto;
establecer_modo_simple_correcto.termina_operacion=true;
establecer_modo_simple_correcto.operacion=&conectar_wifi;
conectar_wifi_correcto.mensaje=mensaje_acierto;
conectar_wifi_correcto.termina_operacion=true;
conectar_wifi_correcto.operacion=&verificar_conexion;
verificar_conexion_correcto.mensaje=“STATUS:5”;
verificar_conexion_correcto.termina_operacion=false;
verificar_conexion_correcto.operacion=&conectar_servidor;
verificar_conexion_terminado.mensaje=mensaje_acierto;
verificar_conexion_terminado.termina_operacion=true;
verificar_conexion_terminado.operacion=&reiniciar_esp8266;
conectar_servidor_correcto.mensaje=“CONNECT”;
conectar_servidor_correcto.termina_operacion=false;
conectar_servidor_correcto.operacion=&informar_cantidad;
conectar_servidor_terminado.mensaje=mensaje_acierto;
conectar_servidor_terminado.termina_operacion=true;
conectar_servidor_terminado.operacion=&reiniciar_esp8266;
informar_cantidad_correcto.mensaje=mensaje_acierto;
informar_cantidad_correcto.termina_operacion=true;
informar_cantidad_correcto.operacion=&enviar_cantidad;
enviar_cantidad_correcto.mensaje=“>”;
enviar_cantidad_correcto.termina_operacion=true;
enviar_cantidad_correcto.operacion=&enviar_prefijo;
enviar_prefijo_correcto.operacion=&enviar_datos;
enviar_datos_correcto.operacion=&enviar_sufijo;
enviar_sufijo_correcto.mensaje=“CLOSED\r\n\r\nOK\r\n”;
enviar_sufijo_correcto.termina_operacion=true;
enviar_sufijo_correcto.operacion=&verificar_conexion;
reiniciar_esp8266.peticion=“AT+RST”;
reiniciar_esp8266.timeout=15000;
reiniciar_esp8266.cantidad_respuestas=2;
reiniciar_esp8266.respuesta=malloc(sizeof(respuesta_esp8266*)*reiniciar_esp8266.cantidad_respuestas);
reiniciar_esp8266.respuesta[FALLO]=&error_operacion;
reiniciar_esp8266.respuesta[ACIERTO]=&reiniciar_esp8266_correcto;
desconectar_wifi.peticion=“AT+CWQAP”;
desconectar_wifi.timeout=2500;
desconectar_wifi.cantidad_respuestas=2;
desconectar_wifi.respuesta=malloc(sizeof(respuesta_esp8266*)*desconectar_wifi.cantidad_respuestas);
desconectar_wifi.respuesta[FALLO]=&error_operacion;
desconectar_wifi.respuesta[ACIERTO]=&desconectar_wifi_correcto;
establecer_modo_estacion.peticion=“AT+CWMODE=1”;
establecer_modo_estacion.timeout=2500;
establecer_modo_estacion.cantidad_respuestas=2;
establecer_modo_estacion.respuesta=malloc(sizeof(respuesta_esp8266*)*establecer_modo_estacion.cantidad_respuestas);
establecer_modo_estacion.respuesta[FALLO]=&error_operacion;
establecer_modo_estacion.respuesta[ACIERTO]=&establecer_modo_estacion_correcto;
establecer_modo_simple.peticion=“AT+CIPMUX=0”;
establecer_modo_simple.timeout=2500;
establecer_modo_simple.cantidad_respuestas=2;
establecer_modo_simple.respuesta=malloc(sizeof(respuesta_esp8266*)*establecer_modo_simple.cantidad_respuestas);
establecer_modo_simple.respuesta[FALLO]=&error_operacion;
establecer_modo_simple.respuesta[ACIERTO]=&establecer_modo_simple_correcto;
conectar_wifi.peticion=“AT+CWJAP=\”polaridad.es\”,\”54lLij1RiTn3MEd3v41C\””;;
conectar_wifi.timeout=20000;
conectar_wifi.cantidad_respuestas=2;
conectar_wifi.respuesta=malloc(sizeof(respuesta_esp8266*)*conectar_wifi.cantidad_respuestas);
conectar_wifi.respuesta[FALLO]=&error_operacion;
conectar_wifi.respuesta[ACIERTO]=&conectar_wifi_correcto;
verificar_conexion.peticion=“AT+CIPSTATUS”;
verificar_conexion.timeout=5000;
verificar_conexion.cantidad_respuestas=3;
verificar_conexion.respuesta=malloc(sizeof(respuesta_esp8266*)*verificar_conexion.cantidad_respuestas);
verificar_conexion.respuesta[FALLO]=&error_operacion;
verificar_conexion.respuesta[ACIERTO]=&verificar_conexion_correcto;
verificar_conexion.respuesta[OTRO_MENSAJE]=&verificar_conexion_terminado;
conectar_servidor.peticion=“AT+CIPSTART=\”TCP\”,\”servidoriot.com\”,80″;
conectar_servidor.timeout=15000;
conectar_servidor.cantidad_respuestas=3;
conectar_servidor.respuesta=malloc(sizeof(respuesta_esp8266*)*conectar_servidor.cantidad_respuestas);
conectar_servidor.respuesta[FALLO]=&error_operacion;
conectar_servidor.respuesta[ACIERTO]=&conectar_servidor_correcto;
conectar_servidor.respuesta[OTRO_MENSAJE]=&conectar_servidor_terminado; // OK, no significa que haya conexión pero sí termina la operación
informar_cantidad.peticion=“AT+CIPSEND=”;
informar_cantidad.timeout=1000;
informar_cantidad.cantidad_respuestas=1;
informar_cantidad.respuesta=malloc(sizeof(respuesta_esp8266*)*informar_cantidad.cantidad_respuestas);
informar_cantidad.respuesta[0]=&informar_cantidad_correcto;
//enviar_cantidad.peticion=””; // Se asigna cuando se conoce el valor que se va a enviar y se puede calcular la longitud que ocupa (número de caracteres)
enviar_cantidad.timeout=5000;
enviar_cantidad.cantidad_respuestas=2;
enviar_cantidad.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_cantidad.cantidad_respuestas);
enviar_cantidad.respuesta[FALLO]=&error_enlace;
enviar_cantidad.respuesta[ACIERTO]=&enviar_cantidad_correcto;
enviar_prefijo.peticion=“GET /frigo03/almacenar_temperatura.php?temperatura=”;
enviar_prefijo.timeout=10000;
enviar_prefijo.cantidad_respuestas=1;
enviar_prefijo.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_prefijo.cantidad_respuestas);
enviar_prefijo.respuesta[0]=&enviar_prefijo_correcto;
//enviar_datos.peticion=””; // Se asigna en cuando se conoce el valor que se va a enviar
enviar_datos.timeout=5000;
enviar_datos.cantidad_respuestas=1;
enviar_datos.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_datos.cantidad_respuestas);
enviar_datos.respuesta[0]=&enviar_datos_correcto;
enviar_sufijo.peticion=” HTTP/1.1\r\nHost: www.servidoriot.com\r\nUser-Agent: ESP8266\r\nConnection: close\r\n\r\n”;
enviar_sufijo.timeout=20000;
enviar_sufijo.cantidad_respuestas=2;
enviar_sufijo.respuesta=malloc(sizeof(respuesta_esp8266*)*enviar_sufijo.cantidad_respuestas);
enviar_sufijo.respuesta[FALLO]=&error_enlace;
enviar_sufijo.respuesta[ACIERTO]=&enviar_sufijo_correcto;
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
|
#if defined(ARDUINO_AVR_LEONARDO)||defined(ARDUINO_AVR_MEGA2560) /* ¿Es una placa Arduino Mega 2560 o Arduino Leonardo? */
#define SERIE Serial1 /* Si es una placa Arduino Mega 2560 o Arduino Leonardo usar Serial1 */
#else /* En este proyecto solamente uso Placas Leonardo, Mega 2560 y Uno, así que tiene que ser un Arduino Uno si llega hasta aquí */
#define SERIE Serial /* Si es una placa Arduino Uno usar Serial */
#endif
#define VELOCIDAD 115200 // Velocidad (en baudios) al que está configurado el módulo wifi ESP8266 (Cuidado con la placa utilizada, no todas o siempre son capaces de trabajar a una velocidad tan alta)
#define FALLO 0 // Índice del vector de respuestas que representa el mensaje de error
#define FALLO_TERMINA 0B00000001 // 1<<FALLO
#define ACIERTO 1
#define ACIERTO_TERMINA 0B00000010 // 1<<ACIERTO
#define OTRO_MENSAJE 2
#define OTRO_MENSAJE_TERMINA 0B00000100 // 1<<OTRO_MENSAJE
#define CANTIDAD_RESPUESTAS 3 // Cantidad de posibles respuestas que se buscan en el texto recibido desde el ESP8266
#define ESPERAR_RESPUESTA 0B00001000 // 1<<CANTIDAD_RESPUESTAS
#define NO_ESPERAR_RESPUESTA 0B00000000
#define ENVIAR_OPERACION esperando_respuesta=true;SERIE.print((*operacion_actual).peticion);if((*operacion_actual).cantidad_respuestas>1){SERIE.print(“\r\n”);for(unsigned char numero_respuesta=0;numero_respuesta<(*operacion_actual).cantidad_respuestas;numero_respuesta++){numero_caracter[numero_respuesta]=0;}}cronometro_esp8266=millis();
#define PIN_TEMPERATURA A0 // Pin analógico al que se conecta la salida del sensor de temperatura LM35
#define INTERVALO_LECTURA_TEMPERATURA 30000 // Leer la temperatura cada 30 segundos (30*1000)
#define INTERVALO_GRABACION_TEMPERATURA 300000 // Grabar la media de la temperatura cada 5 minutos (5*60*1000)
unsigned long muestras=0; // Número de veces que se ha medido la temperatura (para calcular la media)
float temperatura; // de -55.0 °C a +150 °C | de 0 V a 2.050 V | de -550 mV a +1500 mV | analogRead*5.0/1023.0*100-55.0 analogRead/2.46-55.0
float media_temperaturas=0.0;
unsigned long cronometro_lectura_temperatura;
unsigned long cronometro_grabacion_temperatura;
char *mensaje_acierto=“OK\r\n”;
typedef struct estructura_operacion_esp8266 operacion_esp8266; // Se define el tipo de datos operacion_esp8266 que corresponde con la estructura (struct) llamada estructura_operacion_esp8266 que se define más adelante
typedef struct estructura_respuesta_esp8266 respuesta_esp8266; // Se define el tipo de datos respuesta_esp8266 que corresponde con la estructura (struct) llamada struct estructura_respuesta_esp8266 que se define más adelante
struct estructura_operacion_esp8266
{
char *peticion; // Datos que se envían al ESP8266 para iniciar una operación (como una orden AT, pero también algo como la petición a un servidor…)
unsigned int timeout; // Tiempo (segundos) que se espera la respuesta del ESP8266 antes de desistir
bool espera_respuesta; // Si no espera respuesta en cuanto se termine de enviar la orden se puede pasar a la siguiente
unsigned char cantidad_respuestas; // Número de posibles respuestas del ESP8266 que se van a analizar, que puede ser variable en cada petición (no solo OK y ERROR)
respuesta_esp8266 **respuesta; // Respuestas (estructura) que se esperan de esta operación
};
struct estructura_respuesta_esp8266
{
char *mensaje; // Mensaje que se espera recibir desde el ESP8266
bool termina_operacion; // Cuando se termina de leer el mensaje ha terminado la operación
operacion_esp8266 *operacion; // Puntero a operación que se ejecutará si se encuentra esta respuesta en el mensaje devuelto por el ESP8266
};
operacion_esp8266 *operacion_actual; // Operación sobre el ESP8266 que se está ejecutando actualmente
operacion_esp8266 *proxima_operacion; // Siguiente operación que se procesará cuando termine la actual
unsigned int longitud_peticion; // Número de caracteres que ocupa la petición HTTP
char texto_longitud_peticion[4]; // 3 caracteres para almacenar la longitud de la petición en formato texto
char valor_enviado[9]; // signo + 4 enteros + punto + 2 decimales + \0 = 9
unsigned long cronometro_esp8266; // Cronómetro para controlar el tiempo máximo de respuesta del ESP8266 antes de desistir
char lectura_serie; // buffer con el carácter leído desde el ESP8266
boolean grabando_datos=false; // Verdadero cuando han terminado todas las operaciones necesarias para grabar los datos
boolean esperando_respuesta; // Verdadero si aún no se ha encontrado una de los mensajes que indica que ha terminado la respuesta
unsigned char numero_caracter[CANTIDAD_RESPUESTAS]; // Número de orden de la letra del mensaje-respuesta que se está almacenando (una matriz de, como máximo, el mayor número de respuestas posible)
operacion_esp8266 reiniciar_esp8266; // Reiniciar el módulo wifi ESP8266
operacion_esp8266 desconectar_wifi; // Desconectar del punto de acceso por defecto (si fuera el caso)
operacion_esp8266 establecer_modo_estacion; // Establecer el modo de estación (no punto de acceso)
operacion_esp8266 establecer_modo_simple; // Establecer el modo de conexión simple
operacion_esp8266 conectar_wifi; // Conectar al punto de acceso
operacion_esp8266 verificar_conexion; // Verificar si hay conexión
operacion_esp8266 conectar_servidor; // Conectar al servidor
operacion_esp8266 informar_cantidad; // Avisar de la cantidad de datos que se envían
operacion_esp8266 enviar_cantidad; // Enviar cantidad
operacion_esp8266 enviar_prefijo; // Enviar el prefijo de la petición
operacion_esp8266 enviar_datos; // Enviar la temperatura
operacion_esp8266 enviar_sufijo; // Enviar el sufijo de la petición
respuesta_esp8266 error_operacion; // Todas las respuestas “ERROR” reinician el módulo wifi ESP8266
respuesta_esp8266 error_enlace; // Las respuestas “link is not” también reinician el módulo wifi ESP8266
respuesta_esp8266 reiniciar_esp8266_correcto;
respuesta_esp8266 desconectar_wifi_correcto;
respuesta_esp8266 establecer_modo_estacion_correcto;
respuesta_esp8266 establecer_modo_simple_correcto;
respuesta_esp8266 conectar_wifi_correcto;
respuesta_esp8266 verificar_conexion_correcto;
respuesta_esp8266 verificar_conexion_terminado;
respuesta_esp8266 conectar_servidor_correcto;
respuesta_esp8266 conectar_servidor_terminado;
respuesta_esp8266 informar_cantidad_correcto;
respuesta_esp8266 enviar_cantidad_correcto;
respuesta_esp8266 enviar_prefijo_correcto;
respuesta_esp8266 enviar_datos_correcto;
respuesta_esp8266 enviar_sufijo_correcto;
void setup()
{
#include “inicializar_operaciones.h”
longitud_peticion=strlen(enviar_prefijo.peticion)+strlen(enviar_sufijo.peticion);
SERIE.begin(VELOCIDAD); // Configurar el puerto serie de Arduino a la velocidad del ESP8266
//delay(8000); // En fase de pruebas se puede introducir un tiempo de espera para conectar una consola/sniffer
cronometro_lectura_temperatura=millis();
cronometro_grabacion_temperatura=millis();
}
void loop()
{
if((unsigned long)(millis()–cronometro_lectura_temperatura)>INTERVALO_LECTURA_TEMPERATURA)
{
cronometro_lectura_temperatura=millis();
temperatura=analogRead(PIN_TEMPERATURA)/2.46–55.0;
muestras++;
media_temperaturas=(float)temperatura/(float)muestras+media_temperaturas*(float)(muestras–1)/(float)(muestras);
}
if(grabando_datos)
{
if((unsigned long)(millis()–cronometro_esp8266)>(unsigned long)(*operacion_actual).timeout) // Si se ha superado el tiempo de espera máximo
{
operacion_actual=(*(*operacion_actual).respuesta[FALLO]).operacion; // Pasar a la operación correspondiente al error
ENVIAR_OPERACION
}
else // Si no se ha superado el tiempo de espera máximo
{
if((*operacion_actual).cantidad_respuestas>1) // Si la siguiente operación depende de la respuesta a la actual desde el ESP8266 hay que leer la información que llegue desde el puerto serie
{
while(SERIE.available())
{
lectura_serie=SERIE.read();
for(unsigned char numero_respuesta=0;numero_respuesta<(*operacion_actual).cantidad_respuestas;numero_respuesta++) // Comparar la letra cargada desde el puerto serie con la correspondiente de los mensajes disponibles
{
if(lectura_serie==(*(*operacion_actual).respuesta[numero_respuesta]).mensaje[numero_caracter[numero_respuesta]]) // Si el dato que ha llegado es igual al que correspondería del mensaje buscado…
{
numero_caracter[numero_respuesta]++; // Como el carácter coincide, se puede comparar con el siguiente lo próximo que llegue por el puerto serie
if((*(*operacion_actual).respuesta[numero_respuesta]).mensaje[numero_caracter[numero_respuesta]]==0) // Si el carácter que toca es \0 es que se ha terminado de analizar el mensaje
{
if(esperando_respuesta) // Todavía no se ha encontrado un mensaje que determine la siguiente operación
{
proxima_operacion=(*(*operacion_actual).respuesta[numero_respuesta]).operacion; // La próxima operación que habrá que procesar será la que indique el mensaje encontrado para la operación actual
esperando_respuesta=false; // Ya se ha encontrado un mensaje que determina la siguiente operación
}
if((*(*operacion_actual).respuesta[numero_respuesta]).termina_operacion) // Si el mensaje encontrado es uno de los que terminan la operación…
{
if(operacion_actual==&enviar_sufijo) // Se ha completado la última operación de la tarea compleja
{
grabando_datos=false; // Se ha terminado la tarea compleja (grabar datos en el servidor)
}
else // No es la última operación de la tarea compleja, hay que seguir realizando otras operaciones
{
operacion_actual=proxima_operacion; // Ejecutar la siguiente operación
ENVIAR_OPERACION
}
}
}
}
else // Si la letra recibida no es igual que la correspondiente del mensaje
{
numero_caracter[numero_respuesta]=0; // Empezar a comparar desde la primera letra del mensaje
}
}
}
}
else // Si no hay que esperar datos desde el puerto serie
{
operacion_actual=(*(*operacion_actual).respuesta[0]).operacion;
ENVIAR_OPERACION
}
}
}
else
{
if((unsigned long)(millis()–cronometro_grabacion_temperatura)>INTERVALO_GRABACION_TEMPERATURA)
{
cronometro_grabacion_temperatura=millis();
dtostrf(media_temperaturas,4,2,valor_enviado); // snprintf(valor_enviado,9,”%+3.2f”,media_temperaturas); // printf y derivadas no funcionan en AVR
snprintf(texto_longitud_peticion,4,“%d”,longitud_peticion+strlen(valor_enviado));
grabando_datos=true;
operacion_actual=&verificar_conexion;
enviar_datos.peticion=valor_enviado;
enviar_cantidad.peticion=texto_longitud_peticion;
ENVIAR_OPERACION
}
}
}
|
この例 (両方の実装) ではあまり意味がありませんが、操作を一般化して他のケースに適用できるようにするために、次の点に注意する必要があります。 データの送信は常に同じプロトコルを繰り返します。送信されるバイト数を通知し、インジケーター (>) を待ってデータを送信します。.
この例では一度だけ使用されている (リクエスト全体が XNUMX つのパケットで行われる) ため、あまり有用とは思えませんが、一般に、同じ操作で複数の送信を実行する必要がある場合があります。大量のデータが送信されるため、メモリのオーバーフローを避けるために断片化する必要があります。 ESP8266.
この動作を実装するには、接続の最後の XNUMX つの要素を使用して、データが送信されるたびにデータに対応する値が入力されるようにすることができます。最初の場合は送信されたバイト数、XNUMX 番目の場合は (リクエストの一部が送信されます。
送信する必要があるさまざまな要素の割り当てと送信を繰り返すために、ベクトルに格納できます。この新しいベクトルは、これまでのように最後の操作ではなく、複雑な操作の終了を決定するものになります。
1コメント