Arduino + ESP8266 with EspProxy (develop without rewiring)

// Sketch to bridge Serial to esp8266 on Serial3.
// Supports esp8266 flashing.
// For the reset of esp to flashing mode CH_EN and GPIO0 of esp must be connected
// to Atmega pins for pulling them LOW (use diodes to prevent accidental 5V output to esp)

//#define FLASHING  // uncomment for use with esptool and Flash Download Tool, if flashing detection always fails

#if !defined(HAVE_HWSERIAL3)
#include <NeoSWSerial.h>
NeoSWSerial Serial3(6, 7);
#ifdef FLASHING
#define BAUD 19200
#else
#define BAUD 38400
#endif
#else
#define BAUD 115200
#endif

#define ESP_ENABLE_PIN 2
#define ESP_GPIO0_PIN 3

void resetESP(boolean toBootloader) {
  if (toBootloader) {
    pinMode(ESP_GPIO0_PIN, OUTPUT);
    digitalWrite(ESP_GPIO0_PIN, LOW);
  }
  pinMode(ESP_ENABLE_PIN, OUTPUT);
  digitalWrite(ESP_ENABLE_PIN, LOW);
  delay(5);
  pinMode(ESP_ENABLE_PIN, INPUT); // let it to pull-up resistor
  if (toBootloader) {
    delay(100);
    pinMode(ESP_GPIO0_PIN, INPUT); // let it to pull-up resistor
  }
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  
  Serial.begin(BAUD);

#ifdef FLASHING
  digitalWrite(LED_BUILTIN, HIGH);
  Serial3.begin(BAUD * 2); //double speed is necessary for stub upload, but it disturbs frequency test of the 'download tool'
  resetESP(true); // reset to bootloader. no need to push the B/L button while connecting to USB
#else
  digitalWrite(LED_BUILTIN, LOW);
  Serial3.begin(BAUD);
  resetESP(false);
#endif
}

void loop() {
  while (Serial.available()) {
    detectFlashing();
    Serial3.write(Serial.read());
  }
  while (Serial3.available()) {
    Serial.write(Serial3.read());
  }
}

#ifdef FLASHING
void detectFlashing() {
  // empty
}
#else
const byte syncFrame[] = {0xC0, 0x00, 0x08, 0x24, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x07, 0x07, 0x12, 0x20};
const byte checkSumPos = 5; // esptool.py and FDT do not send the checksum for sync frame, IDE does
byte syncFrameIndex = 0;
boolean detectSyncFrame = true;

void detectFlashing() {
  if (!detectSyncFrame)
    return;
  byte b = Serial.peek();
  if (!(b == syncFrame[syncFrameIndex] || (syncFrameIndex == checkSumPos && b == 0x00))) {
    syncFrameIndex = 0;
  } else {
    syncFrameIndex++;
    if (syncFrameIndex == sizeof(syncFrame)) {
      detectSyncFrame = false;
      resetESP(true); // reset to bootloader
      digitalWrite(LED_BUILTIN, HIGH);
      Serial3.write(syncFrame, sizeof(syncFrame) - 1); // last byte was not read, only peek
    }
  }
}
#endif

This is inspired by how I work with Uno WiFi. On UNO WiFi you can't disconnect the esp8266 from Serial1 and connect it to USB adapter for flashing like you do it with a module. The solution is a software bridge, a sketch ESPProxy in ATmega which copies bytes from USB to esp8266 and back.

On Uno WiFi there are connections to esp8266's CH_EN, GPIO0 and to a mosfet controling the power line to esp8266. It is possible to reset or reset to bootloader the on-board esp8266 from the Atmega sketch.

Flashing tools send DTR signal to USB chip to trigger the reset of the processor to bootloader. The signal causes the reset of the ATmega and restart of the ESPProxy sketch. And ESPProxy calls a reset or a reset to bootloader of esp8266. This way the esp8266 is in bootloader mode if the tool or IDE requires it.

The EspProxy sketch can detect uploading to esp8266 and put the esp into bootloader mode. If you enable DTR in boards.txt and let Serial Monitor open, then after the uploading the EspProxy returns to normal mode and bridges the Serial port of esp to USB for use of Serial Monitor with Serial of the esp.

With EspProxy I upload sketches or firmware to on-board esp8266 or sketches to Atmega, without event looking at the board. No disconnecting from power, pushing of B/L button or changing switches or cabling. Ideal for a lazy man like me. And it is fun experimenting with firmwares and esp sketches co-working with Atmega sketches with quick uploads.

