譯者按:這篇是使用 ESP8266 的簡化板,希望我儘快能翻譯到第 9 課,大家就可以用電腦控制 Arudino 小車了!

Arduino: Lesson 6 - Control ESP8266-01 to connect a TCP Server

第六課 - 利用 ESP8266-01 連接 TCP 伺服器

6.1 Client Server Socket connection using ESP8266-01 board

6.1 利用 ESP8266-01 板創建 Socket 連接

This lesson provide an example on how to receive message sent from a TCP Server, youll need the following parts:

這一課是一個例子,讓我們接受 TCP 伺服器發過來的信息,你需要以下的零件:

  1. An Arduino UNO/MEGA board.

1. 一隻 Arduino UNO 或 MEGA 板。

2. ONE ESP8266-01 board, the price of this board is around US$1.50, sold in the following web site:

2. 一隻 ESP8266-01 板,價格大概是 12 元人民幣,某寶有售:

http://www.taobao.com

Figure 6.1

6.2 Hardware Setup

6.2 硬體安裝

Setup hardware as shown below:

按下圖安裝硬體:

zephan.top/arduinolesso

6.3 Programs

6.3 程序

6.3.1 Prepare the TCP Server Test Program

6.3.1 準備 TCP 伺服器測試程序

You may write your own TCP Server program or download the TCP Server Test EXE from the following URL:

你可以自己寫一個 TCP 伺服器程序,或者在以下網址下載一個測試程序:

sockettest.sourceforge.net

Run the program as shown:

運行該程序:

Figure 6.2

The IP Address is the IP Address of your PC, or the IP Address of another PC not connected . Select any port you want, however, if you are using Windows Server as the Operating System, remember to add an "In policy" in your Windows Firewall, otherwise, the ESP8266 is not allowed to connect to that Port.

那個 【IP Address】是電腦的 IP 地址。隨便選擇一個埠號,但是,如果你是使用 Windows,你需要在 Windows 防火牆的 "In policy" 里加一條允許其他計算機訪問該埠,否則 ESP8266 將不能連接該電腦。

In our lessons, we intended to build a ROBOT class that has many functions for many purposes, so that in the future, no matter what our project is, we can use the ROBOT class as the base class of our projects. The ROBOTH header files may become larger and larger, and somtimes it contains some modules that are not required by the current project, but we still need to include them otherwise the compilation of the programs will fail.

在我們的教程裏,我們嘗試建立一個【ROBOT 類】,它包含了很多的方法,可以用於不同的目的。將來,不管你的項目是什麼,我們都可以使用這個類作為項目的基類。那個 ROBOTH 頭文件會變得越來越大,有時候會包括一些方法是你的項目不需要的,但是我們也必須把這些沒用的源代碼留下,否則編譯程序會失敗。

If you did not start our lessons from lesson 1, you may need to go back to the previous lessons to learn how we can "INCLUDE" some libraries in the ROBOTH header files that are required by other lessons.

如果你沒有從我們的第一課開始看,你可能需要回看之前的教程,學習一下我們怎麼 "INLCUDE" 一些教程有用的庫。

Copy and Paste the following codes to corresponding files:

拷貝以下程序到相關檔案裏:

// ROBOTH.h
// We declare all variables(properties) and functions(methods) of the ROBOT object inside this header file

#ifndef _ROBOTH_h
#define _ROBOTH_h

#include "Arduino.h"
#include

// Class for MOTOR
class MOTOR
{
public:
// Motor Type:
// "D" - DC Motor
// "E" - EV3 Motor
String gstrMotorType = "";

// bolVSpeed = true means: Use analogWrite(pin, value) to enable setting the speed of the DC motor
// On Arduino UNO, the following PINs support analogWrite: 3, 5, 6, 9, 10, 11
// On Arduino MEGA, the following PINS support analogWrite: 2-13, 44-46
// If all PINS support analogWrite are occupied, turn gbolVSpeed = false, which means that the Motor will run in FULL speed.
bool gbolVSpeed = true;

// Direction of the Motor, 1 or -1
int gintDir = 1;

// PIN No. for Input PIN 1
int gintInput1PIN = 0;

// PIN No. for Input PIN 2
int gintInput2PIN = 0;

// Initialize a Motor
void Init(int intMotorNo, String strMotorType, bool bolVSpeed, int intDir, int intInput1PIN, int intInput2PIN);

// Move a Motor
void Move(int intMotorNo, int intDir, int intSpeed);

// Stop a Motor
void Stop(int intMotorNo);
};

