UART Connection with ESP32 and Arduino Uno R3

Good day everyone! I am a student that was required to use Arduino uno r3 and esp32 with that said, I was planning to make use of UART communication between uno r3 and esp32 then use it to transfer data from uno to esp32 and the ESP32 upload the data to a webserver.

I did some research and saw that using a voltage divider works as a level shifter for the Rx of the ESP32 to the TX of Arduino uno and saw that data can be transferred that way but, is it also possible to make use of the received data from uno to the ESP32 web server?

If so, is it possible to see examples?

I made the initial wirings, following some showcases online and specifically this one: UART communication between Arduino Uno and ESP32 - Hackster.io ...to create the voltage divider.

I'm quite new to UART communication and esp32 so, I'll apologize in advance if I sound like an idiot.

And the teacher thinks that is a good plan because ? The ESP32 is so much more powerful than an UNO and can do pretty much everything an UNO can do. On top of that there is the UNO R4 which is already a combo of those 2 boards sort of. But anyway assignments are what they are.

Very good ! Keep in mind that you should use fairly low value resistors for the voltage divider or the capacitance will prevent higher baud rates from being received properly.

Of course, once you have the data you can do with it what you want.

Webserver examples plenty, already as part of the built in Webserver library for instance

To achieve this I have made a voltage divider circuit using one 10k resistors and one 20k resistor

Use 1K & 2x 1K instead or your speed will be limited.
The ESP32 has 3 UARTs. The pins can be assigned freely, but the default assignments come with some extra information. UART0 uses GPIO 3 & 1 as default pins, but those pins are connected to the USB to TTL converter and that wiring wil interfere with reception. UART1 uses GPIO 9 & 10 as default pins, but those pins are also connected to the SPI flash, and using them will cause the ESP32 to crash (guru meditation error). UART2 uses pins 16 & 17 by default and there are no conflicts with those, so i suggest you start with using UART2 (which will also leave you with UART0 as a debug output over USB)

1 Like

Hello Deva_Rishi, I appreciate your response. It is actually a bigger project that required us to use a TFT LCD, push buttons, and Arduino uno r3 with an ESP32. But, honestly I believe it had been easier if we were given the option to use Arduino Uno R4 (which was my initial plan but we were limited to Arduino Uno R3 and an ESP32)
[Edit; The only part I needed to finish is the UART communication between ESP32 and uno r3 then ESP32 uploads data to web server part of my project ]

I have switched up the 10k and 20k resistors to 1k resistor and 2x resistors. (Still following the schematics of the website I used as a reference).

I'm also glad that what I thought (conceptually) was going to work because I just learned UART communication yesterday and have never saw examples that involved Arduino Uno R3 -> ESP 32 -> Web Server examples.

I checked the web for a bit and was suggested to use WiFi.h and WebServer.h and for storing the data using ArduinoJson.h

I also am using the UART2 (The Pin 16&17 for this).. if I'm not wrong, it is labeled as Rx and Tx in the ESP32? (I have a ESP32S [NodeMCU]).

Here is also the connections of the voltage divider. I was following the schematics but, I may be wrong in some way as I haven't got any sleep since Monday because of this project being due next week and was given on Monday as well.


For easier understanding, the black wire is the GND of the Arduino uno r3, the brown wire the GND of the ESP32 and the white wire the Rx of the ESP32 then, the black wire is connected to pin 1 or the TX of the Arduino uno.
(Edit 2: I also forgot to note that this is still my draft for the project and would have to solder everything after)

Thank you.

Yeah that looks right. Basically it's UNO-TX -> 1K -> ESP32-RX -> 1K -> 1K -> GND and that reduces 5v to 3.3v which is what is required.

Yes those are the ones.


This is the pinout i use for 30-pin dev-board.

The UNO R3 is probably not required for such a project and needlessly complicating since the ESP32 can handle those tasks, but let's not fuss about it.

I never use json. Mainly i would suggest just looking at the basic examples that come with the ESP32 core for the webserver, although i have of course kept my own. That said, there appear to be some issues with the latest ESP32 core and from what i've heard the last reliable version is 3.0.7 i am still using 2.0.11 though since updating cores always involves all sorts of troubles on existing projects.

This is the basic webserver example from the most recent core

