A Beginner's Guide To The ESP8266
User Manual:
Open the PDF directly: View PDF
.
Page Count: 64

ABeginner'sGuidetotheESP8266
PieterP, 08-03-2017
Sometimeago,IwroteaBeginner'sGuidetoArduinothatseemstobeverypopular,soIdecidedto
createafollow-up:ABeginner'sGuidetotheESP8266.That'sright,atutorialonhowtousethe
world'smostpopular$3Wi-Fiboard.
Thisisgoingtobeaveryin-depthtutorial,coveringsomenetworkingconceptsaswell.Ifyou'rea
beginner,andjustwanttogostraighttothemoreexcitingWi-Fipart,feelfreetodoso,Iincludedshort
TL;DR'sinthelonger,moretechnicalparts.
AshortoverviewofwhatI'llcoverinthisarticle:
1. WhatisanESP8266?AshortoverviewofwhatanESP8266is,andwhatyoucandowithit
2. Decidingonwhatboardtobuy:There'sloadsofdifferentESP8266availablethesedays,finding
theonethat'sbestforyoucanbehard
3. Installingthesoftware:youneedtoinstallsomesoftwaretoprogramtheESP8266,andmaybea
USBdriver
4. Settingupthehardware:somemodulesandboardsneedsomeexternalcomponents
5. TheESP8266asamicrocontroller:theESP8266canbeusedasanormalmicrocontroller,just
likeanArduino
6. Networkprotocols:BeforewestartusingtheWi-FicapabilitiesoftheESP8266,I'llteachyou
someofthenetworkprotocolsinvolved
7. SettingupaWi-Ficonnection:That'sprobablywhyyou'rereadingthis,right?
8. Nameresolution:FindtheESP8266onyourlocalnetworkusingmDNS
9. Settingupasimplewebserver:ThisenablesyoutoaddwebpagestotheESP8266,and
browsethemfromyourcomputerorphone
10. Settingupanadvancedwebserver:amoreadvancedserverwitharealfilesystemthatallows
youtouploadnewfilesoverWi-Fi
11. OTA-uploadingprogramsoverWi-Fi:Youdon'thavetouploadprogramsoverUSB,youcan
useWi-Fiinstead
12. WirelesslycontrollingyourRGBlighting:ChangethecolorofyourLEDstripsusingyourphone
orcomputer
13. Gettingthetime:ConnecttoatimeserverusingNTPandsynctheESP'sclock
14. Monitoringsensors:logthetemperatureinyourlivingroom,saveitinflashmemoryandshowit
inafancygraphinyourbrowser
15. Gettingemailnotifications:Turnonanotificationlightwhenyou'vegotunreademails
16. Advancedfeatures:useDNS,captiveportals,Wi-Ficonnectorlibraries,OSC...
ThisguideexpectssomebasicknowledgeofmicrocontrollersliketheArduino.Ifthat'ssomethingyou're
notalreadyfamiliarwith,I'drecommendyoutoreadmyBeginner'sGuidetoArduinofirst,itcoversalot
ofthebasicsthatIwon'tgointointhisarticle.
IreallywanttofocusontheESP8266-specificthings,likeWi-Fiandothernetworkprotocols,theESP's
hardware,software,IoT,etc...
WhatisanESP8266?
TheESP8266isaSystemonaChip(SoC),manufacturedbytheChinesecompanyEspressif.Itconsists
ofaTensilicaL10632-bitmicrocontrollerunit(MCU)andaWi-Fitransceiver.Ithas11GPIOpins*
(GeneralPurposeInput/Outputpins),andananaloginputaswell.Thismeansthatyoucanprogramit
likeanynormalArduinoorothermicrocontroller.Andontopofthat,yougetWi-Ficommunication,so
youcanuseittoconnecttoyourWi-Finetwork,connecttotheInternet,hostawebserverwithrealweb
pages,letyoursmartphoneconnecttoit,etc...Thepossibilitiesareendless!It'snowonderthatthis
chiphasbecomethemostpopularIOTdeviceavailable.
Therearemanydifferentmodulesavailable,standalonemodulesliketheESP-##seriesbyAIThinker,or
completedevelopmentboardsliketheNodeMCUDevKitortheWeMosD1.Differentboardsmayhave
differentpinsbrokenout,havedifferentWi-Fiantennas,oradifferentamountofflashmemoryonboard.
(*)TheESP8266chipitselfhas17GPIOpins,but6ofthesepins(6-11)areusedforcommunicationwiththeon-boardflash
memorychip.
Programming

TherearedifferentwaystoprogramtheESP8266,butI'llonlycoverthemethodusingtheArduinoIDE.
Thisisreallyeasyforbeginners,andit'saveryfamiliarenvironmentifyou'veusedArduinoboards
before.
Justkeepinmindthatit'snotlimitedtothisoption:there'salsoanofficialSDKavailabletoprogramitin
realC,thisisveryusefulifyouwanttooptimizeyourcodeordosomeadvancedtricksthataren't
supportedbytheArduinoIDE.AnotherpossibilityistoflashitwithaLUAinterpreter,soyoucanupload
andrunLUAscripts.Ormaybeyou'remorefamiliarwithPython?Thenyoushouldcheckoutthe
MicroPythonfirmwaretointerpretMicroPythonscripts.I'msurethere'sotherlanguagesavailableaswell,
sojustdoaquickGooglesearchandwriteyourcodeinthelanguageofyourchoice.
Requirements
You'llneedacoupleofthingsinordertofollowthisguide:
AnESP8266board
AcomputerthatcanruntheArduinoIDE(Windows,MacorLinux)
AUSB-to-Serialconverter,itisveryimportantthatyouusea3.3Vmodel*
AUSBcable
A3.3Vpowersupplyorvoltageregulator*
AWi-Finetworktoconnectto
(*)Yourboardmayalreadyincludethese.Moreinformationcanbefoundinthenextchapter.

Hardware
Decidingonwhatboardtobuy
ESP8266isjustthenameofthechip,manycompanieshavedesignedtheirownboardsthatusethis
chip,sotherearemanydifferentESP8266boardsonthemarket.Ifyoudon'tknowthedifference
betweenallthesedifferentmodels,youmighthaveahardtimedecidingonwhatboardtobuy.
Theeasiest(andfastest)waytogetanESP8266boardistobuyonefromawell-knownelectronicsshop
likeAdafruitorSparkFun,butifyouwantitcheap,youshouldcheckoutEbayorothersiteswhereyou
canorderthemdirectlyfromChina.
Developmentboards
Someboardshaveallkindsoffeatureson-boardtohelpdevelopingESP8266hardwareandsoftware:for
example,aUSBtoSerialconverterforprogramming,a3.3Vregulatorforpower,on-boardLEDsfor
debugging,avoltagedividertoscaletheanaloginput...
Ifyou'reabeginner,Iwoulddefinitelyrecommendadevelopmentboard.It'seasiertogetstartedifyou
don'thavetoworryaboutallthesethings.
Bare-bonesAIThinkerboards
IfyouwanttoaddanESP8266toasmallproject,orifyouwantacheaper*board,youmightwantto
buyaboardthatdoesn'thavethesefeatures.Inthatcase,youcanbuyoneofthemanyESP-##
modulesdevelopedbyAIThinker.TheycontainjusttheESP8266andthenecessarycomponentstorun
it.
Toprogramtheboard,you'llneedanexternalUSB-to-Serialconverter.
Withsomemodules,yougetanon-boardantenna(PCBorceramic)andanLED,someboardshavejust
anantennaconnector,ornoLEDsatall.Theyalsodifferinphysicalsize,andflashmemorysize.An
importantthingtonotice,isthatsomeboardsdonotbreakoutallGPIOpins.Forexample,theESP-01
onlyhas2I/Opinsavailable(apartfromtheTXandRXpins),whileothermodulesliketheESP-07or
ESP-12breakoutallavailableI/Opins.
(*)Theboarditselfischeaper,butyou'llhavetospendmoreonexternalparts.
Overview
Here'satablewithsomeofthemostpopularESP8266developmentboardsandtheirfeatures:
Board GPIO 3.3V
Vreg
USB-
to-
Serial
Auto-
Reset
Auto-
Program Flash ADC
range Extra
SparkFunESP8266
Thing 11 + - + ±* 512KB
(4Mb) 0-1V
Batterycharger,crypto
element,temperaturesensor,
lightsensor
SparkFunESP8266
Thing-DevBoard 11 + + + + 512KB
(4Mb) 0-1V
NodeMCU 11 + + + + 4MB(32Mb) 0-
3.3V
AdafruitFeather
HUZZAHwith
ESP8266
11 + + + + 4MB(32Mb) 0-1V Batterycharger
AdafruitHUZZAH
ESP8266Breakout 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,AdafruitFeather
HUZZAHwithESP8266Wi-Fi...
TheseboardswillshowupinDevicemanager(Windows)orinlsusb(Linux)assoonasyouplugthemin.
Theyhavea3.3Vregulatoron-board,andcanbeprogrammedoverUSBdirectly,soyoudon'tneedany
externalcomponentstogetitworking.
Theonlythingyoumayneedtodo,issolderonsomeheaders.
Bare-bonesboardsandboardswithoutaUSBinterface
Thiscategoryhas2sub-categories:boardswitha3.3Vregulatoron-board,andboardswithjustthe
ESP8266andaflashmemorychip,without3.3Vregulator.Ifyourboarddoesn'thavea5Vto3.3V
regulator,buyoneseparately.YoucoulduseanLM1117-3.3forexample.Theon-board3.3Vregulatorof
mostArduinoboardsisnotpowerfulenoughtopowertheESP.
Toprogramtheboard,you'llneedaUSB-to-Serialconverter.TheFTDIFT232RLisquitepopular,because
itcanswitchbetween5Vand3.3V.ItisessentialthattheUSB-to-Serialconverteryoubuy
operatesat3.3V.Ifyoubuya5Vmodel,youwilldamagetheESP8266.
ConnectingtheUSB-to-Serialconverter
1. Connecttheground(GND)oftheUSB-to-SerialconvertertothegroundoftheESP8266.
2. ConnecttheRX-pinoftheUSB-to-SerialconvertertotheTXDpinoftheESP8266.(Onsomeboards,
it'slabelledTXinsteadofTXD,butit'sthesamepin.)
3. ConnecttheTX-pinoftheUSB-to-SerialconvertertotheRXDpinoftheESP8266.(Onsomeboards,
it'slabelledRXinsteadofRXD,butit'sthesamepin.)
4. IfyourESP8266boardhasaDTRpin,connectittotheDTRpinoftheUSB-to-Serialconverter.This
enablesauto-resetwhenuploadingasketch,moreonthatlater.
Enablingthechip
Ifyou'reusingabare-boneESP-##boardbyAIThinker,youhavetoaddsomeresistorstoturnonthe
ESP8266,andtoselecttherightbootmode.
1. EnablethechipbyconnectingtheCH_PD(ChipPowerDown,sometimeslabeledCH_ENorchip
enable)pintoVCCthrougha10KΩresistor.
2. DisableSD-cardbootbyconnectingGPIO15togroundthrougha10KΩresistor.
3. SelectnormalbootmodebyconnectingGPIO0toVCCthrougha10KΩresistor.
4. PreventrandomresetsbyconnectingtheRST(reset)pintoVCCthrougha10KΩresistor.
5. Makesureyoudon'thaveanythingconnectedtoGPIO2(moreinformationinthenextchapter).
Addingresetandprogrambuttons
IfyourESP8266boarddoesn'thavearesetbutton,youcouldaddonebyconnectingapushbuttonto
betweentheRSTpinandground.
Toputthechipintoprogrammingmode,youhavetopullGPIO0lowduringstartup.That'swhywealso
needaprogrambutton.Becauseit'spossibletouseGPIO0asanoutput,wecan'tdirectlyshortitto
ground,thatcoulddamagethechip.Topreventthis,connect470Ωresistorinserieswiththeswitch.It's
importantthatthisresistanceislowenough,otherwise,itwillbepulledhighbythe10KΩresistorwe
addedinthepreviousparagraph.
Connectingthepowersupply
IftheESP8266moduleyouhavedoesn'thavea3.3Vvoltageregulatoronboard,youhavetoaddone
externally.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:Themostimportantthingisthatit
runsat3.3V,soifyouconnectittoa5Vpowersupply,you'llkillit.Unlikesome3.3VArduinoorTeensy
boards,theESP8266'sI/Opinsarenot5Vtolerant,soifyouusea5VUSB-to-Serialconverter,or5V
sensorsetc.you'llblowitup.
AsecondthingtokeepinmindisthattheESP8266canonlysourceorsink12mAperoutputpin,
comparedto20-40mAformostArduinos.
TheESP8266hasoneanalogtodigitalconverter,butithasastrangevoltagerange:0-1V,voltages
above1Vmightdamagetheboard.
OnelastthingtokeepinmindisthattheESP8266hastosharethesystemresourcesandCPUtime
betweenyoursketchandtheWi-Fidriver.Also,featureslikePWM,interruptsorI²Careemulatedin
software,mostArduinosontheotherhand,havededicatedhardwarepartsforthesetasks.
Formostapplicationshowever,thisisnottoomuchofanissue.

Software
Installationoftherequiredsoftware
ThefirststepistodownloadandinstalltheArduinoIDE.IexplainedthisinABeginner'sGuideto
Arduino.(AsofFebruary7th2017,thelateststableversionoftheIDEis1.8.1.)
ToprogramtheESP8266,you'llneedapluginfortheArduinoIDE,itcanbedownloadedfromGitHub
manually,butitiseasiertojustaddtheURLintheArduinoIDE:
1. OpentheArduinoIDE.
2. GotoFile>Preferences.
3. PastetheURLhttp://arduino.esp8266.com/stable/package_esp8266com_index.jsonintothe
AdditionalBoardManagerURLsfield.
(YoucanaddmultipleURLs,separatingthemwithcommas.)
4. GotoTools>Board>BoardManagerandsearchfor'esp8266'.Selectthenewestversion,and
clickinstall.(AsofFebruary7th2017,thelateststableversionis2.3.0.)
Youcancheckouttheofficialinstallguidehere.
Drivers
IfyouareusingaboardwiththeCH340(G)USB-to-Serialchip,liketheNodeMCU,you'llprobablyhaveto
installtheUSBdriversforit.
TheycanbefoundonGitHub.
IfyouareusingaboardwiththeCP2104USB-to-Serialchip,liketheAdafruitFeatherHUZZAHboard,
you'llprobablyhavetoinstallUSBdriversaswell.YoucanfindthemontheSiliconLabswebsite.
BoardswithanFTDIchipshouldworkrightoutofthebox,withouttheneedofinstallinganydrivers.
Python
IfyouwanttouseOverTheAirupdatesonWindows,youhavetoinstallPython2.7.Youcandownloadit
frompython.org.Duringtheinstallation,youhavetoselecttheoptiontoaddPythontoyourpath.Ifyou
don'tdothis,theArduinoIDEwon'tbeabletofindthePythonexecutable.
Examples
YoucanfindallexamplesusedinthisarticleonmyGitHub.Justdownloaditasa.ZIPfile,unzipittoa
convenientlocation,andyou'regoodtogo!