// Class for ESP826601
class ESP826601
{
public:

void Init(SoftwareSerial &mySerial);

// Connect to Wifi AP
void ConnectWiFi(SoftwareSerial &mySerial, String strSSID, String strPWD);

// Connect TCP Server
void ConnectTCPServer(SoftwareSerial &mySerial, String strTCPServer, String strPort);

// Send Message to TCP Server
// void SendMessage(String strMessage);

};

// Class for ROBOT
class ROBOT
{
public:
// Part A - Public vars

// A.1 LED Related vars

// Total No. of LED
int gintNoOfLED = 0;
// Pin No. of the LEDs, a Maximum of 10 LED is allowed
int garyLED[10];

// A.11 Motor Related vars

// Total No. of Motor, 0 - 10
int gintNoOfMotor = 0;

// Type of Each Motor
// "D" = DC Motor
// "S" = Step Motor
// "V" = Servo Motor
// "E" = EV3 Motor
String gaMotorType[10];

// Allow vary speed for each Motor
bool gaBolVSpeed[10];

// Direction for each Motor
int gaMDir[10];

// Input X PIN for each Motor
int gaInput1PIN[10];
int gaInput2PIN[10];

// Set Left / Right for each motor
// "L" - means this motor use in turn Left
// "R" - means this motor use in turn right
// "N" - means this is a special purpose motor, it is not connected to a wheel, and wont be used to turn left / turn right / MoveForward / MoveBackward the vehicle.
String gaLRN[10];

// Motor Object
MOTOR gaM[10];

// ESP8266 Object
int gintNoOfESP = 0;

ESP826601 gESP;

// Part B - Init
void Init(SoftwareSerial &mySerial);

// Part C - Functions

// C1 - LED Related functions

// Turn On an LED
void LEDOn(int intLedNo);

// Turn Off an LED
void LEDOff(int intLedNo);

// C11 - Motor Related functions

// Move a Single Motor
// intDir: 1 or -1
// intSpeed: 0 to 255
void Move(int intMotorNo, int intDir, int intSpeed);

// Stop a Single Motor
void Stop(int intMotorNo);

// Move Robot
// MoveRobot only move motors with gaLRN = L or R
// floPowerL / floPowerR: -1.0 to 1.0, % of speed of the L/R wheel, if those wheels support variable speed, i.e. gbolVSpeed = true
// for example:
// floPowerL = 1, floPowerR = 1 means: Move Forward at full speed
// floPowerL = 1, floPowerR = -1 means: Left Wheel go forward at full speed, Right Wheel go backward at full speed, i.e. Turn Right at full speed.
// floPowerL = 1, floPowerR = 0.5 means: Left Wheel go forward at full speed, Right Wheel go forward at half speed, i.e. Go Forward And Right at the same time.
void MoveRobot(float floPowerL, float floPowerR);

// Stop Robot
void StopRobot();

private:
// Part D - Private Functions

// D1 - LED Related Private Functions

// Init. LED
void InitLED();

// D11 - Motor Related Private Functions

// Init. Motor
void InitMotor();

void InitESP(SoftwareSerial &mySerial);
};

#endif

zephan.top/arduinolesso

以上是 ROBOTH.h

#include "ROBOTH.h"
#include "Arduino.h"
#include

// If ESP8266-01 is being used, connect the RX to Pin 10 and TX to Pin 11 of Arduino UNO or MEGA (Or use other TX/RX PINS)

void ESP826601::Init(SoftwareSerial &mySerial) {
mySerial.begin(9600);
}

// ESP8266 Connect WiFi
void ESP826601::ConnectWiFi(SoftwareSerial &mySerial, String strSSID, String strPWD) {
// Test AT
mySerial.println("AT");
delay(1000);
Serial.println("AT Sent");
while (mySerial.available())
Serial.write(mySerial.read());

// Reset ESP8266
mySerial.println("AT+RST");
Serial.println("AT+RST Sent");
while (mySerial.available())
Serial.write(mySerial.read());

// Connect to Router
mySerial.println("AT+CWJAP="" + strSSID + "","" + strPWD + """);
delay(10000);
Serial.println("Connect Router Sent");
while (mySerial.available())
Serial.write(mySerial.read());
}