As for UART communication there are of course several options.
You can send raw data, but how is the receiver going to interpret what is what ? If we depend on the sequence of bytes, how will we define which is the first and so on. It is a whole study all by itself. Quite a while ago Robin made This tutorial on the matter and that still shows many of the options and is known to work, though unfortunately not supported by the author anymore on the forum due to health reason i am led to believe.

There are more simple options available. If you clear the ESP32 input buffer, send a request from the ESP32 to the UNO (it can be a single character) and then let the UNO send RAW data back to the ESP, that should work as well.

First thing to do would be to upload a SerialPassthru example onto the ESP32 to confirm your hardware is working properly.

Onto the UNO you can upload.

void setup() {
  Serial.begin(115200);
}
void loop() {
  Serial.println("The UNO says Hi !");
  delay(1000);        
}

and to the ESP32 you can upload

void setup() {
  Serial.begin(115200);
  Serial2.begin(115200);
}
void loop() {
  if (Serial.available()) {    
    Serial2.write(Serial.read());   
  }
  if (Serial2.available()) {     
    Serial.write(Serial2.read());  
  }
}

And the ESP32 should forward whatever comes in on UART2 to it's UART0 which is connected to the USB to TTL converter, and you should be able to see that on the Serial monitor of the ESP32 port. (and on the UNO port btw, but that is just a side effect)

1 Like

Hello Deva_Rishi, I see! Thank you. Now I know it wasn't on my wirings but rather on how I coded it (most probably). I was using
Serial1.begin(9600, Serial_8N1, 16, 17);
This was written on the ESP32 (I was following this: How to Exchange Data between Arduino and ESP32 using Serial Communication?
[EDIT; I just realized now that the website directly attach the uno and esp32 without voltage divider and that's probably why the code is supposed to be like that (I'm not quite sure though)]

To make it more understandable:

This was my ESP32 Code (following the same logic of the website that I sent and adding the ArduinoJson.h, Wifi.h and WebServer.h I mentioned).. for the uno, I just used a simple Serial.print(); command to know if it's working but, I could never received data from uno in the serial monitor of the ESP32.

#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>

//WIFI CONNECTION
const char* ssid= "my_wifi";
const char* password="my_password";

WebServer server(80);

DynamicJsonDocument json(1024);
String receivedData;
unsigned long startTime = 0;
const unsigned long dataTimeout = 10000; // 10 seconds

void handleRoot() {
  String html = R"=====(
<!DOCTYPE html>
<html>
<head>
  <title>Message</title>
  <script>
    function fetchData() {
      fetch('/data')
        .then(response => response.json())
        .then(data => {
          document.getElementById('data').innerText = data.value;
          if (data.timeout) {
            document.getElementById('data').innerText = ""; // Clear on timeout
          }
        });
    }

    setInterval(fetchData, 1000); // Update every second
  </script>
</head>
<body>
  <h1>Message: <span id="data"></span></h1>
</body>
</html>
)=====";
  server.send(200, "text/html", html);
}

void handleData() {
  json["response"] = receivedData;
  json["timeout"] = (millis() - startTime > dataTimeout && receivedData != "");

  String jsonString;
  serializeJson(json, jsonString);

  server.send(200, "application/json", jsonString);
}

//PINS CONNECTED TO ARDUINO
#define RXp2 16
#define TXp2 17

void setup() {
  Serial.begin(115200);
  Serial1.begin(9600, SERIAL_8N1, RXp2, TXp2);
  delay(10);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/data", handleData);
  server.begin();
  Serial.println("HTTP server started");
}
 void loop() {
  server.handleClient();

  while (Serial1.available()) {
    Serial.println(Serial1.readString());
    receivedData = Serial1.readString();
    receivedData.trim();
    startTime = millis();
    Serial.println(receivedData);
  }

  if (millis() - startTime > dataTimeout && receivedData != "") {
    receivedData = ""; // Clear data on ESP32 after timeout
  }
}

I got the website working here given the IP address of the ESP32 but the it will always say undefined so, I'll try out the recommendations you given.