TheESP8266asamicrocontroller-Hardware
WhiletheESP8266isoftenusedasa‘dumb’Serial-to-WiFibridge,it’saverypowerfulmicrocontrolleron
itsown.Inthischapter,we’lllookatthenon-Wi-FispecificfunctionsoftheESP8266.
DigitalI/O
JustlikeanormalArduino,theESP8266hasdigitalinput/outputpins(I/OorGPIO,GeneralPurpose
Input/Outputpins).Asthenameimplies,theycanbeusedasdigitalinputstoreadadigitalvoltage,or
asdigitaloutputstooutputeither0V(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-leggedchiprightnexttothe
ESP8266.Ifyoutrytouseoneofthesepins,youmightcrashyourprogram.
GPIO1and3areusedasTXandRXofthehardwareSerialport(UART),soinmostcases,youcan’tuse
themasnormalI/Owhilesending/receivingserialdata.
Bootmodes
Asmentionedinthepreviouschapter,someI/Opinshaveaspecialfunctionduringboot:Theyselect1
of3bootmodes:
GPIO15 GPIO0 GPIO2 Mode
0V 0V 3.3V UartBootloader
0V 3.3V 3.3V Bootsketch(SPIflash)
3.3V x x SDIOmode(notusedforArduino)
Note:youdon’thavetoaddanexternalpull-upresistortoGPIO2,theinternaloneisenabledatboot.
Wemadesurethattheseconditionsaremetbyaddingexternalresistorsinthepreviouschapter,orthe
boardmanufacturerofyourboardaddedthemforyou.Thishassomeimplications,however:
GPIO15isalwayspulledlow,soyoucan’tusetheinternalpull-upresistor.Youhavetokeepthisin
mindwhenusingGPIO15asaninputtoreadaswitchorconnectittoadevicewithanopen-
collector(oropen-drain)output,likeI²C.
GPIO0ispulledhighduringnormaloperation,soyoucan’tuseitasaHi-Zinput.
GPIO2can’tbelowatboot,soyoucan’tconnectaswitchtoit.
Internalpull-up/-downresistors
GPIO0-15allhaveabuilt-inpull-upresistor,justlikeinanArduino.GPIO16hasabuilt-inpull-down
resistor.
PWM
UnlikemostAtmelchips(Arduino),theESP8266doesn’tsupporthardwarePWM,however,softwarePWM
issupportedonalldigitalpins.ThedefaultPWMrangeis10-bits@1kHz,butthiscanbechanged(upto
>14-bit@1kHz).
Analoginput
TheESP8266hasasingleanaloginput,withaninputrangeof0-1.0V.Ifyousupply3.3V,forexample,
youwilldamagethechip.SomeboardsliketheNodeMCUhaveanon-boardresistivevoltagedivider,to
getaneasier0-3.3Vrange.Youcouldalsojustuseatrimpotasavoltagedivider.

TheADC(analogtodigitalconverter)hasaresolutionof10bits.
Communication
Serial
TheESP8266hastwohardwareUARTS(Serialports):
UART0onpins1and3(TX0andRX0resp.),andUART1onpins2and8(TX1andRX1resp.),however,
GPIO8isusedtoconnecttheflashchip.ThismeansthatUART1canonlytransmitdata.
UART0alsohashardwareflowcontrolonpins15and13(RTS0andCTS0resp.).Thesetwopinscanalso
beusedasalternativeTX0andRX0pins.
I²C
TheESPdoesn’thaveahardwareTWI(TwoWireInterface),butitisimplementedinsoftware.This
meansthatyoucanuseprettymuchanytwodigitalpins.Bydefault,theI²Clibraryusespin4asSDA
andpin5asSCL.(ThedatasheetspecifiesGPIO2asSDAandGPIO14asSCL.)Themaximumspeedis
approximately450kHz.
SPI
TheESP8266hasoneSPIconnectionavailabletotheuser,referredtoasHSPI.ItusesGPIO14asCLK,12
asMISO,13asMOSIand15asSlaveSelect(SS).ItcanbeusedinbothSlaveandMastermode(in
software).
GPIOoverview
GPIO Function State Restrictions
0 Bootmodeselect 3.3V NoHi-Z
1 TX0 - NotusableduringSerialtransmission
2Bootmodeselect
TX1 3.3V(bootonly) Don’tconnecttogroundatboottime
Sendsdebugdataatboottime
3 RX0 - NotusableduringSerialtransmission
4 SDA(I²C) - -
5 SCL(I²C) - -
6-11 Flashconnection x Notusable,andnotbrokenout
12 MISO(SPI) - -
13 MOSI(SPI) - -
14 SCK(SPI) - -
15 SS(SPI) 0V Pull-upresistornotusable
16 Wakeupfromsleep - Nopull-upresistor,butpull-downinstead
ShouldbeconnectedtoRSTtowakeup
TheESP8266asamicrocontroller-Software
MostofthemicrocontrollerfunctionalityoftheESPusesexactlythesamesyntaxasanormalArduino,
makingitreallyeasytogetstarted.
DigitalI/O
JustlikewitharegularArduino,youcansetthefunctionofapinusingpinMode(pin,mode);wherepinis
theGPIOnumber*,andmodecanbeeitherINPUT,whichisthedefault,OUTPUT,orINPUT_PULLUPtoenablethe
built-inpull-upresistorsforGPIO0-15.Toenablethepull-downresistorforGPIO16,youhavetouse
INPUT_PULLDOWN_16.
(*)NodeMCUusesadifferentpinmapping,readmorehere.ToaddressaNodeMCUpin,e.g.pin5,use
D5:forinstance:pinMode(D5,OUTPUT);
Tosetanoutputpinhigh(3.3V)orlow(0V),usedigitalWrite(pin,value);wherepinisthedigitalpin,and
valueeither1or0(orHIGHandLOW).
Toreadaninput,usedigitalRead(pin);

ToenablePWMonacertainpin,useanalogWrite(pin,value);wherepinisthedigitalpin,andvaluea
numberbetween0and1023.
Youcanchangetherange(bitdepth)ofthePWMoutputbyusinganalogWriteRange(new_range);
ThefrequencycanbechangedbyusinganalogWriteFreq(new_frequency);.new_frequencyshouldbebetween
100and1000Hz.
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.
SharingCPUtimewiththeRFpart
OnethingtokeepinmindwhilewritingprogramsfortheESP8266isthatyoursketchhastoshare
resources(CPUtimeandmemory)withtheWi-Fi-andTCP-stacks(thesoftwarethatrunsinthe
backgroundandhandlesallWi-FiandIPconnections).
Ifyourcodetakestoolongtoexecute,anddon’tlettheTCPstacksdotheirthing,itmightcrash,oryou
couldlosedata.It’sbesttokeeptheexecutiontimeofyouloopunderacoupleofhundredsof
milliseconds.
Everytimethemainloopisrepeated,yoursketchyieldstotheWi-FiandTCPtohandleallWi-FiandTCP
requests.
Ifyourlooptakeslongerthanthis,youwillhavetoexplicitlygiveCPUtimetotheWi-Fi/TCPstacks,by
usingincludingdelay(0);oryield();.Ifyoudon’t,networkcommunicationwon’tworkasexpected,andif
it’slongerthan3seconds,thesoftWDT(WatchDogTimer)willresettheESP.IfthesoftWDTisdisabled,
afteralittleover8seconds,thehardwareWDTwillresetthechip.
Fromamicrocontroller’sperspectivehowever,3secondsisaverylongtime(240millionclockcycles),so
unlessyoudosomeextremelyheavynumbercrunching,orsendingextremelylongstringsoverSerial,
youwon’tbeaffectedbythis.Justkeepinmindthatyouaddtheyield();insideyourfororwhileloops
thatcouldtakelongerthan,say100ms.
Sources
ThisiswhereIgotmostofmyinformationtowritthisarticle,there’ssomemoredetailsontheGitHub
pages,ifyou’reintosomemoreadvancedstuff,likeEEPROMordeepsleepetc.
https://github.com/esp8266/Arduino/issues/2942
https://github.com/esp8266/Arduino/pull/2533/files
https://github.com/esp8266/Arduino/blob/master/doc/libraries.md
https://github.com/esp8266/Arduino/blob/master/doc/reference.md
https://github.com/esp8266/Arduino/blob/master/doc/boards.md

Wi-Fi
UsingtheESP8266asasimplemicrocontrollerisgreat,butthereasonwhymostpeopleuseit,isitsWi-
Ficapabilities.Inthischapter,we'lldiveintothewonderfulworldofnetworkprotocols,likeWi-Fi,TCP,
UDP,HTTP,DNS...Alltheseacronymsmightintimidateyou,butI'lltrymybesttoexplainthemstep-by-
stepandinaneasyway.
Someparagraphsareinitalic.Theseprovidesomeextrainformation,butarenotcriticalto
understandingtheESP'sWi-Fifunctions,sodon'tgetfrustratediftherearethingsyoudon'tunderstand.
It'sreallyhardtogiveaclearexplanation,withoutover-complicatingthingsandwhilekeepingitshort
enoughaswell.Ifyou'vegotanyfeedbackorremarks,besuretoleaveacommenttohelpimprovethis
article.Thanks!
TheTCP/IPstack
Thesystemmostpeoplerefertoas'TheInternet'isn'tjustoneprotocol:it'sanentirestackoflayersof
protocols,oftenreferredtoastheTCP/IPstack.We'llgooverthesedifferentlayers,becauseweneed
tounderstandhowourESP8266communicateswithotherdevicesonthenetwork.
Layer Protocols
Application HTTP,FTP,mDNS,WebSocket,OSC...
Transport TCP,UDP
Internet IP
Link Ethernet,Wi-Fi...
TheLinklayer
Thelinklayercontainsthephysicallinkbetweentwodevices,anEthernetcable,forexample,oraWi-Fi
connection.Thisisthelayerthatisclosesttothehardware.
ToconnectanESP8266tothenetwork,youhavetocreateaWi-Filink.Thiscanhappenintwodifferent
ways:
1. TheESP8266connectstoawirelessaccesspoint(WAPorsimplyAP).TheAPcanbebuilt-intoyour
modemorrouter,forexample.
Inthisconfiguration,theESPactslikeawirelessstation.
2. TheESP8266actsasanaccesspointandwirelessstationscanconnecttoit.Thesestationscould
beyourlaptop,asmartphone,orevenanotherESPinstationmode.
OncetheWi-Filinkisestablished,theESP8266ispartofalocalareanetwork(LAN).Alldevicesona
LANcancommunicatewitheachother.
Mostofthetime,theAPisconnectedtoaphysicalEthernetnetworkaswell,thismeansthatthe
ESP8266canalsocommunicatewithdevicesthatareconnectedtotheAP(modem/router)viaawired
Ethernetconnection(desktopcomputers,gamingconsolesandset-topboxes,forinstance).
IftheESP8266isinaccesspointmode,itcancommunicatewithanystationthatisconnectedtoit,and
twostations(e.g.alaptopandasmartphone)canalsocommunicatewitheachother.
TheESPcanbeusedinAP-only,station-only,orAP+stationmode.
TL;DR
Thelinklayeristhephysicallinkbetweendevices:inthecaseoftheESP8266,thisisaWiFiconnection.
TheESPcanactasastationandconnecttoanaccesspoint,oractasanaccesspointandletother
devicesconnecttoit.
TheInternetorNetworklayer
Althoughthedevicesarenowphysicallyconnected(eitherthroughactualwires(Ethernet)orthrough
radiowaves(Wi-Fi)),theycan'tactuallytalktoeachotheryet,becausetheyhavenowayofknowing
wheretosendthemessageto.
That'swheretheInternetProtocol(IP)comesin.EverydeviceonthenetworkhasapersonalIP
address.TheDHCPserver(DynamicHostConfigurationProtocolServer)makessurethatthese
addressesareunique.

Thismeansthatyoucannowsendamessagetoaspecificaddress.
TherearetwoversionsoftheInternetProtocol:IPv4andIPv6.IPv6isanimprovedversionofIPv4and
hasmuchmoreaddressesthanIPv4(becausetherearemuchmoredevicesthanavailableIPv4
addresses).Inthisarticle,we'llonlytalkaboutIPv4addresses,sincemostLANsstillusethem.
TheIPaddressconsistsof4numbers,forexample192.168.1.5isavalidIPv4address.Itactually
consistsoftwoparts:thefirstpartis192.168.1,thisistheaddressofthelocalnetwork.Thelastdigit,5
inthiscase,isspecifictothedevice.
ByusingIPaddresses,wecanfindtheESP8266onthenetwork,andsendmessagestoit.TheESPcan
alsofindourcomputerorourphone,ifitknowstheirrespectiveIPaddresses.
Sub-netmask(optional)
ThissubdivisionoftheIPaddressisdeterminedbythesub-netmask,oftenwrittenas255.255.255.0.
Youcanseethatitconsistsoffournumbers,justliketheIPaddress.Ifapartofthesub-netmaskis255,
itmeansthatthecorrespondingpartoftheIPaddressispartofthenetworkaddress,ifit's0,the
correspondingIPpartispartoftheaddressofthespecificaddress.Adifferentnotationto"IP:
192.168.1.5,sub-netmask:255.255.255.0"wouldbe"192.168.1.5/24",becausethebinary
representationofthesub-netmaskis11111111.11111111.11111111.00000000,andithas24ones.
Ifyouwanttoknowmoreaboutsub-nets,I'drecommendyoutoreadtheWikipediaarticle.
(Aquicktiptohelpyouremember:it'scalledthesub-netmask,becauseifyouperformabitwiseAND
operationontheIPaddressandthesub-netmask(i.e.usethesub-netmaskasamaskfortheIP
address),yougettheaddressofthesub-net.)
MACaddressesandARP(optional)
ItisactuallyimpossibletosendpacketsdirectlytoanothermachineusingonlytheIPaddress.Tosenda
packettoaspecificdeviceontheLAN(Wi-FiorEthernet),youhavetoknowitsMAC-address.TheMAC
addressisauniquenumberthatisuniqueforeverynetworkdevice,anditneverchanges,it'shardwired
inthenetworkchip.ThismeansthateveryESP8266,everynetworkcard,everysmartphone...ever
made,hasadifferentMACaddress.
SobeforetheESPcansendapackettoyoursmartphoneforexample,ithastoknowitsMACaddress.It
doesn'tknowthisyet,theESPonlyknowstheIPaddressofthesmartphone,say192.168.1.6.Todothis,
theESPsendsabroadcastmessage(i.e.amessageaddressedtoalldevicesontheLAN)saying"I'm
lookingfortheMACaddressofthedevicewiththeIPaddress192.168.1.6".TheESPalsoincludesits
ownIPandMACaddresswiththemessage.Whenthesmartphonereceivesthisbroadcastmessage,it
recognizesitsownIPaddress,andrespondstotheESPbysendingitsownMACaddress.NowtheESP
andthephonebothknoweachother'sIPandMACaddresses,andtheycancommunicateusingIP
addresses.ThismethodiscalledtheAddresResolutionProtocol,orARP.
WhatabouttheInternet?
Asyoumighthavenoticed,Ionlytalkedaboutthelocalareanetwork,thesearethecomputersinyour
ownhouse.SohowcantheESP8266communicatewiththeInternet,youmayask?Well,there'salotof
networkinfrastructureinvolvedin'TheInternet',andtheyallobeytheIPrules,tomakesuremostof
yourpacketsarriveattheredestination.It'snotthatsimpleofcourse,there'salotofthingsgoingon,
likeroutingandNetworkAddressTranslation(NAT),butthatfallsoutsidethescopeofthisarticle,andit's
notreallysomethingmostpeoplehavetoworryabout.
TL;DR
TheInternetlayerusesIPaddressesinordertoknowwhereitshouldsendthedata.Thismeansthattwo
devicescannowsendpacketsofdatatoeachother,evenovertheInternet.
TheTransportlayer
ThedifferentdevicesinthenetworkdotheirbesttodelivertheseIPpacketstotheaddressee,however,
it'snotuncommonforapackettogetlost,soitwillneverarrive.Orthepacketmightgetcorruptedon
theway:thedataisnolongercorrect.IPalsocan'tguaranteethatthepacketsarriveinthesameorder
theyweresentin.
Thismeansthatwecan'treliablysendmessagesyetbyonlyusingthelinkandtheInternetlayer,since
wecanneverknowwhenandwhetherapacketwillarrive,orknowforcertainthatareceivedpacketis
correct.

WeneedathirdlayerontopoftheInternetlayer:theTransportlayer.
Therearemainlytwoprotocolsthatmakeupthisthirdlayer:theTransmissionControlProtocol(TCP)and
theUserDatagramProtocol(UDP).
TCPmakessurethatallpacketsarereceived,thatthepacketsareinorder,andthatcorrupted
packetsarere-sent.Thismeansthatitcanbeusedforcommunicationbetweenmultiple
applications,withouthavingtoworryaboutdataintegrityorpacketloss.Thisiswhyit'susedfor
thingslikedownloadingwebpages,sendingemail,uploadingfilesetc.
UDPontheotherhand,doesn'tguaranteethateverypacketreachesitsdestination,itdoescheck
forerrorshowever,butwhenitfindsone,itjustdestroysthepacket,withoutre-sendingit.This
meansthatit'snotasreliableasTCP,butit'sfaster,andhasamuchlowerlatency,becauseit
doesn'trequireanopenconnectiontosendmessages,likeTCPdoes.That'swhyit'susedinvoice
andvideochats,andforexampleinonlinegames.
IfyouwanttoknowmoreaboutthedifferencesbetweenTCPandUDP,checkoutthisvideo.
TL;DR
TheIPprotocolisnotreliable,andhasnoerrorchecking.TCPsolvesthisbyre-sendinglostorcorrupt
packages,andorderspacketsthatarereceivedinthewrongorder.UDPalsochecksforcorrupt
packages,butdoesn'tre-sendthem,soithaslesslatencythanTCP.
TheApplicationlayer
WenowhavereliablecommunicationusingTCP,butthere'sstilloneproblem.Thinkofitthisway:you
aresendingaletter,andTCPguaranteesthatitwillarriveatitsdestination,butifthereceiverdoesn't
understandthelanguageit'swrittenin,hewon'tknowwhattodowithit.
Inotherwords,weneedafourthlayerofprotocols,fortwoprogramstobeabletocommunicatewith
eachother.
There'slotsofdifferentprotocolsoutthere,butwe'llmostlyfocusontheprotocolsforwebserversand
browsers.
HyperTextTransferProtocol
TheHyperTextTransferProtocol,orHTTP,istheprotocol(cfr.language)thatisusedbybothwebservers
andwebclientsinordertocommunicate.Itusestexttoperformsendrequestsandresponsesfromthe
clienttotheserverandbackagain.
Forexample,whenyoutypehttp://www.google.comintotheaddressbarofawebbrowser(client),itwill
sendanHTTPGETrequesttotheGooglewebserver.TheserverunderstandsthisHTTPrequest,andwill
sendtheGooglewebpageasaresponse.OrwhenyouuploadanimagetoInstagram,yourbrowser
sendsanHTTPPOSTrequestwithyourselfieattachedtotheInstagramserver.Theserverunderstands
therequest,savestheimageandaddsitintothedatabase,sendstheURLofthenewimagebackto
yourbrowser,andthebrowserwilladdtheimageonthewebpage.
Asyoucansee,neithertheclientnortheserverhastoworryabouttheintegrityofthemessagesthey
send,andtheyknowthattherecipientunderstandstheirlanguage,andthatitwillknowwhattodowith
acertainHTTPrequest.
MostmodernsitesuseasecureversionofHTTP,calledHTTPS.Thissecureconnectionencryptsthedata,
forsecurityreasons.(Youdon'twantanyonereadingthepacketsfromyourmailserver,orthepackets
yousenttoyourbank,forinstance.)
WebSocket
HTTPisgreatforthingslikedownloadingwebpages,uploadingphotosetc.butit'squiteslow:everytime
yousendanHTTPrequest,youhavetostartanewTCPconnectiontotheserver,thensendyour
request,waitfortheservertorespond,anddownloadtheresponse.Wouldn'titbegreatifwedidn't
havetoopenanewconnectioneverytimewewanttosendsomedata,andifwecouldsendandreceive
dataatthesametimeatanymomentwe'dlike?That'swhereWebSocketcomestotherescue:youcan
keeptheTCPconnectionwiththeserveropenatalltimes,yougetperfectTCPreliability,andit'spretty
fast.
OpenSoundControl
HTTPandWebSocketbothuseTCPconnections.Whatifyouwantlowerlatency?Well,OpenSound
Control,orOSC,usesUDPtosendsmallpiecesofdata,likeints,floats,shorttextetc...withverylow
latency.Itwasoriginallydesignedforcontrollinglowlatencyaudioapplications,butit'saveryflexible
protocol,soit'softenusedforlow-latencytasksotherthanaudiocontrol.
DomainNameSystem

Asmentionedbefore,youcanonlysendamessagetoanothercomputerifyouknowitsIPaddress.But
whenyoubrowsetheInternet,youonlyknowawebsite'sdomainname(e.g.www.google.com).Your
computerusestheDomainNameSystemtotranslatethisdomainnametotherightIPaddress.Moreon
thislater.
Sources
https://en.wikipedia.org/wiki/Internet_protocol_suite
https://en.wikipedia.org/wiki/Port_(computer_networking)
https://en.wikipedia.org/wiki/Transmission_Control_Protocol
https://en.wikipedia.org/wiki/Internet_Protocol
https://en.wikipedia.org/wiki/User_Datagram_Protocol

UploadingsketchestotheESP8266
TheuploadprocedureforESP8266boardsisalittledifferentfromthenormalArduinoprocedure.Most
Arduinoswillautomaticallyresetwhenanewprogramisbeinguploaded,andwillautomaticallyenter
programmingmode.
OnsomeESPboardsyouhavetomanuallyenterprogrammingmode,andonthebare-bonesmodules,
youevenhavetoresetthemmanually.
However,therearesomesimplecircuitsyoucanusetogetautomaticuploads.
Auto-reset
Thisonlyappliestoboardswithoutanon-boardUSB-to-Serialconverter.
IftheUSB-to-Serialconverteryou'reusinghasaDTRflowcontrolline,youcanautomatethereset
signal.WhensendingdatatotheESP,theDTRlinegoeslow,andstayslowforsometime.Toresetthe
ESP,weneedalowpulseontheRSTpin.TheproblemisthattheDTRpinstayslow.Tosolvethis,we're
goingtobuildacrudeedgedetectorcircuit,usingacapacitor.Takealookatthefollowingschematic:
Youmightrecognizethatthisisbasicallyalow-cutfilter.Innormalconditions,DTRishigh(3.3V),and
theresetlineisalsohigh,becauseofthepull-upresistorR2.Thismeansthatthevoltageacrossthe
capacitoris0V.WhenDTRsuddenlydrops(to0V),thevoltageacrossthecapacitorisstill0V,meaning
thattheresetlinewillbeat0V+0V=0V,andaresetistriggered.
However,C1immediatelystartschargingthroughR2,andreaches3.3V.Atthispoint,DTRisstillat0V,
meaningthatthere'snow3.3Vacrossthecapacitor.WhenDTRrisesagain,theresetlinewillbeat3.3V
+3.3V=6.6V,andthenimmediatelystartstodischargethroughR2,finallyreaching3.3Vagain,with0V
acrossC1.
Thisisaproblem:6.6VcandamagetheESP,sowehavetofindawaytogetridofthepositivepeak.
OneglanceatthisMATLABsimulationshowsustheproblemevenbetter:

ThebluesignalisthevoltageontheDTRpin,andtheyellowsignalisthevoltageontheresetpin.
Thesolutionistoaddadiode:whilechargingthecapacitor,itshouldn'tchangeanything,soitshouldbe
reversebiased(justafancywayofsayingthatit'snotconductinganycurrentbecausethepolarityisthe
otherwayaround),andwhilethecapacitorisdischarging,itshoulddischargethecapacitor
"immediately".
Here'swhatthatlookslike:
Let'srunthesimulationagaintocheckifourproblemissolved:

Asyoucansee,the6.6Vpeakisnowverynarrow,justlikewewanted.It'simpossibletodischargethe
capacitorinstantly,thatwouldrequireacapacitorandadiodewith0Ωofseriesresistance,andan
infinitecurrent,whichisimpossible,obviously.There'salsoasmallerbutrelativelywidepeakof
approximately3.9V.Thisisbecauseadiodeonlyconductswhenthevoltageacrossitishigherthan
~600mV.Thismeansthatthelast0.6Vthat'sleftinthecapacitor(from3.9to3.3V)willstillbe
dischargedthroughR2only.
Nevertheless,thevoltagepeakismuchlowerandnarrowerthanwithoutthediode,andit'ssafeto
connecttotheESP8266.
ThisexactcircuitisalsousedintheArduinoUno,forexample.
Note:ifyoufollowedtheinstructionsinthehardwarestepcorrectly,youshouldalreadyhaveaddedR2
toyourESP.
HowtouseAuto-reset
Tousethisauto-resetcircuit,connectittotheDTRlineofyourUSB-to-Serialconverter,andtothereset
lineoftheESP,asshowninthediagram.Thenclickcompile(justbecausethefirstcompilationcantake
quitesometime).GotoTools>Resetandselect'ck'.Whenit'sdonecompiling,holddowntheprogram
buttonweaddedinthehardwarestep,andclickupload.Waitforittosay"Uploading..."andthen
releasetheprogrambutton.
Auto-resetandAuto-program
Thisonlyappliestoboardswithoutanon-boardUSB-to-Serialconverter.
Themethodabovestillrequiresyoutopressabuttontouploadanewsketch.IfyourUSB-to-Serial
converterhasaRTSlineaswellasaDTRline,youcanautomatetheentireprocess.

Youmayfindoutthatthe4.7kΩresistordoesn'tworkforyou.Inthatcase,trysomeothervalue,like
10kΩ,forexample.
ThismethodwasfirstusedintheNodeMCU,sogotoTools>ResetMethod,andselect"nodemcu".This
willdrivetheDTR&RTSpinshighandlowintherightsequencetogetitinprogrammingmodebefore
uploading.
Thisisbyfarthebestmethod,buttheproblemisthatyouneedaccesstoboththeRTSandDTRpins,
whilemostUSB-to-Serialadaptersbreakoutonlyoneofthetwo.
Manualresetandmanualprogram
Thisonlyappliestoboardswithoutanon-boardUSB-to-Serialconverter.
Ifyoudon'thaveaUSB-to-SerialconverterwithDTRandRTSlines,youcouldalsojustusetheresetand
programbuttonsweaddedinthehardwarechapter.TogettheESPinprogrammode,GPIO0mustbelow
whilebooting:
1. pressandholdtheresetbutton
2. pressandholdtheprogrambutton
3. releasetheresetbutton,theESPwillbootinprogrammode
4. releasetheprogrambutton
5. uploadthesketch
Ifyouwanttogetoutofprogrammodewithoutuploading,justpressreset(withoutpressingthe
programbutton).
Boardoptions
IfyourspecificboardisintheTools>Boardlist(e.g.NodeMCU,SparkFunandAdafruitboards),youcan
justselectit,andyouwillgettherightsettings.Whenyourboardisn'tinthelist,you'llhavetoselecta
GenericESP8266.Inthatcasethere'slotsofnewoptionsintheToolsmenuoftheArduinoIDE,solet's
gooverthemandpicktherightsettings.

FlashMode
LikeIsaidbefore,theESP8266usesanexternalflashchipforstorage.Youcancommunicatewiththis
chipover2datalines(DIO),oroverall4datalines(QIO).Using4linesistwotimesfasterthan2lines,so
inmostcases,youshouldchooseQIO.(Ifyou'redoingsomeadvancedstuffandyouneed2moreGPIO
pins,youcoulduse2linesinsteadof4,andusethe2linesasI/O.Mostmodulesdon'tgiveyouaccess
tothesepins,though.)
FlashSize
Differentboards/moduleshavedifferentsizesofflashchipsonboard.Thereareboardswith512kB,1MB,
2MBand4MBofflash.Toknowhowmuchflashyourboardhas,youcantrytheExamples>ESP8266>
CheckFlashConfigtoseeifyourflashsettingiscorrect,oryoucancheckthespecificationsofyour
specificboardonline.
YoucanalsoselecttheSPIFFS(SPIFlashFileSystem)size.TheSPIFFSpartitionisasmallfilesystemto
storefiles.Ifyou'renotusingit,youcanselecttheminimum.Lateroninthearticle,we'lluseSPIFFS,
andI'llremindyoutoselectalargerSPIFFSsize,butfornow,itdoesn'treallymatter.
Debugport
There'saloadofthingsgoingonwhentheESPisrunning:ThingslikeWi-Ficonnections,TCP
connections,DNSlookups...younameit.Allthesesmalltasksproduceawholelotofdebugoutputto
helpyoutroubleshoot.However,inanormalsituation,whereyourprogramisbehavingasexpected,you
don'tneedallthosedebugmessagestofloodtheSerialMonitor,soyoucanjustturnthemoffby
selecting'Disabled'.
Ifyoudowishtoreceivedebugmessages,youcanselecttheporttosendthemto.(Serialonpins1and
3,orSerial1onpin2)
Debuglevel
Thisallowsyoutochoosewhatkindofdebugmessagesyouwanttoshow.
ResetMethod
Asmentionedintheparagraphsabove,therearedifferentmethodsforauto-resetandauto-program.If
you'reusingthefirstmethod(usingtheedgedetector),youshoulduse'ck',ifyouusethetwo-transistor
circuit,select'nodemcu'.
FlashFrequency
Ifyouneedsomeextramemoryspeed,youcouldchangetheflashfrequencyfrom40MHzto80MHz.
ThisistheclockfrequencyoftheSPI/SDIOlink.
CPUFrequency
IfyouneedsomeextraCPUperformance,youcandoubletheclockspeedfrom80MHzto160MHz.It's
actuallyanoverclock,butI'veneverhadanyissuesorinstability.
UploadSpeed
ThebaudrateforuploadingtotheESP.Thedefaultis115200baud,butyoucangohigher(ifyou're
changingyoursketchalot,itmightbetooslow).921600baudworksmostofthetime,butyoumayget
anerrorsometimes,ifthat'sthecase,switchingbackto115200willprobablysolveallproblems.

EstablishingaWi-Ficonnection
LikeImentionedinthepreviouschapter,theESP8266canoperateinthreedifferentmodes:Wi-Fi
station,Wi-Fiaccesspoint,andbothatthesametime.We'llstartbylookingattheconfigurationofaWi-
Fistation.
Stationmode
Connectingtoonespecificnetwork
#include<ESP8266WiFi.h>//IncludetheWi-Filibrary
constchar*ssid="SSID";//TheSSID(name)oftheWi-Finetworkyouwanttoconnectto
constchar*password="PASSWORD";//ThepasswordoftheWi-Finetwork
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
WiFi.begin(ssid,password);//Connecttothenetwork
Serial.print("Connectingto");
Serial.print(ssid);Serial.println("...");
inti=0;
while(WiFi.status()!=WL_CONNECTED){//WaitfortheWi-Fitoconnect
delay(1000);
Serial.print(++i);Serial.print('');
}
Serial.println('\n');
Serial.println("Connectionestablished!");
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
}
voidloop(){}
Thecodetoconnecttoawirelessaccesspointisrelativelystraightforward:entertheSSIDandthe
passwordofthenetworkyouwanttoconnectto,andcalltheWiFi.beginfunction.Thenwaitforthe
connectiontocomplete,etvoilà,yourESP8266isnowconnectedtoyourLocalAreaNetwork.
Don'tbelieveme?I'llproveittoyou:opentheSerialmonitor(CTRL+SHIFT+M)anduploadthesketch.
Youshouldseesomethinglikethis:
ConnectingtoSSID...
123456...
Connectionestablished!
IPaddress: 192.168.1.3
Nowgotoyourcomputerandopenupaterminal:OnWindows,searchfor"CommandPrompt",onMac
orLinux,searchfor"Terminal".Youcouldalsousetheshortcuts:onWindows,hit +R,type"cmd"and
hitenter,onLinux,useCTRL+ALT+T.
Next,typeping,andthentheIPaddressyoureceivedintheSerialmonitor.Ifyou'reonMacorLinux,
useCTRL+Ctostopitafteracoupleoflines.Theoutputshouldlooksomethinglikethis:
user@computername:~$ ping192.168.1.3
PING192.168.1.3(192.168.1.3)56(84)bytesofdata.
64bytesfrom192.168.1.3:icmp_seq=1ttl=128time=6.38ms
64bytesfrom192.168.1.3:icmp_seq=2ttl=128time=45.2ms
64bytesfrom192.168.1.3:icmp_seq=3ttl=128time=69.1ms
64bytesfrom192.168.1.3:icmp_seq=4ttl=128time=94.0ms
64bytesfrom192.168.1.3:icmp_seq=5ttl=128time=20.5ms
64bytesfrom192.168.1.3:icmp_seq=6ttl=128time=7.37ms
^C
---192.168.1.3pingstatistics---
6packetstransmitted,6received,0%packetloss,time5003ms
rttmin/avg/max/mdev=6.384/40.463/94.047/32.588ms
ThepingcommandsendssmallpacketstotheIPaddressoftheESP8266.WhentheESPreceivessucha