// ESP8266 Connect TCP Server
void ESP826601::ConnectTCPServer(SoftwareSerial &mySerial, String strTCPServer, String strPort) {
// AT + CIPSTART = "TCP", "192.168.10.223", 8082
// Connect to TCP Server
mySerial.println("AT+CIPSTART="TCP","" + strTCPServer + ""," + strPort);
delay(5000);
while (mySerial.available())
Serial.write(mySerial.read());
}

// Update the five pin assignments in the constructor below.
// The arguments are: enPin, dirPin, pwmPin, encoderPin1, encoderPin2
// There are a few considerations for pin assignments:
// A. pwmPin needs to be a pin with PWM capabilities (that is, it supports analogWrite)
// Uno: pins 3, 5, 6, 9, 10, and 11
// Mega 2560: pins 2 - 13 and 44 - 46
// B. There are three ways to connect the encoder pins (labeled T1/T2 on the board).
// ** Best performance: Both signals are connected to true interrupt pins (listed below).
// ** Good performance: The FIRST signal (T1) is connected to an interrupt pin, the second signal is a regular pin. This is the mode used for the Bricktronics Shield/Megashield. For this mode it is CRITICAL that the true interrupt pin is used for T1 and not T2.
// ** Low performance: Both signals are connected to non-interrupt pins.
// Regardless of which performance mode used, you MUST list the pin T1 before T2 in
// the constructor, otherwise the encoder will be connected backwards and the
// PID algorithm will get all confused and freak out.
// Location of true interrupt pins:
// Uno: pins 2 and 3
// Mega 2560: pins 2, 3, 21, 20, 19, and 18

// Since there are only 6 true interrupt pins, use Good Performance if there are 4 motors

// Bugs for a particular Arduino Mega 2560
// If mB use 34 as Dir pin and mC use 35 ass Dir pin, mB always turn in same direction!!!!!
BricktronicsMotor BM[4] = { BricktronicsMotor(32, 38, 44, 2, 3), BricktronicsMotor(33, 35, 45, 20, 21), BricktronicsMotor(32, 38, 44, 2, 3), BricktronicsMotor(32, 38, 44, 2, 3) };

// Init Motor
void MOTOR::Init(int intMotorNo, String strMotorType, bool bolVSpeed, int intDir, int intInput1PIN, int intInput2PIN) {
gstrMotorType = strMotorType;
gbolVSpeed = bolVSpeed;
gintDir = intDir;
gintInput1PIN = intInput1PIN;
gintInput2PIN = intInput2PIN;

if (strMotorType == "D") {
// Set Pins Output
pinMode(gintInput1PIN, OUTPUT);
pinMode(gintInput2PIN, OUTPUT);
}
else if (strMotorType == "E") {
BM[intMotorNo - 1].begin();
}
}

// Motor.Move
void MOTOR::Move(int intMotorNo, int intDir, int intSpeed) {
// Calculate Final Direction of this Motor
int intFinalDir = gintDir * intDir;

if (gstrMotorType == "D") {
// Move for DC Motor

// Reset Speed if needed, since allowed value = 0 - 255
if (intSpeed < 0) {
intSpeed = 0 - intSpeed;
}
if (intSpeed > 255) {
intSpeed = 255;
}

// Move Motor
if (gbolVSpeed) {
// Allow Different Speed
if (intFinalDir == 1) {
analogWrite(gintInput1PIN, intSpeed);
digitalWrite(gintInput2PIN, LOW);
}
else {
digitalWrite(gintInput1PIN, LOW);
analogWrite(gintInput2PIN, intSpeed);
}
}
else {
// Use Maximum Speed;
if (intFinalDir == 1) {
digitalWrite(gintInput1PIN, HIGH);
digitalWrite(gintInput2PIN, LOW);
}
else {
digitalWrite(gintInput1PIN, LOW);
digitalWrite(gintInput2PIN, HIGH);
}
}
}
else if (gstrMotorType == "S") {
// Move for Step Motor
}
else if (gstrMotorType == "V") {
// Move for Servo Motor
}
else if (gstrMotorType == "E") {
// Move for EV3 Motor
BM[intMotorNo - 1].setFixedDrive(int(intSpeed * intFinalDir));
}
}

