Pieter P, 08-03-2017 A Beginner's Guide to the ESP8266 · A Beginner's Guide to the ESP8266 Pieter P, 08-03-2017 Some time ago, I wrote a Beginner's Guide to Arduino that seems to

Post on 13-Oct-2019

12 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

Transcript

ABeginner'sGuidetotheESP8266PieterP, 08-03-2017

Sometimeago,IwroteaBeginner'sGuidetoArduinothatseemstobeverypopular,soIdecidedtocreateafollow-up:ABeginner'sGuidetotheESP8266.That'sright,atutorialonhowtousetheworld'smostpopular$3Wi-Fiboard.

Thisisgoingtobeaveryin-depthtutorial,coveringsomenetworkingconceptsaswell.Ifyou'reabeginner,andjustwanttogostraighttothemoreexcitingWi-Fipart,feelfreetodoso,IincludedshortTL;DR'sinthelonger,moretechnicalparts.

AshortoverviewofwhatI'llcoverinthisarticle:

1. WhatisanESP8266?AshortoverviewofwhatanESP8266is,andwhatyoucandowithit2. Decidingonwhatboardtobuy:There'sloadsofdifferentESP8266availablethesedays,findingtheonethat'sbestforyou

canbehard3. Installingthesoftware:youneedtoinstallsomesoftwaretoprogramtheESP8266,andmaybeaUSBdriver4. Settingupthehardware:somemodulesandboardsneedsomeexternalcomponents5. TheESP8266asamicrocontroller:theESP8266canbeusedasanormalmicrocontroller,justlikeanArduino6. Networkprotocols:BeforewestartusingtheWi-FicapabilitiesoftheESP8266,I'llteachyousomeofthenetworkprotocols

involved7. SettingupaWi-Ficonnection:That'sprobablywhyyou'rereadingthis,right?8. Nameresolution:FindtheESP8266onyourlocalnetworkusingmDNS9. Settingupasimplewebserver:ThisenablesyoutoaddwebpagestotheESP8266,andbrowsethemfromyourcomputeror

phone10. Settingupanadvancedwebserver:amoreadvancedserverwitharealfilesystemthatallowsyoutouploadnewfilesover

Wi-Fi11. OTA-uploadingprogramsoverWi-Fi:Youdon'thavetouploadprogramsoverUSB,youcanuseWi-Fiinstead12. WirelesslycontrollingyourRGBlighting:ChangethecolorofyourLEDstripsusingyourphoneorcomputer13. Gettingthetime:ConnecttoatimeserverusingNTPandsynctheESP'sclock14. Monitoringsensors:logthetemperatureinyourlivingroom,saveitinflashmemoryandshowitinafancygraphinyour

browser15. Gettingemailnotifications:Turnonanotificationlightwhenyou'vegotunreademails16. Advancedfeatures:useDNS,captiveportals,Wi-Ficonnectorlibraries,OSC...

ThisguideexpectssomebasicknowledgeofmicrocontrollersliketheArduino.Ifthat'ssomethingyou'renotalreadyfamiliarwith,I'drecommendyoutoreadmyBeginner'sGuidetoArduinofirst,itcoversalotofthebasicsthatIwon'tgointointhisarticle.IreallywanttofocusontheESP8266-specificthings,likeWi-Fiandothernetworkprotocols,theESP'shardware,software,IoT,etc...

WhatisanESP8266?TheESP8266isaSystemonaChip(SoC),manufacturedbytheChinesecompanyEspressif.ItconsistsofaTensilicaL10632-bitmicrocontrollerunit(MCU)andaWi-Fitransceiver.Ithas11GPIOpins*(GeneralPurposeInput/Outputpins),andananaloginputaswell.ThismeansthatyoucanprogramitlikeanynormalArduinoorothermicrocontroller.Andontopofthat,yougetWi-Ficommunication,soyoucanuseittoconnecttoyourWi-Finetwork,connecttotheInternet,hostawebserverwithrealwebpages,letyoursmartphoneconnecttoit,etc...Thepossibilitiesareendless!It'snowonderthatthischiphasbecomethemostpopularIOTdeviceavailable.

Therearemanydifferentmodulesavailable,standalonemodulesliketheESP-##seriesbyAIThinker,orcompletedevelopmentboardsliketheNodeMCUDevKitortheWeMosD1.Differentboardsmayhavedifferentpinsbrokenout,havedifferentWi-Fiantennas,oradifferentamountofflashmemoryonboard.

(*)TheESP8266chipitselfhas17GPIOpins,but6ofthesepins(6-11)areusedforcommunicationwiththeon-boardflashmemorychip.

Programming

TherearedifferentwaystoprogramtheESP8266,butI'llonlycoverthemethodusingtheArduinoIDE.Thisisreallyeasyforbeginners,andit'saveryfamiliarenvironmentifyou'veusedArduinoboardsbefore.Justkeepinmindthatit'snotlimitedtothisoption:there'salsoanofficialSDKavailabletoprogramitinrealC,thisisveryusefulifyouwanttooptimizeyourcodeordosomeadvancedtricksthataren'tsupportedbytheArduinoIDE.AnotherpossibilityistoflashitwithaLUAinterpreter,soyoucanuploadandrunLUAscripts.Ormaybeyou'remorefamiliarwithPython?ThenyoushouldcheckouttheMicroPythonfirmwaretointerpretMicroPythonscripts.I'msurethere'sotherlanguagesavailableaswell,sojustdoaquickGooglesearchandwriteyourcodeinthelanguageofyourchoice.

RequirementsYou'llneedacoupleofthingsinordertofollowthisguide:

AnESP8266boardAcomputerthatcanruntheArduinoIDE(Windows,MacorLinux)AUSB-to-Serialconverter,itisveryimportantthatyouusea3.3Vmodel*AUSBcableA3.3Vpowersupplyorvoltageregulator*AWi-Finetworktoconnectto

(*)Yourboardmayalreadyincludethese.Moreinformationcanbefoundinthenextchapter.

HardwareDecidingonwhatboardtobuy

ESP8266isjustthenameofthechip,manycompanieshavedesignedtheirownboardsthatusethischip,sotherearemanydifferentESP8266boardsonthemarket.Ifyoudon'tknowthedifferencebetweenallthesedifferentmodels,youmighthaveahardtimedecidingonwhatboardtobuy.

Theeasiest(andfastest)waytogetanESP8266boardistobuyonefromawell-knownelectronicsshoplikeAdafruitorSparkFun,butifyouwantitcheap,youshouldcheckoutEbayorothersiteswhereyoucanorderthemdirectlyfromChina.

Developmentboards

Someboardshaveallkindsoffeatureson-boardtohelpdevelopingESP8266hardwareandsoftware:forexample,aUSBtoSerialconverterforprogramming,a3.3Vregulatorforpower,on-boardLEDsfordebugging,avoltagedividertoscaletheanaloginput...Ifyou'reabeginner,Iwoulddefinitelyrecommendadevelopmentboard.It'seasiertogetstartedifyoudon'thavetoworryaboutallthesethings.

Bare-bonesAIThinkerboards

IfyouwanttoaddanESP8266toasmallproject,orifyouwantacheaper*board,youmightwanttobuyaboardthatdoesn'thavethesefeatures.Inthatcase,youcanbuyoneofthemanyESP-##modulesdevelopedbyAIThinker.TheycontainjusttheESP8266andthenecessarycomponentstorunit.Toprogramtheboard,you'llneedanexternalUSB-to-Serialconverter.Withsomemodules,yougetanon-boardantenna(PCBorceramic)andanLED,someboardshavejustanantennaconnector,ornoLEDsatall.Theyalsodifferinphysicalsize,andflashmemorysize.Animportantthingtonotice,isthatsomeboardsdonotbreakoutallGPIOpins.Forexample,theESP-01onlyhas2I/Opinsavailable(apartfromtheTXandRXpins),whileothermodulesliketheESP-07orESP-12breakoutallavailableI/Opins.

(*)Theboarditselfischeaper,butyou'llhavetospendmoreonexternalparts.

Overview

Here'satablewithsomeofthemostpopularESP8266developmentboardsandtheirfeatures:

Board GPIO 3.3VVreg

USB-to-Serial

Auto-Reset

Auto-Program Flash ADC

range Extra

SparkFunESP8266Thing 11 + - + ±* 512KB(4Mb) 0-1V Batterycharger,cryptoelement,temperaturesensor,lightsensor

SparkFunESP8266Thing-DevBoard 11 + + + + 512KB(4Mb) 0-1V

NodeMCU 11 + + + + 4MB(32Mb) 0-3.3V

AdafruitFeatherHUZZAHwithESP8266 11 + + + + 4MB(32Mb) 0-1V Batterycharger

AdafruitHUZZAHESP8266Breakout 11 + - - - 4MB(32Mb) 0-1V 5V-tolerantRXandResetpins

ESP-## 4-11 - - - - 512KB(4Mb)–

4MB(32Mb) 0-1V Smallandcheap

YoucanfindthefulllistofESP-##moduleshere.

Asyoucansee,boththeNodeMCUandtheAdafruitFeatherHUZZAHaresolidchoices.

(*)Whenauto-programontheSparkFunESP8266Thingisenabled,youcan'tusetheSerialMonitor.

Gettingthehardwareready

TherearetwomaincategoriesofESP8266boards:developmentboardswithaUSBinterface(USB-to-Serialconvertor)on-board,andboardswithoutaUSBconnection.

DevelopmentboardswithaUSBinterface

Forexample:NodeMCU,SparkFunESP8266Thing-DevBoard,SparkFunBlynkBoard,AdafruitFeatherHUZZAHwithESP8266Wi-Fi...

TheseboardswillshowupinDevicemanager(Windows)orinlsusb(Linux)assoonasyouplugthemin.Theyhavea3.3Vregulatoron-board,andcanbeprogrammedoverUSBdirectly,soyoudon'tneedanyexternalcomponentstogetitworking.Theonlythingyoumayneedtodo,issolderonsomeheaders.

Bare-bonesboardsandboardswithoutaUSBinterface

Thiscategoryhas2sub-categories:boardswitha3.3Vregulatoron-board,andboardswithjusttheESP8266andaflashmemorychip,without3.3Vregulator.Ifyourboarddoesn'thavea5Vto3.3Vregulator,buyoneseparately.YoucoulduseanLM1117-3.3forexample.Theon-board3.3VregulatorofmostArduinoboardsisnotpowerfulenoughtopowertheESP.

Toprogramtheboard,you'llneedaUSB-to-Serialconverter.TheFTDIFT232RLisquitepopular,becauseitcanswitchbetween5Vand3.3V.ItisessentialthattheUSB-to-Serialconverteryoubuyoperatesat3.3V.Ifyoubuya5Vmodel,youwilldamagetheESP8266.

ConnectingtheUSB-to-Serialconverter