packet,itsendsitbacktothesender.PingispartofthesecondlayeroftheTCP/IPstack,theInternet
layer.ItreliesonboththeDataLinklayer(Wi-Fi)andtheInternetProtocol*.
Youcanseethatintheexampleabove,wesent6packetstotheESP,andwealsoreceived6response
(echo)packets.ThistellsusthattheDataLink,theWi-Ficonnection,andtheInternetProtocolare
workingcorrectly.
WenowknowthattheESPcansuccessfullycommunicatewithotherdevicesonthenetwork,andifyour
localnetworkisonline(ifitisconnectedtotheInternetviayourmodem),theESPcanalsocommunicate
withanydeviceontheweb!
PingisagreattooltocheckiftheESP(oranydevice,really)isstillconnectedtothenetwork,andifit's
stillworkingfine.
OnedrawbackisthatIPaddressescanchangeovertime,butthat'saproblemwe'lladdressinoneofthe
followingchapters...
(*)I'msimplifyingthingsabithere.Actually,pingispartoftheInternetControlMessageProtocol(ICMP),that'salsopartofthe
secondlayer,justliketheInternetProtocol.Don'tworrytoomuchaboutit,justrememberthatifyoucansendpingpacketstoa
device,youcanalsosendIPpackets.
Thedevicewiththeantennaservesmanydifferentpurposes:
Accesspoint:OtherWi-Fidevicescanconnecttoit,tobepartofthelocalnetwork.
Router:ItroutesIPpacketstotherightsub-netssothattheywillarriveattheirdestination.E.g.if
thecomputersendsamessagethatismeantfortheESPovertheEthernetsub-net,therouterwill
sendthepackettotheWi-Fisub-net,becauseitknowsthat'swheretheESPis.
Modem:iftheroutercan'tfindtheaddresseeonthelocalnetwork,thepacketwillbepassedonto
theintegratedmodem,anditwillbesenttotheInternetServiceProvideroveraDSLline,heading
fortheInternet,wherelotsofotherrouterswilltrytogetthepackettotherightdestination.
Butinreality,youdon'thavetoworrytoomuchaboutit,becauseit'salldoneforyou,inafractionofa
secondwithoutyouevennoticingit!
Automaticallyconnecttothestrongestnetwork
Thesketchabovemightbeenoughforyourspecificapplication,butifyouneedtobeabletoconnectto
multipleWi-Finetworks,forexampletheWi-FiathomeandtheWi-Fiattheoffice,itwon'twork.
Tosolvethisproblem,we'llusetheWi-Fi-Multilibrary:Youcanaddasmanynetworksasyoulike,andit
automaticallyconnectstotheonewiththestrongestsignal.