Of course to upload to esp8266, OTA upload can be used in many cases, but sometimes it hangs and flashing over Serial is necessary.

I created this EspProxy sketch for Mega/Uno used with esp8266 module or for boards like the Robotdyn Mega/Uno + WiFi R3.

With Mega the esp8266 can be connected to Serial1, 2 or 3. With Uno only software serial is an option if you want to have Serial Monitor and Atmega flashing on Serial(0). For Uno the SoftwareSerial can handle the flashing from IDE at 38400 baud.

To reset the esp8266 to flashing mode it is necessary to connect the esp's CH_EN and GPIO0 to Mega/Uno pins for pulling them LOW in OUTPUT mode. The EspProxy doesn't set them HIGH to 5V, but some other sketch could do it, so add a guarding diodes to the connections.

EspProxy.ino (2.44 KB)

Developing an Atmega sketch which co-works with esp sketch requires to upload to both mcu. To upload to esp connected to Atmega the developed Atmega sketch can have EspProxy features.

Example:

#if !defined(HAVE_HWSERIAL3)
#include <NeoSWSerial.h>
NeoSWSerial Serial3(6, 7);
#define BAUD 38400
#else
#define BAUD 115200
#endif

#define ESP_ENABLE_PIN 2
#define ESP_GPIO0_PIN 3

void setup() {
  Serial.begin(BAUD);
  Serial3.begin(BAUD);

  resetESP(false); // to bridge the reset
  detectEspFlashing();
}

void loop() {

  static unsigned long next = millis();
  if (millis() > next) {
    next += 5000;

    // read the input on analog pin 0:
    int sensorValue = analogRead(A0);
    // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
    float voltage = sensorValue * (5.0 / 1023.0);
    // print out the value you read:

    Serial.println(voltage);

    // and here send the value to your esp sketch over Serial3

  }
}

void detectEspFlashing() {
  const byte syncFrame[] = {0xC0, 0x00, 0x08, 0x24, 0x00, 0xDD, 0x00, 0x00, 0x00, 0x07, 0x07, 0x12, 0x20};
  byte syncFrameIndex = 0;
  while (millis() < 2000) { // 2 seconds after reset
    if (!Serial.available())
      continue;
    byte b = Serial.read();
    if (b == -1)
      return;
    if (b != syncFrame[syncFrameIndex]) {
      syncFrameIndex = 0;
    } else {
      syncFrameIndex++;
      if (syncFrameIndex == sizeof(syncFrame)) {
        resetESP(true); // reset to bootloader
        Serial3.write(syncFrame, sizeof(syncFrame));
        while (true) {
          while (Serial.available()) {
            Serial3.write(Serial.read());
          }
          while (Serial3.available()) {
            Serial.write(Serial3.read());
          }
        }
      }
    }
  }
}

void resetESP(boolean toBootloader) {
  if (toBootloader) {
    pinMode(ESP_GPIO0_PIN, OUTPUT);
    digitalWrite(ESP_GPIO0_PIN, LOW);
  }
  digitalWrite(ESP_ENABLE_PIN, LOW);
  delay(5);
  pinMode(ESP_ENABLE_PIN, INPUT); // let it to pull-up resistor
  if (toBootloader) {
    delay(100);
    pinMode(ESP_GPIO0_PIN, INPUT); // let it to pull-up resistor
  }
}

library FirmataMaster enables esp8266 to use resources of other MCU with Firmata installed.

and Firmata sketch with ability to bridge flashing of esp8266 connected to Atmega is very convenient for developing esp sketch with FirmataMaster

SlaveForEspFirmata.ino (4.08 KB)

FirmataMasterTest.ino (3.06 KB)

Juraj:
Of course to upload to esp8266, OTA upload can be used in many cases, but sometimes it hangs and flashing over Serial is necessary.

I don't think I've ever had OTA fail in a way that I could not retry. I have of course had programs hang or otherwise be unresponsive. To get those back to life I'm adding these lines in setup() after setting up WiFi and OTA:

  Serial.println("Starting 5-second OTA recovery window.");
  uint32_t startTime = millis();
  while (millis() - startTime < 5000) {
    ArduinoOTA.handle();
    yield();
  }
  Serial.println("Recovery window closed; continuing normal setup.");

Saved me many times :slight_smile: Of course every reset you have this 5-second delay but that's a little price to pay. Not sure if that yield() is necessary but it won't hurt anyway.