// Motor.Stop
void MOTOR::Stop(int intMotorNo) {
if (gstrMotorType == "D") {
// Stop for DC Motor
digitalWrite(gintInput1PIN, LOW);
digitalWrite(gintInput2PIN, LOW);
}
else if (gstrMotorType == "S") {
// Move for Step Motor
}
else if (gstrMotorType == "V") {
// Move for Servo Motor
}
else if (gstrMotorType == "E") {
// Move for EV3 Motor
BM[intMotorNo - 1].brake();
}
}

// Init Robot
void ROBOT::Init(SoftwareSerial &mySerial) {
// Init LED, if any
InitLED();
// Init Motor
InitMotor();
// Init ESP
InitESP(mySerial);
}

// Init LED, if any
void ROBOT::InitLED() {
for (int i = 1; i <= gintNoOfLED; i++) {
pinMode(garyLED[i - 1], OUTPUT);
};
}

// Init Motor, if any
void ROBOT::InitMotor() {
for (int i = 1; i <= gintNoOfMotor; i++) {
if (gaMotorType[i - 1] == "D") {
// Init DC Motor
gaM[i - 1].Init(i, gaMotorType[i - 1], gaBolVSpeed[i - 1], gaMDir[i - 1], gaInput1PIN[i - 1], gaInput2PIN[i - 1]);
}
else if (gaMotorType[i - 1] == "E") {
gaM[i - 1].Init(i, gaMotorType[i - 1], gaBolVSpeed[i - 1], gaMDir[i - 1], gaInput1PIN[i - 1], gaInput2PIN[i - 1]);
}
};
}

void ROBOT::InitESP(SoftwareSerial &mySerial) {
if (gintNoOfESP == 1) {
gESP.Init(mySerial);
}
}

// LED Related functions

// LED On
void ROBOT::LEDOn(int intLedNo) {
digitalWrite(garyLED[intLedNo - 1], HIGH);
}

// LED Off
void ROBOT::LEDOff(int intLedNo) {
digitalWrite(garyLED[intLedNo - 1], LOW);
}

// Motor Related functions

// Move a Single Motor
void ROBOT::Move(int intMotorNo, int intDir, int intSpeed) {
gaM[intMotorNo - 1].Move(intMotorNo, intDir, intSpeed);
}

// Stop a Single Motor
void ROBOT::Stop(int intMotorNo) {
gaM[intMotorNo - 1].Stop(intMotorNo);
}

void ROBOT::MoveRobot(float floPowerL, float floPowerR) {
int intSpeedL = abs((int)(255 * floPowerL));
int intSpeedR = abs((int)(255 * floPowerR));
int intDirL;
int intDirR;
if (floPowerL > 0)
intDirL = 1;
else
intDirL = -1;
if (floPowerR > 0)
intDirR = 1;
else
intDirR = -1;

for (int i = 1; i <= gintNoOfMotor; i++) {
if (gaLRN[i - 1] == "L") {
gaM[i - 1].Move(i, intDirL, intSpeedL);
}
else if (gaLRN[i - 1] == "R") {
gaM[i - 1].Move(i, intDirR, intSpeedR);
}
}
}

void ROBOT::StopRobot() {
for (int i = 1; i <= gintNoOfMotor; i++) {
if (gaLRN[i - 1] == "L" || gaLRN[i - 1] == "R") {
gaM[i - 1].Stop(i);
}
}
}

zephan.top/arduinolesso

以上是 ROBOTH.cpp

#include
#include "ROBOTH.h"

// Declare the ROBOT rbt1
ROBOT rbt1;

SoftwareSerial mySerial(10,11);