#include<ESP8266WiFi.h>//IncludetheWi-Filibrary
#include<ESP8266WiFiMulti.h>//IncludetheWi-Fi-Multilibrary
ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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,
andconnecttothestrongestofthenetworksabove
delay(1000);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
}
voidloop(){}
AccessPointmode
ToconfiguretheESP8266asanaccesspoint,toallowotherdeviceslikesmartphonesorlaptopsto
connecttoit,youcanusethesoftAPfunction:
#include<ESP8266WiFi.h>//IncludetheWi-Filibrary
constchar*ssid="ESP8266AccessPoint";//ThenameoftheWi-Finetworkthatwillbecreated
constchar*password="thereisnospoon";//Thepasswordrequiredtoconnecttoit,leaveblankforan
opennetwork
voidsetup(){
Serial.begin(115200);
delay(10);
Serial.println('\n');
WiFi.softAP(ssid,password);//Starttheaccesspoint
Serial.print("AccessPoint\"");
Serial.print(ssid);
Serial.println("\"started");
Serial.print("IPaddress:\t");
Serial.println(WiFi.softAPIP());//SendtheIPaddressoftheESP8266tothecomputer
}
voidloop(){}
Toseeifitworks,opentheWi-Fisettingsonyourcomputer,lookforanetworkcalled"ESP8266Access
Point",enterthepassword"thereisnospoon",andconnecttoit.Thenopenaterminal,andpingto
192.168.4.1(thisisthedefaultIPaddressofourESPAP).You'llseethattheESPrespondstoyourpings.
However,ifyoutrytogotoanonlinewebsite,you'llgetatimeoutoraDNSerror.Thisisbecausethe
ESPitselfisnotconnectedtotheinternet.Thesub-netthatconsistsoftheESPandthecomputerisnot
connectedtoanyothernetworks,sothere'snowayforapacketonthisnetworktomakeittothe
Internet.
IfyouconnectedasecondstationtotheESPaccesspointontheotherhand,youwouldbeabletoping
fromonestationtotheotherwithoutproblems,becausethey'reonthesamenetwork.

MulticastDomainNameSystem
DNS
Let'sfaceit,constantlytypingIPaddressesisreallycumbersome,anditwouldbeimpossibleto
rememberallyourfavoritewebsites'addresses,especiallyiftheyuseIPv6.
That'swhydomainnameswereintroduced:asimplestringoftextthat'seasytoremember,forexample
www.google.com.
However,tosendarequesttoawebsite,yourcomputerstillneedstoknowitsIPaddress.That'swhere
DNScomesin.ItstandsforDomainNameSystem,andisawaytotranslateawebsite'sdomainnameto
itsIPaddress.OntheInternet,therearealotofDNSservers.EachDNSserverhasalonglistofdomain
namesandtheircorrespondingIPaddresses.DevicescanconnecttoaDNSserverandsendadomain
name,theDNSserverwillthenrespondwiththeIPaddressoftherequestedsite.
Youcouldcompareittoatelephonedirectory:youcanlookupanametofindthecorrespondingphone
number.
TheDNSlookuphappenscompletelyinthebackground:whenyougotoawebsiteinyourbrowser,itwill
firstsendarequesttoaDNSserver(thisimpliesthatthecomputerknowstheIPaddressoftheDNS
serveritself),waitfortheresponseofthelookup,andthensendtheactualrequesttotherightIP
address.
mDNS
DNSworksgreatfornormalsitesontheInternet,butmostlocalnetworksdon'thavetheirownDNS
server.Thismeansthatyoucan'treachlocaldevicesusingadomainname,andyou'restuckusingIP
addresses...
Fortunately,there'sanotherway:multicastDNS,ormDNS.
mDNSusesdomainnameswiththe.localsuffix,forexamplehttp://esp8266.local.Ifyourcomputer
needstosendarequesttoadomainnamethatendsin.local,itwillsendamulticastquerytoallother
devicesontheLANthatsupportmDNS,askingthedevicewiththatspecificdomainnametoidentify
itself.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);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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,
andconnecttothestrongestofthenetworksabove
delay(1000);
Serial.print(++i);Serial.print('');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
if(!MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.local
Serial.println("ErrorsettingupMDNSresponder!");
}
Serial.println("mDNSresponderstarted");
}
voidloop(){}

Uploaditandopenpingagain.Trytopingtoesp8266.local:
user@computername:~$ pingesp8266.local
PINGesp8266.local(10.92.237.128)56(84)bytesofdata.
64bytesfrom10.92.237.128:icmp_seq=1ttl=128time=5.68ms
64bytesfrom10.92.237.128:icmp_seq=2ttl=128time=3.41ms
64bytesfrom10.92.237.128:icmp_seq=3ttl=128time=2.55ms
64bytesfrom10.92.237.128:icmp_seq=4ttl=128time=2.19ms
64bytesfrom10.92.237.128:icmp_seq=5ttl=128time=2.29ms
64bytesfrom10.92.237.128:icmp_seq=6ttl=128time=2.74ms
^C
---esp8266.localpingstatistics---
6packetstransmitted,6received,0%packetloss,time5007ms
rttmin/avg/max/mdev=2.190/3.148/5.687/1.202ms
Asyoucansee,pingwillautomaticallyfindtheIPaddressoftheESPforyou.
mDNSissupportedonWindows,OSX,LinuxandiOS,butnot(yet?)onAndroid.
It'sarealshamethatAndroiddoesn'tsupportit,youcanhelpbystarringthisissuereportforthe
ChromiumprojecttoaskformDNSsupportinChromeonAndroid.
Ofcourse,youcanchangethedomainnameoftheESPbychangingtheparameterofMDNS.begin.

ESP8266WebServer
BeingabletopingtheESPisquiteanachievementifyoulookatitfromatechnicalpointofview,butfor
mostpeople,it'snotthatexciting,andnotreallyuseful.
Inthischapter,I'llcoverthebasicsofawebserver,andteachyouhowtohostawebpageontheESP.
Webservers
AwebserverisanInternet-connecteddevicethatstoresandservesfiles.Clientscanrequestsuchafile
oranotherpieceofdata,andtheserverwillthensendtherightdata/filesbacktotheclient.Requests
aremadeusingHTTP.
HTTP
HTTPortheHypertextTransferProtocolisthetext-basedprotocolusedtocommunicatewith(web)
servers.TherearemultipleHTTPrequestmethods,butI'llonlycoverthetwomostwidelyusedones:
GETandPOST.
HTTPGET
GETrequestsareusedtoretrievedatafromaserver,awebpageforinstance.Itshouldn'tchange
anythingontheserver,itjustgetsthedatafromtheserver,withoutsideeffects.
Whenyouopenawebpageinyourbrowser,itwilltaketheURLandputitinanHTTPGETrequest.This
isjustplaintext.ThenitwillsendtherequesttotherightserverusingTCP.Theserverwillreadthe
request,checktheURL,andsendtherightHTTPresponseforthatURLbacktothebrowser.
TheanatomyofaGETrequest
ThemostimportantpartsofaGETrequestaretherequestlineandthehostheader.Let'stakealook
atanexample:
Ifyouclickthefollowinglink:https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html,yourbrowserwill
sendoutthefollowingHTTPrequest:
GET/Protocols/rfc2616/rfc2616-sec5.htmlHTTP/1.1
Host:www.w3.org
Connection:keep-alive
Pragma:no-cache
Cache-Control:no-cache
Upgrade-Insecure-Requests:1
User-Agent:Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/56.0.2924.87
Safari/537.36
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
DNT:1
Referer:https://www.google.be/
Accept-Encoding:gzip,deflate,sdch,br
Accept-Language:en-US,en;q=0.8
Thefirstlineistherequestline:itcontainstherequestmethod:GET,inthiscase,theURIorUniform
ResourceIdentifier:/Protocols/rfc2616/rfc2616-sec5.html,andtheHTTPversion:1.1.
Thesecondlineisthehostheader,itspecifiesthedomainnameofthehost(server).
Therearemanyotherheadersaswell,butthey'renotreallyimportantwhenusinganESP8266.
MostserverswillcheckiftheURIisafileontheirfilesystem,andifthat'sthecase,they'llsendthatfile
asaresponse.
ViewingHTTPheadersinthebrowser
Ifyouwanttochecktheheadersyourbrowsersends,youcanpressF12,gotothenetworktab,reload
thepage,andclicktherequestyouwanttoinspect.Ifyouwant,youcanclick'viewsource',thiswill
showyoutheactualHTTPtext.
Here'swhatthatlookslikeinChrome:

Sendingextrainformationtotheserver
Sometimes,youmightwanttoaddextrainformationtotheGETrequest.Youcansendkey-valuepairs
byaddingaquestionmark(?)totheURI,followedbykey=value.Multiplepairsareseparatedbyan
ampersand(&).
Forexample:
GET/get-phone-number.php?firstName=John&lastName=DoeHTTP/1.1
Host:www.phonebook.example.com
...
Ifyouuseanyspecialcharactersinthekeyorvaluenames,youhavetoURL-encodethem.
HTTPPOST
POSTrequestsareusedtosenddatatotheserver,forexample,tosendyourusernameandpassword
totheserverwhenyoulogin,orwhenyouuploadaphoto.UnlikeGET,POSTcanchangethedataon
theserverorthestateoftheserver.
POSThasabodythatcancontaindatathatissenttotheserver.
TheanatomyofaPOSTrequest
Forexample,theloginpageofyourfavoritesitemightsendsomethinglikethiswhenyouenteryour
credentialsandclicktheloginbutton:
POST/login.phpHTTP/1.1
Host:www.example.com
Connection:keep-alive
Content-Length:480
Origin:http://www.example.com
Content-Type:multipart/form-data;boundary=----WebKitFormBoundaryQNEJOasMvgAOg8Kt
...
Asyoucansee,therequestlinenowhasthePOSTmethodinit,andisstillfollowedbyaURI,
/login.php,andtheHTTPversion,1.1.Thehostheaderstillcontainsjustthedomainname.
Therealdifferenceistherequestbody:aGETrequesthasnopayload,whileyoucanaddalotofdata
tothebodyofaPOSTrequest.Thisdatacouldbenormalkey-valuepairs,likeausernameanda
password,oractualfilesthatarebeinguploaded.
AlsonotetheContent-Typeheader:ittellstheserverwhatkindofdatacanbefoundinthebodyofthe
POSTrequest.
Let'stakealookatthebodyoftheloginexample:
------WebKitFormBoundaryQNEJOasMvgAOg8Kt

Content-Disposition:form-data;name="username"
JohnDoe
------WebKitFormBoundaryQNEJOasMvgAOg8Kt
Content-Disposition:form-data;name="password"
p@ssw0rd123
------WebKitFormBoundaryQNEJOasMvgAOg8Kt
Content-Disposition:form-data;name="token"
9i9ZoLHl5pkRAeuKCEu76TbaCnMphwYkPEovEUY9PHk=
------WebKitFormBoundaryQNEJOasMvgAOg8Kt--
Asyoucansee,therearethreeparametersinsidethebody,everyparameterhasaname(e.g.
username),andavalue(e.g.JohnDoe).
YoucouldalsousethesamesyntaxweusedbeforewhenaddingparameterstoaGETrequest:
POST/add-user.phpHTTP/1.1
Host:www.example.com
Content-Length:27
Content-Type:application/x-www-form-urlencoded
...
Andthepayload:
firstName=John&lastName=Doe
Asyoucansee,theContent-Typeheaderisdifferent,indicatingthattheencodingofthevaluesinthe
payloadisdifferent.
HTTPstatuscodes
AservershouldanswerallrequestswithanHTTPstatuscode.Thisisa3-digitnumberindicatingifthe
requestwassuccessfulortellingtheclientwhatwentwrong.Here'satablewithsomeofthemost
importantandusefulones.
Status
Code Meaning
200 OK:therequestwassuccessful
303 SeeOther:usedtoredirecttoadifferentURI,afteraPOSTrequest,forinstance
400 BadRequest:theservercouldn'tunderstandtherequest,becausethesyntaxwasincorrect
401 Unauthorized:userauthenticationisrequired
403 Forbidden:theserverrefusestoexecutetherequest,authorizationwon'thelp
404 NotFound:therequestedURIwasnotfound
500 InternalServerError:Theserverencounteredanunexpectedconditionandcouldn'tfulfillthe
request
TCP&UDPPorts
Inmostcases,onedevicehasmanydifferentservices,forexample,awebserver,anemailserver,an
FTPserver,aSpotifystreamingservice,...
IfthedevicehadjustanIPaddress,itwouldbeimpossibletoknowwhichapplicationapacketwassent
to.That'swhyeveryservicehasaportnumber.It'sanidentifierforalldifferentservicesorapplications
onasingledevice.Intheexampleabove,thewebserverwillonlylistenforrequestsonport80,the
emailserveronlyonport25,theFTPserveronlyonport20,Spotifywillonlyreceivestreamsonport
4371...
Tospecifyacertainport,youcanuseacolonaftertheIPaddressofafterthedomainname.Butmostof
thetime,youdon'thavetoadditexplicitly.Forexample,allwebserverslistenonport80,soaweb
browserwillalwaysconnecttoport80.
http://stackoverflow.com/questions/176264/what-is-the-difference-between-a-uri-a-url-and-a-urn
https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes

ESP8266FirstWebServer
Theactualimplementationofawebserverismucheasierthanitsounds,becausetheESP8266Arduino
Coreincludessomegreatlibrariesthathandleprettymucheverythingforyou.Let'slookatabasicHello
World!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();//functionprototypesforHTTPhandlers
voidhandleNotFound();
voidsetup(void){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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,
andconnecttothestrongestofthenetworksabove
delay(250);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.local
Serial.println("mDNSresponderstarted");
}else{
Serial.println("ErrorsettingupMDNSresponder!");
}
server.on("/",handleRoot);//Callthe'handleRoot'functionwhenaclientrequestsURI
"/"
server.onNotFound(handleNotFound);//WhenaclientrequestsanunknownURI(i.e.something
otherthan"/"),callfunction"handleNotFound"
server.begin();//Actuallystarttheserver
Serial.println("HTTPserverstarted");
}
voidloop(void){
server.handleClient();//ListenforHTTPrequestsfromclients
}
voidhandleRoot(){
server.send(200,"text/plain","Helloworld!");//SendHTTPstatus200(Ok)andsendsometextto
thebrowser/client
}
voidhandleNotFound(){
server.send(404,"text/plain","404:Notfound");//SendHTTPstatus404(NotFound)whenthere'sno
handlerfortheURIintherequest
}
There'salotofcodethat'sthesameasintheWi-FiandmDNSexamples.
Theactualservercodeisprettystraightforward.First,wecreateaserverinstancethatlistensforHTTP
requestsonport80.Thisisthedefaultportforwebservers.Inthesetup,wetelltheserverwhattodo
withcertainHTTPrequests.IftheURI'/'isrequested,theservershouldreplywithaHTTPstatuscodeof
200(Ok)andthensendaresponsewiththewords'Helloworld!'.Weputthecodeforgeneratinga
responseinaseparatefunction,andthewetelltheservertoexecuteitwhen'/'isrequested,usingthe
server.onfunction.
Wehaven'tspecifiedwhattheservershoulddoiftheclientrequestsanyURIotherthan'/'.Itshould

