A resistor can only drop a certain voltage when there is a certain current - as you know from Ohm's Law. If you have resistance R and current I the voltage dropped is R×I.
So if the current changes the voltage changes - and if the voltage changes the current changes.
So what do we do? Well, we have one known fixed value, and that is the 5V from the GPIO pin. And since that is fixed (at least when HIGH) we could add a fixed resistance to it to give a fixed current, right?
So let's do that. Pick a random value. Experience teaches you that certain values are better than others, but the actual value doesn't matter too much, as long as:
- It's not so small that too much current flows out of the GPIO pin, thus damaging it, and
- It doesn't create too high an output impedance, which we will come on to later.
10kΩ is a good figure to start with. Not only does it keep the current low enough, but it's pretty much guaranteed that you'll have one in your bits box somewhere. And we connect it to the GPIO like this (we completely ignore the ESP8266 for the moment):
simulate this circuit – Schematic created using CircuitLab
When TX is HIGH it's at 5V. It's connected directly across a 10kΩ resistor, so therefore we can use Ohm's Law to calculate the current - I=V÷R = 5 ÷ 10,000 = 0.0005, or 0.5mA. (Another useful thing with 10kΩ resistors is it makes calculations nice and easy).
Now, what if we were to split that resistor in half, so it was two 5kΩ resistors in series? The circuit would look like this:
simulate this circuit
Resistors in series add together, so those two 5kΩ resistors are the same as one 10kΩ resistor. We know that 0.5mA flows through 10kΩ at 5V, so the same must run through 5+5kΩ at 5V. But now we have two fixed value resistors. Because of that we can now apply Ohm's Law to each one to find the voltage drop across it.
So with 0.5mA flowing through a 5kΩ resistor we get V=I×R = 0.0005 × 5000 = 2.5V. That's 2.5V across R1 and 2.5V across R2, since they are both the same. That's exactly 50% of the 5V across each one. At the point in the middle, point "A" on the schematic, you can see that the voltage has been cut in half. Once the current has gone through one resistor the voltage has halved (dropped by 2.5V), and when it goes through the other resistor it drops the rest to 0V, which is the other 2.5V.
If we were then to connect something to point A it would see a logic signal of 0V (LOW) and 2.5V (HIGH) from our 0V (LOW) and 5V (HIGH) signal.
But 2.5V is too low for the ESP8266 to read. Well, the answer is simple. We see that if we use two resistors we divide the voltage into two. The same applies for dividing it into three. For instance:
simulate this circuit
The sum is 10kΩ (well, 9.9kΩ but we'll call it 10kΩ for simplicity - a third each resistor, 3333.33Ω). 0.5mA through 3.3kΩ would be 0.0005 × 3333 = 1.66V. Funnily enough, 5 ÷ 3 = 1.66, so we have proved that it splits it into a third. That means at point A 1.66V of our 5V has been dropped (across R1), so 5V is now (5-1.66) 3.33V. Perfect for the ESP8266. Incidentally, point B would be 1.66V (3.33 - 1.66).
So now we have three resistors and the result we desire. However it's a little messy still. Remember that resistors in series add together? So why not add R2 and R3 together into a single resistor? That would be 6.6kΩ. Now I don't know of a 6.6kΩ resistor that's easily available, but 6.8kΩ ones are, though chances are you may not have one. If you did then a 3.3kΩ and a 6.8kΩ would net us near enough 3.3V to be usable.
But as I say you may not have one. So you pick resistor values that you do have. For instance you could use a 2.2kΩ and a 4.7kΩ, or a 10kΩ and a 22kΩ - anything that gives a roughly 1:2 ratio of value (the "upper" R1 resistor about half the value of the lower "R2" resistor).
Now - we get onto that mysterious phrase I mentioned earlier: Output Impedance. You recall we calculated the current that flows through the resistors - 0.5mA. The higher the value of resistors we use the lower the current. Well, that current not only flows through the resistors - a small amount also heads off into the ESP8266. The more that the ESP8266 wants for its RX pin the less there will be available for the R2 resistor. The result? Less current through the resistor = less voltage dropped across it.
The current drawn by the pin attached to point A on our circuit affects the voltage dropped across the resistors.
Too much current drawn and the voltage at point A will droop. Less will be dropped across the (current starved) R2, which means more must be dropped across R1. So point A is now a lower voltage. If that gets too low (too much current is drawn) then the RX pin can't read it properly.
Increasing the resistances decreases the current, but also decreasing the resistances increases the current. If the current is increased then the fraction of the current needed by the RX pin becomes reduced compared to what is available, so the output voltage drops less.
This output impedance, in case you're interested, is measured in Ohms, and is the same as the value of the two resistors in parallel - so (R1×R2)÷(R1+R2).
For instance, I tried connecting an ESP8266 the other day using 3 10kΩ resistors - R1 = 10K, R2 = 20K. It didn't work. You'd expect it to, given the ratios involved, however the output impedance (6.66kΩ) was too high for the ESP8266 I was using. Replacing them for 1kΩ resistors, so it was 666Ω output impedance, made it work straight away.
You can understand it easier if you think of the ESP8266 as just another resistor (it's actually more complex than that - it's also like a capacitor, so it forms a low pass filter which limits the frequency of, and deforms the shape of, the incoming digital waveform). For example:
simulate this circuit
I have deliberately shown the ESP8266 as a lower resistance that it would really be (you can expect MΩ) so as to illustrate the problem clearer.
Your R2 now has R(esp) in parallel with it. So the nice clean 20kΩ is now actually (20,000 × 100,000) ÷ (20,000 + 100,000) = 16.66kΩ. That gives us a total of 26.66kΩ, and at 5V that means a current of 0.188mA. Through the 10kΩ resistor we end up with (10,000 * 0.000188) 1.875V dropped. Take that from 5V and you can see that the ESP8266 would only be getting 3.125V.
By reducing the resistors in our network the difference that R(esp) makes to R2 reduces. For instance, reducing it to 1kΩ + 2kΩ gives us an R2 total of (2,000 * 100,000) ÷ (2,000 + 100,000) = 1.96kΩ - hardly any different from the 2kΩ we started with, so the voltage difference will be minimal (3.31 as opposed to the ideal 3.33).
The simple formula for calculating the output voltage from an input voltage and pair of resistors, so you don't have to go long-hand through Ohm's Law every time (though it is useful for understanding exactly what is happening) is:
Vout = R2 ÷ (R1 + R2) × Vin
I can never remember it, though, and usually use an online voltage divider calculator.