[Edit 2: The forum that was talking about serial inputs by Robin was very insightful. Thank you for that. I'll be applying changes on my code.]

[Edit 3: Still having issues on the uno data -> esp32 (I am honestly very puzzled), it gives me Data: ??????????. I used the same code you given to test it out but, added... (After the if else statements)

Serial.print("Data");
Serial.println(Serial2.readstring());

I could be very wrong with this so I apologize in advance]

That is using UART1 but on alternate pins, an ESP32 can free assign most pins to most peripherals, but why to use UART1 on the UART2 default pins ? anyway doesn't matter also works.

No there is protecting hardware on the ESP32 which should protect the pins against 5v logic levels, but i do not recommend doing that. The code and the hardware are not related on this matter. Mind you at that speed 10K & 20K would have been fine for the voltage divider.

And now with the simple test sketches ?

1 Like

Hello Deva_Rishi! Thank you for the clarification. I really am still confused with how this whole UART thing works but, I seem to be a step closer to understanding it.

I see, this is actually something I hadn't learned from the tutorials I've read and watched so far. Thank you for the information. The videos/tutorials I read so far just suggested I add a level/logic shifter but since I didn't have that, it was recommended I use a voltage divider.

I tested it out, see Edit 3 in my reply 5. It seems to give me Data: ?????, I could be very wrong on coding it though but this is the full code on the ESP32...

void setup() {
   Serial.begin(115200);
   Serial.begin(115200);
}

void loop() {
  if(Serial.available()) {
      Serial2.write(Serial.read());
}
  if(Serial2.available()) {
      Serial.write(Serial2.read());
}
  Serial.print("Data: ");
  Serial.println(Serial2.readString());
}

I really doubt this would actually work but it does give me something but it's in ?????

[EDIT: I tested it out without the printing part, it still is a set of question marks]

[EDIT 2: I see my mistake now, I forgot to change the Serial.begin(); of my Uno from 9600 -> 115200... I apologize. I'll test it out

EDIT2A: It still gives me the same set of question marks. Even after editing Serial.begin(9600); to Serial.begin(115200);]

EDIT 3: I believe this is a good thing. Now I know it's receiving. I tested out with my actual code and it's working but with the question marks.

No it's not. You should receive what you send.
As long as you don't manage the simple hardware test there is no point to progress.

btw for the opposite direction (ESP-tx to UNO-rx) be sure to add a 1K resistor in line to limit the current when there may accidentally be a transmission from the UNO's USB to TTL converter and from the ESP32 at the same time.

In your ESP32 code i shows :

That should be

void setup() {
   Serial.begin(115200);
   Serial2.begin(115200);
}

I think that will resolve the issue.

1 Like

What i do read & forward characters 1 by 1 and you read possibly multiple ones.
It's Ok, but it makes use of the String class, which can cause issues on a small memory board like an UNO.

Either way, use only one or the other method.

1 Like

Hello Deva_Rishi! I see, I didn't know that. Thank you. I'll try adding a 1k resistor on the ESP TX -> Uno Rx Connection

I have edited that, thank you. I was a bit sleepy yesterday.

[EDIT: I had added the 1k on the Uno Rx & ESP TX as recommended. These are the changes I made in my actual code to this (but I'm not sure if I'm implementing your suggestions right)

#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>

//WIFI CONNECTION
const char* ssid= "here-lies-wifi";
const char* password="the-password";

WebServer server(80);

DynamicJsonDocument json(1024);
char receivedData;
unsigned long startTime = 0;
const unsigned long dataTimeout = 10000; // 10 seconds

void handleRoot() {
  String html = R"=====(
<!DOCTYPE html>
<html>
<head>
  <title>Message</title>
  <script>
    function fetchData() {
      fetch('/data')
        .then(response => response.json())
        .then(data => {
          document.getElementById('data').innerText = data.value;
          if (data.timeout) {
            document.getElementById('data').innerText = ""; // Clear on timeout
          }
        });
    }

    setInterval(fetchData, 1000); // Update every second
  </script>
</head>
<body>
  <h1>Message: <span id="data"></span></h1>
</body>
</html>
)=====";
  server.send(200, "text/html", html);
}

void handleData() {
  json["response"] = receivedData;
  json["timeout"] = (millis() - startTime > dataTimeout && receivedData != "");

  String jsonString;
  serializeJson(json, jsonString);

  server.send(200, "application/json", jsonString);
}

//PINS CONNECTED TO ARDUINO
#define RXp2 16
#define TXp2 17

void setup() {
  Serial.begin(115200);
  Serial2.begin(115200);
  delay(10);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi...");
  }
  Serial.println("Connected!");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/data", handleData);
  server.begin();
  Serial.println("HTTP server started");

}