respondwithanHTTPstatus404(NotFound)andamessagefortheuser.Weputthisinafunctionas
well,anduseserver.onNotFoundtotellitthatitshouldexecuteitwhenitreceivesarequestforaURIthat
wasn'tspecifiedwithserver.on.
ThenwestartlisteningforHTTPrequestsbyusingserver.begin.
Duringtheloop,weconstantlycheckifanewHTTPrequestisreceivedbyrunningserver.handleClient.If
handleClientdetectsnewrequests,itwillautomaticallyexecutetherightfunctionsthatwespecifiedin
thesetup.
Totestitout,uploadthesketch,openanewbrowsertab,andbrowsetohttp://esp8266.local.Youshould
getawebpagesayingHelloworld!.Ifyoutrytogotoadifferentpage,http://esp8266.local/test,for
instance,youshouldgeta404error:404:Notfound.
TurningonandoffanLEDoverWi-Fi
Wecanusethewebservertoserveinteractivepages,andtoreacttocertainPOSTrequest.Inthe
followingexample,theESP8266hostsawebpagewithabutton.Whenthebuttonispressed,the
browsersendsaPOSTrequestto/LED.WhentheESPreceivessuchaPOSTrequestonthe/LEDURI,it
willturnonorofftheLED,andthenredirectthebrowserbacktothehomepagewiththebutton.
Inordertoperformthisredirect,theESPhastoaddaLocationheadertotheresponse,andusea303
(SeeOther)HTTPstatuscode.
ThebuttontosendthePOSTrequestinthebrowserispartofanHTMLform.Youhavetospecifythe
targetURItosendtherequestto,andtherequestmethod,inthiscasethisis"/LED"andPOST
respectively.
NotethatIchangedthecontenttypeoftheresponsefrom"text/plain"to"text/html".Ifyousenditas
plaintext,thebrowserwilldisplayitastextinsteadofinterpretingitasHTMLandshowingitasa
button.
#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();//functionprototypesforHTTPhandlers
voidhandleLED();
voidhandleNotFound();
voidsetup(void){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
pinMode(led,OUTPUT);
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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,
andconnecttothestrongestofthenetworksabove
delay(250);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.local
Serial.println("mDNSresponderstarted");
}else{
Serial.println("ErrorsettingupMDNSresponder!");
}
server.on("/",HTTP_GET,handleRoot);//Callthe'handleRoot'functionwhenaclientrequestsURI
"/"
server.on("/LED",HTTP_POST,handleLED);//Callthe'handleLED'functionwhenaPOSTrequestismade

toURI"/LED"
server.onNotFound(handleNotFound);//WhenaclientrequestsanunknownURI(i.e.something
otherthan"/"),callfunction"handleNotFound"
server.begin();//Actuallystarttheserver
Serial.println("HTTPserverstarted");
}
voidloop(void){
server.handleClient();//ListenforHTTPrequestsfromclients
}
voidhandleRoot(){//WhenURI/isrequested,sendawebpagewithabuttonto
toggletheLED
server.send(200,"text/html","<formaction=\"/LED\"method=\"POST\"><inputtype=\"submit\"
value=\"ToggleLED\"></form>");
}
voidhandleLED(){//IfaPOSTrequestismadetoURI/LED
digitalWrite(led,!digitalRead(led));//ChangethestateoftheLED
server.sendHeader("Location","/");//Addaheadertorespondwithanewlocationforthe
browsertogotothehomepageagain
server.send(303);//SenditbacktothebrowserwithanHTTPstatus303(See
Other)toredirect
}
voidhandleNotFound(){
server.send(404,"text/plain","404:Notfound");//SendHTTPstatus404(NotFound)whenthere'sno
handlerfortheURIintherequest
}
Asyoucansee,theserver.onfunctionnowtakesthreeparameters:theURI,therequestmethod(GETor
POST)andthefunctiontoexecute.
ConnectanLEDtoGPIO2,anduploadthesketch.Thengotohttp://esp8266.local/andclickthebuttonto
turntheLEDonoroff.
YoucanopenthedeveloperoptionsinChrome(F12)tochecktheHTTPrequestthataremadewhenyou
clickthebutton:you'llseethatitfirstsendaPOSTrequest,andthenreceivesa303(SeeOther)HTTP
statusasaresponse.TheresponsealsohasaLocationheadercontainingtheURI"/",sothebrowserwill
sendaGETrequesttotheURIofthisnewlocation:

Ifyoucheckthepagesource(CTRL+U),youcanseethesimpleHTMLformthat'sused:
<formaction="/LED"method="POST">
<inputtype="submit"value="ToggleLED">
</form>
SendingdatatotheESPusingHTTPPOST
Inthepreviousexample,wesentanemptyPOSTrequesttotheESP8266.Inthepreviouschapter
however,Iexplainedthatit'spossibletosendallkindsofdatainthebodyofthePOSTrequest.
Inthisexample,I'llshowyouhowtosendausernameandapasswordtotheESP.TheESPwillthen
checkiftheyarecorrect,andrespondtotherequestwiththeappropriatepage.
#include<ESP8266WiFi.h>
#include<WiFiClient.h>
#include<ESP8266WiFiMulti.h>
#include<ESP8266mDNS.h>
#include<ESP8266WebServer.h>
ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'
ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80
voidhandleRoot();//functionprototypesforHTTPhandlers
voidhandleLogin();

voidhandleNotFound();
voidsetup(void){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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,
andconnecttothestrongestofthenetworksabove
delay(250);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.local
Serial.println("mDNSresponderstarted");
}else{
Serial.println("ErrorsettingupMDNSresponder!");
}
server.on("/",HTTP_GET,handleRoot);//Callthe'handleRoot'functionwhenaclientrequests
URI"/"
server.on("/login",HTTP_POST,handleLogin);//Callthe'handleLogin'functionwhenaPOSTrequestis
madetoURI"/login"
server.onNotFound(handleNotFound);//WhenaclientrequestsanunknownURI(i.e.something
otherthan"/"),callfunction"handleNotFound"
server.begin();//Actuallystarttheserver
Serial.println("HTTPserverstarted");
}
voidloop(void){
server.handleClient();//ListenforHTTPrequestsfromclients
}
voidhandleRoot(){//WhenURI/isrequested,sendawebpagewithabutton
totoggletheLED
server.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/login
if(!server.hasArg("username")||!server.hasArg("password")
||server.arg("username")==NULL||server.arg("password")==NULL){//IfthePOSTrequest
doesn'thaveusernameandpassworddata
server.send(400,"text/plain","400:InvalidRequest");//Therequestisinvalid,sosend
HTTPstatus400
return;
}
if(server.arg("username")=="JohnDoe"&&server.arg("password")=="password123"){//Ifboththe
usernameandthepasswordarecorrect
server.send(200,"text/html","<h1>Welcome,"+server.arg("username")+"!</h1><p>Login
successful</p>");
}else{//Usernameand
passworddon'tmatch
server.send(401,"text/plain","401:Unauthorized");
}
}
voidhandleNotFound(){
server.send(404,"text/plain","404:Notfound");//SendHTTPstatus404(NotFound)whenthere'sno
handlerfortheURIintherequest
}
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.Ifyouleave
onorbothofthefieldsblank,youshouldgeta400(BadRequest)error.Ifyouenterawrongusername
orpassword,youshouldgeta401(Unauthorized)error.
ThedataofthePOSTbodycanbeaccessedusingserver.arg("key"),andyoucancheckifaspecifickey
existsusingserver.hasArg("key").ThekeynameontheESP8266correspondstothenameargumentin
theHTMLformonthewebpage.
WhenwegetaPOSTrequest,wefirstcheckifthenecessaryarguments(usernameandpassword)are
present.Ifthat'snotthecase,wesenda400(InvalidRequest)status.
Thenwecheckifthecredentialsmatch'JohnDoe'&'password123'.Ifthat'sthecase,werespondwitha
status200(Ok)andawelcomepage.Iftheusernameand/orpassworddoesn'tmatch,wesenda401
(Unauthorized)status.
Inlinefunctions
Inthepreviousexamples,wepassedhandleRootandhandleNotFoundtotheserver.onfunctionasa
parameter(callbackfunction).Insomecaseshowever,it'smorereadabletojustwritethedefinitionof
thefunctioninline,likeso:
voidsetup(){
//...
server.onNotFound([](){
server.send(404,"text/plain","404:Notfound");
});
}

SPIFlashFileSystem
Upuntilnow,we'vealwaysincludedtheHTMLforourwebpagesasstringliteralsinoursketch.This
makesourcodeveryhardtoread,andyou'llrunoutofmemoryratherquickly.
Ifyouremembertheintroduction,ImentionedtheSerialPeripheralInterfaceFlashFileSystem,orSPIFFS
forshort.It'salight-weightfilesystemformicrocontrollerswithanSPIflashchip.Theon-boardflash
chipoftheESP8266hasplentyofspaceforyourwebpages,especiallyifyouhavethe1MB,2MBor4MB
version.
SPIFFSlet'syouaccesstheflashmemoryasifitwasanormalfilesystemliketheoneonyourcomputer
(butmuchsimplerofcourse):youcanreadandwritefiles,createfolders...
TheeasiestwaytolearnhowtouseSPIFFSistolookatsomeexamples.Butafileserverwithnofilesto
serveisprettypointless,soI'llexplainhowtouploadfilestotheSPIFFSfirst.
UploadingfilestoSPIFFS
Toselecttherightfilestoupload,youhavetoplacetheminafoldercalleddata,insidethesketchfolder
ofyourproject:OpenyoursketchintheArduinoIDE,andhitCTRL+K.Waitforafileexplorerwindowto
open,andcreateanewfoldernameddata.Copyyourfilesovertothisfolder.(Onlyusesmallfileslike
textfilesoricons.There'snotenoughspaceforlargephotosorvideos.)
Next,selectallfilesinthefolder(CTRL+A)andcheckthesizeofallfilescombined(don'tforget
subfolders).GototheArduinoIDEagain,andunderTools>FlashSize,selectanoptionwiththeright
flashsizeforyourboard,andaSPIFFSsizethatislargerthanthesizeofyourdatafolder.
Thenuploadthesketch.Whenthat'sfinished,makesurethattheSerialMonitorisclosed,thenopenthe
Toolsmenu,andclickESP8266sketchdataupload.IfyourESPhasauto-resetandauto-program,it
shouldworkautomatically,ifyoudon'thaveauto-program,youhavetomanuallyenterprogrammode
beforeuploadingthedatatoSPIFFS.Theprocedureisexactlythesameasenteringprogrammode
beforeuploadingasketch.
IfyougetanerrorsayingSPIFFS_writeerror(-10001):Filesystemisfull,thismeansthatyourfilesare
toolargetofitintotheSPIFFSmemory.SelectalargerSPIFFSsizeunderTools>FlashSize,ordelete
somefiles.
EvenifyourcomputersaysthatthefilesaresmallerthantheselectedSPIFFSsize,youcanstillgetthis
error:thishastodowithblocksizes,andmetadatalikefileandfoldernamesthattakeupspaceaswell.
IfyouchangetheSPIFFSsize,youhavetoreuploadyoursketch,becausewhenyouchangetheSPIFFS
size,thememorylocationwillbedifferent.TheprogramhastoknowtheupdatedSPIFFSaddressoffset
tobeabletoreadthefiles.
SPIFFSFileServer
Thefollowingexampleisaverybasicfileserver:itjusttakestheURIoftheHTTPrequest,checksifthe
URIpointstoafileintheSPIFFS,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);//convertthefileextensiontotheMIMEtype
boolhandleFileRead(Stringpath);//sendtherightfiletotheclient(ifitexists)
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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

delay(250);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
if(MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.local
Serial.println("mDNSresponderstarted");
}else{
Serial.println("ErrorsettingupMDNSresponder!");
}
SPIFFS.begin();//StarttheSPIFlashFilesSystem
server.onNotFound([](){//IftheclientrequestsanyURI
if(!handleFileRead(server.uri()))//senditifitexists
server.send(404,"text/plain","404:NotFound");//otherwise,respondwitha404(NotFound)
error
});
server.begin();//Actuallystarttheserver
Serial.println("HTTPserverstarted");
}
voidloop(void){
server.handleClient();
}
StringgetContentType(Stringfilename){//convertthefileextensiontotheMIMEtype
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";
return"text/plain";
}
boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)
Serial.println("handleFileRead:"+path);
if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindexfile
StringcontentType=getContentType(path);//GettheMIMEtype
if(SPIFFS.exists(path)){//Ifthefileexists
Filefile=SPIFFS.open(path,"r");//Openit
size_tsent=server.streamFile(file,contentType);//Andsendittotheclient
file.close();//Thenclosethefileagain
returntrue;
}
Serial.println("\tFileNotFound");
returnfalse;//Ifthefiledoesn'texist,returnfalse
}
Asyoucansee,wedon'tuseserver.oninthisexample.Instead,weuseserver.onNotFound:thiswillmatch
anyURI,sincewedidn'tdeclareanyspecificURIhandlerslikeinthepreviousserverexamples.
WhenaURIisrequested,wecallthefunctionhandleFileRead.ThisfunctionchecksiftheURIoftheHTTP
requestisthepathtoanexistingfileintheSPIFFS.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.
Compressingfiles
TheESP8266'sflashmemoryisn'thuge,andmosttextfiles,likehtml,cssetc.canbecompressedby
quitealargefactor.Modernwebbrowsersacceptcompressedfilesasaresponse,sowe'lltake

advantageofthisbyuploadingcompressedversionsofourhtmlandiconfilestotheSPIFFS,inorderto
savespaceandbandwidth.
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,sendtheindex
file
StringcontentType=getContentType(path);//GettheMIMEtype
StringpathWithGz=path+".gz";
if(SPIFFS.exists(pathWithGz)||SPIFFS.exists(path)){//Ifthefileexists,eitherasacompressed
archive,ornormal
if(SPIFFS.exists(pathWithGz))//Ifthere'sacompressedversionavailable
path+=".gz";//Usethecompressedversion
Filefile=SPIFFS.open(path,"r");//Openthefile
size_tsent=server.streamFile(file,contentType);//Sendittotheclient
file.close();//Closethefileagain
Serial.println(String("\tSentfile:")+path);
returntrue;
}
Serial.println(String("\tFileNotFound:")+path);
returnfalse;//Ifthefiledoesn'texist,returnfalse
}
Now,trycompressingsomeofthefilestotheGNUzipformat(.gz),anduploadingthemtoSPIFFS.Or
youcanjustdownloadthenewdatafolder(unzipitfirst).
Everytimeaclientrequestsacertainfile,theESPwillcheckifacompressedversionisavailable.Ifso,it
willusethatinsteadoftheuncompressedfile.TheoutputintheSerialMonitorshouldlooksomething
likethis:
handleFileRead:/
Sentfile:/index.html.gz
handleFileRead:/main.css
Sentfile:/main.css
handleFileRead:/JavaScript.js
Sentfile:/JavaScript.js
handleFileRead:/folder/JavaScript.js
Sentfile:/folder/JavaScript.js
handleFileRead:/favicon.ico
Sentfile:/favicon.ico.gz
Itautomaticallydetectedthatithadtosendthecompressedversionsofindex.htmlandfavicon.ico.