void setup()
{
// Set vars for LED

// Set the no. of LED in this robot, if any
rbt1.gintNoOfLED = 0;

// Set the PIN numbers of those LEDs, if any
// rbt1.garyLED[0] = 13;

// Set vars for Motor

// Set No. of Motor
rbt1.gintNoOfMotor = 0;

// Set Motor Type for each Motor
// "D" for DC Motor
// "E" for EV3 Motor
//rbt1.gaMotorType[0] = "D";
//rbt1.gaMotorType[1] = "D";

// Set Vary Speed for each DC Motor
//rbt1.gaBolVSpeed[0] = false;
//rbt1.gaBolVSpeed[1] = false;

// Set Direction for each Motor
// If the direction of the Motor is not as expected, turn that value to -1
//rbt1.gaMDir[0] = -1;
//rbt1.gaMDir[1] = -1;

// Set Input PINs for each DC Motor
// For EV3 Motor, Set PINS in ROBOTH.cpp
//rbt1.gaInput1PIN[0] = 32;
//rbt1.gaInput2PIN[0] = 38;
//rbt1.gaInput1PIN[1] = 33;
//rbt1.gaInput2PIN[1] = 35;

// Set which motor(s) for turn left/right/forward/backward
//rbt1.gaLRN[0] = "L";
//rbt1.gaLRN[1] = "R";

// Above for other parts

// Below for ESP8266-01

// Open serial communications and wait for port to open:
Serial.begin(9600);
// while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
// }

// set the data rate for the SoftwareSerial port
// pinMode(10,INPUT_PULLUP);
// pinMode(11,INPUT_PULLUP);
// If use ESP8266-01 from China, the following should be mySerial.begin(115200),
// then use the command
// AT+UART=9600,8,1,0,0
// to change the rate back to 9600
// then change the following back to mySerial.begin(9600)
// otherwise the message received from TCP Server will be incomplete.
rbt1.gintNoOfESP = 1;

// Init Robot
rbt1.Init(mySerial);

// Connect Router
rbt1.gESP.ConnectWiFi(mySerial, "YourSSID", "YourPassword");

// Connect TCP Server
rbt1.gESP.ConnectTCPServer(mySerial, "192.168.118.88", "8851");
}

void loop() // run over and over
{
if (mySerial.available())
Serial.write(mySerial.read());

if (Serial.available())
mySerial.write(Serial.read());
}

zephan.top/arduinolesso

以上是 lesson6.ino

Build and upload this project, wait for around 15 seconds, you should see in the TCP Server that a New Client is connected. Then type something in the TCP Server and send, you should see what you type in your PCs Arduino Serial Monitor Window:

編譯並上傳這個項目到 Arduino,等待大概 15 秒,你應該可以在上面的電腦 TCP 伺服器程序看到一個新的客戶端已經連接了。然後在伺服器輸入一些信息並發送,你將可以在 Arduino 的串口 Monitor 視窗,看見伺服器輸入的信息:

Figure 6.3

Figure 6.4

(譯者按:上面兩張圖片,電腦 TCP 伺服器輸入並發送的 "Hello World",出現在 Arduino 你串口視窗裏了!)

The source codes are self explained, but you need to look at them line by line, and pay attention to those comments.

源代碼已經解釋自己了(這個是英語寫法,就是說一邊看就能看明白),你需要逐行的去看源代碼,並且留意注釋。

To send data from the Arduino to the TCP Server, in the Serial Window, type:

如果需要在 Arduino 發送信息給 TCP 伺服器電腦,在串口視窗裏,輸入:

AT+CIPSEND=4

and press Enter (here "4" means no. of characters = 4). After you press Enter, you MAY or MAY NOT SEE what you type in the Serial Window, it depends on whether you set "ECHO OFF" or "ECHO ON" in your Serial Window. Then type:

並按 Enter (這裡 "4" 的意思是有 4 個字元將會被發送)。按了 Enter 後,你可能會在串口視窗【看見】或【沒看見】「AT+CIPSEND=4」這幾個字,如果你設置了 【ECHO ON】,就會看見;設置了 【ECHO OFF】,就會看不見。

然後輸入:

ABCD

and press Enter, as shown below:

然後按 Enter,就像下圖一樣:

Figure 6.5

Figure 6.6

Then youll see "ABCD" in the TCP Server:

然後在運行 TCP 伺服器的電腦裏,你就會看見 "ABCD":

Figure 6.7

In our next lesson, well learn how to detect distance read by an Ultrasonic Sensor.

在下一課,我們會學習如何使用超聲波感測器量距離。


推薦閱讀:
相關文章