1. Connecttheground(GND)oftheUSB-to-SerialconvertertothegroundoftheESP8266.2. ConnecttheRX-pinoftheUSB-to-SerialconvertertotheTXDpinoftheESP8266.(Onsomeboards,it'slabelledTXinsteadof

TXD,butit'sthesamepin.)3. ConnecttheTX-pinoftheUSB-to-SerialconvertertotheRXDpinoftheESP8266.(Onsomeboards,it'slabelledRXinsteadof

RXD,butit'sthesamepin.)4. IfyourESP8266boardhasaDTRpin,connectittotheDTRpinoftheUSB-to-Serialconverter.Thisenablesauto-resetwhen

uploadingasketch,moreonthatlater.

Enablingthechip

Ifyou'reusingabare-boneESP-##boardbyAIThinker,youhavetoaddsomeresistorstoturnontheESP8266,andtoselecttherightbootmode.

1. EnablethechipbyconnectingtheCH_PD(ChipPowerDown,sometimeslabeledCH_ENorchipenable)pintoVCCthrougha10KΩresistor.

2. DisableSD-cardbootbyconnectingGPIO15togroundthrougha10KΩresistor.3. SelectnormalbootmodebyconnectingGPIO0toVCCthrougha10KΩresistor.4. PreventrandomresetsbyconnectingtheRST(reset)pintoVCCthrougha10KΩresistor.5. Makesureyoudon'thaveanythingconnectedtoGPIO2(moreinformationinthenextchapter).

Addingresetandprogrambuttons

IfyourESP8266boarddoesn'thavearesetbutton,youcouldaddonebyconnectingapushbuttontobetweentheRSTpinandground.

Toputthechipintoprogrammingmode,youhavetopullGPIO0lowduringstartup.That'swhywealsoneedaprogrambutton.Becauseit'spossibletouseGPIO0asanoutput,wecan'tdirectlyshortittoground,thatcoulddamagethechip.Topreventthis,connect470Ωresistorinserieswiththeswitch.It'simportantthatthisresistanceislowenough,otherwise,itwillbepulledhighbythe10KΩresistorweaddedinthepreviousparagraph.

Connectingthepowersupply

IftheESP8266moduleyouhavedoesn'thavea3.3Vvoltageregulatoronboard,youhavetoaddoneexternally.YoucoulduseanLM1117-3.3forexample.

1. Connectthefirstpinoftheregulatortoground.2. Placea10µFcapacitorbetweenpin2(Vout)andground.Watchthepolarity!3. Placea10µFcapacitorbetweenpin3(Vin)andground.4. Connectpin2tothe3.3VorVCCoftheESP8266.5. Connectpin3toa5Vpowersource,aUSBport,forexample.

Beforeyoubegin...There'safewthingsyouhavetolookoutforwhenusinganESP8266:Themostimportantthingisthatitrunsat3.3V,soifyouconnectittoa5Vpowersupply,you'llkillit.Unlikesome3.3VArduinoorTeensyboards,theESP8266'sI/Opinsarenot5Vtolerant,soifyouusea5VUSB-to-Serialconverter,or5Vsensorsetc.you'llblowitup.AsecondthingtokeepinmindisthattheESP8266canonlysourceorsink12mAperoutputpin,comparedto20-40mAformostArduinos.TheESP8266hasoneanalogtodigitalconverter,butithasastrangevoltagerange:0-1V,voltagesabove1Vmightdamagetheboard.

OnelastthingtokeepinmindisthattheESP8266hastosharethesystemresourcesandCPUtimebetweenyoursketchandtheWi-Fidriver.Also,featureslikePWM,interruptsorI²Careemulatedinsoftware,mostArduinosontheotherhand,havededicatedhardwarepartsforthesetasks.Formostapplicationshowever,thisisnottoomuchofanissue.

SoftwareInstallationoftherequiredsoftware

ThefirststepistodownloadandinstalltheArduinoIDE.IexplainedthisinABeginner'sGuidetoArduino.(AsofFebruary7th2017,thelateststableversionoftheIDEis1.8.1.)

ToprogramtheESP8266,you'llneedapluginfortheArduinoIDE,itcanbedownloadedfromGitHubmanually,butitiseasiertojustaddtheURLintheArduinoIDE:

1. OpentheArduinoIDE.2. GotoFile>Preferences.3. PastetheURLhttp://arduino.esp8266.com/stable/package_esp8266com_index.jsonintotheAdditionalBoardManagerURLsfield.

(YoucanaddmultipleURLs,separatingthemwithcommas.)4. GotoTools>Board>BoardManagerandsearchfor'esp8266'.Selectthenewestversion,andclickinstall.(AsofFebruary7th

2017,thelateststableversionis2.3.0.)

Youcancheckouttheofficialinstallguidehere.

Drivers

IfyouareusingaboardwiththeCH340(G)USB-to-Serialchip,liketheNodeMCU,you'llprobablyhavetoinstalltheUSBdriversforit.TheycanbefoundonGitHub.IfyouareusingaboardwiththeCP2104USB-to-Serialchip,liketheAdafruitFeatherHUZZAHboard,you'llprobablyhavetoinstallUSBdriversaswell.YoucanfindthemontheSiliconLabswebsite.BoardswithanFTDIchipshouldworkrightoutofthebox,withouttheneedofinstallinganydrivers.

Python

IfyouwanttouseOverTheAirupdatesonWindows,youhavetoinstallPython2.7.Youcandownloaditfrompython.org.Duringtheinstallation,youhavetoselecttheoptiontoaddPythontoyourpath.Ifyoudon'tdothis,theArduinoIDEwon'tbeabletofindthePythonexecutable.

Examples

YoucanfindallexamplesusedinthisarticleonmyGitHub.Justdownloaditasa.ZIPfile,unzipittoaconvenientlocation,andyou'regoodtogo!

TheESP8266asamicrocontroller-HardwareWhiletheESP8266isoftenusedasa‘dumb’Serial-to-WiFibridge,it’saverypowerfulmicrocontrolleronitsown.Inthischapter,we’lllookatthenon-Wi-FispecificfunctionsoftheESP8266.

DigitalI/OJustlikeanormalArduino,theESP8266hasdigitalinput/outputpins(I/OorGPIO,GeneralPurposeInput/Outputpins).Asthenameimplies,theycanbeusedasdigitalinputstoreadadigitalvoltage,orasdigitaloutputstooutputeither0V(sinkcurrent)or3.3V(sourcecurrent).

Voltageandcurrentrestrictions

TheESP8266isa3.3Vmicrocontroller,soitsI/Ooperatesat3.3Vaswell.Thepinsarenot5Vtolerant,applyingmorethan3.6Vonanypinwillkillthechip.

ThemaximumcurrentthatcanbedrawnfromasingleGPIOpinis12mA.

Usablepins

TheESP8266has17GPIOpins(0-16),however,youcanonlyuse11ofthem,because6pins(GPIO6-11)areusedtoconnecttheflashmemorychip.Thisisthesmall8-leggedchiprightnexttotheESP8266.Ifyoutrytouseoneofthesepins,youmightcrashyourprogram.

GPIO1and3areusedasTXandRXofthehardwareSerialport(UART),soinmostcases,youcan’tusethemasnormalI/Owhilesending/receivingserialdata.

Bootmodes

Asmentionedinthepreviouschapter,someI/Opinshaveaspecialfunctionduringboot:Theyselect1of3bootmodes:

GPIO15 GPIO0 GPIO2 Mode0V 0V 3.3V UartBootloader0V 3.3V 3.3V Bootsketch(SPIflash)3.3V x x SDIOmode(notusedforArduino)

Note:youdon’thavetoaddanexternalpull-upresistortoGPIO2,theinternaloneisenabledatboot.

Wemadesurethattheseconditionsaremetbyaddingexternalresistorsinthepreviouschapter,ortheboardmanufacturerofyourboardaddedthemforyou.Thishassomeimplications,however:

GPIO15isalwayspulledlow,soyoucan’tusetheinternalpull-upresistor.YouhavetokeepthisinmindwhenusingGPIO15asaninputtoreadaswitchorconnectittoadevicewithanopen-collector(oropen-drain)output,likeI²C.GPIO0ispulledhighduringnormaloperation,soyoucan’tuseitasaHi-Zinput.GPIO2can’tbelowatboot,soyoucan’tconnectaswitchtoit.

Internalpull-up/-downresistors

GPIO0-15allhaveabuilt-inpull-upresistor,justlikeinanArduino.GPIO16hasabuilt-inpull-downresistor.

PWM

UnlikemostAtmelchips(Arduino),theESP8266doesn’tsupporthardwarePWM,however,softwarePWMissupportedonalldigitalpins.ThedefaultPWMrangeis10-bits@1kHz,butthiscanbechanged(upto>14-bit@1kHz).

AnaloginputTheESP8266hasasingleanaloginput,withaninputrangeof0-1.0V.Ifyousupply3.3V,forexample,youwilldamagethechip.SomeboardsliketheNodeMCUhaveanon-boardresistivevoltagedivider,togetaneasier0-3.3Vrange.Youcouldalsojustuseatrimpotasavoltagedivider.

TheADC(analogtodigitalconverter)hasaresolutionof10bits.

Communication

Serial

TheESP8266hastwohardwareUARTS(Serialports):UART0onpins1and3(TX0andRX0resp.),andUART1onpins2and8(TX1andRX1resp.),however,GPIO8isusedtoconnecttheflashchip.ThismeansthatUART1canonlytransmitdata.

UART0alsohashardwareflowcontrolonpins15and13(RTS0andCTS0resp.).ThesetwopinscanalsobeusedasalternativeTX0andRX0pins.

I²C

TheESPdoesn’thaveahardwareTWI(TwoWireInterface),butitisimplementedinsoftware.Thismeansthatyoucanuseprettymuchanytwodigitalpins.Bydefault,theI²Clibraryusespin4asSDAandpin5asSCL.(ThedatasheetspecifiesGPIO2asSDAandGPIO14asSCL.)Themaximumspeedisapproximately450kHz.

SPI

TheESP8266hasoneSPIconnectionavailabletotheuser,referredtoasHSPI.ItusesGPIO14asCLK,12asMISO,13asMOSIand15asSlaveSelect(SS).ItcanbeusedinbothSlaveandMastermode(insoftware).

GPIOoverview

GPIO Function State Restrictions0 Bootmodeselect 3.3V NoHi-Z1 TX0 - NotusableduringSerialtransmission

2 BootmodeselectTX1 3.3V(bootonly) Don’tconnecttogroundatboottime

Sendsdebugdataatboottime3 RX0 - NotusableduringSerialtransmission4 SDA(I²C) - -5 SCL(I²C) - -6-11 Flashconnection x Notusable,andnotbrokenout12 MISO(SPI) - -13 MOSI(SPI) - -14 SCK(SPI) - -15 SS(SPI) 0V Pull-upresistornotusable

16 Wakeupfromsleep - Nopull-upresistor,butpull-downinsteadShouldbeconnectedtoRSTtowakeup

TheESP8266asamicrocontroller-SoftwareMostofthemicrocontrollerfunctionalityoftheESPusesexactlythesamesyntaxasanormalArduino,makingitreallyeasytogetstarted.

DigitalI/O

JustlikewitharegularArduino,youcansetthefunctionofapinusingpinMode(pin,mode);wherepinistheGPIOnumber*,andmodecanbeeitherINPUT,whichisthedefault,OUTPUT,orINPUT_PULLUPtoenablethebuilt-inpull-upresistorsforGPIO0-15.Toenablethepull-downresistorforGPIO16,youhavetouseINPUT_PULLDOWN_16.

(*)NodeMCUusesadifferentpinmapping,readmorehere.ToaddressaNodeMCUpin,e.g.pin5,useD5:forinstance:pinMode(D5,OUTPUT);

Tosetanoutputpinhigh(3.3V)orlow(0V),usedigitalWrite(pin,value);wherepinisthedigitalpin,andvalueeither1or0(orHIGHandLOW).

Toreadaninput,usedigitalRead(pin);

ToenablePWMonacertainpin,useanalogWrite(pin,value);wherepinisthedigitalpin,andvalueanumberbetween0and1023.

Youcanchangetherange(bitdepth)ofthePWMoutputbyusinganalogWriteRange(new_range);

ThefrequencycanbechangedbyusinganalogWriteFreq(new_frequency);.new_frequencyshouldbebetween100and1000Hz.

Analoginput

JustlikeonanArduino,youcanuseanalogRead(A0)togettheanalogvoltageontheanaloginput.(0=0V,1023=1.0V).

TheESPcanalsousetheADCtomeasurethesupplyvoltage(VCC).Todothis,includeADC_MODE(ADC_VCC);atthetopofyoursketch,anduseESP.getVcc();toactuallygetthevoltage.Ifyouuseittoreadthesupplyvoltage,youcan’tconnectanythingelsetotheanalogpin.

Communication

Serialcommunication

TouseUART0(TX=GPIO1,RX=GPIO3),youcanusetheSerialobject,justlikeonanArduino:Serial.begin(baud).

Tousethealternativepins(TX=GPIO15,RX=GPIO13),useSerial.swap()afterSerial.begin.

TouseUART1(TX=GPIO2),usetheSerial1object.

AllArduinoStreamfunctions,likeread,write,print,println,...aresupportedaswell.

I²CandSPI

YoucanjustusethedefaultArduinolibrarysyntax,likeyounormallywould.

SharingCPUtimewiththeRFpartOnethingtokeepinmindwhilewritingprogramsfortheESP8266isthatyoursketchhastoshareresources(CPUtimeandmemory)withtheWi-Fi-andTCP-stacks(thesoftwarethatrunsinthebackgroundandhandlesallWi-FiandIPconnections).Ifyourcodetakestoolongtoexecute,anddon’tlettheTCPstacksdotheirthing,itmightcrash,oryoucouldlosedata.It’sbesttokeeptheexecutiontimeofyouloopunderacoupleofhundredsofmilliseconds.

Everytimethemainloopisrepeated,yoursketchyieldstotheWi-FiandTCPtohandleallWi-FiandTCPrequests.

Ifyourlooptakeslongerthanthis,youwillhavetoexplicitlygiveCPUtimetotheWi-Fi/TCPstacks,byusingincludingdelay(0);oryield();.Ifyoudon’t,networkcommunicationwon’tworkasexpected,andifit’slongerthan3seconds,thesoftWDT(WatchDogTimer)willresettheESP.IfthesoftWDTisdisabled,afteralittleover8seconds,thehardwareWDTwillresetthechip.

Fromamicrocontroller’sperspectivehowever,3secondsisaverylongtime(240millionclockcycles),sounlessyoudosomeextremelyheavynumbercrunching,orsendingextremelylongstringsoverSerial,youwon’tbeaffectedbythis.Justkeepinmindthatyouaddtheyield();insideyourfororwhileloopsthatcouldtakelongerthan,say100ms.

SourcesThisiswhereIgotmostofmyinformationtowritthisarticle,there’ssomemoredetailsontheGitHubpages,ifyou’reintosomemoreadvancedstuff,likeEEPROMordeepsleepetc.

https://github.com/esp8266/Arduino/issues/2942https://github.com/esp8266/Arduino/pull/2533/fileshttps://github.com/esp8266/Arduino/blob/master/doc/libraries.mdhttps://github.com/esp8266/Arduino/blob/master/doc/reference.mdhttps://github.com/esp8266/Arduino/blob/master/doc/boards.md

Wi-FiUsingtheESP8266asasimplemicrocontrollerisgreat,butthereasonwhymostpeopleuseit,isitsWi-Ficapabilities.Inthischapter,we'lldiveintothewonderfulworldofnetworkprotocols,likeWi-Fi,TCP,UDP,HTTP,DNS...Alltheseacronymsmightintimidateyou,butI'lltrymybesttoexplainthemstep-by-stepandinaneasyway.

Someparagraphsareinitalic.Theseprovidesomeextrainformation,butarenotcriticaltounderstandingtheESP'sWi-Fifunctions,sodon'tgetfrustratediftherearethingsyoudon'tunderstand.

It'sreallyhardtogiveaclearexplanation,withoutover-complicatingthingsandwhilekeepingitshortenoughaswell.Ifyou'vegotanyfeedbackorremarks,besuretoleaveacommenttohelpimprovethisarticle.Thanks!

TheTCP/IPstackThesystemmostpeoplerefertoas'TheInternet'isn'tjustoneprotocol:it'sanentirestackoflayersofprotocols,oftenreferredtoastheTCP/IPstack.We'llgooverthesedifferentlayers,becauseweneedtounderstandhowourESP8266communicateswithotherdevicesonthenetwork.

Layer ProtocolsApplication HTTP,FTP,mDNS,WebSocket,OSC...Transport TCP,UDPInternet IPLink Ethernet,Wi-Fi...

TheLinklayer

Thelinklayercontainsthephysicallinkbetweentwodevices,anEthernetcable,forexample,oraWi-Ficonnection.Thisisthelayerthatisclosesttothehardware.ToconnectanESP8266tothenetwork,youhavetocreateaWi-Filink.Thiscanhappenintwodifferentways:

1. TheESP8266connectstoawirelessaccesspoint(WAPorsimplyAP).TheAPcanbebuilt-intoyourmodemorrouter,forexample.Inthisconfiguration,theESPactslikeawirelessstation.

2. TheESP8266actsasanaccesspointandwirelessstationscanconnecttoit.Thesestationscouldbeyourlaptop,asmartphone,orevenanotherESPinstationmode.

OncetheWi-Filinkisestablished,theESP8266ispartofalocalareanetwork(LAN).AlldevicesonaLANcancommunicatewitheachother.Mostofthetime,theAPisconnectedtoaphysicalEthernetnetworkaswell,thismeansthattheESP8266canalsocommunicatewithdevicesthatareconnectedtotheAP(modem/router)viaawiredEthernetconnection(desktopcomputers,gamingconsolesandset-topboxes,forinstance).IftheESP8266isinaccesspointmode,itcancommunicatewithanystationthatisconnectedtoit,andtwostations(e.g.alaptopandasmartphone)canalsocommunicatewitheachother.

TheESPcanbeusedinAP-only,station-only,orAP+stationmode.

TL;DR

Thelinklayeristhephysicallinkbetweendevices:inthecaseoftheESP8266,thisisaWiFiconnection.TheESPcanactasastationandconnecttoanaccesspoint,oractasanaccesspointandletotherdevicesconnecttoit.

TheInternetorNetworklayer

Althoughthedevicesarenowphysicallyconnected(eitherthroughactualwires(Ethernet)orthroughradiowaves(Wi-Fi)),theycan'tactuallytalktoeachotheryet,becausetheyhavenowayofknowingwheretosendthemessageto.That'swheretheInternetProtocol(IP)comesin.EverydeviceonthenetworkhasapersonalIPaddress.TheDHCPserver(DynamicHostConfigurationProtocolServer)makessurethattheseaddressesareunique.Thismeansthatyoucannowsendamessagetoaspecificaddress.

TherearetwoversionsoftheInternetProtocol:IPv4andIPv6.IPv6isanimprovedversionofIPv4andhasmuchmoreaddressesthanIPv4(becausetherearemuchmoredevicesthanavailableIPv4addresses).Inthisarticle,we'llonlytalkaboutIPv4addresses,sincemostLANsstillusethem.

TheIPaddressconsistsof4numbers,forexample192.168.1.5isavalidIPv4address.Itactuallyconsistsoftwoparts:thefirstpartis192.168.1,thisistheaddressofthelocalnetwork.Thelastdigit,5inthiscase,isspecifictothedevice.

ByusingIPaddresses,wecanfindtheESP8266onthenetwork,andsendmessagestoit.TheESPcanalsofindourcomputerorourphone,ifitknowstheirrespectiveIPaddresses.

Sub-netmask(optional)

ThissubdivisionoftheIPaddressisdeterminedbythesub-netmask,oftenwrittenas255.255.255.0.Youcanseethatitconsistsoffournumbers,justliketheIPaddress.Ifapartofthesub-netmaskis255,itmeansthatthecorrespondingpartoftheIPaddressispartofthenetworkaddress,ifit's0,thecorrespondingIPpartispartoftheaddressofthespecificaddress.Adifferentnotationto"IP:192.168.1.5,sub-netmask:255.255.255.0"wouldbe"192.168.1.5/24",becausethebinaryrepresentationofthesub-netmaskis11111111.11111111.11111111.00000000,andithas24ones.Ifyouwanttoknowmoreaboutsub-nets,I'drecommendyoutoreadtheWikipediaarticle.(Aquicktiptohelpyouremember:it'scalledthesub-netmask,becauseifyouperformabitwiseANDoperationontheIPaddressandthesub-netmask(i.e.usethesub-netmaskasamaskfortheIPaddress),yougettheaddressofthesub-net.)

MACaddressesandARP(optional)

ItisactuallyimpossibletosendpacketsdirectlytoanothermachineusingonlytheIPaddress.TosendapackettoaspecificdeviceontheLAN(Wi-FiorEthernet),youhavetoknowitsMAC-address.TheMACaddressisauniquenumberthatisuniqueforeverynetworkdevice,anditneverchanges,it'shardwiredinthenetworkchip.ThismeansthateveryESP8266,everynetworkcard,everysmartphone...evermade,hasadifferentMACaddress.SobeforetheESPcansendapackettoyoursmartphoneforexample,ithastoknowitsMACaddress.Itdoesn'tknowthisyet,theESPonlyknowstheIPaddressofthesmartphone,say192.168.1.6.Todothis,theESPsendsabroadcastmessage(i.e.amessageaddressedtoalldevicesontheLAN)saying"I'mlookingfortheMACaddressofthedevicewiththeIPaddress192.168.1.6".TheESPalsoincludesitsownIPandMACaddresswiththemessage.Whenthesmartphonereceivesthisbroadcastmessage,itrecognizesitsownIPaddress,andrespondstotheESPbysendingitsownMACaddress.NowtheESPandthephonebothknoweachother'sIPandMACaddresses,andtheycancommunicateusingIPaddresses.ThismethodiscalledtheAddresResolutionProtocol,orARP.

WhatabouttheInternet?

Asyoumighthavenoticed,Ionlytalkedaboutthelocalareanetwork,thesearethecomputersinyourownhouse.SohowcantheESP8266communicatewiththeInternet,youmayask?Well,there'salotofnetworkinfrastructureinvolvedin'TheInternet',andtheyallobeytheIPrules,tomakesuremostofyourpacketsarriveattheredestination.It'snotthatsimpleofcourse,there'salotofthingsgoingon,likeroutingandNetworkAddressTranslation(NAT),butthatfallsoutsidethescopeofthisarticle,andit'snotreallysomethingmostpeoplehavetoworryabout.

TL;DR

TheInternetlayerusesIPaddressesinordertoknowwhereitshouldsendthedata.Thismeansthattwodevicescannowsendpacketsofdatatoeachother,evenovertheInternet.

TheTransportlayer

ThedifferentdevicesinthenetworkdotheirbesttodelivertheseIPpacketstotheaddressee,however,it'snotuncommonforapackettogetlost,soitwillneverarrive.Orthepacketmightgetcorruptedontheway:thedataisnolongercorrect.IPalsocan'tguaranteethatthepacketsarriveinthesameordertheyweresentin.Thismeansthatwecan'treliablysendmessagesyetbyonlyusingthelinkandtheInternetlayer,sincewecanneverknowwhenandwhetherapacketwillarrive,orknowforcertainthatareceivedpacketiscorrect.WeneedathirdlayerontopoftheInternetlayer:theTransportlayer.

Therearemainlytwoprotocolsthatmakeupthisthirdlayer:theTransmissionControlProtocol(TCP)andtheUserDatagramProtocol(UDP).

TCPmakessurethatallpacketsarereceived,thatthepacketsareinorder,andthatcorruptedpacketsarere-sent.Thismeansthatitcanbeusedforcommunicationbetweenmultipleapplications,withouthavingtoworryaboutdataintegrityorpacketloss.Thisiswhyit'susedforthingslikedownloadingwebpages,sendingemail,uploadingfilesetc.UDPontheotherhand,doesn'tguaranteethateverypacketreachesitsdestination,itdoescheckforerrorshowever,butwhenitfindsone,itjustdestroysthepacket,withoutre-sendingit.Thismeansthatit'snotasreliableasTCP,butit'sfaster,andhasamuchlowerlatency,becauseitdoesn'trequireanopenconnectiontosendmessages,likeTCPdoes.That'swhyit'susedinvoiceandvideochats,andforexampleinonlinegames.

IfyouwanttoknowmoreaboutthedifferencesbetweenTCPandUDP,checkoutthisvideo.

TL;DR

TheIPprotocolisnotreliable,andhasnoerrorchecking.TCPsolvesthisbyre-sendinglostorcorruptpackages,andorderspacketsthatarereceivedinthewrongorder.UDPalsochecksforcorruptpackages,butdoesn'tre-sendthem,soithaslesslatencythanTCP.

TheApplicationlayer

WenowhavereliablecommunicationusingTCP,butthere'sstilloneproblem.Thinkofitthisway:youaresendingaletter,andTCPguaranteesthatitwillarriveatitsdestination,butifthereceiverdoesn'tunderstandthelanguageit'swrittenin,hewon'tknowwhattodowithit.Inotherwords,weneedafourthlayerofprotocols,fortwoprogramstobeabletocommunicatewitheachother.There'slotsofdifferentprotocolsoutthere,butwe'llmostlyfocusontheprotocolsforwebserversandbrowsers.

HyperTextTransferProtocol

TheHyperTextTransferProtocol,orHTTP,istheprotocol(cfr.language)thatisusedbybothwebserversandwebclientsinordertocommunicate.Itusestexttoperformsendrequestsandresponsesfromtheclienttotheserverandbackagain.Forexample,whenyoutypehttp://www.google.comintotheaddressbarofawebbrowser(client),itwillsendanHTTPGETrequesttotheGooglewebserver.TheserverunderstandsthisHTTPrequest,andwillsendtheGooglewebpageasaresponse.OrwhenyouuploadanimagetoInstagram,yourbrowsersendsanHTTPPOSTrequestwithyourselfieattachedtotheInstagramserver.Theserverunderstandstherequest,savestheimageandaddsitintothedatabase,sendstheURLofthenewimagebacktoyourbrowser,andthebrowserwilladdtheimageonthewebpage.Asyoucansee,neithertheclientnortheserverhastoworryabouttheintegrityofthemessagestheysend,andtheyknowthattherecipientunderstandstheirlanguage,andthatitwillknowwhattodowithacertainHTTPrequest.MostmodernsitesuseasecureversionofHTTP,calledHTTPS.Thissecureconnectionencryptsthedata,forsecurityreasons.(Youdon'twantanyonereadingthepacketsfromyourmailserver,orthepacketsyousenttoyourbank,forinstance.)

WebSocket

HTTPisgreatforthingslikedownloadingwebpages,uploadingphotosetc.butit'squiteslow:everytimeyousendanHTTPrequest,youhavetostartanewTCPconnectiontotheserver,thensendyourrequest,waitfortheservertorespond,anddownloadtheresponse.Wouldn'titbegreatifwedidn'thavetoopenanewconnectioneverytimewewanttosendsomedata,andifwecouldsendandreceivedataatthesametimeatanymomentwe'dlike?That'swhereWebSocketcomestotherescue:youcankeeptheTCPconnectionwiththeserveropenatalltimes,yougetperfectTCPreliability,andit'sprettyfast.

OpenSoundControl

HTTPandWebSocketbothuseTCPconnections.Whatifyouwantlowerlatency?Well,OpenSoundControl,orOSC,usesUDPtosendsmallpiecesofdata,likeints,floats,shorttextetc...withverylowlatency.Itwasoriginallydesignedforcontrollinglowlatencyaudioapplications,butit'saveryflexibleprotocol,soit'softenusedforlow-latencytasksotherthanaudiocontrol.

DomainNameSystem

Asmentionedbefore,youcanonlysendamessagetoanothercomputerifyouknowitsIPaddress.ButwhenyoubrowsetheInternet,youonlyknowawebsite'sdomainname(e.g.www.google.com).YourcomputerusestheDomainNameSystemtotranslatethisdomainnametotherightIPaddress.Moreonthislater.

Sources

https://en.wikipedia.org/wiki/Internet_protocol_suitehttps://en.wikipedia.org/wiki/Port_(computer_networking)https://en.wikipedia.org/wiki/Transmission_Control_Protocolhttps://en.wikipedia.org/wiki/Internet_Protocolhttps://en.wikipedia.org/wiki/User_Datagram_Protocol

UploadingsketchestotheESP8266TheuploadprocedureforESP8266boardsisalittledifferentfromthenormalArduinoprocedure.MostArduinoswillautomaticallyresetwhenanewprogramisbeinguploaded,andwillautomaticallyenterprogrammingmode.OnsomeESPboardsyouhavetomanuallyenterprogrammingmode,andonthebare-bonesmodules,youevenhavetoresetthemmanually.However,therearesomesimplecircuitsyoucanusetogetautomaticuploads.

Auto-resetThisonlyappliestoboardswithoutanon-boardUSB-to-Serialconverter.

IftheUSB-to-Serialconverteryou'reusinghasaDTRflowcontrolline,youcanautomatetheresetsignal.WhensendingdatatotheESP,theDTRlinegoeslow,andstayslowforsometime.ToresettheESP,weneedalowpulseontheRSTpin.TheproblemisthattheDTRpinstayslow.Tosolvethis,we'regoingtobuildacrudeedgedetectorcircuit,usingacapacitor.Takealookatthefollowingschematic:

Youmightrecognizethatthisisbasicallyalow-cutfilter.Innormalconditions,DTRishigh(3.3V),andtheresetlineisalsohigh,becauseofthepull-upresistorR2.Thismeansthatthevoltageacrossthecapacitoris0V.WhenDTRsuddenlydrops(to0V),thevoltageacrossthecapacitorisstill0V,meaningthattheresetlinewillbeat0V+0V=0V,andaresetistriggered.

However,C1immediatelystartschargingthroughR2,andreaches3.3V.Atthispoint,DTRisstillat0V,meaningthatthere'snow3.3Vacrossthecapacitor.WhenDTRrisesagain,theresetlinewillbeat3.3V+3.3V=6.6V,andthenimmediatelystartstodischargethroughR2,finallyreaching3.3Vagain,with0VacrossC1.Thisisaproblem:6.6VcandamagetheESP,sowehavetofindawaytogetridofthepositivepeak.

OneglanceatthisMATLABsimulationshowsustheproblemevenbetter:

ThebluesignalisthevoltageontheDTRpin,andtheyellowsignalisthevoltageontheresetpin.

Thesolutionistoaddadiode:whilechargingthecapacitor,itshouldn'tchangeanything,soitshouldbereversebiased(justafancywayofsayingthatit'snotconductinganycurrentbecausethepolarityistheotherwayaround),andwhilethecapacitorisdischarging,itshoulddischargethecapacitor"immediately".Here'swhatthatlookslike:

Let'srunthesimulationagaintocheckifourproblemissolved:

Asyoucansee,the6.6Vpeakisnowverynarrow,justlikewewanted.It'simpossibletodischargethecapacitorinstantly,thatwouldrequireacapacitorandadiodewith0Ωofseriesresistance,andaninfinitecurrent,whichisimpossible,obviously.There'salsoasmallerbutrelativelywidepeakofapproximately3.9V.Thisisbecauseadiodeonlyconductswhenthevoltageacrossitishigherthan~600mV.Thismeansthatthelast0.6Vthat'sleftinthecapacitor(from3.9to3.3V)willstillbedischargedthroughR2only.Nevertheless,thevoltagepeakismuchlowerandnarrowerthanwithoutthediode,andit'ssafetoconnecttotheESP8266.

ThisexactcircuitisalsousedintheArduinoUno,forexample.

Note:ifyoufollowedtheinstructionsinthehardwarestepcorrectly,youshouldalreadyhaveaddedR2toyourESP.

HowtouseAuto-reset

Tousethisauto-resetcircuit,connectittotheDTRlineofyourUSB-to-Serialconverter,andtotheresetlineoftheESP,asshowninthediagram.Thenclickcompile(justbecausethefirstcompilationcantakequitesometime).GotoTools>Resetandselect'ck'.Whenit'sdonecompiling,holddowntheprogrambuttonweaddedinthehardwarestep,andclickupload.Waitforittosay"Uploading..."andthenreleasetheprogrambutton.

Auto-resetandAuto-program

Thisonlyappliestoboardswithoutanon-boardUSB-to-Serialconverter.

Themethodabovestillrequiresyoutopressabuttontouploadanewsketch.IfyourUSB-to-SerialconverterhasaRTSlineaswellasaDTRline,youcanautomatetheentireprocess.

Youmayfindoutthatthe4.7kΩresistordoesn'tworkforyou.Inthatcase,trysomeothervalue,like10kΩ,forexample.

ThismethodwasfirstusedintheNodeMCU,sogotoTools>ResetMethod,andselect"nodemcu".ThiswilldrivetheDTR&RTSpinshighandlowintherightsequencetogetitinprogrammingmodebeforeuploading.

Thisisbyfarthebestmethod,buttheproblemisthatyouneedaccesstoboththeRTSandDTRpins,whilemostUSB-to-Serialadaptersbreakoutonlyoneofthetwo.

ManualresetandmanualprogramThisonlyappliestoboardswithoutanon-boardUSB-to-Serialconverter.

Ifyoudon'thaveaUSB-to-SerialconverterwithDTRandRTSlines,youcouldalsojustusetheresetandprogrambuttonsweaddedinthehardwarechapter.TogettheESPinprogrammode,GPIO0mustbelowwhilebooting:

1. pressandholdtheresetbutton2. pressandholdtheprogrambutton3. releasetheresetbutton,theESPwillbootinprogrammode4. releasetheprogrambutton5. uploadthesketch

Ifyouwanttogetoutofprogrammodewithoutuploading,justpressreset(withoutpressingtheprogrambutton).

Boardoptions

IfyourspecificboardisintheTools>Boardlist(e.g.NodeMCU,SparkFunandAdafruitboards),youcanjustselectit,andyouwillgettherightsettings.Whenyourboardisn'tinthelist,you'llhavetoselectaGenericESP8266.Inthatcasethere'slotsofnewoptionsintheToolsmenuoftheArduinoIDE,solet'sgooverthemandpicktherightsettings.

FlashMode

LikeIsaidbefore,theESP8266usesanexternalflashchipforstorage.Youcancommunicatewiththischipover2datalines(DIO),oroverall4datalines(QIO).Using4linesistwotimesfasterthan2lines,soinmostcases,youshouldchooseQIO.(Ifyou'redoingsomeadvancedstuffandyouneed2moreGPIOpins,youcoulduse2linesinsteadof4,andusethe2linesasI/O.Mostmodulesdon'tgiveyouaccesstothesepins,though.)

FlashSize

Differentboards/moduleshavedifferentsizesofflashchipsonboard.Thereareboardswith512kB,1MB,2MBand4MBofflash.To

knowhowmuchflashyourboardhas,youcantrytheExamples>ESP8266>CheckFlashConfigtoseeifyourflashsettingiscorrect,oryoucancheckthespecificationsofyourspecificboardonline.YoucanalsoselecttheSPIFFS(SPIFlashFileSystem)size.TheSPIFFSpartitionisasmallfilesystemtostorefiles.Ifyou'renotusingit,youcanselecttheminimum.Lateroninthearticle,we'lluseSPIFFS,andI'llremindyoutoselectalargerSPIFFSsize,butfornow,itdoesn'treallymatter.

Debugport

There'saloadofthingsgoingonwhentheESPisrunning:ThingslikeWi-Ficonnections,TCPconnections,DNSlookups...younameit.Allthesesmalltasksproduceawholelotofdebugoutputtohelpyoutroubleshoot.However,inanormalsituation,whereyourprogramisbehavingasexpected,youdon'tneedallthosedebugmessagestofloodtheSerialMonitor,soyoucanjustturnthemoffbyselecting'Disabled'.Ifyoudowishtoreceivedebugmessages,youcanselecttheporttosendthemto.(Serialonpins1and3,orSerial1onpin2)

Debuglevel

Thisallowsyoutochoosewhatkindofdebugmessagesyouwanttoshow.

ResetMethod

Asmentionedintheparagraphsabove,therearedifferentmethodsforauto-resetandauto-program.Ifyou'reusingthefirstmethod(usingtheedgedetector),youshoulduse'ck',ifyouusethetwo-transistorcircuit,select'nodemcu'.

FlashFrequency

Ifyouneedsomeextramemoryspeed,youcouldchangetheflashfrequencyfrom40MHzto80MHz.ThisistheclockfrequencyoftheSPI/SDIOlink.

CPUFrequency

IfyouneedsomeextraCPUperformance,youcandoubletheclockspeedfrom80MHzto160MHz.It'sactuallyanoverclock,butI'veneverhadanyissuesorinstability.

UploadSpeed

ThebaudrateforuploadingtotheESP.Thedefaultis115200baud,butyoucangohigher(ifyou'rechangingyoursketchalot,itmightbetooslow).921600baudworksmostofthetime,butyoumaygetanerrorsometimes,ifthat'sthecase,switchingbackto115200willprobablysolveallproblems.

EstablishingaWi-FiconnectionLikeImentionedinthepreviouschapter,theESP8266canoperateinthreedifferentmodes:Wi-Fistation,Wi-Fiaccesspoint,andbothatthesametime.We'llstartbylookingattheconfigurationofaWi-Fistation.

Stationmode

Connectingtoonespecificnetwork

#include<ESP8266WiFi.h>//IncludetheWi-Filibrary

constchar*ssid="SSID";//TheSSID(name)oftheWi-Finetworkyouwanttoconnecttoconstchar*password="PASSWORD";//ThepasswordoftheWi-Finetwork

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');WiFi.begin(ssid,password);//ConnecttothenetworkSerial.print("Connectingto");Serial.print(ssid);Serial.println("...");

inti=0;while(WiFi.status()!=WL_CONNECTED){//WaitfortheWi-Fitoconnectdelay(1000);Serial.print(++i);Serial.print('');}

Serial.println('\n');Serial.println("Connectionestablished!");Serial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer}

voidloop(){}

Thecodetoconnecttoawirelessaccesspointisrelativelystraightforward:entertheSSIDandthepasswordofthenetworkyouwanttoconnectto,andcalltheWiFi.beginfunction.Thenwaitfortheconnectiontocomplete,etvoilà,yourESP8266isnowconnectedtoyourLocalAreaNetwork.

Don'tbelieveme?I'llproveittoyou:opentheSerialmonitor(CTRL+SHIFT+M)anduploadthesketch.Youshouldseesomethinglikethis:

ConnectingtoSSID...123456...

Connectionestablished!IPaddress: 192.168.1.3

Nowgotoyourcomputerandopenupaterminal:OnWindows,searchfor"CommandPrompt",onMacorLinux,searchfor"Terminal".Youcouldalsousetheshortcuts:onWindows,hit +R,type"cmd"andhitenter,onLinux,useCTRL+ALT+T.

Next,typeping,andthentheIPaddressyoureceivedintheSerialmonitor.Ifyou'reonMacorLinux,useCTRL+Ctostopitafteracoupleoflines.Theoutputshouldlooksomethinglikethis:

user@computername:~$ ping192.168.1.3PING192.168.1.3(192.168.1.3)56(84)bytesofdata.64bytesfrom192.168.1.3:icmp_seq=1ttl=128time=6.38ms64bytesfrom192.168.1.3:icmp_seq=2ttl=128time=45.2ms64bytesfrom192.168.1.3:icmp_seq=3ttl=128time=69.1ms64bytesfrom192.168.1.3:icmp_seq=4ttl=128time=94.0ms64bytesfrom192.168.1.3:icmp_seq=5ttl=128time=20.5ms64bytesfrom192.168.1.3:icmp_seq=6ttl=128time=7.37ms^C---192.168.1.3pingstatistics---6packetstransmitted,6received,0%packetloss,time5003msrttmin/avg/max/mdev=6.384/40.463/94.047/32.588ms

ThepingcommandsendssmallpacketstotheIPaddressoftheESP8266.WhentheESPreceivessuchapacket,itsendsitbacktothesender.PingispartofthesecondlayeroftheTCP/IPstack,theInternetlayer.ItreliesonboththeDataLinklayer(Wi-Fi)andtheInternetProtocol*.Youcanseethatintheexampleabove,wesent6packetstotheESP,andwealsoreceived6response(echo)packets.ThistellsusthattheDataLink,theWi-Ficonnection,andtheInternetProtocolareworkingcorrectly.

WenowknowthattheESPcansuccessfullycommunicatewithotherdevicesonthenetwork,andifyourlocalnetworkisonline(ifitisconnectedtotheInternetviayourmodem),theESPcanalsocommunicatewithanydeviceontheweb!

PingisagreattooltocheckiftheESP(oranydevice,really)isstillconnectedtothenetwork,andifit'sstillworkingfine.OnedrawbackisthatIPaddressescanchangeovertime,butthat'saproblemwe'lladdressinoneofthefollowingchapters...

(*)I'msimplifyingthingsabithere.Actually,pingispartoftheInternetControlMessageProtocol(ICMP),that'salsopartofthesecondlayer,justliketheInternetProtocol.Don'tworrytoomuchaboutit,justrememberthatifyoucansendpingpacketstoadevice,youcanalsosendIPpackets.

Thedevicewiththeantennaservesmanydifferentpurposes:

Accesspoint:OtherWi-Fidevicescanconnecttoit,tobepartofthelocalnetwork.Router:ItroutesIPpacketstotherightsub-netssothattheywillarriveattheirdestination.E.g.ifthecomputersendsamessagethatismeantfortheESPovertheEthernetsub-net,therouterwillsendthepackettotheWi-Fisub-net,becauseitknowsthat'swheretheESPis.Modem:iftheroutercan'tfindtheaddresseeonthelocalnetwork,thepacketwillbepassedontotheintegratedmodem,anditwillbesenttotheInternetServiceProvideroveraDSLline,headingfortheInternet,wherelotsofotherrouterswilltrytogetthepackettotherightdestination.

Butinreality,youdon'thavetoworrytoomuchaboutit,becauseit'salldoneforyou,inafractionofasecondwithoutyouevennoticingit!

Automaticallyconnecttothestrongestnetwork

Thesketchabovemightbeenoughforyourspecificapplication,butifyouneedtobeabletoconnecttomultipleWi-Finetworks,forexampletheWi-FiathomeandtheWi-Fiattheoffice,itwon'twork.Tosolvethisproblem,we'llusetheWi-Fi-Multilibrary:Youcanaddasmanynetworksasyoulike,anditautomaticallyconnectstotheonewiththestrongestsignal.

#include<ESP8266WiFi.h>//IncludetheWi-Filibrary#include<ESP8266WiFiMulti.h>//IncludetheWi-Fi-Multilibrary

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect:scanforWi-Finetworks,andconnecttothestrongestofthenetworksabovedelay(1000);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer}

voidloop(){}

AccessPointmode

ToconfiguretheESP8266asanaccesspoint,toallowotherdeviceslikesmartphonesorlaptopstoconnecttoit,youcanusethesoftAPfunction:

#include<ESP8266WiFi.h>//IncludetheWi-Filibrary

constchar*ssid="ESP8266AccessPoint";//ThenameoftheWi-Finetworkthatwillbecreatedconstchar*password="thereisnospoon";//Thepasswordrequiredtoconnecttoit,leaveblankforanopennetwork

voidsetup(){Serial.begin(115200);delay(10);Serial.println('\n');

WiFi.softAP(ssid,password);//StarttheaccesspointSerial.print("AccessPoint\"");Serial.print(ssid);Serial.println("\"started");

Serial.print("IPaddress:\t");Serial.println(WiFi.softAPIP());//SendtheIPaddressoftheESP8266tothecomputer}

voidloop(){}

Toseeifitworks,opentheWi-Fisettingsonyourcomputer,lookforanetworkcalled"ESP8266AccessPoint",enterthepassword"thereisnospoon",andconnecttoit.Thenopenaterminal,andpingto192.168.4.1(thisisthedefaultIPaddressofourESPAP).You'llseethattheESPrespondstoyourpings.

However,ifyoutrytogotoanonlinewebsite,you'llgetatimeoutoraDNSerror.ThisisbecausetheESPitselfisnotconnectedtotheinternet.Thesub-netthatconsistsoftheESPandthecomputerisnotconnectedtoanyothernetworks,sothere'snowayforapacketonthisnetworktomakeittotheInternet.

IfyouconnectedasecondstationtotheESPaccesspointontheotherhand,youwouldbeabletopingfromonestationtotheotherwithoutproblems,becausethey'reonthesamenetwork.

MulticastDomainNameSystemDNS

Let'sfaceit,constantlytypingIPaddressesisreallycumbersome,anditwouldbeimpossibletorememberallyourfavoritewebsites'addresses,especiallyiftheyuseIPv6.That'swhydomainnameswereintroduced:asimplestringoftextthat'seasytoremember,forexamplewww.google.com.

However,tosendarequesttoawebsite,yourcomputerstillneedstoknowitsIPaddress.That'swhereDNScomesin.ItstandsforDomainNameSystem,andisawaytotranslateawebsite'sdomainnametoitsIPaddress.OntheInternet,therearealotofDNSservers.EachDNSserverhasalonglistofdomainnamesandtheircorrespondingIPaddresses.DevicescanconnecttoaDNSserverandsendadomainname,theDNSserverwillthenrespondwiththeIPaddressoftherequestedsite.Youcouldcompareittoatelephonedirectory:youcanlookupanametofindthecorrespondingphonenumber.

TheDNSlookuphappenscompletelyinthebackground:whenyougotoawebsiteinyourbrowser,itwillfirstsendarequesttoaDNSserver(thisimpliesthatthecomputerknowstheIPaddressoftheDNSserveritself),waitfortheresponseofthelookup,andthensendtheactualrequesttotherightIPaddress.

mDNS

DNSworksgreatfornormalsitesontheInternet,butmostlocalnetworksdon'thavetheirownDNSserver.Thismeansthatyoucan'treachlocaldevicesusingadomainname,andyou'restuckusingIPaddresses...

Fortunately,there'sanotherway:multicastDNS,ormDNS.mDNSusesdomainnameswiththe.localsuffix,forexamplehttp://esp8266.local.Ifyourcomputerneedstosendarequesttoadomainnamethatendsin.local,itwillsendamulticastquerytoallotherdevicesontheLANthatsupportmDNS,askingthedevicewiththatspecificdomainnametoidentifyitself.ThedevicewiththerightnamewillthenrespondwithanothermulticastandsenditsIPaddress.NowthatyourcomputerknowstheIPaddressofthedevice,itcansendnormalrequests.

Luckilyforus,theESP8266ArduinoCoresupportsmDNS:

#include<ESP8266WiFi.h>//IncludetheWi-Filibrary#include<ESP8266WiFiMulti.h>//IncludetheWi-Fi-Multilibrary#include<ESP8266mDNS.h>//IncludethemDNSlibrary

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect:scanforWi-Finetworks,andconnecttothestrongestofthenetworksabovedelay(1000);Serial.print(++i);Serial.print('');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

if(!MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.localSerial.println("ErrorsettingupMDNSresponder!");}Serial.println("mDNSresponderstarted");}

voidloop(){}

Uploaditandopenpingagain.Trytopingtoesp8266.local:

user@computername:~$ pingesp8266.localPINGesp8266.local(10.92.237.128)56(84)bytesofdata.64bytesfrom10.92.237.128:icmp_seq=1ttl=128time=5.68ms64bytesfrom10.92.237.128:icmp_seq=2ttl=128time=3.41ms64bytesfrom10.92.237.128:icmp_seq=3ttl=128time=2.55ms64bytesfrom10.92.237.128:icmp_seq=4ttl=128time=2.19ms64bytesfrom10.92.237.128:icmp_seq=5ttl=128time=2.29ms64bytesfrom10.92.237.128:icmp_seq=6ttl=128time=2.74ms^C---esp8266.localpingstatistics---6packetstransmitted,6received,0%packetloss,time5007msrttmin/avg/max/mdev=2.190/3.148/5.687/1.202ms

Asyoucansee,pingwillautomaticallyfindtheIPaddressoftheESPforyou.

mDNSissupportedonWindows,OSX,LinuxandiOS,butnot(yet?)onAndroid.It'sarealshamethatAndroiddoesn'tsupportit,youcanhelpbystarringthisissuereportfortheChromiumprojecttoaskformDNSsupportinChromeonAndroid.

Ofcourse,youcanchangethedomainnameoftheESPbychangingtheparameterofMDNS.begin.

ESP8266WebServerBeingabletopingtheESPisquiteanachievementifyoulookatitfromatechnicalpointofview,butformostpeople,it'snotthatexciting,andnotreallyuseful.Inthischapter,I'llcoverthebasicsofawebserver,andteachyouhowtohostawebpageontheESP.

WebserversAwebserverisanInternet-connecteddevicethatstoresandservesfiles.Clientscanrequestsuchafileoranotherpieceofdata,andtheserverwillthensendtherightdata/filesbacktotheclient.RequestsaremadeusingHTTP.

HTTP

HTTPortheHypertextTransferProtocolisthetext-basedprotocolusedtocommunicatewith(web)servers.TherearemultipleHTTPrequestmethods,butI'llonlycoverthetwomostwidelyusedones:GETandPOST.

HTTPGET

GETrequestsareusedtoretrievedatafromaserver,awebpageforinstance.Itshouldn'tchangeanythingontheserver,itjustgetsthedatafromtheserver,withoutsideeffects.

Whenyouopenawebpageinyourbrowser,itwilltaketheURLandputitinanHTTPGETrequest.Thisisjustplaintext.ThenitwillsendtherequesttotherightserverusingTCP.Theserverwillreadtherequest,checktheURL,andsendtherightHTTPresponseforthatURLbacktothebrowser.

TheanatomyofaGETrequest

ThemostimportantpartsofaGETrequestaretherequestlineandthehostheader.Let'stakealookatanexample:Ifyouclickthefollowinglink:https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html,yourbrowserwillsendoutthefollowingHTTPrequest:

GET/Protocols/rfc2616/rfc2616-sec5.htmlHTTP/1.1Host:www.w3.orgConnection:keep-alivePragma:no-cacheCache-Control:no-cacheUpgrade-Insecure-Requests:1User-Agent:Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/56.0.2924.87Safari/537.36Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8DNT:1Referer:https://www.google.be/Accept-Encoding:gzip,deflate,sdch,brAccept-Language:en-US,en;q=0.8

Thefirstlineistherequestline:itcontainstherequestmethod:GET,inthiscase,theURIorUniformResourceIdentifier:/Protocols/rfc2616/rfc2616-sec5.html,andtheHTTPversion:1.1.Thesecondlineisthehostheader,itspecifiesthedomainnameofthehost(server).Therearemanyotherheadersaswell,butthey'renotreallyimportantwhenusinganESP8266.MostserverswillcheckiftheURIisafileontheirfilesystem,andifthat'sthecase,they'llsendthatfileasaresponse.

ViewingHTTPheadersinthebrowser

Ifyouwanttochecktheheadersyourbrowsersends,youcanpressF12,gotothenetworktab,reloadthepage,andclicktherequestyouwanttoinspect.Ifyouwant,youcanclick'viewsource',thiswillshowyoutheactualHTTPtext.Here'swhatthatlookslikeinChrome:

Sendingextrainformationtotheserver

Sometimes,youmightwanttoaddextrainformationtotheGETrequest.Youcansendkey-valuepairsbyaddingaquestionmark(?)totheURI,followedbykey=value.Multiplepairsareseparatedbyanampersand(&).Forexample:

GET/get-phone-number.php?firstName=John&lastName=DoeHTTP/1.1Host:www.phonebook.example.com...

Ifyouuseanyspecialcharactersinthekeyorvaluenames,youhavetoURL-encodethem.

HTTPPOST

POSTrequestsareusedtosenddatatotheserver,forexample,tosendyourusernameandpasswordtotheserverwhenyoulogin,orwhenyouuploadaphoto.UnlikeGET,POSTcanchangethedataontheserverorthestateoftheserver.POSThasabodythatcancontaindatathatissenttotheserver.

TheanatomyofaPOSTrequest

Forexample,theloginpageofyourfavoritesitemightsendsomethinglikethiswhenyouenteryourcredentialsandclicktheloginbutton:

POST/login.phpHTTP/1.1Host:www.example.comConnection:keep-aliveContent-Length:480Origin:http://www.example.comContent-Type:multipart/form-data;boundary=----WebKitFormBoundaryQNEJOasMvgAOg8Kt...

Asyoucansee,therequestlinenowhasthePOSTmethodinit,andisstillfollowedbyaURI,/login.php,andtheHTTPversion,1.1.Thehostheaderstillcontainsjustthedomainname.

Therealdifferenceistherequestbody:aGETrequesthasnopayload,whileyoucanaddalotofdatatothebodyofaPOSTrequest.Thisdatacouldbenormalkey-valuepairs,likeausernameandapassword,oractualfilesthatarebeinguploaded.AlsonotetheContent-Typeheader:ittellstheserverwhatkindofdatacanbefoundinthebodyofthePOSTrequest.

Let'stakealookatthebodyoftheloginexample:

------WebKitFormBoundaryQNEJOasMvgAOg8KtContent-Disposition:form-data;name="username"

JohnDoe------WebKitFormBoundaryQNEJOasMvgAOg8KtContent-Disposition:form-data;name="password"

p@ssw0rd123------WebKitFormBoundaryQNEJOasMvgAOg8KtContent-Disposition:form-data;name="token"

9i9ZoLHl5pkRAeuKCEu76TbaCnMphwYkPEovEUY9PHk=------WebKitFormBoundaryQNEJOasMvgAOg8Kt--

Asyoucansee,therearethreeparametersinsidethebody,everyparameterhasaname(e.g.username),andavalue(e.g.JohnDoe).

YoucouldalsousethesamesyntaxweusedbeforewhenaddingparameterstoaGETrequest:

POST/add-user.phpHTTP/1.1Host:www.example.comContent-Length:27Content-Type:application/x-www-form-urlencoded...

Andthepayload:

firstName=John&lastName=Doe

Asyoucansee,theContent-Typeheaderisdifferent,indicatingthattheencodingofthevaluesinthepayloadisdifferent.

HTTPstatuscodesAservershouldanswerallrequestswithanHTTPstatuscode.Thisisa3-digitnumberindicatingiftherequestwassuccessfulortellingtheclientwhatwentwrong.Here'satablewithsomeofthemostimportantandusefulones.StatusCode Meaning200 OK:therequestwassuccessful303 SeeOther:usedtoredirecttoadifferentURI,afteraPOSTrequest,forinstance400 BadRequest:theservercouldn'tunderstandtherequest,becausethesyntaxwasincorrect401 Unauthorized:userauthenticationisrequired403 Forbidden:theserverrefusestoexecutetherequest,authorizationwon'thelp404 NotFound:therequestedURIwasnotfound500 InternalServerError:Theserverencounteredanunexpectedconditionandcouldn'tfulfilltherequest

TCP&UDPPorts

Inmostcases,onedevicehasmanydifferentservices,forexample,awebserver,anemailserver,anFTPserver,aSpotifystreamingservice,...IfthedevicehadjustanIPaddress,itwouldbeimpossibletoknowwhichapplicationapacketwassentto.That'swhyeveryservicehasaportnumber.It'sanidentifierforalldifferentservicesorapplicationsonasingledevice.Intheexampleabove,thewebserverwillonlylistenforrequestsonport80,theemailserveronlyonport25,theFTPserveronlyonport20,Spotifywillonlyreceivestreamsonport4371...Tospecifyacertainport,youcanuseacolonaftertheIPaddressofafterthedomainname.Butmostofthetime,youdon'thavetoadditexplicitly.Forexample,allwebserverslistenonport80,soawebbrowserwillalwaysconnecttoport80.

http://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urnhttps://www.w3.org/Protocols/rfc2616/rfc2616-sec10.htmlhttps://en.wikipedia.org/wiki/List_of_HTTP_status_codes

ESP8266FirstWebServerTheactualimplementationofawebserverismucheasierthanitsounds,becausetheESP8266ArduinoCoreincludessomegreatlibrariesthathandleprettymucheverythingforyou.Let'slookatabasicHelloWorld!example.

#include<ESP8266WiFi.h>#include<WiFiClient.h>#include<ESP8266WiFiMulti.h>#include<ESP8266mDNS.h>#include<ESP8266WebServer.h>//IncludetheWebServerlibrary

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80

voidhandleRoot();//functionprototypesforHTTPhandlersvoidhandleNotFound();

voidsetup(void){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect:scanforWi-Finetworks,andconnecttothestrongestofthenetworksabovedelay(250);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.localSerial.println("mDNSresponderstarted");}else{Serial.println("ErrorsettingupMDNSresponder!");}

server.on("/",handleRoot);//Callthe'handleRoot'functionwhenaclientrequestsURI"/"server.onNotFound(handleNotFound);//WhenaclientrequestsanunknownURI(i.e.somethingotherthan"/"),callfunction"handleNotFound"

server.begin();//ActuallystarttheserverSerial.println("HTTPserverstarted");}

voidloop(void){server.handleClient();//ListenforHTTPrequestsfromclients}

voidhandleRoot(){server.send(200,"text/plain","Helloworld!");//SendHTTPstatus200(Ok)andsendsometexttothebrowser/client}

voidhandleNotFound(){server.send(404,"text/plain","404:Notfound");//SendHTTPstatus404(NotFound)whenthere'snohandlerfortheURIintherequest}

There'salotofcodethat'sthesameasintheWi-FiandmDNSexamples.Theactualservercodeisprettystraightforward.First,wecreateaserverinstancethatlistensforHTTPrequestsonport80.Thisisthedefaultportforwebservers.Inthesetup,wetelltheserverwhattodowithcertainHTTPrequests.IftheURI'/'isrequested,theservershouldreplywithaHTTPstatuscodeof200(Ok)andthensendaresponsewiththewords'Helloworld!'.Weputthecodeforgeneratingaresponseinaseparatefunction,andthewetelltheservertoexecuteitwhen'/'isrequested,usingtheserver.onfunction.

Wehaven'tspecifiedwhattheservershoulddoiftheclientrequestsanyURIotherthan'/'.ItshouldrespondwithanHTTPstatus404(NotFound)andamessagefortheuser.Weputthisinafunctionaswell,anduseserver.onNotFoundtotellitthatitshouldexecuteitwhenitreceivesarequestforaURIthatwasn'tspecifiedwithserver.on.

ThenwestartlisteningforHTTPrequestsbyusingserver.begin.Duringtheloop,weconstantlycheckifanewHTTPrequestisreceivedbyrunningserver.handleClient.IfhandleClientdetectsnewrequests,itwillautomaticallyexecutetherightfunctionsthatwespecifiedinthesetup.

Totestitout,uploadthesketch,openanewbrowsertab,andbrowsetohttp://esp8266.local.YoushouldgetawebpagesayingHelloworld!.Ifyoutrytogotoadifferentpage,http://esp8266.local/test,forinstance,youshouldgeta404error:404:Notfound.

TurningonandoffanLEDoverWi-FiWecanusethewebservertoserveinteractivepages,andtoreacttocertainPOSTrequest.Inthefollowingexample,theESP8266hostsawebpagewithabutton.Whenthebuttonispressed,thebrowsersendsaPOSTrequestto/LED.WhentheESPreceivessuchaPOSTrequestonthe/LEDURI,itwillturnonorofftheLED,andthenredirectthebrowserbacktothehomepagewiththebutton.

Inordertoperformthisredirect,theESPhastoaddaLocationheadertotheresponse,andusea303(SeeOther)HTTPstatuscode.

ThebuttontosendthePOSTrequestinthebrowserispartofanHTMLform.YouhavetospecifythetargetURItosendtherequestto,andtherequestmethod,inthiscasethisis"/LED"andPOSTrespectively.

NotethatIchangedthecontenttypeoftheresponsefrom"text/plain"to"text/html".Ifyousenditasplaintext,thebrowserwill

displayitastextinsteadofinterpretingitasHTMLandshowingitasabutton.

#include<ESP8266WiFi.h>#include<WiFiClient.h>#include<ESP8266WiFiMulti.h>#include<ESP8266mDNS.h>#include<ESP8266WebServer.h>

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80

constintled=2;

voidhandleRoot();//functionprototypesforHTTPhandlersvoidhandleLED();voidhandleNotFound();

voidsetup(void){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

pinMode(led,OUTPUT);

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect:scanforWi-Finetworks,andconnecttothestrongestofthenetworksabovedelay(250);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.localSerial.println("mDNSresponderstarted");}else{Serial.println("ErrorsettingupMDNSresponder!");}

server.on("/",HTTP_GET,handleRoot);//Callthe'handleRoot'functionwhenaclientrequestsURI"/"server.on("/LED",HTTP_POST,handleLED);//Callthe'handleLED'functionwhenaPOSTrequestismadetoURI"/LED"server.onNotFound(handleNotFound);//WhenaclientrequestsanunknownURI(i.e.somethingotherthan"/"),callfunction"handleNotFound"

server.begin();//ActuallystarttheserverSerial.println("HTTPserverstarted");}

voidloop(void){server.handleClient();//ListenforHTTPrequestsfromclients}

voidhandleRoot(){//WhenURI/isrequested,sendawebpagewithabuttontotoggletheLEDserver.send(200,"text/html","<formaction=\"/LED\"method=\"POST\"><inputtype=\"submit\"value=\"ToggleLED\"></form>");}

voidhandleLED(){//IfaPOSTrequestismadetoURI/LEDdigitalWrite(led,!digitalRead(led));//ChangethestateoftheLEDserver.sendHeader("Location","/");//Addaheadertorespondwithanewlocationforthebrowsertogotothehomepageagainserver.send(303);//SenditbacktothebrowserwithanHTTPstatus303(SeeOther)toredirect}

voidhandleNotFound(){server.send(404,"text/plain","404:Notfound");//SendHTTPstatus404(NotFound)whenthere'snohandlerfortheURIintherequest}

Asyoucansee,theserver.onfunctionnowtakesthreeparameters:theURI,therequestmethod(GETorPOST)andthefunctiontoexecute.

ConnectanLEDtoGPIO2,anduploadthesketch.Thengotohttp://esp8266.local/andclickthebuttontoturntheLEDonoroff.

YoucanopenthedeveloperoptionsinChrome(F12)tochecktheHTTPrequestthataremadewhenyouclickthebutton:you'llseethatitfirstsendaPOSTrequest,andthenreceivesa303(SeeOther)HTTPstatusasaresponse.TheresponsealsohasaLocationheadercontainingtheURI"/",sothebrowserwillsendaGETrequesttotheURIofthisnewlocation:

Ifyoucheckthepagesource(CTRL+U),youcanseethesimpleHTMLformthat'sused:

<formaction="/LED"method="POST"><inputtype="submit"value="ToggleLED"></form>

SendingdatatotheESPusingHTTPPOSTInthepreviousexample,wesentanemptyPOSTrequesttotheESP8266.Inthepreviouschapterhowever,Iexplainedthatit'spossibletosendallkindsofdatainthebodyofthePOSTrequest.

Inthisexample,I'llshowyouhowtosendausernameandapasswordtotheESP.TheESPwillthencheckiftheyarecorrect,andrespondtotherequestwiththeappropriatepage.

#include<ESP8266WiFi.h>#include<WiFiClient.h>#include<ESP8266WiFiMulti.h>#include<ESP8266mDNS.h>#include<ESP8266WebServer.h>

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80

voidhandleRoot();//functionprototypesforHTTPhandlersvoidhandleLogin();voidhandleNotFound();

voidsetup(void){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect:scanforWi-Finetworks,andconnecttothestrongestofthenetworksabovedelay(250);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");

Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.localSerial.println("mDNSresponderstarted");}else{Serial.println("ErrorsettingupMDNSresponder!");}

server.on("/",HTTP_GET,handleRoot);//Callthe'handleRoot'functionwhenaclientrequestsURI"/"server.on("/login",HTTP_POST,handleLogin);//Callthe'handleLogin'functionwhenaPOSTrequestismadetoURI"/login"server.onNotFound(handleNotFound);//WhenaclientrequestsanunknownURI(i.e.somethingotherthan"/"),callfunction"handleNotFound"

server.begin();//ActuallystarttheserverSerial.println("HTTPserverstarted");}

voidloop(void){server.handleClient();//ListenforHTTPrequestsfromclients}

voidhandleRoot(){//WhenURI/isrequested,sendawebpagewithabuttontotoggletheLEDserver.send(200,"text/html","<formaction=\"/login\"method=\"POST\"><inputtype=\"text\"name=\"username\"placeholder=\"Username\"></br><inputtype=\"password\"name=\"password\"placeholder=\"Password\"></br><inputtype=\"submit\"value=\"Login\"></form><p>Try'JohnDoe'and'password123'...</p>");}

voidhandleLogin(){//IfaPOSTrequestismadetoURI/loginif(!server.hasArg("username")||!server.hasArg("password")||server.arg("username")==NULL||server.arg("password")==NULL){//IfthePOSTrequestdoesn'thaveusernameandpassworddataserver.send(400,"text/plain","400:InvalidRequest");//Therequestisinvalid,sosendHTTPstatus400return;}if(server.arg("username")=="JohnDoe"&&server.arg("password")=="password123"){//Ifboththeusernameandthepasswordarecorrectserver.send(200,"text/html","<h1>Welcome,"+server.arg("username")+"!</h1><p>Loginsuccessful</p>");}else{//Usernameandpassworddon'tmatchserver.send(401,"text/plain","401:Unauthorized");}}

voidhandleNotFound(){server.send(404,"text/plain","404:Notfound");//SendHTTPstatus404(NotFound)whenthere'snohandlerfortheURIintherequest}

TheHTMLinhandleRootis:

<formaction="/login"method="POST"><inputtype="text"name="username"placeholder="Username"></br><inputtype="password"name="password"placeholder="Password"></br><inputtype="submit"value="Login"></form><p>Try'JohnDoe'and'password123'...</p>

Uploadthesketchandgotohttp://esp8266.local/,thentype'JohnDoe'intotheusernamefield,and'password123'intothepasswordfield,andclick'Login'.Youshouldgetawelcomescreen.Ifyouleaveonorbothofthefieldsblank,youshouldgeta400(BadRequest)error.Ifyouenterawrongusernameorpassword,youshouldgeta401(Unauthorized)error.

ThedataofthePOSTbodycanbeaccessedusingserver.arg("key"),andyoucancheckifaspecifickeyexistsusingserver.hasArg("key").ThekeynameontheESP8266correspondstothenameargumentintheHTMLformonthewebpage.

WhenwegetaPOSTrequest,wefirstcheckifthenecessaryarguments(usernameandpassword)arepresent.Ifthat'snotthecase,wesenda400(InvalidRequest)status.Thenwecheckifthecredentialsmatch'JohnDoe'&'password123'.Ifthat'sthecase,werespondwithastatus200(Ok)andawelcomepage.Iftheusernameand/orpassworddoesn'tmatch,wesenda401(Unauthorized)status.

InlinefunctionsInthepreviousexamples,wepassedhandleRootandhandleNotFoundtotheserver.onfunctionasaparameter(callbackfunction).Insomecaseshowever,it'smorereadabletojustwritethedefinitionofthefunctioninline,likeso:

voidsetup(){//...server.onNotFound([](){server.send(404,"text/plain","404:Notfound");});}

SPIFlashFileSystemUpuntilnow,we'vealwaysincludedtheHTMLforourwebpagesasstringliteralsinoursketch.Thismakesourcodeveryhardtoread,andyou'llrunoutofmemoryratherquickly.Ifyouremembertheintroduction,ImentionedtheSerialPeripheralInterfaceFlashFileSystem,orSPIFFSforshort.It'salight-weightfilesystemformicrocontrollerswithanSPIflashchip.Theon-boardflashchipoftheESP8266hasplentyofspaceforyourwebpages,especiallyifyouhavethe1MB,2MBor4MBversion.

SPIFFSlet'syouaccesstheflashmemoryasifitwasanormalfilesystemliketheoneonyourcomputer(butmuchsimplerofcourse):youcanreadandwritefiles,createfolders...

TheeasiestwaytolearnhowtouseSPIFFSistolookatsomeexamples.Butafileserverwithnofilestoserveisprettypointless,soI'llexplainhowtouploadfilestotheSPIFFSfirst.

UploadingfilestoSPIFFSToselecttherightfilestoupload,youhavetoplacetheminafoldercalleddata,insidethesketchfolderofyourproject:OpenyoursketchintheArduinoIDE,andhitCTRL+K.Waitforafileexplorerwindowtoopen,andcreateanewfoldernameddata.Copyyourfilesovertothisfolder.(Onlyusesmallfilesliketextfilesoricons.There'snotenoughspaceforlargephotosorvideos.)Next,selectallfilesinthefolder(CTRL+A)andcheckthesizeofallfilescombined(don'tforgetsubfolders).GototheArduinoIDEagain,andunderTools>FlashSize,selectanoptionwiththerightflashsizeforyourboard,andaSPIFFSsizethatislargerthanthesizeofyourdatafolder.Thenuploadthesketch.Whenthat'sfinished,makesurethattheSerialMonitorisclosed,thenopentheToolsmenu,andclickESP8266sketchdataupload.IfyourESPhasauto-resetandauto-program,itshouldworkautomatically,ifyoudon'thaveauto-program,youhavetomanuallyenterprogrammodebeforeuploadingthedatatoSPIFFS.Theprocedureisexactlythesameasenteringprogrammodebeforeuploadingasketch.

IfyougetanerrorsayingSPIFFS_writeerror(-10001):Filesystemisfull,thismeansthatyourfilesaretoolargetofitintotheSPIFFSmemory.SelectalargerSPIFFSsizeunderTools>FlashSize,ordeletesomefiles.EvenifyourcomputersaysthatthefilesaresmallerthantheselectedSPIFFSsize,youcanstillgetthiserror:thishastodowithblocksizes,andmetadatalikefileandfoldernamesthattakeupspaceaswell.

IfyouchangetheSPIFFSsize,youhavetoreuploadyoursketch,becausewhenyouchangetheSPIFFSsize,thememorylocationwillbedifferent.TheprogramhastoknowtheupdatedSPIFFSaddressoffsettobeabletoreadthefiles.

SPIFFSFileServer

Thefollowingexampleisaverybasicfileserver:itjusttakestheURIoftheHTTPrequest,checksiftheURIpointstoafileintheSPIFFS,andifitfindsthefile,itsendsitasaresponse.

#include<ESP8266WiFi.h>#include<WiFiClient.h>#include<ESP8266WiFiMulti.h>#include<ESP8266mDNS.h>#include<ESP8266WebServer.h>#include<FS.h>//IncludetheSPIFFSlibrary

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80

StringgetContentType(Stringfilename);//convertthefileextensiontotheMIMEtypeboolhandleFileRead(Stringpath);//sendtherightfiletotheclient(ifitexists)

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnectdelay(250);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.localSerial.println("mDNSresponderstarted");}else{Serial.println("ErrorsettingupMDNSresponder!");}

SPIFFS.begin();//StarttheSPIFlashFilesSystemserver.onNotFound([](){//IftheclientrequestsanyURIif(!handleFileRead(server.uri()))//senditifitexistsserver.send(404,"text/plain","404:NotFound");//otherwise,respondwitha404(NotFound)error});

server.begin();//ActuallystarttheserverSerial.println("HTTPserverstarted");}

voidloop(void){server.handleClient();}

StringgetContentType(Stringfilename){//convertthefileextensiontotheMIMEtypeif(filename.endsWith(".html"))return"text/html";elseif(filename.endsWith(".css"))return"text/css";elseif(filename.endsWith(".js"))return"application/javascript";elseif(filename.endsWith(".ico"))return"image/x-icon";return"text/plain";}

boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)Serial.println("handleFileRead:"+path);if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindexfileStringcontentType=getContentType(path);//GettheMIMEtypeif(SPIFFS.exists(path)){//IfthefileexistsFilefile=SPIFFS.open(path,"r");//Openitsize_tsent=server.streamFile(file,contentType);//Andsendittotheclientfile.close();//Thenclosethefileagainreturntrue;}Serial.println("\tFileNotFound");returnfalse;//Ifthefiledoesn'texist,returnfalse}

Asyoucansee,wedon'tuseserver.oninthisexample.Instead,weuseserver.onNotFound:thiswillmatchanyURI,sincewedidn'tdeclareanyspecificURIhandlerslikeinthepreviousserverexamples.WhenaURIisrequested,wecallthefunctionhandleFileRead.ThisfunctionchecksiftheURIoftheHTTPrequestisthepathtoanexistingfileintheSPIFFS.Ifthat'sthecase,itsendsthefilebacktotheclient.Ifthepathdoesn'texist,itreturnsfalse,anda404(NotFound)HTTPstatuswillbesent.

TheMIMEtypeforthedifferentfilesisbasedonthefileextension.Youcouldaddotherfiletypesaswell.Forinstance:

StringgetContentType(Stringfilename){if(filename.endsWith(".htm"))return"text/html";elseif(filename.endsWith(".html"))return"text/html";elseif(filename.endsWith(".css"))return"text/css";elseif(filename.endsWith(".js"))return"application/javascript";elseif(filename.endsWith(".png"))return"image/png";elseif(filename.endsWith(".gif"))return"image/gif";elseif(filename.endsWith(".jpg"))return"image/jpeg";elseif(filename.endsWith(".ico"))return"image/x-icon";elseif(filename.endsWith(".xml"))return"text/xml";elseif(filename.endsWith(".pdf"))return"application/x-pdf";elseif(filename.endsWith(".zip"))return"application/x-zip";elseif(filename.endsWith(".gz"))return"application/x-gzip";return"text/plain";}

ThisexampleisadaptedfromtheFSBrowserexamplebyHristoGochkov.

CompressingfilesTheESP8266'sflashmemoryisn'thuge,andmosttextfiles,likehtml,cssetc.canbecompressedbyquitealargefactor.Modernwebbrowsersacceptcompressedfilesasaresponse,sowe'lltakeadvantageofthisbyuploadingcompressedversionsofourhtmlandiconfilestotheSPIFFS,inordertosavespaceandbandwidth.

Todothis,weneedtoaddtheGNUzipfiletypetoourlistofMIMEtypes:

StringgetContentType(Stringfilename){if(filename.endsWith(".html"))return"text/html";elseif(filename.endsWith(".css"))return"text/css";elseif(filename.endsWith(".js"))return"application/javascript";elseif(filename.endsWith(".ico"))return"image/x-icon";elseif(filename.endsWith(".gz"))return"application/x-gzip";return"text/plain";}

AndweneedtochangeourhandleFileReadfunctionaswell:

boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)Serial.println("handleFileRead:"+path);if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindexfileStringcontentType=getContentType(path);//GettheMIMEtypeStringpathWithGz=path+".gz";if(SPIFFS.exists(pathWithGz)||SPIFFS.exists(path)){//Ifthefileexists,eitherasacompressedarchive,ornormalif(SPIFFS.exists(pathWithGz))//Ifthere'sacompressedversionavailablepath+=".gz";//UsethecompressedversionFilefile=SPIFFS.open(path,"r");//Openthefilesize_tsent=server.streamFile(file,contentType);//Sendittotheclientfile.close();//ClosethefileagainSerial.println(String("\tSentfile:")+path);returntrue;}Serial.println(String("\tFileNotFound:")+path);returnfalse;//Ifthefiledoesn'texist,returnfalse}

Now,trycompressingsomeofthefilestotheGNUzipformat(.gz),anduploadingthemtoSPIFFS.Oryoucanjustdownloadthenewdatafolder(unzipitfirst).Everytimeaclientrequestsacertainfile,theESPwillcheckifacompressedversionisavailable.Ifso,itwillusethatinsteadoftheuncompressedfile.TheoutputintheSerialMonitorshouldlooksomethinglikethis:

handleFileRead:/ Sentfile:/index.html.gzhandleFileRead:/main.css Sentfile:/main.csshandleFileRead:/JavaScript.js Sentfile:/JavaScript.jshandleFileRead:/folder/JavaScript.js Sentfile:/folder/JavaScript.jshandleFileRead:/favicon.ico Sentfile:/favicon.ico.gz

Itautomaticallydetectedthatithadtosendthecompressedversionsofindex.htmlandfavicon.ico.

UploadingfilestotheserverTherearescenarioswhereyoumaywanttouploadnewfilestotheserverfromwithinabrowser,withouthavingtoconnecttotheESP8266overUSBinordertoflashanewSPIFFSimage.Inthischapter,I'llshowyouhowtouseHTMLformsandPOSTrequeststouploadoreditfilestoourlittleESPserver.

Client:HTMLformTheeasiestwaytouploadfilesisbyusinganHTMLform,justlikeinthefirstserverexamples,whereweusedformstoturnon/offLEDs,andtosendthelogincredentialsbacktotheserver.Ifyouchooseafileinput,youautomaticallygetafilepicker,andthebrowserwillsendtherightPOSTrequesttotheserver,withthefileattached.

<formmethod="post"enctype="multipart/form-data"><inputtype="file"name="name"><inputclass="button"type="submit"value="Upload"></form>

Server

IntheESPcode,wehavetoaddahandlertoourserverthathandlesPOSTrequeststothe/uploadURI.WhenitreceivesaPOSTrequest,itsendsastatus200(OK)backtotheclienttostartreceivingthefile,andthenwriteittotheSPIFFS.Whenthefileisuploadedsuccessfully,itredirectstheclienttoasuccesspage.TherelevantnewcodeisfoundinthesetupandthehandleFileUploadfunction.

#include<ESP8266WiFi.h>#include<WiFiClient.h>#include<ESP8266WiFiMulti.h>#include<ESP8266mDNS.h>#include<ESP8266WebServer.h>#include<FS.h>//IncludetheSPIFFSlibrary

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80

FilefsUploadFile;//aFileobjecttotemporarilystorethereceivedfile

StringgetContentType(Stringfilename);//convertthefileextensiontotheMIMEtypeboolhandleFileRead(Stringpath);//sendtherightfiletotheclient(ifitexists)voidhandleFileUpload();//uploadanewfiletotheSPIFFS

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnectdelay(1000);Serial.print(++i);Serial.print('');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

if(!MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.localSerial.println("ErrorsettingupMDNSresponder!");}Serial.println("mDNSresponderstarted");

SPIFFS.begin();//StarttheSPIFlashFilesSystem

server.on("/upload",HTTP_GET,[](){//iftheclientrequeststheuploadpageif(!handleFileRead("/upload.html"))//senditifitexistsserver.send(404,"text/plain","404:NotFound");//otherwise,respondwitha404(NotFound)error});

server.on("/upload",HTTP_POST,//iftheclientpoststotheuploadpage[](){server.send(200);},//Sendstatus200(OK)totelltheclientwearereadytoreceivehandleFileUpload//Receiveandsavethefile);

server.onNotFound([](){//IftheclientrequestsanyURIif(!handleFileRead(server.uri()))//senditifitexistsserver.send(404,"text/plain","404:NotFound");//otherwise,respondwitha404(NotFound)error});

server.begin();//ActuallystarttheserverSerial.println("HTTPserverstarted");}

voidloop(){server.handleClient();}

StringgetContentType(Stringfilename){//convertthefileextensiontotheMIMEtypeif(filename.endsWith(".html"))return"text/html";elseif(filename.endsWith(".css"))return"text/css";elseif(filename.endsWith(".js"))return"application/javascript";elseif(filename.endsWith(".ico"))return"image/x-icon";elseif(filename.endsWith(".gz"))return"application/x-gzip";return"text/plain";}

boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)Serial.println("handleFileRead:"+path);if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindexfileStringcontentType=getContentType(path);//GettheMIMEtypeStringpathWithGz=path+".gz";if(SPIFFS.exists(pathWithGz)||SPIFFS.exists(path)){//Ifthefileexists,eitherasacompressedarchive,ornormalif(SPIFFS.exists(pathWithGz))//Ifthere'sacompressedversionavailablepath+=".gz";//UsethecompressedverionFilefile=SPIFFS.open(path,"r");//Openthefilesize_tsent=server.streamFile(file,contentType);//Sendittotheclientfile.close();//ClosethefileagainSerial.println(String("\tSentfile:")+path);returntrue;}Serial.println(String("\tFileNotFound:")+path);//Ifthefiledoesn'texist,returnfalsereturnfalse;}

voidhandleFileUpload(){//uploadanewfiletotheSPIFFSHTTPUpload&upload=server.upload();if(upload.status==UPLOAD_FILE_START){Stringfilename=upload.filename;if(!filename.startsWith("/"))filename="/"+filename;Serial.print("handleFileUploadName:");Serial.println(filename);fsUploadFile=SPIFFS.open(filename,"w");//OpenthefileforwritinginSPIFFS(createifitdoesn'texist)filename=String();}elseif(upload.status==UPLOAD_FILE_WRITE){if(fsUploadFile)fsUploadFile.write(upload.buf,upload.currentSize);//Writethereceivedbytestothefile}elseif(upload.status==UPLOAD_FILE_END){if(fsUploadFile){//IfthefilewassuccessfullycreatedfsUploadFile.close();//ClosethefileagainSerial.print("handleFileUploadSize:");Serial.println(upload.totalSize);server.sendHeader("Location","/success.html");//Redirecttheclienttothesuccesspageserver.send(303);}else{server.send(500,"text/plain","500:couldn'tcreatefile");}}}

ThehandleFileUploadfunctionjustwritesthefileattachedtothePOSTrequesttoSPIFFS.

Ifyouwantouseotherfiletypesaswell,youcanjustaddthemtothegetContentTypefunction.

UploadingfilesTouploadanewfiletotheESP,ortoupdateanexistingfile,justgotohttp://esp8266.local/upload,clicktheChooseFilebutton,selectthefileyouwishtoupload,andclickUpload.YoucannowentertheURLintotheURLbar,andopenthenewfile.

Anoteonsafety

Thisexampleisn'tverysecure(obviously).EveryonethatcanconnecttotheESPcanuploadnewfiles,oredittheexistingfilesandinsertXSScode,forexample.There'salsonotalotoferrorchecking/handling,likecheckingifthere'senoughspaceintheSPIFFStouploadanewfile,etc.

AdvancedexampleThecodefortheseSPIFFSserverexamplescomes(forthemostpart)fromanexamplewrittenbyHristoGochkov.YoucanfinditunderFile>Examples>ESP8266WebServer>FSBrowser.Ithasawebinterfaceforbrowsingandeditingfilesinyourbrowser,andhassomeothernicefeaturesaswell.

OverTheAirUpdatesUploadingoverSerialisfineduringdevelopment,whenyouhaveaccesstotheSerialpinsandtheUSBport.Butonceyourprojectisfinished,andyouputitinsideanenclosure,itnotthateasytouploadupdateswithbugfixesornewfeatures.AsolutiontothisproblemisOverTheAirupdating,orOTAforshort.Asthenameimplies,thistechnologyallowsyoutouploadnewcodeoverWi-Fi,insteadofSerial.Theonlydisadvantageisthatyouhavetoexplicitlyaddthecodeforittoeverysketchyouupload.Youalsoneedaflashchipthatistwicethesizeofyoursketch,soitwon'tworkfor512kBboards.(Ithastodownloadthenewsketchwhilestillrunningtheoldcode.)Let'stakealookatanexample...

BlinkOTAThefollowingexampleisbasicallyBlinkWithoutDelay,butwiththenecessaryOTAandWi-Ficodeaddedaswell.

#include<ESP8266WiFi.h>#include<ESP8266WiFiMulti.h>#include<ArduinoOTA.h>

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

constbyteled=13;

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnectdelay(250);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputerArduinoOTA.setHostname("ESP8266");ArduinoOTA.setPassword("esp8266");

ArduinoOTA.onStart([](){Serial.println("Start");});ArduinoOTA.onEnd([](){Serial.println("\nEnd");});ArduinoOTA.onProgress([](unsignedintprogress,unsignedinttotal){Serial.printf("Progress:%u%%\r",(progress/(total/100)));});ArduinoOTA.onError([](ota_error_terror){Serial.printf("Error[%u]:",error);if(error==OTA_AUTH_ERROR)Serial.println("AuthFailed");elseif(error==OTA_BEGIN_ERROR)Serial.println("BeginFailed");elseif(error==OTA_CONNECT_ERROR)Serial.println("ConnectFailed");elseif(error==OTA_RECEIVE_ERROR)Serial.println("ReceiveFailed");elseif(error==OTA_END_ERROR)Serial.println("EndFailed");});ArduinoOTA.begin();Serial.println("OTAready");

pinMode(led,OUTPUT);digitalWrite(led,1);}

unsignedlongpreviousTime=millis();

constunsignedlonginterval=1000;

voidloop(){ArduinoOTA.handle();unsignedlongdiff=millis()-previousTime;if(diff>interval){digitalWrite(led,!digitalRead(led));//ChangethestateoftheLEDpreviousTime+=diff;}}

AddyourWi-Ficredentials,anduploadthesketchoverSerial.ConnectanLED(+resistor)topin13.ThenrestarttheIDE(youhavetocloseallwindows).GotoTools>Port,andyoushouldgetanewoption:NetworkPorts:ESP8266at192.168.1.x.Selectit.

Next,changetheintervalonline59from1000to500milliseconds,andclickupload.Youshouldgetapasswordprompt:enter"esp8266".Thispasswordissetonline31,soyoucanchangeitifyouwantto.Youcanalsodeleteline31altogethertouseitwithoutapassword,butit'snotrecommended-forobvioussecurityreasons.

Thesketchshoulduploadjustfine,andoncetheESPhasresetitself,theLEDshouldblinktwiceasfast.

Onceinawhile,youmightgetanerrorsaying[ERROR]:NoAnswer.Ifthishappens,justenterthepassword,andtryagain.

SerialMonitorOTAUsingtheSerialMonitoroverWi-Fiisnotpossible(yet?).Whenyoutrytoopenit,you'llbepromptedapassword,enteringthepasswordwon'twork,becausethereisnoSSHsupporttoaccesstheESP'sconsole.

YoucanuseadifferentprogramtogetdebugoutputfromthephysicalSerialport.OnWindows,youcantryPortmon.OnLinux,youcantryGTKTerm(sudoapt-getinstallgtkterm)orScreen(sudoapt-getinstallscreentoinstall,andscreen/dev/ttyUSB0115200orscreen/dev/ttyACM0115200torun;CTRL+A,CTRL+Dtoexit).

WebSocketcommunicationUpuntilnow,we'vealwaysusedlinks(GET)andHTMLforms(POST)togetdatafromtheESP,ortosenddatatoit.Thisalwaysresultedinabrowsernavigationaction.TherearemanysituationswhereyouwanttosenddatatotheESPwithoutrefreshingthepage.

OnewaytodothisisbyusingAJAXandXMLHTTPrequests.ThedisadvantageisthatyouhavetoestablishanewTCPconnectionforeverymessageyousend.Thisaddsaloadoflatency.WebSocketisatechnologythatkeepstheTCPconnectionopen,soyoucanconstantlysenddatabackandforthbetweentheESPandtheclient,withlowlatency.Andsinceit'sTCP,you'resurethatthepacketswillarriveintact.

ControllingRGBLEDsfromawebinterfaceusingWebSocketTolearnhowtouseWebSockets,Icreatedthiscomprehensiveexample,itusesprettymucheverythingwe'vecoveredsofar.

TheESPhostsawebpagewiththreesliderstosetthered,greenandbluelevelsofanRGBLED(orLEDstrip).There'salsoabuttontoturnonarainboweffectthatcyclesthroughtheentirecolorwheel.ColordataistransmittedfromthebrowsertotheESPviaaWebSocketconnection.YoucanconnecttotheESPdirectly,usingitasanAP,orlettheESPconnecttoadifferentAP.YoucanusemDNStoopenthewebpage,bybrowsingtohttp://esp8266.local.AllfilesarestoredintheESP'sSPIFFS,andyoucanuploadnewfiles,orupdatefilesviaawebinterface.YoucanalsousetheOTAservicetouploadnewfirmware(sketches)overWi-Fi.

Improvingreadability

Whendealingwithlargeandcomplicatedprograms,it'sagoodideatomakeabstractionofsomethings,andcreatefunctionswithadescriptivenameinsteadofendlesslinesofmeaninglesscode.

Evenifyouhavelotsofcommentsinyourcode,it'llbeveryhardtopreserveanoverview.Usingfunctionswillgreatlyimprovethereadabilityofyourcode.Sojustsplitupthecodeintodifferentpartsandmoveallpiecestofunctionsatthebottomofyoursketch,oreventodifferentfiles.

Inthefollowingexample,thesetupwasverylongandcluttered,soIsplititupintoseveraldifferentfunctions:onetoconnecttotheWi-Fi,onetostarttheOTAupdateservice,onetostarttheSPIFFS...andsoon.

DownloadingWebSocketsforArduino

We'llbeusingthearduinoWebSocketslibrarybyLinks2004.DownloaditfromGitHubandinstallit.(Sketch>IncludeLibrary>Add.ZIPLibrary...)

Libraries,constantsandglobals

Atthetopofthesketchwe'llincludethenecessarylibraries,createsomeglobalserverandfileobjectslikeinthepreviousexamples,andsomeconstantsforthehostname,APssid,passwords,LEDpins...

#include<ESP8266WiFi.h>#include<ESP8266WiFiMulti.h>#include<ArduinoOTA.h>#include<ESP8266WebServer.h>#include<ESP8266mDNS.h>#include<FS.h>#include<WebSocketsServer.h>

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

ESP8266WebServerserver=ESP8266WebServer(80);//createawebserveronport80WebSocketsServerwebSocket=WebSocketsServer(81);//createawebsocketserveronport81

FilefsUploadFile;//aFilevariabletotemporarilystorethereceivedfile

constchar*ssid="ESP8266AccessPoint";//ThenameoftheWi-Finetworkthatwillbecreatedconstchar*password="thereisnospoon";//Thepasswordrequiredtoconnecttoit,leaveblankforanopennetwork

constchar*OTAName="ESP8266";//AnameandapasswordfortheOTAserviceconstchar*OTAPassword="esp8266";

#defineLED_RED15//specifythepinswithanRGBLEDconnected#defineLED_GREEN12#defineLED_BLUE13

constchar*mdnsName="esp8266";//DomainnameforthemDNSresponder

Youshouldalreadybefamiliarwithmostofthiscode.TheonlynewpartistheWebSocketserverlibrarythatisincluded,andtheWebSocketserverobject,butthisshouldn'tbeaproblem.

Setup

voidsetup(){pinMode(LED_RED,OUTPUT);//thepinswithLEDsconnectedareoutputspinMode(LED_GREEN,OUTPUT);pinMode(LED_BLUE,OUTPUT);

Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println("\r\n");

startWiFi();//StartaWi-Fiaccesspoint,andtrytoconnecttosomegivenaccesspoints.ThenwaitforeitheranAPorSTAconnectionstartOTA();//StarttheOTAservice

startSPIFFS();//StarttheSPIFFSandlistallcontents

startWebSocket();//StartaWebSocketserverstartMDNS();//StartthemDNSresponder

startServer();//StartaHTTPserverwithafilereadhandlerandanuploadhandler}

Asyoucansee,thesetupisnowmuchmorecondensedandgivesamuchbetteroverviewofwhatit'sdoing.Tounderstandtheprogram,youdon'thavetoknoweachindividualstepthatisrequiredtoconnecttoaWi-Finetwork,it'senoughtoknowthatitwillconnecttoaWi-Finetwork,becausethat'swhatthestartWiFifunctiondoes.

Loop

boolrainbow=false;//Therainboweffectisturnedoffonstartup

unsignedlongprevMillis=millis();inthue=0;

voidloop(){webSocket.loop();//constantlycheckforwebsocketeventsserver.handleClient();//runtheserverArduinoOTA.handle();//listenforOTAevents

if(rainbow){//iftherainboweffectisturnedonif(millis()>prevMillis+32){if(++hue==360)//Cyclethroughthecolorwheel(incrementbyonedegreeevery32ms)hue=0;setHue(hue);//SettheRGBLEDtotherightcolorprevMillis=millis();}}}

Samegoesfortheloop:mostoftheworkisdonebythefirstthreefunctionsthathandletheWebSocketcommunication,HTTPrequestsandOTAupdates.Whensuchaneventhappens,theappropriatehandlerfunctionswillbeexecuted.Thesearedefinedelsewhere.

Thesecondpartistherainboweffect.Ifitisturnedon,itcyclesthroughthecolorwheelandsetsthecolortotheRGBLED.Ifyoudon'tunderstandwhyIusemillis(),youcantakealookattheBlinkWithoutDelayexample.

Setupfunctions

voidstartWiFi(){//StartaWi-Fiaccesspoint,andtrytoconnecttosomegivenaccesspoints.ThenwaitforeitheranAPorSTAconnectionWiFi.softAP(ssid,password);//StarttheaccesspointSerial.print("AccessPoint\"");Serial.print(ssid);Serial.println("\"started\r\n");

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting");while(wifiMulti.run()!=WL_CONNECTED&&WiFi.softAPgetStationNum()<1){//WaitfortheWi-Fitoconnectdelay(250);Serial.print('.');}Serial.println("\r\n");if(WiFi.softAPgetStationNum()==0){//IftheESPisconnectedtoanAPSerial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.print(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer}else{//IfastationisconnectedtotheESPSoftAPSerial.print("StationconnectedtoESP8266AP");}Serial.println("\r\n");}

voidstartOTA(){//StarttheOTAserviceArduinoOTA.setHostname(OTAName);ArduinoOTA.setPassword(OTAPassword);

ArduinoOTA.onStart([](){Serial.println("Start");digitalWrite(LED_RED,0);//turnofftheLEDsdigitalWrite(LED_GREEN,0);digitalWrite(LED_BLUE,0);});ArduinoOTA.onEnd([](){Serial.println("\r\nEnd");});ArduinoOTA.onProgress([](unsignedintprogress,unsignedinttotal){Serial.printf("Progress:%u%%\r",(progress/(total/100)));});ArduinoOTA.onError([](ota_error_terror){Serial.printf("Error[%u]:",error);if(error==OTA_AUTH_ERROR)Serial.println("AuthFailed");elseif(error==OTA_BEGIN_ERROR)Serial.println("BeginFailed");elseif(error==OTA_CONNECT_ERROR)Serial.println("ConnectFailed");elseif(error==OTA_RECEIVE_ERROR)Serial.println("ReceiveFailed");elseif(error==OTA_END_ERROR)Serial.println("EndFailed");});ArduinoOTA.begin();Serial.println("OTAready\r\n");}

voidstartSPIFFS(){//StarttheSPIFFSandlistallcontentsSPIFFS.begin();//StarttheSPIFlashFileSystem(SPIFFS)Serial.println("SPIFFSstarted.Contents:");{Dirdir=SPIFFS.openDir("/");

while(dir.next()){//ListthefilesystemcontentsStringfileName=dir.fileName();size_tfileSize=dir.fileSize();Serial.printf("\tFSFile:%s,size:%s\r\n",fileName.c_str(),formatBytes(fileSize).c_str());}Serial.printf("\n");}}

voidstartWebSocket(){//StartaWebSocketserverwebSocket.begin();//startthewebsocketserverwebSocket.onEvent(webSocketEvent);//ifthere'sanincommingwebsocketmessage,gotofunction'webSocketEvent'Serial.println("WebSocketserverstarted.");}

voidstartMDNS(){//StartthemDNSresponderMDNS.begin(mdnsName);//startthemulticastdomainnameserverSerial.print("mDNSresponderstarted:http://");Serial.print(mdnsName);Serial.println(".local");}

voidstartServer(){//StartaHTTPserverwithafilereadhandlerandanuploadhandlerserver.on("/edit.html",HTTP_POST,[](){//IfaPOSTrequestissenttothe/edit.htmladdress,server.send(200,"text/plain","");},handleFileUpload);//goto'handleFileUpload'

server.onNotFound(handleNotFound);//ifsomeonerequestsanyotherfileorpage,gotofunction'handleNotFound'//andcheckifthefileexists

server.begin();//starttheHTTPserverSerial.println("HTTPserverstarted.");}

Thesearethefunctiondefinitionsofthefunctionsusedinthesetup.Nothingnewhere,apartfromthestartWebSocketfunction.YoujusthavetostarttheWebSocketserverusingthebeginmethod,andthengiveitacallbackfunctionthatisexecutedwhentheESPreceivesaWebSocketmessage.

Serverhandlers

Thisisthecodethatisexecutedoncertainserver-relatedevents,likewhenanHTTPrequestisreceived,whenafileisbeinguploaded,whenthere'sanincomingWebSocketmessage...etc.

voidhandleNotFound(){//iftherequestedfileorpagedoesn'texist,returna404notfounderrorif(!handleFileRead(server.uri())){//checkifthefileexistsintheflashmemory(SPIFFS),ifso,senditserver.send(404,"text/plain","404:FileNotFound");}}

boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)Serial.println("handleFileRead:"+path);if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindexfileStringcontentType=getContentType(path);//GettheMIMEtypeStringpathWithGz=path+".gz";if(SPIFFS.exists(pathWithGz)||SPIFFS.exists(path)){//Ifthefileexists,eitherasacompressedarchive,ornormalif(SPIFFS.exists(pathWithGz))//Ifthere'sacompressedversionavailablepath+=".gz";//UsethecompressedverionFilefile=SPIFFS.open(path,"r");//Openthefilesize_tsent=server.streamFile(file,contentType);//Sendittotheclientfile.close();//ClosethefileagainSerial.println(String("\tSentfile:")+path);returntrue;}Serial.println(String("\tFileNotFound:")+path);//Ifthefiledoesn'texist,returnfalsereturnfalse;}

voidhandleFileUpload(){//uploadanewfiletotheSPIFFSHTTPUpload&upload=server.upload();Stringpath;if(upload.status==UPLOAD_FILE_START){path=upload.filename;if(!path.startsWith("/"))path="/"+path;if(!path.endsWith(".gz")){//ThefileserveralwaysprefersacompressedversionofafileStringpathWithGz=path+".gz";//Soifanuploadedfileisnotcompressed,theexistingcompressedif(SPIFFS.exists(pathWithGz))//versionofthatfilemustbedeleted(ifitexists)SPIFFS.remove(pathWithGz);}Serial.print("handleFileUploadName:");Serial.println(path);fsUploadFile=SPIFFS.open(path,"w");//OpenthefileforwritinginSPIFFS(createifitdoesn'texist)path=String();}elseif(upload.status==UPLOAD_FILE_WRITE){if(fsUploadFile)fsUploadFile.write(upload.buf,upload.currentSize);//Writethereceivedbytestothefile}elseif(upload.status==UPLOAD_FILE_END){if(fsUploadFile){//IfthefilewassuccessfullycreatedfsUploadFile.close();//ClosethefileagainSerial.print("handleFileUploadSize:");Serial.println(upload.totalSize);server.sendHeader("Location","/success.html");//Redirecttheclienttothesuccesspageserver.send(303);}else{server.send(500,"text/plain","500:couldn'tcreatefile");}}}

voidwebSocketEvent(uint8_tnum,WStype_ttype,uint8_t*payload,size_tlenght){//WhenaWebSocketmessageisreceivedswitch(type){caseWStype_DISCONNECTED://ifthewebsocketisdisconnectedSerial.printf("[%u]Disconnected!\n",num);break;caseWStype_CONNECTED:{//ifanewwebsocketconnectionisestablishedIPAddressip=webSocket.remoteIP(num);Serial.printf("[%u]Connectedfrom%d.%d.%d.%durl:%s\n",num,ip[0],ip[1],ip[2],ip[3],payload);rainbow=false;//Turnrainbowoffwhenanewconnectionisestablished}break;caseWStype_TEXT://ifnewtextdataisreceivedSerial.printf("[%u]getText:%s\n",num,payload);if(payload[0]=='#'){//wegetRGBdata

uint32_trgb=(uint32_t)strtol((constchar*)&payload[1],NULL,16);//decodergbdataintr=((rgb>>20)&0x3FF);//10bitspercolor,soR:bits20-29intg=((rgb>>10)&0x3FF);//G:bits10-19intb=rgb&0x3FF;//B:bits0-9

analogWrite(LED_RED,r);//writeittotheLEDoutputpinsanalogWrite(LED_GREEN,g);analogWrite(LED_BLUE,b);}elseif(payload[0]=='R'){//thebrowsersendsanRwhentherainboweffectisenabledrainbow=true;}elseif(payload[0]=='N'){//thebrowsersendsanNwhentherainboweffectisdisabledrainbow=false;}break;}}

Again,mostofthecodeisadaptedfromthepreviousexamples,onlytheWebSocketpartisnew.

TherearedifferenttypesofWebSocketmessages,butwe'reonlyinterestedinthetexttype,becausetheJavaScriptcodeattheclientsidesendsthecolordataintextformat,asahexadecimalnumber,startingwitha'#'sign.Eachcolorisa10-bitnumber,sointotal,itgivesusa30-bitnumberfortheRGBvalue.

Whentherainbowfunctionisenabled,JavaScriptsendsan'R'character,andwhenit'sdisabled,itsendsa'N'character.

Let'stakealookattheHTMLandJavaScriptcodeaswell:

HTML

<!DOCTYPEhtml><html><head><title>LEDControl</title><linkhref='https://fonts.googleapis.com/css?family=Roboto:300'rel='stylesheet'type='text/css'><linkhref='main.css'rel='stylesheet'type='text/css'><linkrel="apple-touch-icon"sizes="180x180"href="/apple-touch-icon-180x180.png"><linkrel="icon"type="image/png"sizes="144x144"href="/favicon-144x144.png"><linkrel="icon"type="image/png"sizes="48x48"href="/favicon.ico"><linkrel="manifest"href="/manifest.json"><metaname="theme-color"content="#00878f"><metacontent='width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0'name='viewport'><scriptsrc="WebSocket.js"type="text/javascript"></script></head>

<body><center><header><h1>LEDControl</h1></header><div><table><tr><tdstyle="width:14.4px;text-align:right">R:</td><td><inputclass="enabled"id="r"type="range"min="0"max="1023"step="1"oninput="sendRGB();"value="0"></td></tr><tr><tdstyle="width:14.4px;text-align:right">G:</td><td><inputclass="enabled"id="g"type="range"min="0"max="1023"step="1"oninput="sendRGB();"value="0"></td></tr><tr><tdstyle="width:14.4px;text-align:right">B:</td><td><inputclass="enabled"id="b"type="range"min="0"max="1023"step="1"oninput="sendRGB();"value="0"></td></tr></table><pstyle="margin:8px0px"><buttonid="rainbow"class="button"style="background-color:#999"onclick="rainbowEffect();">Rainbow</button></p></div></center></body></html>

There'sreallynotmuchtoit,just3slidersandabuttonlinkedtoJavaScriptfunctions.

JavaScript

varrainbowEnable=false;varconnection=newWebSocket('ws://'+location.hostname+':81/',['arduino']);connection.onopen=function(){connection.send('Connect'+newDate());};connection.onerror=function(error){console.log('WebSocketError',error);};connection.onmessage=function(e){console.log('Server:',e.data);};connection.onclose=function(){console.log('WebSocketconnectionclosed');};

functionsendRGB(){varr=document.getElementById('r').value**2/1023;varg=document.getElementById('g').value**2/1023;varb=document.getElementById('b').value**2/1023;

varrgb=r<<20|g<<10|b;varrgbstr='#'+rgb.toString(16);console.log('RGB:'+rgbstr);connection.send(rgbstr);}

functionrainbowEffect(){rainbowEnable=!rainbowEnable;if(rainbowEnable){

connection.send("R");document.getElementById('rainbow').style.backgroundColor='#00878F';document.getElementById('r').className='disabled';document.getElementById('g').className='disabled';document.getElementById('b').className='disabled';document.getElementById('r').disabled=true;document.getElementById('g').disabled=true;document.getElementById('b').disabled=true;}else{connection.send("N");document.getElementById('rainbow').style.backgroundColor='#999';document.getElementById('r').className='enabled';document.getElementById('g').className='enabled';document.getElementById('b').className='enabled';document.getElementById('r').disabled=false;document.getElementById('g').disabled=false;document.getElementById('b').disabled=false;sendRGB();}}

WejustcreateaWebSocketconnectionobjecttosenddatatotheESP.Theneverytimeasliderismoved,wetakethevaluesofthethreeslidersandwesquarethecolorvaluestogetasmootherandmorenaturalcurve.Wethencombinethemintoa30-bitnumber(10bitspercolor).Finally,theRGBvaluegetsconvertedtoahexadecimalstring,a'#'isadded,andit'ssenttotheESP.Whentherainbowbuttonispressed,theslidersaredisabled,andan'R'issenttotheESP.Whentherainbowbuttonispressedagain,theslidersareenabled,andan'N'issent.

Helperfunctions

BacktotheESP8266Arduinocodeagain.Weneedsomeotherfunctionsaswell,toconvertbytestoKBandMB,todeterminefiletypesbasedonfileextensionsandtoconvertahueangletoRGBvalues.

StringformatBytes(size_tbytes){//convertsizesinbytestoKBandMBif(bytes<1024){returnString(bytes)+"B";}elseif(bytes<(1024*1024)){returnString(bytes/1024.0)+"KB";}elseif(bytes<(1024*1024*1024)){returnString(bytes/1024.0/1024.0)+"MB";}}

StringgetContentType(Stringfilename){//determinethefiletypeofagivenfilename,basedontheextensionif(filename.endsWith(".html"))return"text/html";elseif(filename.endsWith(".css"))return"text/css";elseif(filename.endsWith(".js"))return"application/javascript";elseif(filename.endsWith(".ico"))return"image/x-icon";elseif(filename.endsWith(".gz"))return"application/x-gzip";return"text/plain";}

voidsetHue(inthue){//SettheRGBLEDtoagivenhue(color)(0°=Red,120°=Green,240°=Blue)hue%=360;//hueisananglebetween0and359°floatradH=hue*3.142/180;//Convertdegreestoradiansfloatrf,gf,bf;if(hue>=0&&hue<120){//ConvertfromHSIcolorspacetoRGBrf=cos(radH*3/4);gf=sin(radH*3/4);bf=0;}elseif(hue>=120&&hue<240){radH-=2.09439;gf=cos(radH*3/4);bf=sin(radH*3/4);rf=0;}elseif(hue>=240&&hue<360){radH-=4.188787;bf=cos(radH*3/4);rf=sin(radH*3/4);gf=0;}intr=rf*rf*1023;intg=gf*gf*1023;intb=bf*bf*1023;analogWrite(LED_RED,r);//WritetherightcolortotheLEDoutputpinsanalogWrite(LED_GREEN,g);analogWrite(LED_BLUE,b);}

ToconvertfromhuetoRGB,weusesinesandcosines,becausethesumoftheirsquaresisalwaysone,sothetotalintensitywillalwaysbemoreorlessthesame.

ThisresultsinthefollowingRGBcurves:

UsingtheexampleDownloadtheexamplefromGitHubandopenitintheArduinoIDE.ThenaddyourWi-Ficredentials(lines83-85).ConnectanRGBLEDwithredtopin15,greentopin12andbluetopin13.Don'tforgetthecurrentlimitingresistors!SelecttheSPIFFSsize(64KBshouldbeenough,butifyouwanttouploadmorefileslater,youshouldsetithigher).UploadthesketchoverSerial,andthenuploadtheSPIFFSfilesusingTools>ESP8266SketchDataUpload.

WaitforittoconnecttoaWi-Finetwork,orconnecttotheESP8266AccessPointusingthepassword'thereisnospoon',andgotohttp://esp8266.local.Youshouldgetapagethatlookslikethis:

UsethesliderstoadjustthecolorlevelsoftheLED,andpresstheRainbowbuttontoenabletherainboweffect.

Ifyougotohttp://esp8266.local/edit.html,youcanuploadorupdatefiles:

NetworkTimeProtocolTherearemanyapplicationswhereyouwanttoknowthetime.InanormalArduinoproject,youwouldhavetogetaRTCmodule,settherighttime,sacrificesomeArduinopinsforcommunication...AndwhentheRTCbatteryrunsout,youhavetoreplaceit.

OntheESP8266,allyouneedisanInternetconnection:youcanjustaskatimeserverwhattimeitis.Todothis,theNetworkTimeProtocol(NTP)isused.

Inthepreviousexamples(HTTP,WebSockets)we'veonlyusedTCPconnections,butNTPisbasedonUDP.Thereareacoupleofdifferences,butit'sreallyeasytouse,thankstothegreatlibrariesthatcomewiththeESP8266ArduinoCore.

ThemaindifferencebetweenTCPandUDPisthatTCPneedsaconnectiontosendmessages:Firstahandshakeissentbytheclient,theserverresponds,andaconnectionisestablished,andtheclientcansenditsmessages.Aftertheclienthasreceivedtheresponseoftheserver,theconnectionisclosed(exceptwhenusingWebSockets).Tosendanewmessage,theclienthastoopenanewconnectiontotheserverfirst.Thisintroduceslatencyandoverhead.

UDPdoesn'tuseaconnection,aclientcanjustsendamessagetotheserverdirectly,andtheservercanjustsendaresponsemessagebacktotheclientwhenithasfinishedprocessing.Thereis,however,noguaranteethatthemessageswillarriveattheirdestination,andthere'snowaytoknowwhethertheyarrivedornot(withoutsendinganacknowledgement,ofcourse).Thismeansthatwecan'thalttheprogramtowaitforaresponse,becausetherequestorresponsepacketcouldhavebeenlostontheInternet,andtheESP8266willenteraninfiniteloop.

Insteadofwaitingforaresponse,wejustsendmultiplerequests,withafixedintervalbetweentworequests,andjustregularlycheckifaresponsehasbeenreceived.

GettingthetimeLet'stakealookatanexamplethatusesUDPtorequestthetimefromaNTPserver.

Libraries,constantsandglobals

#include<ESP8266WiFi.h>#include<ESP8266WiFiMulti.h>#include<WiFiUdp.h>

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

WiFiUDPUDP;//CreateaninstanceoftheWiFiUDPclasstosendandreceive

IPAddresstimeServerIP;//time.nist.govNTPserveraddressconstchar*NTPServerName="time.nist.gov";

constintNTP_PACKET_SIZE=48;//NTPtimestampisinthefirst48bytesofthemessage

byteNTPBuffer[NTP_PACKET_SIZE];//buffertoholdincomingandoutgoingpackets

TouseUDP,wehavetoincludetheWiFiUdplibrary,andcreateaUDPobject.We'llalsoneedtoallocatememoryforabuffertostoretheUDPpackets.ForNTP,weneedabufferof48byteslong.ToknowwheretosendtheUDPpacketsto,weneedthehostnameoftheNTPserver,thisistime.nist.gov.

Setup

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println("\r\n");

startWiFi();//Trytoconnecttosomegivenaccesspoints.Thenwaitforaconnection

startUDP();

if(!WiFi.hostByName(NTPServerName,timeServerIP)){//GettheIPaddressoftheNTPserverSerial.println("DNSlookupfailed.Rebooting.");Serial.flush();ESP.reset();}Serial.print("TimeserverIP:\t");Serial.println(timeServerIP);Serial.println("\r\nSendingNTPrequest...");sendNTPpacket(timeServerIP);}

Inthesetup,wejuststartourSerialandWi-Fi,asusual,andwestartUDPaswell.We'lllookattheimplementationofthisfunctionlater.WeneedtheIPaddressoftheNTPserver,soweperformaDNSlookupwiththeserver'shostname.There'snotmuchwecandowithouttheIPaddressofthetimeserver,soifthelookupfails,reboottheESP.IfwedogetanIP,sendthefirstNTPrequest,andentertheloop.

Loop

unsignedlongintervalNTP=60000;//RequestNTPtimeeveryminuteunsignedlongprevNTP=0;unsignedlonglastNTPResponse=millis();uint32_ttimeUNIX=0;

unsignedlongprevActualTime=0;

voidloop(){unsignedlongcurrentMillis=millis();

if(currentMillis-prevNTP>intervalNTP){//IfaminutehaspassedsincelastNTPrequest

prevNTP=currentMillis;Serial.println("\r\nSendingNTPrequest...");sendNTPpacket(timeServerIP);//SendanNTPrequest}

uint32_ttime=getTime();//CheckifanNTPresponsehasarrivedandgetthe(UNIX)timeif(time){//IfanewtimestamphasbeenreceivedtimeUNIX=time;Serial.print("NTPresponse:\t");Serial.println(timeUNIX);lastNTPResponse=currentMillis;}elseif((currentMillis-lastNTPResponse)>3600000){Serial.println("Morethan1hoursincelastNTPresponse.Rebooting.");Serial.flush();ESP.reset();}

uint32_tactualTime=timeUNIX+(currentMillis-lastNTPResponse)/1000;if(actualTime!=prevActualTime&&timeUNIX!=0){//IfasecondhaspassedsincelastprintprevActualTime=actualTime;Serial.printf("\rUTCtime:\t%d:%d:%d",getHours(actualTime),getMinutes(actualTime),getSeconds(actualTime));}}

ThefirstpartoftheloopsendsanewNTPrequesttothetimeservereveryminute.ThisisbasedonBlinkWithoutDelay.ThenwecallthegetTimefunctiontocheckifwe'vegotanewresponsefromtheserver.Ifthisisthecase,weupdatethetimeUNIXvariablewiththenewtimestampfromtheserver.Ifwedon'tgetanyresponsesforanhour,thenthere'ssomethingwrong,sowereboottheESP.Thelastpartprintstheactualtime.TheactualtimeisjustthelastNTPtimeplusthetimesincewereceivedthatNTPmessage.

Setupfunctions

Nothingspecialhere,justafunctiontoconnecttoWi-Fi,andanewfunctiontostartlisteningforUDPmessagesonport123.

voidstartWiFi(){//Trytoconnecttosomegivenaccesspoints.ThenwaitforaconnectionwifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting");while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnectdelay(250);Serial.print('.');}Serial.println("\r\n");Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.print(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputerSerial.println("\r\n");}

voidstartUDP(){Serial.println("StartingUDP");UDP.begin(123);//StartlisteningforUDPmessagesonport123Serial.print("Localport:\t");Serial.println(UDP.localPort());Serial.println();}

Helperfunctions

uint32_tgetTime(){if(UDP.parsePacket()==0){//Ifthere'snoresponse(yet)return0;}UDP.read(NTPBuffer,NTP_PACKET_SIZE);//readthepacketintothebuffer//Combinethe4timestampbytesintoone32-bitnumberuint32_tNTPTime=(NTPBuffer[40]<<24)|(NTPBuffer[41]<<16)|(NTPBuffer[42]<<8)|NTPBuffer[43];//ConvertNTPtimetoaUNIXtimestamp://UnixtimestartsonJan11970.That's2208988800secondsinNTPtime:constuint32_tseventyYears=2208988800UL;//subtractseventyyears:uint32_tUNIXTime=NTPTime-seventyYears;returnUNIXTime;}

voidsendNTPpacket(IPAddress&address){memset(NTPBuffer,0,NTP_PACKET_SIZE);//setallbytesinthebufferto0//InitializevaluesneededtoformNTPrequestNTPBuffer[0]=0b11100011;//LI,Version,Mode//sendapacketrequestingatimestamp:UDP.beginPacket(address,123);//NTPrequestsaretoport123UDP.write(NTPBuffer,NTP_PACKET_SIZE);UDP.endPacket();}

inlineintgetSeconds(uint32_tUNIXTime){returnUNIXTime%60;}

inlineintgetMinutes(uint32_tUNIXTime){returnUNIXTime/60%60;}

inlineintgetHours(uint32_tUNIXTime){returnUNIXTime/3600%24;}

InthegetTimefunction,wefirsttrytoparsetheUDPpacket.Ifthere'snopacketavailable,thefunctionjustreturns0.IfthereisaUDPpacketavailablehowever,readitintothebuffer.TheNTPtimestampis32bitsor4byteswide,sowecombinethesebytesintoonelongnumber.ThisnumberisthenumberofsecondssinceJan1,1900,00:00:00,butmostapplicationsuseUNIXtime,thenumberofsecondssinceJan1,1970,00:00:00(UNIXepoch).ToconvertfromNTPtimetoUNIXtime,wejustsubtract70yearsworthofseconds.

TorequestthetimefromtheNTPserver,youhavetosendacertainsequenceof48bytes.Wedon'tneedanyfancyfeatures,sojustsetthefirstbytetorequestthetime,andleaveallother47byteszero.Toactuallysendthepacket,youhavetostartthepacket,specifyingtheIPaddressoftheserver,andtheNTPportnumber,port123.Thenjustwritethebuffertothepacket,andsenditwithendPacket.

Thelastthreefunctionsarejustsomesimplemathtoconvertsecondstohours,minutesandseconds.

Usingtheexample

EnteryourWi-Ficredentialsonlines79-81,andhitupload.IfyouhaveaworkingInternetconnection,youshouldgetanoutputthatlookslikethis:

Connecting.........

ConnectedtoWi-FiSSIDIPaddress: 192.168.1.2

StartingUDPLocalport: 123

TimeserverIP:216.229.0.179

SendingNTPrequest...NTPresponse: 1488378061UTCtime: 14:21:53SendingNTPrequest...NTPresponse: 1488378114UTCtime: 14:22:53SendingNTPrequest...NTPresponse: 1488378174UTCtime: 14:23:53SendingNTPrequest...NTPresponse: 1488378234UTCtime: 14:24:53SendingNTPrequest...NTPresponse: 1488378294UTCtime: 14:25:53...

Youshouldseethetimeupdateeverysecond,andSendingNTPrequest...shouldshowupeveryminute.Ifyoudon'thaveanInternetconnection,theDNSlookupofthetimeserverwillfail:

Connecting.........

ConnectedtoWi-FiSSIDIPaddress: 192.168.1.2

StartingUDPLocalport: 123

DNSlookupfailed.Rebooting.

etsJan82013,rstcause:2,bootmode:(3,6)

Ifyourconnectionisnotreliable,orifthere'sheavytraffic,youmightencountersomedroppedpackets:

SendingNTPrequest...NTPresponse: 1488378780UTCtime: 14:33:54SendingNTPrequest...UTCtime: 14:34:54SendingNTPrequest...NTPresponse: 1488378895UTCtime: 14:35:0

Asyoucansee,theESPneverreceivedaresponsetothesecondNTPrequest.That'snotreallyanissue,aslongasatleastsomepacketsmakeitthrough.

Localtimeanddaylightsavings

AnNTPserverreturnstheUTCtime.Ifyouwantlocaltime,youhavetocompensateforyourtimezoneanddaylightsavings.Forexample,ifyouwantCET(CentralEuropeanTime),youhavetoadd3600totheUNIXtimeduringwinter,(3600s=1h),and7200duringsummer(DST).

DataloggingAcommonuseforIoTdevicesliketheESP8266ismonitoringsensors.Usingthecodeinthepreviousexample,wecanrequestthetime,andsavesomesensorvaluestoafile.Ifwerunaserveraswell,wecanshowthisdatainaprettygraphinawebpage.

TemperatureloggerInthefollowingexample,we'lluseaDS18S20temperaturesensortologthetemperatureovertimeandsaveittotheSPIFFS.Itcanthenbedisplayedinagraphinthebrowser.

Installinglibraries

First,downloadtheDallasTemperaturelibrarybyMilesBurtonandtheOneWirelibrarybyJimStudt:GotoSketch>IncludeLibrary...>ManageLibrariesandsearchfor'DallasTemperature'and'OneWire'(makesureyoudownloadthecorrectversion).

Hardware

ConnectthegroundoftheDS18S20temperaturesensor(pin1)tothegroundoftheESP,connectthedatapin(pin2)toGPIO5,andVCC(pin3)tothe3.3VoftheESP.Finally,connecta4k7ΩresistorbetweenthedatapinandVCC.

Libraries,constantsandglobals

#include<OneWire.h>#include<DallasTemperature.h>#include<ESP8266WiFi.h>#include<ESP8266WiFiMulti.h>#include<WiFiUdp.h>#include<ArduinoOTA.h>#include<ESP8266WebServer.h>#include<ESP8266mDNS.h>#include<FS.h>

#defineONE_HOUR3600000UL

#defineTEMP_SENSOR_PIN5

OneWireoneWire(TEMP_SENSOR_PIN);//SetupaOneWireinstancetocommunicatewithOneWiredevices

DallasTemperaturetempSensors(&oneWire);//Createaninstanceofthetemperaturesensorclass

ESP8266WebServerserver=ESP8266WebServer(80);//createawebserveronport80

FilefsUploadFile;//aFilevariabletotemporarilystorethereceivedfile

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

constchar*OTAName="ESP8266";//AnameandapasswordfortheOTAserviceconstchar*OTAPassword="esp8266";

constchar*mdnsName="esp8266";//DomainnameforthemDNSresponder

WiFiUDPUDP;//CreateaninstanceoftheWiFiUDPclasstosendandreceiveUDPmessages

IPAddresstimeServerIP;//Thetime.nist.govNTPserver'sIPaddressconstchar*ntpServerName="time.nist.gov";

constintNTP_PACKET_SIZE=48;//NTPtimestampisinthefirst48bytesofthemessage

bytepacketBuffer[NTP_PACKET_SIZE];//Abuffertoholdincomingandoutgoingpackets

TheonlynewthingsherearetheOneWireandDallasTemperaturelibraries,togetthetemperaturefromthesensor.

Setup

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println("\r\n");

tempSensors.setWaitForConversion(false);//Don'tblocktheprogramwhilethetemperaturesensorisreadingtempSensors.begin();//Startthetemperaturesensor

if(tempSensors.getDeviceCount()==0){Serial.printf("NoDS18x20temperaturesensorfoundonpin%d.Rebooting.\r\n",TEMP_SENSOR_PIN);Serial.flush();ESP.reset();}

startWiFi();//StartaWi-Fiaccesspoint,andtrytoconnecttosomegivenaccesspoints.ThenwaitforeitheranAPorSTAconnection

startOTA();//StarttheOTAservice

startSPIFFS();//StarttheSPIFFSandlistallcontents

startMDNS();//StartthemDNSresponder

startServer();//StartaHTTPserverwithafilereadhandlerandanuploadhandler

startUDP();//StartlisteningforUDPmessagestoport123

WiFi.hostByName(ntpServerName,timeServerIP);//GettheIPaddressoftheNTPserverSerial.print("TimeserverIP:\t");Serial.println(timeServerIP);

sendNTPpacket(timeServerIP);}

Inthesetup,there'snotmuchneweither,wejuststartthetemperaturesensor,andcheckifwecancommunicatewithit.Ifnotemperaturesensorisfound,theESPresets.

Gettingthetemperaturefromthesensormaytakesometime(upto750ms).Wedon'twantourlooptotakelongerthanacoupleofmilliseconds,sowecan'twait750ms.Ifwedid,theHTTPserveretc.wouldstarttomisbehave.Thesolutionistorequestthetemperaturefirst.Thesensorwillthenstartreadingtheanalogtemperature,andstoresitinitsmemory.Inthemeantime,theloopjustkeepsonrunning,theserverrefreshesetc.After750ms,wecontactthesensoragain,andreadthetemperaturefromitsmemory.Totellthelibrarythatwedon'twanttowaitfortheanalogtodigitalconversionofthesensor,weusesetWaitForConversion.

Loop

constunsignedlongintervalNTP=ONE_HOUR;//UpdatethetimeeveryhourunsignedlongprevNTP=0;unsignedlonglastNTPResponse=millis();

constunsignedlongintervalTemp=60000;//DoatemperaturemeasurementeveryminuteunsignedlongprevTemp=0;booltmpRequested=false;constunsignedlongDS_delay=750;//ReadingthetemperaturefromtheDS18x20cantakeupto750ms

uint32_ttimeUNIX=0;//Themostrecenttimestampreceivedfromthetimeserver

voidloop(){unsignedlongcurrentMillis=millis();

if(currentMillis-prevNTP>intervalNTP){//RequestthetimefromthetimeservereveryhourprevNTP=currentMillis;sendNTPpacket(timeServerIP);}

uint32_ttime=getTime();//Checkifthetimeserverhasresponded,ifso,gettheUNIXtimeif(time){timeUNIX=time;Serial.print("NTPresponse:\t");Serial.println(timeUNIX);lastNTPResponse=millis();}elseif((millis()-lastNTPResponse)>24UL*ONE_HOUR){Serial.println("Morethan24hourssincelastNTPresponse.Rebooting.");Serial.flush();ESP.reset();}

if(timeUNIX!=0){if(currentMillis-prevTemp>intervalTemp){//Everyminute,requestthetemperaturetempSensors.requestTemperatures();//Requestthetemperaturefromthesensor(ittakessometimetoreadit)tmpRequested=true;prevTemp=currentMillis;Serial.println("Temperaturerequested");}if(currentMillis-prevTemp>DS_delay&&tmpRequested){//750msafterrequestingthetemperatureuint32_tactualTime=timeUNIX+(currentMillis-lastNTPResponse)/1000;//TheactualtimeisthelastNTPtimeplusthetimethathaselapsedsincethelastNTPresponsetmpRequested=false;floattemp=tempSensors.getTempCByIndex(0);//Getthetemperaturefromthesensortemp=round(temp*100.0)/100.0;//roundtemperatureto2digits

Serial.printf("Appendingtemperaturetofile:%lu,",actualTime);Serial.println(temp);FiletempLog=SPIFFS.open("/temp.csv","a");//WritethetimeandthetemperaturetothecsvfiletempLog.print(actualTime);tempLog.print(',');tempLog.println(temp);tempLog.close();}}else{//Ifwedidn'treceiveanNTPresponseyet,sendanotherrequestsendNTPpacket(timeServerIP);delay(500);}

server.handleClient();//runtheserverArduinoOTA.handle();//listenforOTAevents}

Thelooplooksalotmorecomplex,butit'sactuallyprettysimple.It'sallbasedonBlinkWithoutDelay.There'stwothingsgoingon:

1. Everyhour,theESPrequeststhetimefromanNTPserver.Thenitconstantlychecksforaresponse,andupdatesthetimeifitgetsanNTPresponse.Ifithasn'treceivedanyresponsesforover24hours,there'ssomethingwrong,andtheESPresetsitself.

2. Everyminute,theESPrequeststhetemperaturefromtheDS18x20sensor,andsetsthe'tmpRequested'flag.Thesensorwillstarttheanalogtodigitalconversion.750msaftertherequest,whentheconversionshouldbefinished,theESPreadsthetemperaturefromthesensor,andresetstheflag(otherwise,itwouldkeeponreadingthesametemperatureoverandoveragain).ThenitwritesthetimeandthetemperaturetoafileinSPIFFS.BysavingitasaCSVfileinthefilesystem,wecaneasilydownloadittotheclient(usingthewebserverthatisrunning),andit'seasytoparsewithJavaScript.

IfwemissthefirstNTPresponse,timeUNIXwillbezero.Ifthat'sthecase,wesendanotherNTPrequest(otherwise,thenextrequestwouldbeanhourlater,andthetemperatureloggingonlystartswhenthetimeisknown).

WealsoneedtoruntheserverandOTAfunctionstohandleHTTPandOTArequests.

Setupfunctions,serverhandlersandhelperfunctions

Thesefunctionshaven'tchangesincethepreviousexample,sothere'snoneedtocoverthemhere.Youdoneedthemtogettheprogramrunning,though.DownloadtheZIParchivewithexamplesforthefullsketch.

HTMLandJavaScript

There'ssomeHTMLandJavaScriptfilestoplotthetemperatureusingGoogleGraphs.Iwon'tcoverithere,butifyouwan'ttoknow

howitworks,youcanfindthefilesintheZIParchive.

Usingtheexample

SettheSPIFFSsizeto64KBorlargerifyouplantouseitforprolongedperiodsoftime.(Youcouldalsoincreasetheloggingintervalonline80tosavespace.)EnteryourWi-Ficredentialsonlines138-140,andhitupload.ThenuploadthewebpagesandscriptstoSPIFFSusingTools>ESP8266SketchDataUpload.Makesureyouhavethetemperaturesensorconnected,asdescribedatthetopofthispage.Openaterminaltoseeifitworks.Youshouldseesomethinglikethis:

Connecting..........

ConnectedtoSSIDIPaddress: 192.168.1.2

OTAready

SPIFFSstarted.Contents: FSFile:/favicon-144x144.png,size:2.81KB FSFile:/temperatureGraph.js.gz,size:1.17KB FSFile:/temp.csv,size:42.50KB FSFile:/success.html.gz,size:456B FSFile:/edit.html.gz,size:700B FSFile:/main.css.gz,size:349B FSFile:/index.html.gz,size:795B FSFile:/manifest.json,size:169B FSFile:/favicon.ico.gz,size:1.91KB

mDNSresponderstarted:http://esp8266.localHTTPserverstarted.StartingUDPLocalport: 123TimeserverIP:216.229.0.179SendingNTPrequestNTPresponse: 1488666586TemperaturerequestedAppendingtemperaturetofile:1488666627,20.00TemperaturerequestedAppendingtemperaturetofile:1488666687,19.94Temperaturerequested...

Letitrunforacoupleofminutes,togathersometemperaturedata.Thenopenawebbrowser,andgotohttp://esp8266.local/.Youshouldgetagraphshowingthetemperaturecurve.Youcanusethearrowbuttonstotravelthroughtime,andthe+and-buttonstozoominorout.Theresetbuttonresetsthezoom,andjumpsbacktothepresent.Refreshrequeststhelatesttemperaturedata.

Ifyouwant,youcanstillgotohttp://esp8266.local/edit.htmltouploadnewfiles.

Thewebinterfaceshouldlooklikethis:

ItworksonWindows,LinuxandAndroid,butiOSseemstohavesomeproblemsrenderingthegraph(inbothChromeandSafari).

EmailnotifierAnothergreatuseforIoTdevicesisdisplayingthingsliketrafficinformation,weatherforecast,socialmediaupdates...ThisrequiresustosendanHTTPGETrequesttotheserveroftheservicewe'dliketoaccess.MostpopularserviceshaveAPI(ApplicationProgrammingInterface)documentsthatexplainthatexplainhowyoucanretrievecertaininformation,andwhatformatthatinformationisin.Inthefollowingexample,we'lllookatGmailspecifically,butthecodeshouldbesimilarforotherservices.

ShowingthenumberofunreademailsTocommunicatewithGoogle'sGmailservers,wehavetoestablishasecureconnectiontotheserverandsendasecureHTTPSrequestwithouremailaddressandpassword.GmailwillthenrespondwithanXMLdocumentcontainingallkindsofinformation,like(partsof)yourmostrecentmessagesandthenumberofunreademails.

Tomakesurewedon'tsendourGooglepasswordtoamaliciousserver,wehavetochecktheserver'sidentity,usingtheSHA-1fingerprintoftheSSLcertificate.Thisisauniquesequenceofhexadecimalcharactersthatidentifiestheserver.

Allowingaccesstotheemailfeed

Theonlyway(Iknowof)togetemailinformationfromGoogleontheESPcurrentlyistheGoogleAtomFeed.Thisisanoldermethod,soyouhavetochangeyourGmailsettingstoallowaccesstothefeed.Gotohttps://www.google.com/settings/security/lesssecureappstoenableaccessfor"lesssecureapps":

Untilthere'ssupportforthenewOAuth2protocolontheESP,we'llhavetousetheold,lesssecuremethod.

Hardware

ConnectanLED(+resistor)topin13,asanunreademailindicator.

TheCode

#include<WiFiClientSecure.h>//IncludetheHTTPSlibrary#include<ESP8266WiFi.h>//IncludetheWi-Filibrary#include<ESP8266WiFiMulti.h>//IncludetheWi-Fi-Multilibrary

ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'

constchar*host="mail.google.com";//theGmailserverconstchar*url="/mail/feed/atom";//theGmailfeedurlconstinthttpsPort=443;//theporttoconnecttotheemailserver

//TheSHA-1fingerprintoftheSSLcertificatefortheGmailserver(seebelow)constchar*fingerprint="D390FC8207E60DC2CEF99D797FECF6E63ECB8BB3";

//TheBase64encodedversionofyourGmaillogincredentials(seebelow)constchar*credentials="ZW1haWwuYWRkcmVzc0BnbWFpbC5jb206cGFzc3dvcmQ=";

constbyteled=13;

voidsetup(){Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputerdelay(10);Serial.println('\n');

pinMode(led,OUTPUT);

wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-FinetworksyouwanttoconnecttowifiMulti.addAP("ssid_from_AP_2","your_password_for_AP_2");wifiMulti.addAP("ssid_from_AP_3","your_password_for_AP_3");

Serial.println("Connecting...");inti=0;while(wifiMulti.run()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect:scanforWi-Finetworks,andconnecttothestrongestofthenetworksabovedelay(250);Serial.print('.');}Serial.println('\n');Serial.print("Connectedto");Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedtoSerial.print("IPaddress:\t");Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputerSerial.println('\n');}

voidloop(){intunread=getUnread();if(unread==0){Serial.println("\r\nYou'vegotnounreademails");digitalWrite(led,LOW);}elseif(unread>0){Serial.printf("\r\nYou'vegot%dnewmessages\r\n",unread);digitalWrite(led,HIGH);}else{Serial.println("Couldnotgetunreadmails");}Serial.println('\n');delay(5000);}

intgetUnread(){//afunctiontogetthenumberofunreademailsinyourGmailinboxWiFiClientSecureclient;//UseWiFiClientSecureclasstocreateTLS(HTTPS)connectionSerial.printf("Connectingto%s:%d...\r\n",host,httpsPort);if(!client.connect(host,httpsPort)){//ConnecttotheGmailserver,onport443Serial.println("Connectionfailed");//Iftheconnectionfails,stopandreturnreturn-1;}

if(client.verify(fingerprint,host)){//ChecktheSHA-1fingerprintoftheSSLcertificateSerial.println("Certificatematches");

}else{//ifitdoesn'tmatch,it'snotsafetocontinueSerial.println("Certificatedoesn'tmatch");return-1;}

Serial.print("RequestingURL:");Serial.println(url);

client.print(String("GET")+url+"HTTP/1.1\r\n"+"Host:"+host+"\r\n"+"Authorization:Basic"+credentials+"\r\n"+"User-Agent:ESP8266\r\n"+"Connection:close\r\n\r\n");//SendtheHTTPrequestheaders

Serial.println("Requestsent");

intunread=-1;

while(client.connected()){//Waitfortheresponse.TheresponseisinXMLformatclient.readStringUntil('<');//readuntilthefirstXMLtagStringtagname=client.readStringUntil('>');//readuntiltheendofthistagtogetthetagnameif(tagname=="fullcount"){//ifthetagis<fullcount>,thenextstringwillbethenumberofunreademailsStringunreadStr=client.readStringUntil('<');//readuntiltheclosingtag(</fullcount>)unread=unreadStr.toInt();//convertfromStringtointbreak;//stopreading}//ifthetagisnot<fullcount>,repeatandreadthenexttag}Serial.println("Connectionclosed");

returnunread;//Returnthenumberofunreademails}

Howitworks

Thesetupshouldbeprettyfamiliarbynow.TheonlynewthingisthegetUnread()function:First,itstartsanHTTPSconnectiontotheGmailserveronport443.Thenitchecksifthefingerprintofthecertificatematches,soitknowsthatit'stherealGoogleserver,andnotsomehacker.Ifthecertificatedoesn'tmatch,it'snotsafetosendthecredentialstotheserver.

Ifitmatches,wesendaHTTPGETrequesttotheserver:

GET/mail/feed/atomHTTP/1.1\r\nHost:mail.google.com\r\nAuthorization:BasicaVeryLongStringOfBase64EncodedCharacters=\r\nUser-Agent:ESP8266\r\nConnection:close\r\n\r\n

TherequestcontainstheURIwewanttoaccess(inthiscasethisistheAtomfeedURL),thehost(whichismail.google.com),andthebase64-encodedversionofyourlogincredentials.Asyoucansee,thedifferentlinesoftheheaderareseparatedbyaCRLF(CarriageReturn+LineFeed,\r\n).TwoCRLF'smarktheendoftheheader.TheGmailserverwillprocessourrequest,andsendthefeedasaresponseoverthesameHTTPSconnection.ThisresponseisanXMLdocument,thatconsistsoftagswithangledbrackets,justlikeHTML.Ifyouneedalotofdata,it'srecommendedtouseaproperXMLparserlibrary,butweonlyneedonetag,sowecanjustskimthroughtheresponsetextuntilwefindthe<fullcount>x</fullcount>tag.Thenumberinsidethistagisthenumberofunreademailsintheinbox.Wecanjustconvertittoaninteger,andstopreading.

ThisistheformatoftheXMLfeed,youcanseethefullcounttagonline5:

<?xmlversion="1.0"encoding="UTF-8"?><feedxmlns="http://purl.org/atom/ns#"version="0.3"><title>Gmail-Inboxforesp8266.test.mail@gmail.com</title><tagline>NewmessagesinyourGmailInbox</tagline><fullcount>5</fullcount><linkrel="alternate"href="https://mail.google.com/mail"type="text/html"/><modified>2017-03-05T15:54:06Z</modified><entry><title>Newsign-infromFirefoxonLinux</title><summary>Newsign-infromFirefoxonLinuxHiESP8266,YourGoogleAccountesp8266.test.mail@gmail.comwasjustusedtosigninfromFirefoxonLinux.ESP8266Testesp8266.test.mail@gmail.comLinuxSunday,</summary><linkrel="alternate"href="https://mail.google.com/mail?account_id=esp8266.test.mail@gmail.com&amp;message_id=123456789&amp;view=conv&amp;extsrc=atom"type="text/html"/><modified>2017-03-05T15:52:45Z</modified><issued>2017-03-05T15:52:45Z</issued><id>tag:gmail.google.com,2004:123456789123456789</id><author><name>Google</name><email>no-reply@accounts.google.com</email></author></entry>

...

</feed>

Theloopjustprintsthenumberofunreademails,andturnsonanLEDifyouhaveunreadmessages.

GettingthefingerprintoftheGmailserver

LikeImentionedbefore,weneedafingerprinttochecktheidentityoftheserver.Togetthisfingerprint,executethefollowingcommandinaterminal(Linux&Mac):

openssls_client-connectmail.google.com:443</dev/null2>/dev/null|opensslx509-fingerprint-noout-in/dev/stdin|sed's/://g'

Copythehexadecimalfingerprintstringandpasteitintothesketchonline12.Forexample:

constchar*fingerprint="D390FC8207E60DC2CEF99D797FECF6E63ECB8BB3";

Encodingyourlogincredentials

Togetaccesstothefeed,youhavetoenteryouremailaddressandpassword.Youcan'tsendthemasplaintext,youhavetoencodethemtobase64first.Usethefollowingcommandinaterminal(Linux&Mac):

echo-n"email.address@gmail.com:password"|base64

Thenaddittoline15ofthesketch.Forexample:

constchar*credentials="ZW1haWwuYWRkcmVzc0BnbWFpbC5jb206cGFzc3dvcmQ=";

OtherAPIs

ManyservicessendtheirdatainJSONformat.Ifyoujustneedonepieceofinformation,youmaybeabletousethesameapproachofscanningtheentireJSONtextforacertainword,butit'smucheasiertouseaJSONparser,liketheArduinoJsonlibrary.ItwilldeserializetheJSONtext,andcreateaJSONobject,youcouldcompareittoanassociativearray.Youcanbrowsetheentiretreestructure,andeasilyfindthedatayou'relookingfor.Thedownsideisthatitusesmorememory.

AdvancedDNSCaptivePortal

WhenusingtheESP8266inaccesspointmode,youprobablywanttoredirectuserstotherightpage.Youcandothisbycreatingacaptiveportal,usingDNS.It'sbasicallyjustaDNSserverthatwillconvertallhostnamestotheESP'sownIPaddress.ThistechniqueisalsousedbyopenWi-Finetworksthatredirectyoutoaloginpagebeforeyoucanstartbrowsingtheinternet.

Wi-Ficonfiguration

IfyouwanttobeabletochangetheWi-Ficonnectionsettingswithoutre-uploadingthecode,youcouldtakealookattheWiFiManagerlibrarybytzapu.Thiswilltrytoconnecttoknownnetworks,butifitfails,itwillstartaWi-Fiaccesspoint.Youcanthenconnecttothisaccesspoint,openthebrowser,andpickanetworktoconnectto.Thenewconfigurationissaved.TheWiFiManagerlibraryusesacaptiveportaltopresentyouwiththerightWi-Fisettingspage.YoucouldalsoimplementaWi-Fimanageryourself,oryoucanjustcheckouttheexamplethatcomeswiththeESP8266ArduinoCore(Examples>DNSServer>CaptivePortalAdvanced).

I²STheESP8266hasanI²SbusontheRXDpin.Itcanrunat80MHz,andhasDMA(directmemoryaccess),soit'sreallyfast.ItsmainpurposeistoconnectanI²SDAC(DigitaltoAnalogConverter)tohaveanaudiooutput,butyoucanuseitforotherthingsaswell.Forexample,CNLohrmanagedtotransmitanalogtelevision,byconnectinganantennawiretotheI²Spin.YoucanalsouseittocontrolWS2812BsLEDs.YoucanevenuseittocommunicateoverEthernet(notreallyuseful,anddefinitelynotrecommended,butitworks).AnothergreatusefortheI²Sbusisoutputtingdatatoshiftregisters.Thisgivesyouextraoutputsthatarereasonablyfast,forthingslikeLEDsorsteppermotors.

Otherexamples

YoucanfindlotsofotherexamplesintheArduinoIDE,I'drecommendtocheckthoseoutaswell.

YouTubeThere'ssomegreatchannelsonYouTubethatdoamazingthingswiththeESP8266.Here'sashortlistoftheonesI'mcurrentlyfollowing.Ifyou'vegotmorerecommendation,justleaveacomment!

AndreasSpiessCNLohrAcroboticMiikaKurkela

Inconclusion...Congratulations,you'vereachedtheendofthisratherlongarticleonthebasicsoftheESP8266.Ihopethiswasinterestingtoyou,andthatyou'llusethisknowledgeforyourownDIYprojects.

Ifyouhaveanyremarksorifyouwanttohelpimprovethisguide,don'thesitatetoleaveacommentortosendmeamessage.

Thankyouforreading!Pieter,8-3-2017

top related