void loop() {
  server.handleClient();

  if (Serial.available()){
    Serial2.write(Serial.read());
  }
  if (Serial.available()){
    Serial.write(Serial2.read());
  }
  if (millis() - startTime > dataTimeout && receivedData != "") {
   receivedData = ""; // Clear data on ESP32 after timeout
  }
  Serial.println("Data:");
  receivedData = Serial2.read();
  startTime = millis();
  Serial.println(receivedData);
  }

I have not yet compiled this but, it is currently compiling as I post this reply. It just somehow takes long.

[EDIT 2: I had a few typos so, I edited the code I sent]

Hello Deva_Rishi, should I make use of the character serial inputs by Robin? I'll try it out right now.

[EDIT: Upon adding the new code, the ArduinoJson.h has been complicated to fix and was being buggy. Is there any alternative option for ArduinoJson.h that gets the job done?]

I have no idea what it is supposed to be doing to begin with, i have never used it.

So but you got the example hardware test code working ?

Why do you put the hardware test code inside your main code ?

First confirm the hardware is working, then forget about it and write the code that you thin8k you need.
You can read() incoming bytes from the UART buffer only once, so anything that is read

Serial.write(Serial2.read());

that byte is now gone from the buffer, so if you try and read it again

receivedData = Serial2.read();

it won't be there. maybe some other byte is there but that is just coincidence.

Go back to what i posted in #4 . Just copy and paste the code into the IDE, so there won't be any typos. There is no need to incorporate anything into your code. This code is just for testing the hardware. If the hardware works, great, we can forget about the code.

Next we can look at options on how to transfer your data from the UNO to the ESP, and for that you can try and see if you can get Robin's code to work work for you. Again focus on just the UART part and leave the whole webserver section out of your development for now.

Just send (something like) data from the UNO, and receive what you send on the ESP32 UART2 and echo it out of the ESP32 over UART0(USB)

something like sending a bunch of bytes from the UNO , you know what they mean, and so when you receive you store the values where you want them, and then print them to USB 'saying' what they are.

Once that works the way it is supposed to, we can come back to the webserver.

The reason to do it step by step is that if you do a bunch of things at the same time and it doen't work like you expect, there is no way to tell what is going wrong.

1 Like

Hello Deva_Rishi! It's supposed to put the received data -> Jason file so it can be accessed by the web server. Or maybe there's another way to it.

I apologize for the message I sent. I was quite tired and simply attached everything into the code and making it a mess. I got a nap and I just tried the simple program that you have given. So, it doesn't show the question marks anymore... In the Serial monitor, it's just this. Is this a good sign?

