与外部网络通信是Arduino项目的一项重要功能,并且对许多项目来说是强制性的。在本教程中,我们将创建一个ArduinoWiFi项目,该项目允许您向它发送命令以打开/关闭通过WiFi从计算机连接到Arduino的LED。开发周期如下:我们将使用ArduinoMega+ESP8266WiFi板进行开发和调试一旦它正常工作,我们就会将该项目移至ArduinoUno+ESP8266WiFiShield这个教程也很容易适应其他硬件,比如ESP8266WiFi使用Arduino上的主要“串行”设备与其交互-例如许多蓝牙模块。Arduino通信有很多选择,我经常使用有线以太网或USB,因为我有可用的基础设施,它通常易于使用并且对我有用。但有时无线方法会容易得多。有许多无线连接选项可用,例如LoRa、蓝牙、IR、WiFi等。它们每个都有不同的属性和理想的用例。可以说最普遍的是WiFi,这就是本文要讨论的内容。然而,我拥有的所有适用于Arduino的WiFi解决方案(ESP-8266解决方案)和我在网上阅读的其他解决方案,恕我直言,使用起来非常乏味(即痛苦)。Arduino项目开发周期需要硬件黑客和/或DIP开关的不断切换才能使用它们。我提到的乏味是因为Arduino和WiFi硬件之间的交互是通过Arduino的“串行”设备进行的。默认情况下,Arduino串行设备用于两件事:Arduino和WiFi对Serial的共享使用在所有意图和目的上都是一个冲突-也就是说,Serial是用于将新程序(/sketch)上传到Arduino,还是用于向WiFi发送命令或它是否用于输出调试消息?在许多WiFi板上实现的这种冲突的解决方案是使用一系列DIP开关或需要额外的硬件来解决串行端口的用途的困境。这意味着当您正在开发您的Arduino程序并希望在更改后上传它时,您需要将DIP开关设置为“特定配置”(串行设备连接到USB)以允许上传更新的程序.当您完成上传并想要运行您的新代码时,您需要将DIP开关设置为不同的“特定配置”(串行设备连接到WiFi)并重置您的Arduino以重新启动程序-或者有一些导致程序在开始发出WiFi指令之前等待的其他机制(例如,延迟让您有时间重置开关或按下按钮等)。当然,如果您的程序中有调试消息通常会输出到串行设备并因此在ArduinoIDE的串行监视器中可见,那么您就不走运了,因为这些调试消息将被发送到您猜对了的WiFi不太可能处理它们的设备。测试后,您确定新的代码更改、添加新功能或任何需要重置开关以进行上传的内容,然后重新重置开关以运行修改后的ode。每次更改代码时都需要重复此循环-无论更改有多小。对我来说,这非常乏味,并且在某些板上,如果您不小心,开关的切换有损坏它的风险-或者换句话说,恕我直言,这是非常痛苦的!一种选择是使用SoftwareSerial或者如果你有它,另一个串行端口(例如Leonardo上的Serial1)用于调试消息或WiFi交互,但这需要某种转换器(例如另一个Arduino、FTDIUSB串行转换器等)将SoftwareSerial/Serial1“端口”连接到PC等等。并且没有解决主要串行设备用于程序上传以及与WiFi模块通信的主要问题。我在使用SoftwareSerial时遇到的另一个潜在问题,我发现特别令人沮丧的是,对于与WiFi板的每次通信,我经常需要输出几个调试消息来隔离问题。这可能(并且已经)导致数据丢失、串行监视器上的消息损坏和其他问题。这意味着您可能正在尝试调试一个真正不存在的问题,因为它只是由通过SoftwareSerial传递的负载引起的。在我的测试中,当通过硬件串行端口发送相同的调试消息时,这个“数据溢出”问题似乎没有发生。所以这篇文章是关于如何避免痛苦并拥有正常的代码开发周期,上传,测试,根据需要重复-没有DIP开关痛苦来解决串行冲突问题。零件清单:本项目主要使用ArduinoMega+WiFi进行开发项目完成后,我们会将代码移植到ArduinoUno+WiFi(或任何其他带有ESPWiFi扩展板的Arduino)ArduinoMega+WiFi板(可从JaycarXC-4421获得)一个中型面包板3个LED3个限流电阻(440+ohm)连接线可选ArduinoDuinotechESP13WiFishieldArduinoUno+Wifi板第1步:连接组件在这个项目中,我们将在Mega上构建软件,然后将代码(和测试电路)迁移到Uno。您将需要以下组件:ArduinoMega+WiFi面包板3个LED和限流电阻连接线电路相当简单(参见图表),所以我不会详细解释它。确保将带有扁平侧/短腿的LED连接到电阻器,电阻器又接地。测试程序使用数字I/O引脚5、6和7,因此将LED(通过长腿)连接到ArduinoMega上的这些引脚。我使用了不同颜色的LED,但如果您没有多种颜色,则不必这样做。此外,如果您有不同的颜色,顺序并不重要,只需将一个LED(+限流电阻)连接到DIO引脚5、6和7中的每一个。照片、面包板和电路图说明了如何进行连接。第2步:在Mega上设置DIP开关在ArduinoMega+WiFi上,您应该找到我在照片中突出显示的2组开关。在我的板上,一个开关是一个8路DIP开关块(带有需要工具(例如小螺丝刀)才能移动的微小开关)。另一个开关是一个更大的开关,它是一个滑块的形式。为了尽量减少混淆,我将所有Arduino板上可用的主串行端口称为Serial(0)。在代码中,这是名为Serial的串行设备,通常连接到Arduino板上的USB端口。第一块8个开关控制板上的主要通信路径。选项包括:将Mega的串行端口连接到ESP(打开1和2)将USB连接到Mega的串行(0)端口(打开3和4)将USB连接到ESP(打开5和6)使ESP在下次复位时进入编程模式(开关7打开)开关8未使用第二个大开关决定了Mega如何(即通过哪条路径)连接到ESP(开关1和2)。此开关控制使用Mega的哪个串行端口与ESP通信。选项为Serial(0)或Serial3。这意味着我们可以:使用Mega的Serial3端口与ESP通信,从而利用其WiFi功能。和以传统方式使用USB将代码上传到Mega并通过串行监视器与Mega的程序交互,而无需在编程和测试时不断调整开关1&2和开关3&4。简而言之,此开关解决了与我在步骤1中概述的Serial(0)端口相关的硬件冲突。要进行设置,请按如下方式配置开关:转动开关1、2、3和4->ON转动开关5,6,7&8->OFF大滑块应设置在TXD3和RXD3端(即远离USB连接器和8路DIP开关的位置)电路图显示了我如何相信两个开关很可能连接。我这样说是因为我在网上发现了很多图表(我怎么能这么说?)似乎并没有准确反映板上的实际电路-有些人可能会说我发现的图表“完全错误”。我通过广泛的测试/反复试验制作了这个电路图。在我的图中,每个开关对上的TX和RX线可能颠倒了,但这应该无关紧要,因为开关应始终成对使用(即开关1和2应该设置相同的方式,类似地3和4应该设置方式与开关5和6相同)。第3步:开发您的ArduinoWiFi应用程序由于我将提供Arduino代码,因此不会有太多的开发周期,但我将尝试通过建议更改来说明开发周期,以说明我们可以在下一步中轻松重新上传和运行修改后的代码.关键的一点是,我们可以做到这一点,而无需经常更换开关,也无需额外的支持硬件。我们将看到如何配置ArduinoMega+WiFi,以便您可以使用ArduinoPCUSB连接来:上传编译代码通过串行监视器与运行在Arduino上的代码进行交互使用无线网络话虽如此,我们需要对ArduinoMega+WiFi上的开关进行初始设置以支持此开发过程(如上一步所述)。如果您尚未设置开关,请返回上一步并立即设置我们将用于开发的代码可以从我的GitHub下载或从下面复制粘贴注意有两个文件。第一个文件可以任意命名(我叫我的ArduinoESPInteractive.ino)。第二个文件必须命名为NullSerial.h只需在ArduinoIDE中创建一个新项目,并使用下面显示的两个文件中的代码或来自GitHub的代码如果您手动输入代码或复制/粘贴,那么您还需要手动添加第二个文件。要添加第二个文件,只需单击ArduinoIDE编辑器右上角的小向下箭头,然后选择“新选项卡”,然后将其命名为NullSerial.h确保您已按照上一步所述设置Mega+WiFi上的DIP开关照常上传代码。打开串行监视器,将波特率设置为115200,观察一些我将在下一步中描述的启动消息这是主程序(ArduinoESPInteractive.ino):*MegaESPInteractive*------------------**ThisprogramisdesignedforusewithanArduinoMega+ESP8266comboboard.*Itallowsyouto:*-interactwiththeESP8266viatheArudino*and*-uploadprogramstotheArduino*withoutconstantlyswitchingtheDIPswitchestoconnecttheArduinototheESP*and/ortheUSB.**IfyouwanttouploadaprogramtotheESP,youwillstillneedtosettheDIP*switches.**TheabilitytohaveadevelopmentcyclethatdoesnotinvolveswitchingDIP*switchesexpeditestheprogram/debugcycleofWiFibasedappsbecausethevery*tediousandfiddlystepofconstantlyhavingtochangethemiseliminated.This*willalsoprobablyextendthelifeoftheboardasyoudon'thaveasmuch*mechanicalwearandtearonthem.**Hardwarerequired:*-ArduinoMega+ESP8266wificomboboardsuchastheJaycarXC4421:*https://www.jaycar.com.au/mega-with-wi-fi/p/XC4421.*-optionallyanFTDIUSB-SerialboardsuchastheJaycarXC4672:*https://www.jaycar.com.au/isp-programmer-for-arduino-and-avr/p/XC4627)**Thedevelopmentcycleworksasfollows:*1)DevelopyourcodeanduploadusingthestandardArduinoIDEuploadmechanism.*2)useSerial3(viaapreprocessor#defineconstant)forinteractionwiththe*ESP.*2)OptionallyinteractwithyourArduinoprogramviaSerial2orSerial(alsovia*apreprocessorconstant)*3)OutputDebugmessagestoSerial(viaapreprocessorconstant).**PreprocessorsymbolsareusedtorepresentthetwoSerialportsusedasfollows:*ESP->Serial3*HOST->SerialorSerial2**Onceyourprogramiscompleteandcanoperatestandalone,youcantransfertoa*smallerdevice(e.g.theArduinoUNO+Wifiboard).Simplyredefinetheconstants*sothatESPisSerialandHOSTisaninstanceofsoftwareSerial.**Tousethisprogram,twosetsofswitchesontheArduinoMega+ESP8266Wifimust*besetasfollows:**ThelargeDPDTswitchwithlabelsRXD0&TXD0atoneendandRXD3&TXD3mustbe*settotheRXD3&TXD3end.ThisenablestheMegaSerial3portforcommunications*withtheESP.**The8positionDIPswitchmustbesetasfollows:**onoooo*offoooo*Number12345678**Thatis,switches1,2,3&4arebeturnedonand*switches5,6,7&8areturnedoff.*Theseswitchconfigurations:*-ConnecttheMegaSerial3totheESP(forWiFiaccess)*-ConnecttheMegaSerialtotheUSB(forprogramminganddebugging).*/#include"NullSerial.h"#defineVERSION"1.1.0.1"/**Revisions.*1.1.0.1*CorrectedbugforLEDon/offdebugmessages.**1.1.0.0*Addedconceptofdebugmessages.*AddedinitialisationofWiFitostationmodeandconnecttoWiFiboiler*platecode.**/#ifdefined(ARDUINO_AVR_MEGA2560)#defineHOST_BAUD115200#defineHOST_RX"USB(0)"#defineHOST_TX"USB(1)"#defineHOSTSerial#defineDEBUGSerial//NullSerial_debug(1,2);//#defineDEBUG_debug#defineESP_BAUD115200#defineESP_RX15#defineESP_TX14#defineESPSerial3#elseifdefined(ARDUINO_AVR_UNO)/**OnUno,weonlyhaveonehardwareserialdevice.*So,wewilluseSoftwareSerialforanyinteractionsthatmayberequiredwith*thehost.*/#include#defineHOST_BAUD115200#defineHOST_RX10#defineHOST_TX11SoftwareSerial_host(HOST_TX,HOST_RX);/*(my_RX,my_TX)*/#defineHOST_hostNullSerial_debug(HOST_TX,HOST_RX);#defineDEBUG_debug//#defineDEBUG_host#defineESP_BAUD115200#defineESP_RX"USB(0)"#defineESP_TX"USB(1)"#defineESPSerial#endif/*Apairofmacrosthatallowsthevalueofsymbolsdefinedin#define*preprocessordirectives(e.g.HOST)tobeoutputasstrings(e.g.in*printfunctioncalls).*/#define_STRING(x)(#x)#defineSTRING(x)_STRING(x)/******************************ThehasEOLJustBeenSentisusedtotrackwhetheranendoflinehasbeensentto*theESP.*TheversionofcodeshippedwiththedevicerequiresaCarriageReturn(CR)*followedbyaLineFeed(LF)-inthatorder-tomarktheendofaline(EOL).*Thatis,theESPrequiresaCRLFendofline(EOL)sequence.**Thisprogramallowsyouconsiderableflexibilityofwindowsterminalprograms*(IusePutty,CoolTerm,theArduinoSerialmonitorandothers)intheirdefault*configurations.Theseprogramsallgeneratevariouscombinationsofthecommon*lineendingsCRonly,LFonlyandCRLF.*WhenitseesaCR('\r')orLF('\n'),theprogramwillsendanEOLtotheESP.*ToallowforterminalprogramsthatsendbothaCRandaLF,the*hasEOLJustBeenSentisusedtotrackthatanEOLhasbeensentwheneithertheCR*orLFisreceivedfromtheHOST.*TopreventdoublinguponEOL's,ifwehavejustreceivedaCRorLF,andthe*nextcharacterisalsoaCRorLF,thenitwillsimplybeignored.*/booleanhasEOLJustBeenSent=false;charbuf[100];intbufPtr=0;booleanOKseen=false;booleanERRORseen=false;booleanFAILseen=false;booleanTIMEOUTseen=false;/**processInput(msg)*-----------------**ProcessesamessagereceivedfromtheESP.*Thisincludesprocessingresponsestocommands(e.g.OK,FAILetc)*Processinginputfromaclient.*Processingclientconnection/disconectionmessages.*Andothersasrequired.**/voidprocessInput(constchar*msg){DEBUG.println();//DEBUG.print(F("Received:"));//DEBUG.println(msg);OKseen=false;ERRORseen=false;FAILseen=false;if(strncmp(msg,"+IPD",4)==0){//Clientmessage?DEBUG.println(F("Receivedinputfromclient"));charled=msg[9];//ExtracttheLEDnumbercharsetting=msg[10];//Extractthesetting(on/off)if(led>='1'&&ledled=led-'1';//ifvalid,converttoaninteger.}else{//OtherwiseprintanerrormessagetotheUSB.HOST.print(F("InvalidLED:"));HOST.println(led);return;}DEBUG.print(F("SettingLED"));DEBUG.print(led);DEBUG.print(F("onDIO"));DEBUG.print(led+5);DEBUG.println(setting=='+'?"on":"off");//FInally,turntherequestedLEDonoroffdigitalWrite(led+5,setting=='+');//dependinguponthevaluein"setting".//Theled+5adjuststheLEDvalue(0,1or2)//tothecorrespondingArduinodigitalI/Opins//whichthathaveLED'sconnectedwhihcarepins5,6&7.}OKseen=strcmp(msg,"OK")==0;ERRORseen=strcmp(msg,"ERROR")==0;FAILseen=strcmp(msg,"FAIL")==0;}/*accumulateESPData*=================**CheckifacharacterisavailableontheESPSerialport.*Ifitis,accumultatethecharacterinabuffer.*Whenanewlineisobserved,processtheinput.**ReturnstruewhenanewLinehasbeenseenandtheinputprocessed.*falseotherwise*/booleanaccumulateESPData(){booleannewLineSeen=false;if(ESP.available()){charch=ESP.read();HOST.write(ch);if(ch=='\r'||ch=='\n'){buf[bufPtr]='\0';//Nullterminatetheinput;if(bufPtr>0){processInput(buf);newLineSeen=true;}bufPtr=0;}else{if(bufPtrbuf[bufPtr++]=ch;//theinputbufferifspacepermits.}}}returnnewLineSeen;}/***sendESP(msg)*============*SendthesuppliedmsgtotheESPalongwithaCRLFlineending.*WaitforaresponseofOK,ERROR,FAILoraTIMEOUT.*ThetimeoutperiodisspecifiedbythetoPeriodconstantvalue.*/constunsignedlongESP_TO_PERIOD=30000;//millistowaitforreply(30secs).voidsendESP(constchar*msg){ESP.print(msg);ESP.print("\r\n");OKseen=false;ERRORseen=false;FAILseen=false;TIMEOUTseen=false;//Establishtheinitialtimoutperiod.unsignedlongtimeout=millis()+ESP_TO_PERIOD;//Loopuntilwegetoneoftheexpectedreplies//oratimeoutoccurs(withoutgettingoneoftheexpectedreplies)while(!OKseen&&!ERRORseen&&!FAILseen&&!TIMEOUTseen){if(accumulateESPData()){//Aresponsehasbeenrecieved.//DebugoutputthestatusoftheexpectedrepliesDEBUG.print(F("O,E,F="));DEBUG.print(OKseen);DEBUG.print(ERRORseen);DEBUG.println(FAILseen);timeout=millis()+ESP_TO_PERIOD;//messagercvd,resetthetimer.}if(millis()>=timeout){TIMEOUTseen=true;DEBUG.println(F("***TimeoutwaitingforESPreply"));}}}/***AccumulateHOSTData*==================**Checkfordatabeingavailablefromthehost.Ifthereisdata,simplypassiton*totheESP.*IfaCRoraLFisobservedintheHOSTdata,sendtheESPaCRLF.*/voidaccumulateHOSTData(){if(HOST.available()){charch=HOST.read();//HOST.write(ch);//PerformlocalechotoHOST(ornot)if(ch=='\n'||ch=='\r'){//DowehaveaLForCR?if(!hasEOLJustBeenSent){//Yep,didwejustsendone?ESP.write('\r');//Nope,sosendaCRLFtotheESP.ESP.write('\n');hasEOLJustBeenSent=true;//Trackthatwe'vesentanEOL}}else{//NotaCR¬aLF,soESP.write(ch);//WritethecharactertotheESPhasEOLJustBeenSent=false;//SincenotaCR¬aLF,trackthat//wedidn'tjustsendanEOLtoESP.}}}/***setup*-----**InitialiseourSerialdevices,outputsomeconfigurationinformation*andsettheBUILTIN_LEDforoutput.*/voidsetup(){ESP.begin(ESP_BAUD);HOST.begin(HOST_BAUD);while(!HOST){delay(1);}HOST.print(F("Version:"));HOST.println(F(VERSION));HOST.print(F("ESPReadyon:"));HOST.print(STRING(ESP));HOST.print(F("@"));HOST.print(ESP_BAUD);HOST.print(F("bps.RX,TX:"));HOST.print(ESP_RX);HOST.print(F(","));HOST.println(ESP_TX);HOST.print(F("HOSTReadyon:"));HOST.print(STRING(HOST));HOST.print(F("@"));HOST.print(HOST_BAUD);HOST.print(F("bps.RX,TX:"));HOST.print(HOST_RX);HOST.print(F(","));HOST.println(HOST_TX);DEBUG.println(F("****Debugmessagesenabled****"));HOST.println();pinMode(5,OUTPUT);//SetsomepinstooutputforLEDs.pinMode(6,OUTPUT);pinMode(7,OUTPUT);DEBUG.println(F("SendingGetVersion"));sendESP("AT+GMR");//GetVersionInfo.//OnceoffsetmodeandjointheWiFi.DEBUG.println(F("Settingthemode"));sendESP("AT+CWMODE=1");//Setoperatingmodeto"station"DEBUG.println(F("ConnecttomyWiFi"));sendESP("AT+CWJAP=\"YourWiFi\",\"yourpassword\"");DEBUG.println(F("SendingMaxConnections=1"));sendESP("AT+CIPMUX=1");//EnableServerDEBUG.println(F("SendingStartServeronPort80"));sendESP("AT+CIPSERVER=1,80");//Openport80HOST.println(F("Ready"));HOST.println();}/*Loop*====**AccumulatecharactersfromtheHOSTandESP.*Processthemasnecessary.*/voidloop(){accumulateHOSTData();accumulateESPData();}这是第二个文件,它必须命名为NullSerial.h并且与主程序(从上面)位于同一文件夹中。当我们将项目移至ArduinoUno+WiFi时,我将解释此文件的用途。NullSerial*----------**AnemptyimplementationofaSerialInterface.**TheideaforthisNullSerialclassisto,viaconditionalcompilation,*eliminateanyDebugmessagesfromreleasedcode.**/#ifndefNullSerial_h#defineNullSerial_hclassNullSerial{public:NullSerial(uint16_treceivePin,uint16_ttransmitPin,boolinverse_logic=false){}NullSerial(){}voidbegin(longspeed){}boollisten(){returnfalse;}voidend(){}boolisListening(){returnfalse;}boolstopListening(){returnfalse;}booloverflow(){returnfalse;}intpeek(){return-1;}voidprintln(){}voidprint(){}voidprintln(constvoid*msg){}voidprint(constvoid*msg){}voidprintln(constinti){}voidprint(constinti){}};#endif//NullSerial_h第4步:连接到WiFi如果上一步中的一切顺利,您应该在计算机上(在串行监视器中)看到类似以下内容。Version:1.0.0.0ESPReadyon:Serial3@115200bps.RX,TX:15,14HOSTReadyon:Serial@115200bps.RX,TX:USB(0),USB(1)****Debugmessagesenabled****SendingGetVersionAT+GMROK,ERROR,FAIL=000ATversion:0.21.0.0OK,ERROR,FAIL=000SDKversion:0.9.5OK,ERROR,FAIL=000OKOK,ERROR,FAIL=100Ready上面显示的消息是调试消息,显示了Arduino的配置和ESP上运行的软件版本。如果您愿意,您可以通过将其输入到串行监视器的输入文本框中并单击“发送”按钮来向Arduino发送一些文本。您发送的任何文本都将直接发送到ESP。因此,最好发送AT命令,例如“AT+GMR”(不带引号),它要求ESP报告它正在运行的软件版本。我将在本Instructable的其余部分展示一些有用的命令。如果您对上述输出的含义不是特别感兴趣,只是想连接,请跳到下一个名为“连接到您的WiFi”的部分。初始调试消息的分析上述输出的前几行显示了Arduino用于与您和ESP交互的配置。这些是以“ESP”和“HOST”开头的行。内容是:正在使用的串行端口“OK,ERROR,FAIL...”消息是Arduino代码在从ESP收到消息时生成的调试消息。这些消息显示Arduino程序中维护的3个标志的状态,这些标志的设置或清除取决于是否从ESP接收到消息“OK”、“ERROR”或“FAIL”。通常,ESP会以OK、ERROR或FAIL消息结束其输出,因此这些消息用作标记来确定ESP何时完成对命令的响应并准备好下一个命令。我们可以在OK位设置为1的“Ready”消息之前看到这一点。其他消息“AT版本:0.21.0.0”和“SDK版本:0.9.5”是ESP生成的实际输出。Arduino程序将从ESP接收到的所有内容中继到IDE的串行监视器ESP8266WiFi由AT命令驱动。如果您对此历史感兴趣,可以在Wikipedia上找到一篇很好的文章。ESP8266WiFi解决方案使用与维基百科文章中提到的命令集不同的命令集,主要是因为它不是拨号调制解调器——这正是维基百科文章所讨论的内容。但是,AT命令的结构与文章中描述的类似。参考维基百科文章,ESP8266WiFi似乎只在命令模式下运行。将ESP置于“站模式”-“AT+CWMODE=1”命令加入您的WiFi网络-“AT+CWJAP=...”命令进入网络后,我们将要使用以下命令建立服务:开启“服务器模式”——“AT+CIPMUX=1”开始监听端口-"AT+CIPSERVER=1,80"请注意,每次重置ESP时都必须执行最后两个命令(打开服务器模式并开始侦听端口)。最简单的方法是在Arduino的setup()函数中运行它们。ESP会在重置时“记住”前两个命令(站模式和加入WiFi)中的设置。所以一旦成功运行,如果你想这样做,你可以将它们注释掉。每次Arduino重置时都运行它们没有坏处,当我们移动到Uno和/或如果出于某种原因更换了WiFi硬件(例如,有故障的WiFi屏蔽被替换为不错的)。因此,我倾向于不将它们注释掉并在每次Arduino启动时运行它们,以确保我们拥有预期的操作环境。在主Arduino程序的setup函数中,大约在第330行,您会注意到以下代码:DEBUG.println(F("SendingGetVersion"));sendESP("AT+GMR");//GetVersionInfo.//OnceoffsetmodeandjointheWiFi.DEBUG.println(F("Settingthemode"));sendESP("AT+CWMODE=1");//Setoperatingmodeto"station"DEBUG.println(F("ConnecttomyWiFi"));sendESP("AT+CWJAP=\"YourWiFi\",\"YourPassword\"");DEBUG.println(F("SendingMaxConnections=1"));sendESP("AT+CIPMUX=1");//EnableServerDEBUG.println(F("SendingStartServeronPort80"));sendESP("AT+CIPSERVER=1,80");//Openport80您需要通过替换YourWifi和YourPassword来修改“AT+CWJAP”行(如下所示)以包含您的WiFi和密码:sendESP("AT+CWJAP=\"YourWiFi\",\"YourPassword\"");注意不要去掉围绕YourWiFi和YourPassword的反斜杠或双引号。这些是确保编译器将双引号插入到发送到sendESP函数的字符串中所必需的。完成此操作后,重新上传程序。记住这里的关键要点是您只需在以正常方式编辑后上传代码,您不必调整任何开关,也不需要任何额外的硬件来调试您的程序。上传完成后,您应该会在串行监视器上看到更多消息。我们正在寻找的主要两条消息是对加入WiFi网络(AT+CWJAP=...)并开始侦听端口(AT+CIPSERVER=...)的命令的“OK”响应。如果一切顺利,您可以尝试在串口监视器中输入以下“查询网络配置命令”(AT+CIFSR)。您应该会看到如下内容:AT+CIFSR+CIFSR:STAIP,"192.168.3.160"+CIFSR:STAMAC,"18:fe:34:2c:70:45"OK重要的部分是包含文本“+CIFSR:STAIP”的行,它是我们的IP地址。我们将在下一步打开和关闭一些LED时需要IP地址!就我而言,地址是192.168.3.160-您的IP地址几乎肯定会有所不同。记下您的IP地址,而不是我的。第5步:通过WiFi与应用的互动现在我们已经连接到WiFi并开始我们的服务,我们将尝试联系它并让这些LED跳舞-可能不是很跳舞,但肯定会打开和关闭在我们开始之前,让我们检查“开始监听端口”命令(AT+CIPSERVER=1,80)的结构这个命令的意思可以分解如下:AT+-所有命令所需的前导码。CIPSERVER=开始(或停止)侦听指定的网络端口。即启动(或停止)服务/服务器。1、--开始监听(使用0,停止监听)80-我们想要侦听传入连接请求的网络端口-特别是端口80。您可以在此处使用任何有效的端口号。请注意,许多端口号通常用于特定功能(端口80通常由Web服务器使用),如这篇Wikipedia文章中所述。因此,从上一步中,我们知道连接到Arduino所需的IP地址(我的是192.168.3.160),现在我们也知道在建立连接时必须使用端口80。以下使用netcat的脚本显示了使用我的IP地址与Arduino的连接。您需要将192.168.3.160替换为您的IP地址。如果您还没有,可以在步骤1的底部找到有关获取netcat的信息。在Shell(Mac、Linux或Cygwin)提示符或Windows命令提示符中输入以下内容。请记住,使用您的IP地址代替192.168.3.160。如果一切顺利,此时netcat中不会发生任何其他事情。所有的动作都在Arduino上结束。在串行监视器中,您应该看到如下内容以响应netcat命令:这意味着一个客户端已经连接,并且它被分配了会话ID0。如果另一个客户端连接,您将看到“1,CONNECT”形式的消息。这意味着客户端已连接并将被分配会话ID1。接下来,翻回netcat并输入文本“1+”(无引号)并按Enter。您的终端会话现在应如下所示:再一次,不是非常令人兴奋,但再一次,Arduino串行监视器和面包板上的动作都结束了。如果一切顺利,连接到DIO端口5的LED将亮起,您将在串行监视器中看到类似以下内容。文本“+IPD,0,3:1+”是ESPWiFi发送给Arduino的内容。这可以解释如下:+IPD-已从客户端接收到传入数据。0-数据是从会话ID为0的客户端接收的。3:-已从客户端接收到三个字节的数据。实际数据跟在“:”字符之后。1+(后跟一个不显示的换行符)是来自客户端的数据-即您输入到netcat中的数据。Arduino程序对数据的解释如下:+打开LED(或-关闭LED)1(或2或3)打开第1个(DIO5)、第2个(DIO6)或第3个(DIO7)LED。如果需要,您可以通过在Arduino串行监视器中输入AT+CIPSEND=命令将数据发送回netcat。请注意,需要两行输入。第一个是将一些数据连同数据长度一起发送到会话0的命令,然后是要发送的实际文本(将给出“>”提示)。在串行监视器的输出中,您应该看到如下内容:AT+CIPSEND=0,5命令的细分是请求向会话ID0标识的客户端发送5个字节的数据。在您的计算机上的netcat会话中,您应该看到如下内容:第6步:发布您的应用您已经完成了构建应用程序的艰苦工作,现在您可以发布它了。您想将其移动到(物理上)较小的平台,例如ArduinoUno,甚至是自定义电路。所以现在我们需要将代码迁移到该硬件。这是一个可选步骤,但这是本文的全部内容。具体来说,如何相对轻松地开发您的ArduinoWiFi应用程序,并在完成后将其移至较小的平台。一个平台,如果您用于原始开发,则需要您在每次上传代码时不断切换开关,在您想要运行它时重置这些开关以及将需要额外的硬件来观察调试消息。实际上,由于我在WiFiShield和Arduino无法正常通信时遇到的一些头痛问题,因此编写这一步比其他所有步骤加起来花费的时间更长,因此不得不通过将Uno上的DIP开关切换为单调乏味的方式进行调试试着弄清楚发生了什么。问题是ESP软件以某种方式自我损坏并不断重启。解决方案是重新映像ESP模块。请注意,您可能仍然会像我一样在较小的硬件平台上遇到问题,这需要一些故障排除以及我刚刚提到的“痛苦的事情”,但在这个阶段,大部分开发工作已经完成。因此,与在较小平台上进行完整开发的痛苦相比,最终的故障排除痛苦或至少应该大大减少。在这一步中,我们将我们的应用程序迁移到带有WiFi屏蔽的ArduinoUno。但是,您也可以使用ArduinoUno+WiFi集成板,例如JaycarXC-4411或其他组合。子步骤大致如下:将我们的测试电路连接到Uno+WiFi板。将代码上传到我们的ArduinoUno+WiFi。计算分配给此设备的IP地址(此步骤)享受您闪亮的新应用程序,让这些LED发光!连接测试电路对于此子步骤,只需按照步骤1中所述将测试电路连接到UNO。连接与步骤1中所述相同。具体而言,将3个LED连接到DIO引脚5、6和7。然后连接地线到Arduino板上的GND引脚之一。上传代码-MCU到USB此子步骤(“上传代码”)和以下子步骤(“运行代码”)对时间至关重要,因此请在开始之前完整阅读这两个步骤。对于这一步,我假设您没有注释掉设置操作模式和加入WiFi的代码(第4步)。如果这样做,请在继续之前取消注释。使用Uno成功加入WiFi后,可以重新注释掉工作模式的设置(AT+CWMODE=1)和WiFi的加入(AT+CWJAP="...","...”),因为即使断电,ESP也会记住这些设置。同样,我建议不要对此进行评论,但如果您真的想要,请在新硬件上成功运行后进行评论。在Uno上,唯一的硬件串行端口用于上传代码、与串行监视器交互-或-与ESP通信-但并非同时进行。这是一个冲突。为了解决这个冲突,我们需要使用WiFiSheild(或Uno+WiFi)板上的DIP开关告诉电路板我们正在做什么(上传或与ESP对话)。简而言之,我们设置开关以允许通过USB上传Arduino代码。上传完全完成后,我们需要重置开关以允许Arduino与ESP通信(这是时间关键位)。要设置上传开关,请握住ArduinoUno+ESPWiFi扩展板,使USB位于左侧,您会注意到电路板右上角的DIO引脚1和2附近有2个DIP开关。将这两者都切换到关闭位置。参考图中蓝色电路板仅包含2个DIP开关。如果您使用的是Uno+WiFi集成板,则在与集成Mega+WiFi板上的位置类似的位置有一个8路拨码开关。请参阅带有包含8个DIP开关的黑色电路板的图像。将代码上传到Arduino时,您必须按如下方式切换开关:开关1和2-关闭开关3和4-打开开关5、6、7和8-关闭。以正常方式上传代码。除非打开调试模式,否则您将看不到与发送AT+GMR命令相关的任何内容-然而,它已经发送,Arduino将等待回复-永远不会到来。我们需要在Arduino超时等待AT+GMR响应之前将DIP开关重置为运行代码模式。这是时间关键点,所以请继续阅读......运行代码-MCU到ESP在Arduino等待AT+GMR响应超时之前,我们需要重新配置开发板以运行代码。作为替代方案,您可以按照此处所述设置开关并重新启动Arduino(使用Arduino重置,而不是ESP重置-或者只是将其关闭然后再次打开)。如果您使用的是ArduinoUno+WiFi扩展板,请将上面提到的2个DIP开关转到打开位置。如果您使用的是集成的ArduinoUno+WiFi板,请按如下方式设置8个DIP开关块:开关1和2-打开开关3和4-关闭开关5、6、7和8-关闭-即与前一个子步骤没有变化。如果您足够快,或者您使用了替代方案(设置开关并重新启动),您的Arduino应该加入您的WiFi网络。最后一个Arduino子步骤是访问我们的ArduinoLED服务。为此,您需要找出分配给您设备的IP地址。就我而言,我可以访问WiFi路由器管理控制台。我只是记下分配地址列表中出现的新设备(有点像确定Arduino在ArduinoIDE中分配的通信端口的过程)。这相对容易,因为对于我拥有的WiFi模块,系统名称要么是WiFi硬件的MAC地址,要么是名称中包含字母ESP。这两个示例可以从我的WiFi路由器管理控制台的屏幕截图中看到。注意:有时这里的内嵌图片似乎消失了,但我已将它们包含在此步骤的标题中。请注意,第一个包含Mega(192.168.3.160)的IP地址,因此另一个(192.168.3.94)是Uno。和以前一样,我们可以使用netcat在端口80上联系Uno并发出命令来点亮或关闭LED。第7步:后续步骤希望本文对您有所帮助,并使您能够轻松构建基于Arduino的WiFi应用程序。如果你这样做了,请点击“我构建了这个”链接让我知道。还有任何可以改进文章的反馈——例如,根据您在尝试自己做这件事时所面临的经验和挑战,我们将不胜感激。正如开场白中提到的,当与其他使用串行通信的设备(例如蓝牙模块)一起工作时,也可以使用这种技术。最好的下一步是将这个基础提升到一个新的水平。向Arduino代码添加功能。打开或关闭LED的过程全部在processInput(...)函数中处理,大约在主Arduino代码的第166行。在这个processInput(...)函数中,您将在大约第175行看到以下代码块:if(strncmp(msg,"+IPD",4)==0){HOST.println(F("Receivedinputfromclient"));charled=msg[9];charsetting=msg[10];if(led>='1'&&ledled=led-'1';}else{HOST.print(F("InvalidLED:"));HOST.println(led);return;}/**************************************Commentout/Uncommentthisblockofprint*statementstorepresentout"developmentcycle".*************************************/DEBUG.print(F("SettingLED"));DEBUG.print(led);DEBUG.print(F("onDIO"));DEBUG.print(led+5);DEBUG.println(setting=="+"?F("on"):F("off"));digitalWrite(led+5,setting=='+');}首先识别消息是从客户端输入的。如果传入的消息(msg)以“+IPD”开头,那么我们就知道它是来自客户端的消息。if块的其余部分处理消息。注释解释了正在发生的事情,因此我建议您参阅代码以进行解释。您可以更改此设置以识别和处理您自己的消息。当您这样做时,您可以控制您想要的任何内容,包括将消息发送回客户端。最后,程序如何在不将所有调试和状态消息发送到WiFi的情况下从Mega移植到Uno。记住:Mega使用Serial3进行WiFi交互,使用Serial(0)进行调试/状态消息。Uno使用Serial(0)进行WiFi交互,因此无法生成调试/状态消息。诀窍在于使用常量DEBUG、HOST(可以在上面的代码中看到)和ESP(可以在程序的其余部分看到)。这些常量是使用条件编译预处理器指令(即编译器#if语句)在程序顶部附近定义的。这出现在主要Arduino代码的第79到116行。在为Mega编译时,DEBUG和HOST定义为Serial(0),ESP定义为Serial3。为Uno编译时,DEBUG和Host定义为NullSerial(即对我们在步骤3中创建的额外文件中的代码的引用),ESP定义为Serial(0)。在这两种情况下,都定义了一些附加常数(例如引脚数和波特率)来定义操作环境。因此,当在Uno上运行时,调试和状态消息会转到这个NullSerial对象。NullSerial是一个C++类,它定义了一些与Arduino提供的真正串行设备定义类似的方法集。例如,NullSerial有一个println()方法。NullSerial的println方法什么都不做。因此,发送到DEBUG和HOST的调试和状态消息会被简单地丢弃,因为它们无处可发送。