Components · 13 min read

ESP-01 Arduino Guide: AT Commands, Programming and Relay

Connect the ESP-01 (ESP8266) to Arduino: 8 pins, 3.3 V power, Wi-Fi with AT commands, flashing with GPIO0 to GND, the ESP-01S relay module and common errors.

The ESP-01 is a tiny (24.8 × 14.3 mm), very cheap module built around the ESP8266 Wi-Fi chip. By connecting the ESP-01 to an Arduino you can add Wi-Fi to a board without wireless, such as an Uno or Nano, with just a few wires. The module ships with AT command firmware: the Arduino sends short text commands like "AT+CWJAP" over serial, and the ESP-01 joins the network, sends and receives data. If you prefer, you can also program the ESP-01 as a standalone microcontroller without any Arduino at all; its processor is far faster than the Uno's.

In this guide you'll learn the ESP-01's 8 pins, why it absolutely needs a strong 3.3 V supply, how to use AT commands with an Arduino Uno, how to build a web page that switches an LED from your phone, how to program the module directly with a USB-TTL adapter (the GPIO0-to-GND method), how to control a lamp with the ESP-01S relay module and how to use it with a Raspberry Pi Pico. At the end you'll find fixes for the most common errors and when a NodeMCU or an ESP32 is the better choice.

ESP-01 ESP8266 Wi-Fi module
ESP-01 Wi-Fi module: the ESP8266EX chip, flash memory, a PCB antenna and 8 pins in a 2×4 header, all on one small board.

What is the ESP-01 and how does it work?

The ESP-01 is the first and smallest module Ai-Thinker designed around the ESP8266EX chip. The board carries the chip, a 26 MHz crystal, the flash memory that stores the program and settings, and a meandering PCB antenna; there's no USB port, voltage regulator or button. That's why the ESP-01 can't be plugged straight into a computer: you have to provide the power and the serial connection yourself. You can use the module in two ways: as a Wi-Fi modem (the AT firmware is installed and the Arduino sends commands) or as a standalone microcontroller (you upload your own program from the Arduino IDE and the AT firmware is erased). Most modules sold today are the black ESP-01S version.