Also this is the wiring adjustments I made (I wanted to make sure I'm doing it right)...


I'll explain it starting from the black and brown box connected by a resistor/somehow looking divided by them...

Brown is the Rx of Uno and Black being Tx of ESP32

The other black and brown that are beside each other...

Brown is the GND for ESP32 and the Black is the GND for Uno.

Now for the wirings for the voltage divider...
White is the Rx of the ESP32 and the orange is the Tx of the Uno.

That's all for the wirings

And some extra details...
For Uno, the TX (transmitter) is blinking and for the ESP32, only the red led lights up.

For reference, here is an image:


[EDIT: I forgot to add the code I used but this is it...

Uno R3 Sample Sketch:

void setup() {
  Serial.begin(115200);
}
void loop() {
  Serial.println("Hi");
}

ESP32 Sample Sketch:

void setup() {
  Serial.begin(115200);
  Serial2.begin(115200);
}

void loop() {
  if(Serial.available()) {
    Serial2.write(Serial.read());
}
  if(Serial2.available()) {
    Serial.write(Serial2.read());
}
}

That's all for the code]

[EDIT2: Decided to use the code from Robin (using the finalized one and copy and pasted after analyzing it but I don't get any data from it at all.)

Uno code:

void setup() {
    Serial.begin(115200);
}

void loop() {
    Serial.println("<Hello!>");
}

ESP32 code:

//---DATA---
const byte CharNo = 32;
char CharRcv[CharNo];
boolean newdata = false;

void setup(){
  Serial.begin(115200);
  Serial2.begin(115200);
}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = '<';
    char endMarker = '>';
    char rc;
 
    if (Serial2.available() > 0 && newdata == false) {
        rc = Serial2.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                CharRcv[ndx] = rc;
                ndx++;
                if (ndx >= CharNo) {
                    ndx = CharNo - 1;
                }
            }
            else {
                CharRcv[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newdata = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newdata == true) {
        Serial.print("This just in ... ");
        Serial.println(CharRcv);
        newdata = false;
    }
}

void loop(){
    recvWithStartEndMarkers();
    showNewData();
}

If I'm doing this right. I assumed that since I'm using pin 16&17. It should be Serial2.available();
However, for some reason, it will show no data... ]

This is all, thank you. I also apologize for the inconvenience.

No well it should be showing the data that is being transmitted by the UNO.

It looks good, but if it's not working properly there must be something wrong ?!

Well that is all that we can expect. The red power led lights up, and the TX of the UNO should light up.

Pretty much constantly, it do suggest you add a delay in loop, to send something every second or so is fine.

Still the code looks fine, so check again the pins on the UNO & the ESP32. Check again that all the resistors and wires in the proper row of holes. If that doesn't show anything, grab you multimeter and start measuring. There must be something not connected properly.

1 Like

Hello Deva_Rishi! I still can't get it to "post" the data from uno to esp32 serial monitor but it does give me a message in the serial monitor after I pressed the reset . (I was using the Node32s board in Arduino)

I'm using the code that has the code from Robin and for the Arduino uno, I'm using the code you given. But I made it in a way that it has this in the void loop()...

Serial.println("<Hello>");
delay(1000);

I tried using the NodeMCU-32s board in the IDE but, it would be an error (aka it doesn't upload properly despite the code working properly on Node32s)... I'm trying out this solution: A fatal error occurred: Failed to connect to ESP32: No serial data received - #7 by bilaltan as the error is a fatal error and that that the board is not found or not connected. (Which is not actually the case as it gets uploaded properly if using Node32s)

PS. Now that I used that, it also doesn't upload on Node32s now. I believe my connections are right and I have made sure that all the wires are in place.

Any chance that GPIO 0 is somehow connected to GND either thru the 'boot' button or some other way ?

The code was not the issue, here must be something else wrong.

ESP32 devkit is the board you should use.

Hello Deva_Rishi, solved the uploading error now and I'm switching the board to the ESP32 devkit (If I'm not wrong, it's called ESP32 Dev Module in the IDE).

As for the GPIO 0 connected to GND thru boot button, I believe not. For reference, these are the only connections in making use of...


The GND, Rx and Tx pins.

But I believe, it won't affect the UART connection if I use the 3.3v of the ESP32 to power up something that is connected to the uno (I am yet to do this so, for now those mentioned pins are what I'm using)

Another edit: It still doesn't show the data that must be shown. I'm using the same code. No adjustments or anything altered. I also made sure everything is connected. I did it in a way that I removed that wire then returned it to its place then, I made sure everything is connecting properly and are in the grid they should be in the breadboard.

Great !

You should use the UNO's 3.3v pin for anything that is connected to it. The 3.3v from the ESP32 is already providing quite a bit of current for the processor and WiFi. Anyway as long as the UART communication between the board is not working properly, we are still at that step in development and there is no sense in looking at other steps.

1 Like

Hello Deva_Rishi! I'll be checking the wires. So, I might rewire everything for my own sanity cause I might have wired something the wrong way and might be the reason why the UART isn't working.

I'll just have to ensure that the components on Uno remains connected to the Uno and the ESP32 is no way connected to them, right?

I just know that I'm only making use of the TX, Rx and the GND pins of the ESP32 so, I don't believe that the GPIO 0 of the ESP32 is connected to the boot button

Well common GND they share always. But for the rest they should not be connected.

Oh it's connected to the boot button, just making sure there is nothing else connected to it and you are not accidentally holding it down.

It is a recommendation i give at times. Sometimes something just isn't connected right but it doesn't show up till it is re-wired.