Uploadingfilestotheserver
Therearescenarioswhereyoumaywanttouploadnewfilestotheserverfromwithinabrowser,
withouthavingtoconnecttotheESP8266overUSBinordertoflashanewSPIFFSimage.
Inthischapter,I'llshowyouhowtouseHTMLformsandPOSTrequeststouploadoreditfilestoourlittle
ESPserver.
Client:HTMLform
TheeasiestwaytouploadfilesisbyusinganHTMLform,justlikeinthefirstserverexamples,wherewe
usedformstoturnon/offLEDs,andtosendthelogincredentialsbacktotheserver.Ifyouchooseafile
input,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,itredirectstheclienttoasuccess
page.
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);//convertthefileextensiontotheMIMEtype
boolhandleFileRead(Stringpath);//sendtherightfiletotheclient(ifitexists)
voidhandleFileUpload();//uploadanewfiletotheSPIFFS
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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
delay(1000);
Serial.print(++i);Serial.print('');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
if(!MDNS.begin("esp8266")){//StartthemDNSresponderforesp8266.local
Serial.println("ErrorsettingupMDNSresponder!");
}
Serial.println("mDNSresponderstarted");
SPIFFS.begin();//StarttheSPIFlashFilesSystem
server.on("/upload",HTTP_GET,[](){//iftheclientrequeststheuploadpage
if(!handleFileRead("/upload.html"))//senditifitexists
server.send(404,"text/plain","404:NotFound");//otherwise,respondwitha404(NotFound)
error

});
server.on("/upload",HTTP_POST,//iftheclientpoststotheuploadpage
[](){server.send(200);},//Sendstatus200(OK)totelltheclientwe
arereadytoreceive
handleFileUpload//Receiveandsavethefile
);
server.onNotFound([](){//IftheclientrequestsanyURI
if(!handleFileRead(server.uri()))//senditifitexists
server.send(404,"text/plain","404:NotFound");//otherwise,respondwitha404(NotFound)
error
});
server.begin();//Actuallystarttheserver
Serial.println("HTTPserverstarted");
}
voidloop(){
server.handleClient();
}
StringgetContentType(Stringfilename){//convertthefileextensiontotheMIMEtype
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";
}
boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)
Serial.println("handleFileRead:"+path);
if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindex
file
StringcontentType=getContentType(path);//GettheMIMEtype
StringpathWithGz=path+".gz";
if(SPIFFS.exists(pathWithGz)||SPIFFS.exists(path)){//Ifthefileexists,eitherasacompressed
archive,ornormal
if(SPIFFS.exists(pathWithGz))//Ifthere'sacompressedversionavailable
path+=".gz";//Usethecompressedverion
Filefile=SPIFFS.open(path,"r");//Openthefile
size_tsent=server.streamFile(file,contentType);//Sendittotheclient
file.close();//Closethefileagain
Serial.println(String("\tSentfile:")+path);
returntrue;
}
Serial.println(String("\tFileNotFound:")+path);//Ifthefiledoesn'texist,returnfalse
returnfalse;
}
voidhandleFileUpload(){//uploadanewfiletotheSPIFFS
HTTPUpload&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(create
ifitdoesn'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){//Ifthefilewassuccessfullycreated
fsUploadFile.close();//Closethefileagain
Serial.print("handleFileUploadSize:");Serial.println(upload.totalSize);
server.sendHeader("Location","/success.html");//Redirecttheclienttothesuccesspage
server.send(303);
}else{
server.send(500,"text/plain","500:couldn'tcreatefile");
}
}
}
ThehandleFileUploadfunctionjustwritesthefileattachedtothePOSTrequesttoSPIFFS.
Ifyouwantouseotherfiletypesaswell,youcanjustaddthemtothegetContentTypefunction.
Uploadingfiles
TouploadanewfiletotheESP,ortoupdateanexistingfile,justgotohttp://esp8266.local/upload,click
theChooseFilebutton,selectthefileyouwishtoupload,andclickUpload.YoucannowentertheURL
intotheURLbar,andopenthenewfile.
Anoteonsafety

Thisexampleisn'tverysecure(obviously).EveryonethatcanconnecttotheESPcanuploadnewfiles,
oredittheexistingfilesandinsertXSScode,forexample.There'salsonotalotoferror
checking/handling,likecheckingifthere'senoughspaceintheSPIFFStouploadanewfile,etc.
Advancedexample
ThecodefortheseSPIFFSserverexamplescomes(forthemostpart)fromanexamplewrittenbyHristo
Gochkov.YoucanfinditunderFile>Examples>ESP8266WebServer>FSBrowser.Ithasaweb
interfaceforbrowsingandeditingfilesinyourbrowser,andhassomeothernicefeaturesaswell.

OverTheAirUpdates
UploadingoverSerialisfineduringdevelopment,whenyouhaveaccesstotheSerialpinsandtheUSB
port.Butonceyourprojectisfinished,andyouputitinsideanenclosure,itnotthateasytoupload
updateswithbugfixesornewfeatures.
AsolutiontothisproblemisOverTheAirupdating,orOTAforshort.Asthenameimplies,this
technologyallowsyoutouploadnewcodeoverWi-Fi,insteadofSerial.
Theonlydisadvantageisthatyouhavetoexplicitlyaddthecodeforittoeverysketchyouupload.You
alsoneedaflashchipthatistwicethesizeofyoursketch,soitwon'tworkfor512kBboards.(Ithasto
downloadthenewsketchwhilestillrunningtheoldcode.)
Let'stakealookatanexample...
BlinkOTA
ThefollowingexampleisbasicallyBlinkWithoutDelay,butwiththenecessaryOTAandWi-Ficodeadded
aswell.
#include<ESP8266WiFi.h>
#include<ESP8266WiFiMulti.h>
#include<ArduinoOTA.h>
ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'
constbyteled=13;
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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
delay(250);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
ArduinoOTA.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));//ChangethestateoftheLED
previousTime+=diff;
}
}
AddyourWi-Ficredentials,anduploadthesketchoverSerial.ConnectanLED(+resistor)topin13.
ThenrestarttheIDE(youhavetocloseallwindows).GotoTools>Port,andyoushouldgetanew
option:NetworkPorts:ESP8266at192.168.1.x.Selectit.
Next,changetheintervalonline59from1000to500milliseconds,andclickupload.Youshouldgeta
passwordprompt:enter"esp8266".Thispasswordissetonline31,soyoucanchangeitifyouwantto.
Youcanalsodeleteline31altogethertouseitwithoutapassword,butit'snotrecommended-for
obvioussecurityreasons.
Thesketchshoulduploadjustfine,andoncetheESPhasresetitself,theLEDshouldblinktwiceasfast.
Onceinawhile,youmightgetanerrorsaying[ERROR]:NoAnswer.Ifthishappens,justenterthe
password,andtryagain.
SerialMonitorOTA
UsingtheSerialMonitoroverWi-Fiisnotpossible(yet?).Whenyoutrytoopenit,you'llbeprompteda
password,enteringthepasswordwon'twork,becausethereisnoSSHsupporttoaccesstheESP's
console.
YoucanuseadifferentprogramtogetdebugoutputfromthephysicalSerialport.OnWindows,youcan
tryPortmon.OnLinux,youcantryGTKTerm(sudoapt-getinstallgtkterm)orScreen(sudoapt-get
installscreentoinstall,andscreen/dev/ttyUSB0115200orscreen/dev/ttyACM0115200torun;CTRL+A,CTRL+D
toexit).