FeatureValue
ChipESP8266EX (Tensilica L106, 32-bit)
Clock speed80 MHz (can be raised to 160 MHz)
Flash memoryESP-01: 512 KB, ESP-01S: 1 MB
RAMAbout 80 KB of data RAM; ~50 KB left for your program with Wi-Fi connected
Wi-Fi802.11 b/g/n, 2.4 GHz only, PCB antenna
Operating voltage3.0–3.6 V (3.3 V recommended)
Current70–80 mA average while connected, ~170 mA when transmitting, peaks above 300 mA
Deep sleep currentAbout 20 µA (chip)
Usable GPIO4: GPIO0, GPIO2, GPIO1 (TX), GPIO3 (RX)
Analog inputNone (the ADC pin isn't broken out)
Size24.8 × 14.3 mm
Factory firmwareAT command firmware (usually 115200 baud)

ESP-01 vs ESP-01S

FeatureESP-01ESP-01S
Board colorUsually blueBlack
Flash memory512 KB (1 MB on later black versions)1 MB
Power LEDRed, always onNone
Blue LEDOn the TX (GPIO1) line, blinks when data is sentOn GPIO2, lights up when the pin is LOW
Pinout8 pinsSame 8 pins, fully compatible
Power drawA few mA more because of the power LEDLower, better for battery projects

ESP-01 pinout: what the 8 pins do

The ESP-01 has 8 pins in a 2×4 header, and only four of them can be used as inputs and outputs. Not every board has the pin names printed on it, so compare your module with the seller's pinout before wiring it up: swapping VCC and GND kills the module within seconds. Here's what each pin does and how it's connected in normal operation:

PinFunctionConnection in normal operation
VCC3.3 V supply inputA strong 3.3 V source (at least 300 mA)
GNDGroundCommon GND with the Arduino and the supply
TX (GPIO1)Serial data outputTo the Arduino's RX line
RX (GPIO3)Serial data inputTo the Arduino's TX line (through a voltage divider on 5 V boards)
EN (CH_PD)Enables the chipTo 3.3 V (directly or through 10 kΩ)
RSTRestarts the chip when LOWFloating or pulled to 3.3 V through 10 kΩ
GPIO0Selects the boot mode, then general I/OHIGH (floating) at boot; GND while flashing
GPIO2General I/O (blue LED on the ESP-01S)Must stay HIGH at boot
The ESP-01 runs on 3.3 V only; the chip's upper operating limit is 3.6 V. Connecting the Arduino's 5 V to the VCC pin can permanently damage the module. The Arduino Uno's TX pin also outputs a 5 V signal, so don't wire it straight to the ESP-01's RX pin: use a voltage divider or a logic level converter.

Current draw: why the Arduino's 3.3 V pin isn't enough

Most problems that kill the ESP-01 or keep it rebooting come from the power supply. While connected to Wi-Fi the module draws 70–80 mA on average and around 170 mA when transmitting; short peaks at boot and while joining the network can exceed 300 mA. The Arduino Uno's 3.3 V pin, however, is officially rated for just 50 mA. An ESP-01 powered from this pin often answers the "AT" command, but the moment it tries to join a network the voltage collapses and the module resets, which looks like a software bug. The fix is an AMS1117-3.3 regulator (1 A) fed from 5 V, plus a 100–470 µF electrolytic capacitor across the module's VCC and GND pins. You'll find how regulators work in the 7805 voltage regulator article.

Power sourceSuitable?Note
Arduino Uno 3.3 V pinNoOfficially 50 mA; the module resets while joining Wi-Fi
3.3 V pin of a USB-TTL adapterUsually notThe chip's internal regulator typically supplies 50–100 mA
AMS1117-3.3 regulator (5 V input)YesUp to 1 A; 10 µF on the output, 100–470 µF next to the module
ESP-01 USB adapterYesHas a CH340 and an AMS1117; the module plugs straight in
2 × AA batteries (3 V)BorderlineAs the cells drain the voltage drops below 3 V and the module becomes unstable
Single-cell Li-ion battery (3.7–4.2 V)Not directlyExceeds the 3.6 V limit; needs a low-dropout (LDO) 3.3 V regulator

Connecting the ESP-01 to an Arduino Uno (for AT commands)

The Arduino Uno's only hardware serial port (D0 and D1) is shared with the USB connection. That's why the most practical approach is to connect the ESP-01 to pins D2 and D3 with the SoftwareSerial library: you don't have to unplug the ESP-01 when uploading to the Uno, and the Serial Monitor stays free for debugging. The Raspberry Pi Pico already runs on 3.3 V, so it doesn't need a divider:

ESP-01 pinArduino UnoRaspberry Pi PicoNote
VCCAMS1117-3.3 outputAMS1117-3.3 or 3V3(OUT)The Uno's 3.3 V pin isn't enough; the Pico's 3V3 output is recommended up to 300 mA
GNDGNDGNDRegulator, board and module share GND
TXD2GP1 (UART0 RX)Connect directly
RXD3 (through a 1 kΩ / 2 kΩ divider)GP0 (UART0 TX)No divider needed on the Pico
EN (CH_PD)3.3 V3.3 VDirectly or through 10 kΩ
RSTNot connectedNot connectedOptionally pull to 3.3 V through 10 kΩ
GPIO0Not connectedNot connectedDon't tie it to GND in normal operation
GPIO2Not connectedNot connectedMust stay HIGH at boot

To drop the 5 V signal coming from the Uno's D3 pin, connect a 1 kΩ resistor from D3 to the ESP-01's RX and a 2 kΩ resistor from the ESP-01's RX to GND. The output is 5 × 2 / (1 + 2) = 3.33 V; with 2.2 kΩ instead of 2 kΩ you get 3.44 V, and both are safe. You can work out other resistor values with the voltage divider tool. The other direction needs no divider: the 3.3 V from the ESP-01's TX pin is above the 3 V threshold the Uno reads as HIGH. We cover the basics of serial communication in detail in the UART serial communication article.

First test: sending AT commands from the Serial Monitor

  1. Upload the bridge sketch below to the Uno; for the first try set ESP_BAUD to 115200, because most modules ship at that speed (older ones may use 9600).
  2. Open the Serial Monitor at 115200 baud and set the line ending to Both NL & CR; without it the module never knows the command has ended.
  3. Type AT and send it; you should see OK. If you get garbage characters, the speeds don't match.
  4. SoftwareSerial can't read reliably at 115200, so lower the module's speed permanently to 9600 with AT+UART_DEF=9600,8,1,0,0. Even if the reply looks garbled, the command usually gets through.
  5. Change ESP_BAUD in the sketch to 9600, upload it again and repeat the AT command; the OK reply should now look clean.
  6. Check that the module works properly by reading the firmware version with AT+GMR and listing nearby Wi-Fi networks with AT+CWLAP.
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial esp(2, 3);          // D2 = RX, D3 = TX (bölücüyle ESP RX'e) / D3 to ESP RX via divider
4
5const long ESP_BAUD = 9600;        // ilk denemede 115200 yap / use 115200 on the first try
6
7void setup() {
8  Serial.begin(115200);            // Seri Monitör hızı / Serial Monitor speed
9  esp.begin(ESP_BAUD);
10  Serial.println("AT komutu yaz / type an AT command");
11}
12
13void loop() {
14  if (esp.available()) {           // ESP-01'den geleni ekrana yaz / ESP-01 -> screen
15    Serial.write(esp.read());
16  }
17  if (Serial.available()) {        // klavyeden yazılanı ESP-01'e gönder / keyboard -> ESP-01
18    esp.write(Serial.read());
19  }
20}

The most useful ESP-01 AT commands

CommandWhat it doesExample reply
ATTests the connectionOK
AT+RSTRestarts the moduleOK ... ready
AT+GMRShows the firmware versionAT version:... SDK version:... OK
AT+UART_DEF=9600,8,1,0,0Changes the baud rate permanentlyOK
AT+CWMODE=1Station mode; 2 = access point, 3 = bothOK
AT+CWLAPLists nearby Wi-Fi networks+CWLAP:(3,"AgAdi",-58,...)
AT+CWJAP="AgAdi","Sifre123"Joins a Wi-Fi networkWIFI CONNECTED, WIFI GOT IP, OK
AT+CIFSRShows the IP address it received+CIFSR:STAIP,"192.168.1.45"
AT+CIPMUX=1Enables multiple connections (required for a server)OK
AT+CIPSERVER=1,80Starts a TCP server on port 80OK
AT+CIPSTART=0,"TCP","192.168.1.10",80Connects to a server on link 00,CONNECT and OK
AT+CIPSEND=0,20Sends 20 bytes on link 0> ... SEND OK
AT+CIPCLOSE=0Closes link 00,CLOSED and OK
If AT+CWJAP fails, the module prints +CWJAP:1 (timeout), +CWJAP:2 (wrong password), +CWJAP:3 (network not found) or +CWJAP:4 (connection failed), followed by FAIL. The ESP8266 only sees 2.4 GHz networks, so if your network can't be found, make sure the 2.4 GHz band is enabled on your router. Type commands in capitals and put the network name and password in double quotes.

Controlling an LED from your phone with an Arduino Uno and ESP-01

Now let's turn the AT commands into a program. The sketch below connects the ESP-01 to your network, opens a small web server on port 80 and prints the IP address to the Serial Monitor. When you type that address into the browser of a phone on the same Wi-Fi, a page with two buttons opens; the buttons switch the built-in LED on pin 13 of the Uno on and off. Connect a relay module instead of the LED and the same method controls a lamp. Before uploading, make sure you've set the module's speed to 9600 in the previous step.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial esp(2, 3);              // D2 = RX, D3 = TX (bölücüyle) / D3 via divider
4const int LED_PIN = 13;                // Uno'nun dahili LED'i / Uno onboard LED
5
6const char* WIFI_SSID = "AgAdi";       // Wi-Fi adın / your Wi-Fi name
7const char* WIFI_PASS = "Sifre123";    // Wi-Fi şifren / your Wi-Fi password
8
9bool ledOn = false;
10bool pending = false;                  // cevap bekleyen istek var mı / request waiting for a reply
11int linkId = 0;                        // ESP-01'in bağlantı numarası (0-4) / connection id (0-4)
12char line[64];
13byte lineLen = 0;
14
15// Beklenen yazıyı süre dolana kadar ara / wait until the expected text arrives or time runs out
16bool waitFor(const char* expect, unsigned long timeout) {
17  unsigned long start = millis();
18  size_t idx = 0;
19  size_t len = strlen(expect);
20  while (millis() - start < timeout) {
21    if (esp.available()) {
22      char c = esp.read();
23      Serial.write(c);                 // gelen her şeyi göster / echo everything
24      if (c == expect[idx]) {
25        idx++;
26        if (idx == len) return true;
27      } else {
28        idx = (c == expect[0]) ? 1 : 0;
29      }
30    }
31  }
32  return false;
33}
34
35bool sendAT(const char* cmd, const char* expect, unsigned long timeout) {
36  esp.println(cmd);                    // println sonuna \r\n ekler / println adds \r\n
37  return waitFor(expect, timeout);
38}
39
40void sendPage(int id) {
41  char body[220];
42  snprintf(body, sizeof(body),
43           "<meta name='viewport' content='width=device-width'>"
44           "<h2>LED: %s</h2>"
45           "<a href='/on'><button>AC / ON</button></a> "
46           "<a href='/off'><button>KAPAT / OFF</button></a>",
47           ledOn ? "ACIK / ON" : "KAPALI / OFF");
48  char head[110];
49  snprintf(head, sizeof(head),
50           "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"
51           "Content-Length: %d\r\nConnection: close\r\n\r\n",
52           (int)strlen(body));
53
54  esp.print("AT+CIPSEND=");            // önce kaç bayt gideceğini söyle / announce the length first
55  esp.print(id);
56  esp.print(',');
57  esp.println(strlen(head) + strlen(body));
58  if (waitFor(">", 2000)) {            // ">" gelince veriyi yolla / send data after ">"
59    esp.print(head);
60    esp.print(body);
61    waitFor("SEND OK", 5000);
62  }
63  esp.print("AT+CIPCLOSE=");           // bağlantıyı kapat / close the connection
64  esp.println(id);
65  waitFor("OK", 2000);
66}
67
68void handleLine() {
69  if (strncmp(line, "+IPD,", 5) == 0) {          // yeni istek / new request: +IPD,0,384:GET /on ...
70    linkId = line[5] - '0';
71    if (strstr(line, "GET /on ")) ledOn = true;
72    else if (strstr(line, "GET /off ")) ledOn = false;
73    digitalWrite(LED_PIN, ledOn ? HIGH : LOW);
74    pending = true;
75  } else if (pending && lineLen == 0) {           // boş satır = isteğin sonu / empty line = end of request
76    pending = false;
77    sendPage(linkId);
78  }
79}
80
81void setup() {
82  pinMode(LED_PIN, OUTPUT);
83  Serial.begin(115200);
84  esp.begin(9600);                     // AT+UART_DEF=9600,8,1,0,0 ile ayarlanmış olmalı / set with AT+UART_DEF
85
86  sendAT("AT+RST", "ready", 5000);     // temiz başlangıç / clean restart
87  sendAT("AT+CWMODE=1", "OK", 2000);   // istemci modu / station mode
88
89  esp.print("AT+CWJAP=\"");            // AT+CWJAP="ad","şifre" / AT+CWJAP="ssid","password"
90  esp.print(WIFI_SSID);
91  esp.print("\",\"");
92  esp.print(WIFI_PASS);
93  esp.println("\"");
94  if (!waitFor("OK", 20000)) {
95    Serial.println(F("\nWi-Fi baglanamadi / Wi-Fi failed"));
96  }
97
98  sendAT("AT+CIPMUX=1", "OK", 2000);         // sunucu için çoklu bağlantı / multiple connections
99  sendAT("AT+CIPSERVER=1,80", "OK", 2000);   // 80 portunda sunucu / server on port 80
100  sendAT("AT+CIFSR", "OK", 2000);            // IP adresini yaz / print the IP address
101}
102
103void loop() {
104  while (esp.available()) {            // ESP-01'den gelenleri satır satır oku / read line by line
105    char c = esp.read();
106    if (c == '\n') {
107      line[lineLen] = '\0';
108      handleLine();
109      lineLen = 0;
110    } else if (c != '\r' && lineLen < sizeof(line) - 1) {
111      line[lineLen++] = c;
112    }
113  }
114}

Here's how the code works: when a browser connects, the ESP-01 forwards the request to the Arduino as +IPD,0,384:GET /on HTTP/1.1, where 0 is the link number and 384 is the number of bytes received. The Arduino looks for "GET /on" or "GET /off" in that line and sets the LED. When the empty line that marks the end of the request arrives, it announces how many bytes it will send with AT+CIPSEND=0,length, sends the page once the module shows the ">" prompt, and closes the link with AT+CIPCLOSE after the "SEND OK" reply. We kept the page short on purpose: the Uno's 2 KB of RAM isn't enough for long HTML text, and a single CIPSEND can send at most 2048 bytes. For more complex projects, a library such as WiFiEspAT saves you from managing AT commands yourself.

Programming the ESP-01 directly: the GPIO0-to-GND method

The ESP8266 inside the ESP-01, with its 32-bit 80 MHz processor, is much more powerful than an Arduino Uno. If your project only has a relay, a sensor or an LED, you don't need an Arduino at all: you upload the program straight to the ESP-01. When the ESP8266 boots, it checks the levels of GPIO0, GPIO2 and GPIO15 to decide what to do. On the ESP-01 board GPIO15 is already tied to GND and GPIO2 must stay HIGH, so GPIO0 alone selects the boot mode:

GPIO0GPIO2GPIO15Boot modeBoot message at 74880 baud
HIGH (floating)HIGHLOWNormal run: the program in flash startsboot mode:(3,x)
LOW (GND)HIGHLOWProgramming (UART download) modeboot mode:(1,x) and waiting for host
ESP-01 pinConnectionNote
VCC3.3 V regulator outputThe USB-TTL adapter's 3.3 V pin is usually not enough
GNDUSB-TTL GND and regulator GNDEverything on a common ground
TXUSB-TTL RXDCrossed connection
RXUSB-TTL TXDJumper must be set to 3.3 V
EN (CH_PD)3.3 VDirectly or through 10 kΩ
RSTNot connected (briefly to GND to reset)Optionally pull to 3.3 V through 10 kΩ
GPIO0GNDOnly while uploading; disconnect afterwards
GPIO2Not connectedMust stay HIGH at boot
  1. In the Arduino IDE open File > Preferences and add https://arduino.esp8266.com/stable/package_esp8266com_index.json to the "Additional boards manager URLs" field.
  2. In the Boards Manager, search for esp8266 and install the ESP8266 Community package.
  3. Choose Generic ESP8266 Module from Tools > Board. Set Flash Size to 1MB (FS:64KB OTA:~470KB) for the ESP-01S, or one of the 512KB options for the old blue ESP-01.
  4. Set the USB-TTL adapter's jumper to 3.3 V, wire the module according to the table above and keep GPIO0 connected to GND.
  5. Cut the module's power and reconnect it, or briefly touch RST to GND; the module now boots into programming mode.
  6. Pick the USB-TTL adapter's port from Tools > Port and upload the sketch. File > Examples > ESP8266 > Blink is a good first test; on the ESP-01S the blue LED is on GPIO2.
  7. When you see "Hard resetting via RTS pin...", disconnect GPIO0 from GND and restart the module. If GPIO0 stays on GND your program won't run and the module boots into programming mode again.
To make flashing easier you can use an ESP-01 USB adapter with a CH340 and an AMS1117 on board; the module plugs straight into it. Some of these adapters have a UART/PROG switch; on models without one you'll need to solder a button or a jumper between GPIO0 and GND. If you don't have a USB-TTL adapter, you can tie the Arduino Uno's RESET pin to GND and use the board's USB chip as a bridge; in that case TX goes to TX and RX to RX, and the 5 V level must still be divided down. See the USB-TTL adapter article for details.

Relay control with GPIO0 and GPIO2: the ESP-01S relay module

The ESP-01's general-purpose pins are GPIO0 and GPIO2, but both must be HIGH during boot. So whatever you connect to them must not pull the pin LOW while the board starts up. The safest solution is an active-LOW connection: the pin is HIGH at boot so the relay stays off, and when your program drives the pin LOW the relay switches on. The ready-made ESP-01S relay module is designed exactly like this: it runs on 5 V, its on-board regulator makes 3.3 V for the ESP-01S, and GPIO0 drives a relay with 10 A 250 V AC contacts through a transistor. On v4.0 and later boards the relay switches on when GPIO0 is LOW; older v1.0 boards may need a small modification to work with the ESP-01S, so check your board's version.

5 V relay module
A relay lets a small control signal switch a 230 V lamp on and off; on the ESP-01S relay board the same kind of relay is connected to the ESP-01's GPIO0 pin.
C++ (Arduino IDE)
1#include <ESP8266WiFi.h>
2#include <ESP8266WebServer.h>
3#include <ESP8266mDNS.h>
4
5const char* ssid = "AgAdi";            // Wi-Fi adın / your Wi-Fi name
6const char* password = "Sifre123";     // Wi-Fi şifren / your Wi-Fi password
7
8const int RELAY_PIN = 0;               // ESP-01S röle kartında GPIO0 / GPIO0 on the ESP-01S relay board
9const int RELAY_ON = LOW;              // çoğu kartta LOW = çeker; tersse değiştir / LOW = on, swap if yours is inverted
10const int RELAY_OFF = HIGH;
11
12ESP8266WebServer server(80);
13bool relayOn = false;
14
15void sendPage() {
16  String html = F("<!DOCTYPE html><html><head><meta charset='utf-8'>"
17                  "<meta name='viewport' content='width=device-width,initial-scale=1'>"
18                  "<title>ESP-01</title></head>"
19                  "<body style='font-family:sans-serif;text-align:center'>"
20                  "<h1>ESP-01 Röle / Relay</h1><p><b>");
21  html += relayOn ? "AÇIK / ON" : "KAPALI / OFF";
22  html += F("</b></p><p><a href='/on'><button>AÇ / ON</button></a> "
23            "<a href='/off'><button>KAPAT / OFF</button></a></p></body></html>");
24  server.send(200, "text/html", html);
25}
26
27void setRelay(bool on) {
28  relayOn = on;
29  digitalWrite(RELAY_PIN, on ? RELAY_ON : RELAY_OFF);
30  server.sendHeader("Location", "/");  // ana sayfaya dön / back to the main page
31  server.send(303, "text/plain", "");
32}
33
34void setup() {
35  digitalWrite(RELAY_PIN, RELAY_OFF);  // önce kapalı seviye / off level first
36  pinMode(RELAY_PIN, OUTPUT);
37  Serial.begin(115200);
38
39  WiFi.mode(WIFI_STA);
40  WiFi.begin(ssid, password);
41  while (WiFi.status() != WL_CONNECTED) {
42    delay(500);
43    Serial.print('.');
44  }
45  Serial.println();
46  Serial.println(WiFi.localIP());      // bu adresi tarayıcıda aç / open this address in a browser
47  MDNS.begin("esp01");                 // ya da http://esp01.local / or http://esp01.local
48
49  server.on("/", sendPage);
50  server.on("/on", []() { setRelay(true); });
51  server.on("/off", []() { setRelay(false); });
52  server.begin();
53}
54
55void loop() {
56  server.handleClient();
57  MDNS.update();
58}

After uploading the sketch to the ESP-01S with the method above, plug the module into the relay board and power the board with 5 V. You can read the IP address in the Serial Monitor while the USB-TTL adapter is connected, or find it in your router's list of connected devices; from a computer that supports mDNS, http://esp01.local works too. If you want to add an input such as a button, the best pin is GPIO3 (RX), because its level doesn't matter at boot. In that case use Serial.begin(115200, SERIAL_8N1, SERIAL_TX_ONLY) to open the serial port in transmit-only mode.

If you're connecting mains voltage to the relay contacts, wire everything with the power off, leave no exposed contacts and put the circuit in an insulated enclosure. Even if the relay says 10 A, the copper traces on small boards may not carry that current; don't drive loads above a few hundred watts with this module. For details, see What Is a Relay Module?

ESP-01 with the Raspberry Pi Pico (MicroPython)

You can also add wireless to a Raspberry Pi Pico without Wi-Fi using an ESP-01. Since the Pico runs on 3.3 V, no voltage divider is needed: the ESP-01's RX goes to GP0 (UART0 TX) and its TX goes to GP1 (UART0 RX). The Pico's hardware UART reads 115200 baud with ease, so you don't have to change the module's speed either; if you already lowered it to 9600 in the Uno section, set baudrate to 9600 in the code as well. Run the MicroPython code below with Thonny; the module joins your network, prints its IP address and lists nearby networks every 30 seconds. If you have a Pico W, its built-in Wi-Fi is easier to use; see the Pico W Wi-Fi web server article.

MicroPython
1from machine import UART, Pin
2import time
3
4# GP0 (TX) -> ESP-01 RX, GP1 (RX) <- ESP-01 TX
5uart = UART(0, baudrate=115200, tx=Pin(0), rx=Pin(1))
6
7def at(cmd, timeout=2000):
8    uart.write(cmd + "\r\n")                   # her komut \r\n ile biter / every command ends with \r\n
9    start = time.ticks_ms()
10    resp = b""
11    while time.ticks_diff(time.ticks_ms(), start) < timeout:
12        if uart.any():
13            resp += uart.read()
14            if b"OK" in resp or b"ERROR" in resp or b"FAIL" in resp:
15                break
16    print(cmd, "->", resp)                     # cevabı göster / show the reply
17    return resp
18
19at("ATE0")                                     # komut yankısını kapat / turn off echo
20at("AT")                                       # OK gelmeli / should answer OK
21at("AT+CWMODE=1")                              # istemci modu / station mode
22at('AT+CWJAP="AgAdi","Sifre123"', 20000)       # Wi-Fi'ye bağlan / join Wi-Fi
23at("AT+CIFSR")                                 # IP adresini sor / ask for the IP
24
25while True:
26    at("AT+CWLAP", 10000)                      # çevredeki ağları tara / scan nearby networks
27    time.sleep(30)

ESP-01, NodeMCU or ESP32?

FeatureESP-01NodeMCU (ESP8266)ESP32 DevKit
ChipESP8266ESP8266ESP32 (dual-core)
Usable GPIO4 (two are TX/RX)About 11About 25
Analog inputNone1 (A0)18 channels, 12-bit
USB programmingNo, needs a USB-TTL adapterYesYes
Power3.3 V onlyUSB 5 V or VINUSB 5 V or VIN
BluetoothNoNoYes (Classic and BLE)
Size24.8 × 14.3 mmBreadboard-friendly boardBreadboard-friendly board
Best forAdding Wi-Fi to an Arduino, a single relay or sensorGetting started with Wi-Fi, a few sensorsProjects with many pins and Bluetooth

The ESP-01's biggest advantages are its size and price; it's ideal for adding Wi-Fi to an existing Arduino project or making a single relay or sensor wireless. But because it has no USB, swapping wires, adding a regulator and grounding GPIO0 for every upload gets tedious. It has no analog input, and since GPIO16 isn't broken out, timed wake-up from deep sleep doesn't work unless you solder a thin wire to one of the chip's pins. If you're planning a battery-powered project, enter the average current into the battery life calculator to see how many days it will last. If you're still learning or need more than three pins, the NodeMCU ESP8266 gives you the same chip with USB and a regulator. If you need Bluetooth, more pins and more processing power, the ESP32 is the right choice; we compare the boards in Arduino or ESP32?

ESP-01 project ideas

  • Wi-Fi smart plug: switch a lamp from your phone with the ESP-01S relay module and the sketch above; add a timer so it turns on automatically in the evening.
  • Wireless temperature sensor: connect a DS18B20 sensor to GPIO2; the 4.7 kΩ pull-up on the data line also provides the HIGH level needed at boot.
  • Humidity and temperature monitor: show DHT11 readings on a web page you open on your phone.
  • Motion alert: connect a PIR sensor to GPIO3 and send a Telegram message when motion is detected; the method is very similar to the ESP32 Telegram bot article.
  • Wi-Fi for an Arduino project: send the readings of a weather station built on an Uno to a program on your computer or to a server with AT commands.
  • Garage door remote: wire the relay contacts in parallel with the door opener's button and open the door from your phone.

Common mistakes and how to fix them

SymptomLikely causeFix
"Failed to connect to ESP8266: Timed out waiting for packet header"The module isn't in programming mode, or TX and RX are swappedTie GPIO0 to GND, power-cycle the module, connect TX to RXD and RX to TXD
Garbage characters in the Serial MonitorBaud rate mismatch; boot messages are at 74880 baudTry 115200 and 9600; choose 74880 to see the boot message
No reply at all to ATWrong line ending, TX and RX swapped, or EN not connectedChoose Both NL & CR, cross the lines, connect EN to 3.3 V
Module keeps resetting, drops out while joining Wi-FiNot enough currentUse a separate AMS1117-3.3 and a 100–470 µF capacitor, shorten the wires
"busy p..." replyA new command was sent before the previous one finishedWait for OK or ERROR after every command
+CWJAP:3 and FAILNetwork not found (5 GHz network or wrong name)Use a 2.4 GHz network and watch upper and lower case
Module gets very hot, LED stays off5 V applied to VCC, or VCC and GND reversedCut the power immediately; the module is most likely damaged
Module won't boot with the relay attachedThe circuit on GPIO0 or GPIO2 pulls the pin LOW at bootUse an active-LOW connection or move the input to GPIO3
5 V relay module never switches offA 3.3 V HIGH doesn't fully turn off the 5 V module's optocouplerUse a 3.3 V-compatible relay module or a transistor driver
AT commands stop working after uploading from the Arduino IDEYour program replaced the AT firmwareReflash Espressif's AT firmware with esptool or the Flash Download Tool
To check whether the module really boots, set the Serial Monitor to 74880 baud and reset the module. If you see boot mode:(3,x) it started in normal mode; boot mode:(1,x) means programming mode. If you see nothing at all, the problem is the power supply, the EN connection or the TX-RX lines.

Despite its small size, the ESP-01 is a great module for learning all the basic Wi-Fi concepts: IP addresses, TCP connections, web servers and serial communication. If something doesn't work, check the power supply first, then the TX-RX lines and the level on GPIO0; the vast majority of problems come down to these three points. In the BlueGrays app you'll find projects such as Wi-Fi connection, web server, mDNS, over-the-air updates (OTA) and smart relay lighting, with step-by-step circuit diagrams and code.

Frequently asked questions

Can the ESP-01 run on 5V?

No. The ESP-01 runs between 3.0 and 3.6 V, and connecting 5 V to the VCC pin can permanently damage it. If you want to power it from the Arduino's 5 V, put a regulator such as an AMS1117-3.3 in between, and drop the 5 V signal from the Uno's TX line to 3.3 V with a voltage divider.

Can I power the ESP-01 from the Arduino 3.3V pin?

It's not recommended. The Uno's 3.3 V pin is officially rated for 50 mA, while the ESP-01 draws 170 mA when transmitting and over 300 mA in short peaks. Even if the module answers AT commands, it resets while joining the network; use a separate 3.3 V regulator that can supply at least 300–500 mA.

Why is my ESP-01 not responding to AT commands?

The most common causes are a wrong baud rate, the Serial Monitor line ending not set to Both NL & CR, TX and RX not crossed, and the EN (CH_PD) pin not connected to 3.3 V. If a program was uploaded to the module from the Arduino IDE before, the AT firmware has been erased and you need to flash it again.

Does uploading Arduino code erase the ESP-01 AT firmware?

Yes. The program you upload from the Arduino IDE is written to flash in place of the AT firmware, and the module no longer understands AT commands. To go back to AT mode, flash Espressif's ESP8266 AT firmware with esptool or the Flash Download Tool at the addresses that match your module's flash size.

What is the difference between ESP-01 and ESP-01S?

The chip and pinout are the same. The ESP-01S is usually black, has 1 MB of flash, no power LED, and its blue LED is on GPIO2. The old blue ESP-01 has 512 KB of flash, a red power LED that's always on and a blue LED on the TX line.

What is the default baud rate of the ESP-01?

Most modules with current AT firmware ship at 115200 baud; older ones may use 9600 or 57600. The first messages printed at boot, however, are at 74880 baud. If you use SoftwareSerial on an Arduino Uno, lowering the speed permanently to 9600 with AT+UART_DEF=9600,8,1,0,0 makes the link reliable.

See all projects
ESP32 Wi-Fi STA Connection (with LCD Screen)
#127Advanced

ESP32 Wi-Fi STA Connection (with LCD Screen)

In this project, we connect the ESP32 to the home Wi-Fi network in Station (STA) mode in a secure, fault-tolerant manner. We store the SSID and password in a separate secrets.h file to prevent leaking to Git, set up an auto-reconnect mechanism, and display the RSSI signal level and IP address on an I2C LCD screen. The internal LED works as a status indicator. It is a production-grade connection manager.

4.4(21)
34
ESP32 Asynchronous Web Server (with IP Display on LCD)
#129Advanced

ESP32 Asynchronous Web Server (with IP Display on LCD)

In this project, we’re turning the ESP32 into an asynchronous web server that you can control from your phone or computer on the same Wi-Fi network. Thanks to the ESPAsyncWebServer library, the server can process multiple requests simultaneously (unlike the classic WebServer.h, which blocks on a single request). You’ll be able to open the ESP32’s IP address in a browser and click the ON/OFF buttons (the /on and /off routes) to turn the LED connected to GPIO 5 on and off.

4.8(16)
26
ESP32 mDNS: Accessing the Device via bluegrays-esp.local (with LCD Screen)
#131Advanced
Premium

ESP32 mDNS: Accessing the Device via bluegrays-esp.local (with LCD Screen)

In this project, we add an mDNS (Multicast DNS) service to the ESP32, allowing us to access the device using a human-readable hostname like bluegrays-esp.local instead of an IP address. Even if the IP address assigned by DHCP changes, you can always reach the device using the same name. We also run a small web server on the device to send LED control commands via a browser using the address http://bluegrays-esp.local/. The I2C LCD screen displays the hostname, IP address, and LED status in b

4.8(14)
23
ESP32 OTA Update — Wireless Firmware Installation (With LCD Screen)
#132Advanced
Premium

ESP32 OTA Update — Wireless Firmware Installation (With LCD Screen)

In this project, you’ll learn how to upload new firmware to the ESP32 over Wi-Fi without a USB cable. Using the ArduinoOTA library, the ESP32 becomes visible on the network and is automatically listed as bluegrays-ota in the "Network port" tab of the Arduino IDE. The initial upload is done via USB, and all subsequent updates are wireless—a critical feature for field devices. The I2C LCD screen displays the Wi-Fi status, IP address, hostname, OTA upload percentage, and error messages in both

4.6(14)
22
Real-Time Clock from the Internet via ESP32 NTP
#128Advanced
Premium

Real-Time Clock from the Internet via ESP32 NTP

The ESP32 connects to Wi-Fi and retrieves real time with millisecond precision from NTP (Network Time Protocol) servers. Thanks to the configTzTime() function, the Turkey time zone (TRT, UTC+3) is automatically applied, and DST transitions in countries that observe daylight saving time are automatically managed using a POSIX TZ string. The time is displayed both on the Serial Monitor and on an optional I2C LCD. configTzTime() starts the SNTP service; periodic resynchronization takes place wh

4.5(13)
21
ESP32 Smart Lighting (PIR + LDR + Relay + OLED)
#141Advanced
Premium

ESP32 Smart Lighting (PIR + LDR + Relay + OLED)

Turn your hallway, bathroom, or pantry into smart lighting with ESP32! Using a PIR motion sensor, LDR light sensor, and a relay module, we build the classic "light turns on when motion is detected in the dark" scenario — if the environment is already bright, motion does NOT trigger the light, saving energy during the day. The light stays on for 30 seconds; if motion continues, the timer resets and the light stays on longer. The SSD1306 OLED shows large-font status (ON / DAYTIME / WAITING), a cou

4.3(6)
11
Lighting 220V Bulb with Button (On-Off)
#20Advanced
Premium

Lighting 220V Bulb with Button (On-Off)

Want to switch a real household bulb on and off with a tiny button? Each time you press the button, the Arduino flips a relay and the bulb lights up; press again and it goes off. The relay is a go-bet

4.5(33)
44
One-Way Communication Between 2 Arduinos
#57Intermediate
Premium

One-Way Communication Between 2 Arduinos

You get two Arduinos talking over a single wire: press the button on one and the LED on the other lights up, press it again and it goes off. The sending board reports the button press with a short mes

4.4(37)
50