WebSocketcommunication
Upuntilnow,we'vealwaysusedlinks(GET)andHTMLforms(POST)togetdatafromtheESP,ortosend
datatoit.Thisalwaysresultedinabrowsernavigationaction.Therearemanysituationswhereyou
wanttosenddatatotheESPwithoutrefreshingthepage.
OnewaytodothisisbyusingAJAXandXMLHTTPrequests.Thedisadvantageisthatyouhaveto
establishanewTCPconnectionforeverymessageyousend.Thisaddsaloadoflatency.
WebSocketisatechnologythatkeepstheTCPconnectionopen,soyoucanconstantlysenddataback
andforthbetweentheESPandtheclient,withlowlatency.Andsinceit'sTCP,you'resurethatthe
packetswillarriveintact.
ControllingRGBLEDsfromawebinterfaceusingWebSocket
TolearnhowtouseWebSockets,Icreatedthiscomprehensiveexample,itusesprettymucheverything
we'vecoveredsofar.
TheESPhostsawebpagewiththreesliderstosetthered,greenandbluelevelsofanRGBLED(orLED
strip).There'salsoabuttontoturnonarainboweffectthatcyclesthroughtheentirecolorwheel.Color
dataistransmittedfromthebrowsertotheESPviaaWebSocketconnection.
YoucanconnecttotheESPdirectly,usingitasanAP,orlettheESPconnecttoadifferentAP.Youcan
usemDNStoopenthewebpage,bybrowsingtohttp://esp8266.local.
AllfilesarestoredintheESP'sSPIFFS,andyoucanuploadnewfiles,orupdatefilesviaawebinterface.
YoucanalsousetheOTAservicetouploadnewfirmware(sketches)overWi-Fi.
Improvingreadability
Whendealingwithlargeandcomplicatedprograms,it'sagoodideatomakeabstractionofsomethings,
andcreatefunctionswithadescriptivenameinsteadofendlesslinesofmeaninglesscode.
Evenifyouhavelotsofcommentsinyourcode,it'llbeveryhardtopreserveanoverview.Using
functionswillgreatlyimprovethereadabilityofyourcode.
Sojustsplitupthecodeintodifferentpartsandmoveallpiecestofunctionsatthebottomofyour
sketch,oreventodifferentfiles.
Inthefollowingexample,thesetupwasverylongandcluttered,soIsplititupintoseveraldifferent
functions:onetoconnecttotheWi-Fi,onetostarttheOTAupdateservice,onetostarttheSPIFFS...and
soon.
DownloadingWebSocketsforArduino
We'llbeusingthearduinoWebSocketslibrarybyLinks2004.DownloaditfromGitHubandinstallit.
(Sketch>IncludeLibrary>Add.ZIPLibrary...)
Libraries,constantsandglobals
Atthetopofthesketchwe'llincludethenecessarylibraries,createsomeglobalserverandfileobjects
likeinthepreviousexamples,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(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80
WebSocketsServerwebSocket(81);//createawebsocketserveronport81
FilefsUploadFile;//aFilevariabletotemporarilystorethereceivedfile
constchar*ssid="ESP8266AccessPoint";//ThenameoftheWi-Finetworkthatwillbecreated
constchar*password="thereisnospoon";//Thepasswordrequiredtoconnecttoit,leaveblankforan
opennetwork
constchar*OTAName="ESP8266";//AnameandapasswordfortheOTAservice
constchar*OTAPassword="esp8266";

#defineLED_RED15//specifythepinswithanRGBLEDconnected
#defineLED_GREEN12
#defineLED_BLUE13
constchar*mdnsName="esp8266";//DomainnameforthemDNSresponder
Youshouldalreadybefamiliarwithmostofthiscode.TheonlynewpartistheWebSocketserverlibrary
thatisincluded,andtheWebSocketserverobject,butthisshouldn'tbeaproblem.
Setup
voidsetup(){
pinMode(LED_RED,OUTPUT);//thepinswithLEDsconnectedareoutputs
pinMode(LED_GREEN,OUTPUT);
pinMode(LED_BLUE,OUTPUT);
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println("\r\n");
startWiFi();//StartaWi-Fiaccesspoint,andtrytoconnecttosomegivenaccess
points.ThenwaitforeitheranAPorSTAconnection
startOTA();//StarttheOTAservice
startSPIFFS();//StarttheSPIFFSandlistallcontents
startWebSocket();//StartaWebSocketserver
startMDNS();//StartthemDNSresponder
startServer();//StartaHTTPserverwithafilereadhandlerandanuploadhandler
}
Asyoucansee,thesetupisnowmuchmorecondensedandgivesamuchbetteroverviewofwhatit's
doing.Tounderstandtheprogram,youdon'thavetoknoweachindividualstepthatisrequiredto
connecttoaWi-Finetwork,it'senoughtoknowthatitwillconnecttoaWi-Finetwork,becausethat's
whatthestartWiFifunctiondoes.
Loop
boolrainbow=false;//Therainboweffectisturnedoffonstartup
unsignedlongprevMillis=millis();
inthue=0;
voidloop(){
webSocket.loop();//constantlycheckforwebsocketevents
server.handleClient();//runtheserver
ArduinoOTA.handle();//listenforOTAevents
if(rainbow){//iftherainboweffectisturnedon
if(millis()>prevMillis+32){
if(++hue==360)//Cyclethroughthecolorwheel(incrementbyonedegree
every32ms)
hue=0;
setHue(hue);//SettheRGBLEDtotherightcolor
prevMillis=millis();
}
}
}
Samegoesfortheloop:mostoftheworkisdonebythefirstthreefunctionsthathandletheWebSocket
communication,HTTPrequestsandOTAupdates.Whensuchaneventhappens,theappropriatehandler
functionswillbeexecuted.Thesearedefinedelsewhere.
Thesecondpartistherainboweffect.Ifitisturnedon,itcyclesthroughthecolorwheelandsetsthe
colortotheRGBLED.
Ifyoudon'tunderstandwhyIusemillis(),youcantakealookattheBlinkWithoutDelayexample.
Setupfunctions
voidstartWiFi(){//StartaWi-Fiaccesspoint,andtrytoconnecttosomegivenaccesspoints.Then
waitforeitheranAPorSTAconnection
WiFi.softAP(ssid,password);//Starttheaccesspoint
Serial.print("AccessPoint\"");
Serial.print(ssid);

Serial.println("\"started\r\n");
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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-Fito
connect
delay(250);
Serial.print('.');
}
Serial.println("\r\n");
if(WiFi.softAPgetStationNum()==0){//IftheESPisconnectedtoanAP
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.print(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
}else{//IfastationisconnectedtotheESPSoftAP
Serial.print("StationconnectedtoESP8266AP");
}
Serial.println("\r\n");
}
voidstartOTA(){//StarttheOTAservice
ArduinoOTA.setHostname(OTAName);
ArduinoOTA.setPassword(OTAPassword);
ArduinoOTA.onStart([](){
Serial.println("Start");
digitalWrite(LED_RED,0);//turnofftheLEDs
digitalWrite(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(){//StarttheSPIFFSandlistallcontents
SPIFFS.begin();//StarttheSPIFlashFileSystem(SPIFFS)
Serial.println("SPIFFSstarted.Contents:");
{
Dirdir=SPIFFS.openDir("/");
while(dir.next()){//Listthefilesystemcontents
StringfileName=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(){//StartaWebSocketserver
webSocket.begin();//startthewebsocketserver
webSocket.onEvent(webSocketEvent);//ifthere'sanincommingwebsocketmessage,goto
function'webSocketEvent'
Serial.println("WebSocketserverstarted.");
}
voidstartMDNS(){//StartthemDNSresponder
MDNS.begin(mdnsName);//startthemulticastdomainnameserver
Serial.print("mDNSresponderstarted:http://");
Serial.print(mdnsName);
Serial.println(".local");
}
voidstartServer(){//StartaHTTPserverwithafilereadhandlerandanuploadhandler
server.on("/edit.html",HTTP_POST,[](){//IfaPOSTrequestissenttothe/edit.htmladdress,
server.send(200,"text/plain","");
},handleFileUpload);//goto'handleFileUpload'
server.onNotFound(handleNotFound);//ifsomeonerequestsanyotherfileorpage,goto
function'handleNotFound'
//andcheckifthefileexists
server.begin();//starttheHTTPserver

Serial.println("HTTPserverstarted.");
}
Thesearethefunctiondefinitionsofthefunctionsusedinthesetup.Nothingnewhere,apartfromthe
startWebSocketfunction.YoujusthavetostarttheWebSocketserverusingthebeginmethod,andthen
giveitacallbackfunctionthatisexecutedwhentheESPreceivesaWebSocketmessage.
Serverhandlers
Thisisthecodethatisexecutedoncertainserver-relatedevents,likewhenanHTTPrequestisreceived,
whenafileisbeinguploaded,whenthere'sanincomingWebSocketmessage...etc.
voidhandleNotFound(){//iftherequestedfileorpagedoesn'texist,returna404notfounderror
if(!handleFileRead(server.uri())){//checkifthefileexistsintheflashmemory(SPIFFS),
ifso,sendit
server.send(404,"text/plain","404:FileNotFound");
}
}
boolhandleFileRead(Stringpath){//sendtherightfiletotheclient(ifitexists)
Serial.println("handleFileRead:"+path);
if(path.endsWith("/"))path+="index.html";//Ifafolderisrequested,sendtheindex
file
StringcontentType=getContentType(path);//GettheMIMEtype
StringpathWithGz=path+".gz";
if(SPIFFS.exists(pathWithGz)||SPIFFS.exists(path)){//Ifthefileexists,eitherasacompressed
archive,ornormal
if(SPIFFS.exists(pathWithGz))//Ifthere'sacompressedversionavailable
path+=".gz";//Usethecompressedverion
Filefile=SPIFFS.open(path,"r");//Openthefile
size_tsent=server.streamFile(file,contentType);//Sendittotheclient
file.close();//Closethefileagain
Serial.println(String("\tSentfile:")+path);
returntrue;
}
Serial.println(String("\tFileNotFound:")+path);//Ifthefiledoesn'texist,returnfalse
returnfalse;
}
voidhandleFileUpload(){//uploadanewfiletotheSPIFFS
HTTPUpload&upload=server.upload();
Stringpath;
if(upload.status==UPLOAD_FILE_START){
path=upload.filename;
if(!path.startsWith("/"))path="/"+path;
if(!path.endsWith(".gz")){//Thefileserveralwaysprefersacompressed
versionofafile
StringpathWithGz=path+".gz";//Soifanuploadedfileisnotcompressed,
theexistingcompressed
if(SPIFFS.exists(pathWithGz))//versionofthatfilemustbedeleted(ifit
exists)
SPIFFS.remove(pathWithGz);
}
Serial.print("handleFileUploadName:");Serial.println(path);
fsUploadFile=SPIFFS.open(path,"w");//OpenthefileforwritinginSPIFFS(createif
itdoesn'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){//Ifthefilewassuccessfullycreated
fsUploadFile.close();//Closethefileagain
Serial.print("handleFileUploadSize:");Serial.println(upload.totalSize);
server.sendHeader("Location","/success.html");//Redirecttheclienttothesuccesspage
server.send(303);
}else{
server.send(500,"text/plain","500:couldn'tcreatefile");
}
}
}
voidwebSocketEvent(uint8_tnum,WStype_ttype,uint8_t*payload,size_tlenght){//WhenaWebSocket
messageisreceived
switch(type){
caseWStype_DISCONNECTED://ifthewebsocketisdisconnected
Serial.printf("[%u]Disconnected!\n",num);
break;
caseWStype_CONNECTED:{//ifanewwebsocketconnectionisestablished
IPAddressip=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://ifnewtextdataisreceived
Serial.printf("[%u]getText:%s\n",num,payload);

if(payload[0]=='#'){//wegetRGBdata
uint32_trgb=(uint32_t)strtol((constchar*)&payload[1],NULL,16);//decodergbdata
intr=((rgb>>20)&0x3FF);//10bitspercolor,soR:bits20-29
intg=((rgb>>10)&0x3FF);//G:bits10-19
intb=rgb&0x3FF;//B:bits0-9
analogWrite(LED_RED,r);//writeittotheLEDoutputpins
analogWrite(LED_GREEN,g);
analogWrite(LED_BLUE,b);
}elseif(payload[0]=='R'){//thebrowsersendsanRwhentherainbow
effectisenabled
rainbow=true;
}elseif(payload[0]=='N'){//thebrowsersendsanNwhentherainbow
effectisdisabled
rainbow=false;
}
break;
}
}
Again,mostofthecodeisadaptedfromthepreviousexamples,onlytheWebSocketpartisnew.
TherearedifferenttypesofWebSocketmessages,butwe'reonlyinterestedinthetexttype,because
theJavaScriptcodeattheclientsidesendsthecolordataintextformat,asahexadecimalnumber,
startingwitha'#'sign.
Eachcolorisa10-bitnumber,sointotal,itgivesusa30-bitnumberfortheRGBvalue.
Whentherainbowfunctionisenabled,JavaScriptsendsan'R'character,andwhenit'sdisabled,itsends
a'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,wetakethevaluesofthethreeslidersandwesquarethecolor
valuestogetasmootherandmorenaturalcurve.Wethencombinethemintoa30-bitnumber(10bits
percolor).Finally,theRGBvaluegetsconvertedtoahexadecimalstring,a'#'isadded,andit'ssentto
theESP.
Whentherainbowbuttonispressed,theslidersaredisabled,andan'R'issenttotheESP.Whenthe
rainbowbuttonispressedagain,theslidersareenabled,andan'N'issent.
Helperfunctions
BacktotheESP8266Arduinocodeagain.Weneedsomeotherfunctionsaswell,toconvertbytestoKB
andMB,todeterminefiletypesbasedonfileextensionsandtoconvertahueangletoRGBvalues.
StringformatBytes(size_tbytes){//convertsizesinbytestoKBandMB
if(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,basedonthe
extension
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";
}

voidsetHue(inthue){//SettheRGBLEDtoagivenhue(color)(0°=Red,120°=Green,240°=Blue)
hue%=360;//hueisananglebetween0and359°
floatradH=hue*3.142/180;//Convertdegreestoradians
floatrf,gf,bf;
if(hue>=0&&hue<120){//ConvertfromHSIcolorspacetoRGB
rf=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);//WritetherightcolortotheLEDoutputpins
analogWrite(LED_GREEN,g);
analogWrite(LED_BLUE,b);
}
ToconvertfromhuetoRGB,weusesinesandcosines,becausethesumoftheirsquaresisalwaysone,
sothetotalintensitywillalwaysbemoreorlessthesame.
ThisresultsinthefollowingRGBcurves:
Usingtheexample
DownloadtheexamplefromGitHubandopenitintheArduinoIDE.ThenaddyourWi-Ficredentials(lines
83-85).
ConnectanRGBLEDwithredtopin15,greentopin12andbluetopin13.Don'tforgetthecurrent
limitingresistors!
SelecttheSPIFFSsize(64KBshouldbeenough,butifyouwanttouploadmorefileslater,youshouldset
ithigher).UploadthesketchoverSerial,andthenuploadtheSPIFFSfilesusingTools>ESP8266Sketch
DataUpload.
WaitforittoconnecttoaWi-Finetwork,orconnecttotheESP8266AccessPointusingthepassword
'thereisnospoon',andgotohttp://esp8266.local.Youshouldgetapagethatlookslikethis:

UsethesliderstoadjustthecolorlevelsoftheLED,andpresstheRainbowbuttontoenabletherainbow
effect.
Ifyougotohttp://esp8266.local/edit.html,youcanuploadorupdatefiles:

NetworkTimeProtocol
Therearemanyapplicationswhereyouwanttoknowthetime.InanormalArduinoproject,youwould
havetogetaRTCmodule,settherighttime,sacrificesomeArduinopinsforcommunication...And
whentheRTCbatteryrunsout,youhavetoreplaceit.
OntheESP8266,allyouneedisanInternetconnection:youcanjustaskatimeserverwhattimeitis.To
dothis,theNetworkTimeProtocol(NTP)isused.
Inthepreviousexamples(HTTP,WebSockets)we'veonlyusedTCPconnections,butNTPisbasedon
UDP.Thereareacoupleofdifferences,butit'sreallyeasytouse,thankstothegreatlibrariesthatcome
withtheESP8266ArduinoCore.
ThemaindifferencebetweenTCPandUDPisthatTCPneedsaconnectiontosendmessages:Firsta
handshakeissentbytheclient,theserverresponds,andaconnectionisestablished,andtheclientcan
senditsmessages.Aftertheclienthasreceivedtheresponseoftheserver,theconnectionisclosed
(exceptwhenusingWebSockets).Tosendanewmessage,theclienthastoopenanewconnectionto
theserverfirst.Thisintroduceslatencyandoverhead.
UDPdoesn'tuseaconnection,aclientcanjustsendamessagetotheserverdirectly,andtheserver
canjustsendaresponsemessagebacktotheclientwhenithasfinishedprocessing.Thereis,however,
noguaranteethatthemessageswillarriveattheirdestination,andthere'snowaytoknowwhether
theyarrivedornot(withoutsendinganacknowledgement,ofcourse).Thismeansthatwecan'thaltthe
programtowaitforaresponse,becausetherequestorresponsepacketcouldhavebeenlostonthe
Internet,andtheESP8266willenteraninfiniteloop.
Insteadofwaitingforaresponse,wejustsendmultiplerequests,withafixedintervalbetweentwo
requests,andjustregularlycheckifaresponsehasbeenreceived.
Gettingthetime
Let'stakealookatanexamplethatusesUDPtorequestthetimefromaNTPserver.
Libraries,constantsandglobals
#include<ESP8266WiFi.h>
#include<ESP8266WiFiMulti.h>
#include<WiFiUdp.h>
ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'
WiFiUDPUDP;//CreateaninstanceoftheWiFiUDPclasstosendandreceive
IPAddresstimeServerIP;//time.nist.govNTPserveraddress
constchar*NTPServerName="time.nist.gov";
constintNTP_PACKET_SIZE=48;//NTPtimestampisinthefirst48bytesofthemessage
byteNTPBuffer[NTP_PACKET_SIZE];//buffertoholdincomingandoutgoingpackets
TouseUDP,wehavetoincludetheWiFiUdplibrary,andcreateaUDPobject.We'llalsoneedtoallocate
memoryforabuffertostoretheUDPpackets.ForNTP,weneedabufferof48byteslong.
ToknowwheretosendtheUDPpacketsto,weneedthehostnameoftheNTPserver,thisis
time.nist.gov.
Setup
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println("\r\n");
startWiFi();//Trytoconnecttosomegivenaccesspoints.Thenwaitfora
connection
startUDP();
if(!WiFi.hostByName(NTPServerName,timeServerIP)){//GettheIPaddressoftheNTPserver
Serial.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'lllookatthe
implementationofthisfunctionlater.
WeneedtheIPaddressoftheNTPserver,soweperformaDNSlookupwiththeserver'shostname.
There'snotmuchwecandowithouttheIPaddressofthetimeserver,soifthelookupfails,rebootthe
ESP.
IfwedogetanIP,sendthefirstNTPrequest,andentertheloop.
Loop
unsignedlongintervalNTP=60000;//RequestNTPtimeeveryminute
unsignedlongprevNTP=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)time
if(time){//Ifanewtimestamphasbeenreceived
timeUNIX=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){//Ifasecondhaspassedsincelastprint
prevActualTime=actualTime;
Serial.printf("\rUTCtime:\t%d:%d:%d",getHours(actualTime),getMinutes(actualTime),
getSeconds(actualTime));
}
}
ThefirstpartoftheloopsendsanewNTPrequesttothetimeservereveryminute.Thisisbasedon
BlinkWithoutDelay.
ThenwecallthegetTimefunctiontocheckifwe'vegotanewresponsefromtheserver.Ifthisisthe
case,weupdatethetimeUNIXvariablewiththenewtimestampfromtheserver.
Ifwedon'tgetanyresponsesforanhour,thenthere'ssomethingwrong,sowereboottheESP.
Thelastpartprintstheactualtime.TheactualtimeisjustthelastNTPtimeplusthetimesincewe
receivedthatNTPmessage.
Setupfunctions
Nothingspecialhere,justafunctiontoconnecttoWi-Fi,andanewfunctiontostartlisteningforUDP
messagesonport123.
voidstartWiFi(){//Trytoconnecttosomegivenaccesspoints.Thenwaitforaconnection
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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-Fitoconnect
delay(250);
Serial.print('.');
}
Serial.println("\r\n");
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.print(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer

Serial.println("\r\n");
}
voidstartUDP(){
Serial.println("StartingUDP");
UDP.begin(123);//StartlisteningforUDPmessagesonport123
Serial.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-bitnumber
uint32_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
//InitializevaluesneededtoformNTPrequest
NTPBuffer[0]=0b11100011;//LI,Version,Mode
//sendapacketrequestingatimestamp:
UDP.beginPacket(address,123);//NTPrequestsaretoport123
UDP.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,thefunction
justreturns0.IfthereisaUDPpacketavailablehowever,readitintothebuffer.TheNTPtimestampis
32bitsor4byteswide,sowecombinethesebytesintoonelongnumber.Thisnumberisthenumberof
secondssinceJan1,1900,00:00:00,butmostapplicationsuseUNIXtime,thenumberofsecondssince
Jan1,1970,00:00:00(UNIXepoch).ToconvertfromNTPtimetoUNIXtime,wejustsubtract70years
worthofseconds.
TorequestthetimefromtheNTPserver,youhavetosendacertainsequenceof48bytes.Wedon't
needanyfancyfeatures,sojustsetthefirstbytetorequestthetime,andleaveallother47byteszero.
Toactuallysendthepacket,youhavetostartthepacket,specifyingtheIPaddressoftheserver,andthe
NTPportnumber,port123.Thenjustwritethebuffertothepacket,andsenditwithendPacket.
Thelastthreefunctionsarejustsomesimplemathtoconvertsecondstohours,minutesandseconds.
Usingtheexample
EnteryourWi-Ficredentialsonlines79-81,andhitupload.IfyouhaveaworkingInternetconnection,
youshouldgetanoutputthatlookslikethis:
Connecting
.........
ConnectedtoWi-FiSSID
IPaddress: 192.168.1.2
StartingUDP
Localport: 123
TimeserverIP:216.229.0.179

SendingNTPrequest...
NTPresponse: 1488378061
UTCtime: 14:21:53
SendingNTPrequest...
NTPresponse: 1488378114
UTCtime: 14:22:53
SendingNTPrequest...
NTPresponse: 1488378174
UTCtime: 14:23:53
SendingNTPrequest...
NTPresponse: 1488378234
UTCtime: 14:24:53
SendingNTPrequest...
NTPresponse: 1488378294
UTCtime: 14:25:53
...
Youshouldseethetimeupdateeverysecond,andSendingNTPrequest...shouldshowupeveryminute.
Ifyoudon'thaveanInternetconnection,theDNSlookupofthetimeserverwillfail:
Connecting
.........
ConnectedtoWi-FiSSID
IPaddress: 192.168.1.2
StartingUDP
Localport: 123
DNSlookupfailed.Rebooting.
etsJan82013,rstcause:2,bootmode:(3,6)
Ifyourconnectionisnotreliable,orifthere'sheavytraffic,youmightencountersomedroppedpackets:
SendingNTPrequest...
NTPresponse: 1488378780
UTCtime: 14:33:54
SendingNTPrequest...
UTCtime: 14:34:54
SendingNTPrequest...
NTPresponse: 1488378895
UTCtime: 14:35:0
Asyoucansee,theESPneverreceivedaresponsetothesecondNTPrequest.That'snotreallyanissue,
aslongasatleastsomepacketsmakeitthrough.
Localtimeanddaylightsavings
AnNTPserverreturnstheUTCtime.Ifyouwantlocaltime,youhavetocompensateforyourtimezone
anddaylightsavings.Forexample,ifyouwantCET(CentralEuropeanTime),youhavetoadd3600to
theUNIXtimeduringwinter,(3600s=1h),and7200duringsummer(DST).

Datalogging
AcommonuseforIoTdevicesliketheESP8266ismonitoringsensors.Usingthecodeintheprevious
example,wecanrequestthetime,andsavesomesensorvaluestoafile.Ifwerunaserveraswell,we
canshowthisdatainaprettygraphinawebpage.
Temperaturelogger
Inthefollowingexample,we'lluseaDS18S20temperaturesensortologthetemperatureovertimeand
saveittotheSPIFFS.Itcanthenbedisplayedinagraphinthebrowser.
Installinglibraries
First,downloadtheDallasTemperaturelibrarybyMilesBurtonandtheOneWirelibrarybyJimStudt:Go
toSketch>IncludeLibrary...>ManageLibrariesandsearchfor'DallasTemperature'and'OneWire'
(makesureyoudownloadthecorrectversion).
Hardware
ConnectthegroundoftheDS18S20temperaturesensor(pin1)tothegroundoftheESP,connectthe
datapin(pin2)toGPIO5,andVCC(pin3)tothe3.3VoftheESP.Finally,connecta4k7Ωresistorbetween
thedatapinandVCC.
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);//SetupaOneWireinstancetocommunicatewithOneWire
devices
DallasTemperaturetempSensors(&oneWire);//Createaninstanceofthetemperaturesensorclass
ESP8266WebServerserver(80);//CreateawebserverobjectthatlistensforHTTPrequestonport80
FilefsUploadFile;//aFilevariabletotemporarilystorethe
receivedfile
ESP8266WiFiMultiwifiMulti;//CreateaninstanceoftheESP8266WiFiMulticlass,called'wifiMulti'
constchar*OTAName="ESP8266";//AnameandapasswordfortheOTAservice
constchar*OTAPassword="esp8266";
constchar*mdnsName="esp8266";//DomainnameforthemDNSresponder
WiFiUDPUDP;//CreateaninstanceoftheWiFiUDPclasstosendandreceiveUDP
messages
IPAddresstimeServerIP;//Thetime.nist.govNTPserver'sIPaddress
constchar*ntpServerName="time.nist.gov";
constintNTP_PACKET_SIZE=48;//NTPtimestampisinthefirst48bytesofthemessage
bytepacketBuffer[NTP_PACKET_SIZE];//Abuffertoholdincomingandoutgoingpackets
TheonlynewthingsherearetheOneWireandDallasTemperaturelibraries,togetthetemperaturefrom
thesensor.
Setup
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer

delay(10);
Serial.println("\r\n");
tempSensors.setWaitForConversion(false);//Don'tblocktheprogramwhilethetemperaturesensoris
reading
tempSensors.begin();//Startthetemperaturesensor
if(tempSensors.getDeviceCount()==0){
Serial.printf("NoDS18x20temperaturesensorfoundonpin%d.Rebooting.\r\n",TEMP_SENSOR_PIN);
Serial.flush();
ESP.reset();
}
startWiFi();//StartaWi-Fiaccesspoint,andtrytoconnecttosomegivenaccess
points.ThenwaitforeitheranAPorSTAconnection
startOTA();//StarttheOTAservice
startSPIFFS();//StarttheSPIFFSandlistallcontents
startMDNS();//StartthemDNSresponder
startServer();//StartaHTTPserverwithafilereadhandlerandanuploadhandler
startUDP();//StartlisteningforUDPmessagestoport123
WiFi.hostByName(ntpServerName,timeServerIP);//GettheIPaddressoftheNTPserver
Serial.print("TimeserverIP:\t");
Serial.println(timeServerIP);
sendNTPpacket(timeServerIP);
}
Inthesetup,there'snotmuchneweither,wejuststartthetemperaturesensor,andcheckifwecan
communicatewithit.Ifnotemperaturesensorisfound,theESPresets.
Gettingthetemperaturefromthesensormaytakesometime(upto750ms).Wedon'twantourloopto
takelongerthanacoupleofmilliseconds,sowecan'twait750ms.Ifwedid,theHTTPserveretc.would
starttomisbehave.
Thesolutionistorequestthetemperaturefirst.Thesensorwillthenstartreadingtheanalog
temperature,andstoresitinitsmemory.Inthemeantime,theloopjustkeepsonrunning,theserver
refreshesetc.After750ms,wecontactthesensoragain,andreadthetemperaturefromitsmemory.
Totellthelibrarythatwedon'twanttowaitfortheanalogtodigitalconversionofthesensor,weuse
setWaitForConversion.
Loop
constunsignedlongintervalNTP=ONE_HOUR;//Updatethetimeeveryhour
unsignedlongprevNTP=0;
unsignedlonglastNTPResponse=millis();
constunsignedlongintervalTemp=60000;//Doatemperaturemeasurementeveryminute
unsignedlongprevTemp=0;
booltmpRequested=false;
constunsignedlongDS_delay=750;//ReadingthetemperaturefromtheDS18x20cantakeupto
750ms
uint32_ttimeUNIX=0;//Themostrecenttimestampreceivedfromthetimeserver
voidloop(){
unsignedlongcurrentMillis=millis();
if(currentMillis-prevNTP>intervalNTP){//Requestthetimefromthetimeservereveryhour
prevNTP=currentMillis;
sendNTPpacket(timeServerIP);
}
uint32_ttime=getTime();//Checkifthetimeserverhasresponded,ifso,getthe
UNIXtime
if(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,requestthetemperature
tempSensors.requestTemperatures();//Requestthetemperaturefromthesensor(ittakessometime
toreadit)
tmpRequested=true;

prevTemp=currentMillis;
Serial.println("Temperaturerequested");
}
if(currentMillis-prevTemp>DS_delay&&tmpRequested){//750msafterrequestingthe
temperature
uint32_tactualTime=timeUNIX+(currentMillis-lastNTPResponse)/1000;
//TheactualtimeisthelastNTPtimeplusthetimethathaselapsedsincethelastNTPresponse
tmpRequested=false;
floattemp=tempSensors.getTempCByIndex(0);//Getthetemperaturefromthesensor
temp=round(temp*100.0)/100.0;//roundtemperatureto2digits
Serial.printf("Appendingtemperaturetofile:%lu,",actualTime);
Serial.println(temp);
FiletempLog=SPIFFS.open("/temp.csv","a");//Writethetimeandthetemperaturetothecsv
file
tempLog.print(actualTime);
tempLog.print(',');
tempLog.println(temp);
tempLog.close();
}
}else{//Ifwedidn'treceiveanNTPresponseyet,sendanother
request
sendNTPpacket(timeServerIP);
delay(500);
}
server.handleClient();//runtheserver
ArduinoOTA.handle();//listenforOTAevents
}
Thelooplooksalotmorecomplex,butit'sactuallyprettysimple.It'sallbasedonBlinkWithoutDelay.
There'stwothingsgoingon:
1. Everyhour,theESPrequeststhetimefromanNTPserver.Thenitconstantlychecksforaresponse,
andupdatesthetimeifitgetsanNTPresponse.Ifithasn'treceivedanyresponsesforover24
hours,there'ssomethingwrong,andtheESPresetsitself.
2. Everyminute,theESPrequeststhetemperaturefromtheDS18x20sensor,andsetsthe
'tmpRequested'flag.Thesensorwillstarttheanalogtodigitalconversion.
750msaftertherequest,whentheconversionshouldbefinished,theESPreadsthetemperature
fromthesensor,andresetstheflag(otherwise,itwouldkeeponreadingthesametemperature
overandoveragain).ThenitwritesthetimeandthetemperaturetoafileinSPIFFS.
BysavingitasaCSVfileinthefilesystem,wecaneasilydownloadittotheclient(usingtheweb
serverthatisrunning),andit'seasytoparsewithJavaScript.
IfwemissthefirstNTPresponse,timeUNIXwillbezero.Ifthat'sthecase,wesendanotherNTPrequest
(otherwise,thenextrequestwouldbeanhourlater,andthetemperatureloggingonlystartswhenthe
timeisknown).
WealsoneedtoruntheserverandOTAfunctionstohandleHTTPandOTArequests.
Setupfunctions,serverhandlersandhelperfunctions
Thesefunctionshaven'tchangesincethepreviousexample,sothere'snoneedtocoverthemhere.You
doneedthemtogettheprogramrunning,though.DownloadtheZIParchivewithexamplesforthefull
sketch.
HTMLandJavaScript
There'ssomeHTMLandJavaScriptfilestoplotthetemperatureusingGoogleGraphs.Iwon'tcoverit
here,butifyouwan'ttoknowhowitworks,youcanfindthefilesintheZIParchive.
Usingtheexample
SettheSPIFFSsizeto64KBorlargerifyouplantouseitforprolongedperiodsoftime.(Youcouldalso
increasetheloggingintervalonline80tosavespace.)
EnteryourWi-Ficredentialsonlines138-140,andhitupload.Thenuploadthewebpagesandscriptsto
SPIFFSusingTools>ESP8266SketchDataUpload.
Makesureyouhavethetemperaturesensorconnected,asdescribedatthetopofthispage.Opena
terminaltoseeifitworks.Youshouldseesomethinglikethis:
Connecting
..........
ConnectedtoSSID
IPaddress: 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.local
HTTPserverstarted.
StartingUDP
Localport: 123
TimeserverIP:216.229.0.179
SendingNTPrequest
NTPresponse: 1488666586
Temperaturerequested
Appendingtemperaturetofile:1488666627,20.00
Temperaturerequested
Appendingtemperaturetofile:1488666687,19.94
Temperaturerequested
...
Letitrunforacoupleofminutes,togathersometemperaturedata.Thenopenawebbrowser,andgo
tohttp://esp8266.local/.
Youshouldgetagraphshowingthetemperaturecurve.Youcanusethearrowbuttonstotravelthrough
time,andthe+and-buttonstozoominorout.Theresetbuttonresetsthezoom,andjumpsbackto
thepresent.Refreshrequeststhelatesttemperaturedata.
Ifyouwant,youcanstillgotohttp://esp8266.local/edit.htmltouploadnewfiles.
Thewebinterfaceshouldlooklikethis:
ItworksonWindows,LinuxandAndroid,butiOSseemstohavesomeproblemsrenderingthegraph(in
bothChromeandSafari).

Emailnotifier
AnothergreatuseforIoTdevicesisdisplayingthingsliketrafficinformation,weatherforecast,social
mediaupdates...ThisrequiresustosendanHTTPGETrequesttotheserveroftheservicewe'dliketo
access.MostpopularserviceshaveAPI(ApplicationProgrammingInterface)documentsthatexplainthat
explainhowyoucanretrievecertaininformation,andwhatformatthatinformationisin.Inthefollowing
example,we'lllookatGmailspecifically,butthecodeshouldbesimilarforotherservices.
Showingthenumberofunreademails
TocommunicatewithGoogle'sGmailservers,wehavetoestablishasecureconnectiontotheserver
andsendasecureHTTPSrequestwithouremailaddressandpassword.Gmailwillthenrespondwithan
XMLdocumentcontainingallkindsofinformation,like(partsof)yourmostrecentmessagesandthe
numberofunreademails.
Tomakesurewedon'tsendourGooglepasswordtoamaliciousserver,wehavetochecktheserver's
identity,usingtheSHA-1fingerprintoftheSSLcertificate.Thisisauniquesequenceofhexadecimal
charactersthatidentifiestheserver.
Allowingaccesstotheemailfeed
Theonlyway(Iknowof)togetemailinformationfromGoogleontheESPcurrentlyistheGoogleAtom
Feed.Thisisanoldermethod,soyouhavetochangeyourGmailsettingstoallowaccesstothefeed.
Gotohttps://www.google.com/settings/security/lesssecureappstoenableaccessfor"lesssecureapps":


Untilthere'ssupportforthenewOAuth2protocolontheESP,we'llhavetousetheold,lesssecure
method.
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";//theGmailserver
constchar*url="/mail/feed/atom";//theGmailfeedurl
constinthttpsPort=443;//theporttoconnecttotheemailserver
//TheSHA-1fingerprintoftheSSLcertificatefortheGmail
server(seebelow)
constchar*fingerprint="D390FC8207E60DC2CEF99D797FECF6E63ECB8BB3";
//TheBase64encodedversionofyourGmaillogincredentials(see
below)
constchar*credentials="ZW1haWwuYWRkcmVzc0BnbWFpbC5jb206cGFzc3dvcmQ=";
constbyteled=13;
voidsetup(){
Serial.begin(115200);//StarttheSerialcommunicationtosendmessagestothecomputer
delay(10);
Serial.println('\n');
pinMode(led,OUTPUT);
wifiMulti.addAP("ssid_from_AP_1","your_password_for_AP_1");//addWi-Finetworksyouwantto
connectto
wifiMulti.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,
andconnecttothestrongestofthenetworksabove
delay(250);
Serial.print('.');
}
Serial.println('\n');
Serial.print("Connectedto");
Serial.println(WiFi.SSID());//Telluswhatnetworkwe'reconnectedto
Serial.print("IPaddress:\t");
Serial.println(WiFi.localIP());//SendtheIPaddressoftheESP8266tothecomputer
Serial.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(){//afunctiontogetthenumberofunreademailsinyourGmailinbox
WiFiClientSecureclient;//UseWiFiClientSecureclasstocreateTLS(HTTPS)connection
Serial.printf("Connectingto%s:%d...\r\n",host,httpsPort);
if(!client.connect(host,httpsPort)){//ConnecttotheGmailserver,onport443
Serial.println("Connectionfailed");//Iftheconnectionfails,stopandreturn
return-1;
}
if(client.verify(fingerprint,host)){//ChecktheSHA-1fingerprintoftheSSLcertificate
Serial.println("Certificatematches");
}else{//ifitdoesn'tmatch,it'snotsafetocontinue
Serial.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.TheresponseisinXML
format
client.readStringUntil('<');//readuntilthefirstXMLtag
Stringtagname=client.readStringUntil('>');//readuntiltheendofthistagtogetthetag
name
if(tagname=="fullcount"){//ifthetagis<fullcount>,thenextstring
willbethenumberofunreademails
StringunreadStr=client.readStringUntil('<');//readuntiltheclosingtag(</fullcount>)
unread=unreadStr.toInt();//convertfromStringtoint
break;//stopreading
}//ifthetagisnot<fullcount>,repeatand
readthenexttag
}
Serial.println("Connectionclosed");
returnunread;//Returnthenumberofunreademails
}
Howitworks
Thesetupshouldbeprettyfamiliarbynow.
TheonlynewthingisthegetUnread()function:
First,itstartsanHTTPSconnectiontotheGmailserveronport443.Thenitchecksifthefingerprintof
thecertificatematches,soitknowsthatit'stherealGoogleserver,andnotsomehacker.Ifthe
certificatedoesn'tmatch,it'snotsafetosendthecredentialstotheserver.
Ifitmatches,wesendaHTTPGETrequesttotheserver:
GET/mail/feed/atomHTTP/1.1\r\n
Host:mail.google.com\r\n
Authorization:BasicaVeryLongStringOfBase64EncodedCharacters=\r\n
User-Agent:ESP8266\r\n
Connection:close\r\n\r\n
TherequestcontainstheURIwewanttoaccess(inthiscasethisistheAtomfeedURL),thehost(which
ismail.google.com),andthebase64-encodedversionofyourlogincredentials.
Asyoucansee,thedifferentlinesoftheheaderareseparatedbyaCRLF(CarriageReturn+LineFeed,
\r\n).TwoCRLF'smarktheendoftheheader.
TheGmailserverwillprocessourrequest,andsendthefeedasaresponseoverthesameHTTPS
connection.ThisresponseisanXMLdocument,thatconsistsoftagswithangledbrackets,justlike
HTML.Ifyouneedalotofdata,it'srecommendedtouseaproperXMLparserlibrary,butweonlyneed
onetag,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,YourGoogleAccount
esp8266.test.mail@gmail.comwasjustusedtosigninfromFirefoxonLinux.ESP8266Test
esp8266.test.mail@gmail.comLinuxSunday,</summary>
<linkrel="alternate"href="https://mail.google.com/mail?
account_id=esp8266.test.mail@gmail.com&message_id=123456789&view=conv&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'tsendthemas
plaintext,youhavetoencodethemtobase64first.Usethefollowingcommandinaterminal(Linux&
Mac):
echo-n"email.address@gmail.com:password"|base64
Thenaddittoline15ofthesketch.Forexample:
constchar*credentials="ZW1haWwuYWRkcmVzc0BnbWFpbC5jb206cGFzc3dvcmQ=";
OtherAPIs
ManyservicessendtheirdatainJSONformat.Ifyoujustneedonepieceofinformation,youmaybeable
tousethesameapproachofscanningtheentireJSONtextforacertainword,butit'smucheasiertouse
aJSONparser,liketheArduinoJsonlibrary.ItwilldeserializetheJSONtext,andcreateaJSONobject,you
couldcompareittoanassociativearray.Youcanbrowsetheentiretreestructure,andeasilyfindthe
datayou'relookingfor.
Thedownsideisthatitusesmorememory.

Advanced
DNSCaptivePortal
WhenusingtheESP8266inaccesspointmode,youprobablywanttoredirectuserstotherightpage.
Youcandothisbycreatingacaptiveportal,usingDNS.It'sbasicallyjustaDNSserverthatwillconvert
allhostnamestotheESP'sownIPaddress.
ThistechniqueisalsousedbyopenWi-Finetworksthatredirectyoutoaloginpagebeforeyoucanstart
browsingtheinternet.
Wi-Ficonfiguration
IfyouwanttobeabletochangetheWi-Ficonnectionsettingswithoutre-uploadingthecode,youcould
takealookattheWiFiManagerlibrarybytzapu.Thiswilltrytoconnecttoknownnetworks,butifitfails,
itwillstartaWi-Fiaccesspoint.Youcanthenconnecttothisaccesspoint,openthebrowser,andpicka
networktoconnectto.Thenewconfigurationissaved.
TheWiFiManagerlibraryusesacaptiveportaltopresentyouwiththerightWi-Fisettingspage.
YoucouldalsoimplementaWi-Fimanageryourself,oryoucanjustcheckouttheexamplethatcomes
withtheESP8266ArduinoCore(Examples>DNSServer>CaptivePortalAdvanced).
I²S
TheESP8266hasanI²SbusontheRXDpin.Itcanrunat80MHz,andhasDMA(directmemoryaccess),
soit'sreallyfast.ItsmainpurposeistoconnectanI²SDAC(DigitaltoAnalogConverter)tohavean
audiooutput,butyoucanuseitforotherthingsaswell.
Forexample,CNLohrmanagedtotransmitanalogtelevision,byconnectinganantennawiretotheI²S
pin.YoucanalsouseittocontrolWS2812BsLEDs.YoucanevenuseittocommunicateoverEthernet
(notreallyuseful,anddefinitelynotrecommended,butitworks).
AnothergreatusefortheI²Sbusisoutputtingdatatoshiftregisters.Thisgivesyouextraoutputsthat
arereasonablyfast,forthingslikeLEDsorsteppermotors.
Otherexamples
YoucanfindlotsofotherexamplesintheArduinoIDE,I'drecommendtocheckthoseoutaswell.
YouTube
There'ssomegreatchannelsonYouTubethatdoamazingthingswiththeESP8266.Here'sashortlistof
theonesI'mcurrentlyfollowing.Ifyou'vegotmorerecommendation,justleaveacomment!
AndreasSpiess
CNLohr
Acrobotic
MiikaKurkela

Inconclusion...
Congratulations,you'vereachedtheendofthisratherlongarticleonthebasicsoftheESP8266.Ihope
thiswasinterestingtoyou,andthatyou'llusethisknowledgeforyourownDIYprojects.
Ifyouhaveanyremarksorifyouwanttohelpimprovethisguide,don'thesitatetoleaveacommentor
tosendmeamessage.
Thankyouforreading!
Pieter,8-3-2017