ESP32 SoftwareBitBang does not work using voltage level converter
alastaira opened this issue · 33 comments
Hi,
I'm mid-build of a project (an escape room game), which initially consisted of around 8 Arduino Nanos, which communicated beautifully using PJON's SoftwareBitBang strategy (and I must commend you for the elegance of this library - I was very impressed by performance and reliability, and the ease of just a single wire interface :) ).
However, the requirements for some of the devices have now exceeded the limited capability of the Nano, so I've "upgraded" them to use ESP32 chips instead, and I've switched the Nanos to common NodeMCU dev boards like this: https://www.amazon.co.uk/NodeMCU-32S-ESP-32-Development-Ultra-Low-Consumption-Black-Silver/dp/B07W4W8NQ6
Having seen that #207 was closed, I was under the impression that ESP32 was a supported device, but I'm not having any luck with it - I've tried using pins 12 and 25 as documented at https://www.pjon.org/SoftwareBitBang.php (although I notice that lists a particular manufacturer's ESP32 dev board - is it not generally supported?), and I'm using a logic level convertor on the ESP32 end to convert from 3.3V to 5V which the rest of the devices on the bus are running, yet the device is never found on the network. I've tried modifying the most basic "Blink with response" test, but still not able to get any communication.
I would be very grateful if you could clarify whether ESP32 is a supported device, or whether it's included in a roadmap for future development, and if there's anything I can do to support that effort! Thankyou.
Ciao @alastaira thank you very much for your compliments! I would be very curious to see your craft sooner or later :)
but I'm not having any luck with it - I've tried using pins 12 and 25
The voltage level converter may be the culprit, have you tried to look if the output is effectively there with an LED/oscilloscope?
Have you tried test it with just a resistor in series, let's say 1k, instead of the converter?
I notice that lists a particular manufacturer's ESP32 dev board - is it not generally supported?
I have the Heltech WiFi LoRa and it works well on pin 12 and 25, this is the only test result done by me for now on ESP32.
So, the support must be considered preliminary.
I would be very grateful if you could clarify whether ESP32 is a supported device, or whether it's included in a roadmap for future development, and if there's anything I can do to support that effort! Thankyou.
Yes the ESP32 is for sure an interesting and widely used MCU, must be in the list, before the release of 13.0 ESP32 passed all test exchanging data nominally with UNO/Nano. Although I often had issues with logic level converters and SoftwareBitBang, I suspect the ack phase is not liked by them. If the problem is the converter, this could avoid you tedious work and might satisfy your requirements (1x 1N5711, 2x 1N34).
2 batteries are used to simulate the 2 pins, and 2 switches are used to simulate the state of each pin. As you can see I have placed both the suggested current-limiting and pull-down resistors in the circuit. It looks it would work, although it is not effectively converting the voltage, just constraining it within the absolute maximum ratings. On 5v side voltage is 3.1v so well above threshold (0.6v), on 3v3 side voltage is 3.89v so within ESP32's IO absolute maximum ratings.
Thankyou for the quick reply. I have some progress to report, and I think you may be right about it being a problem with the level convertor rather than strictly with the ESP32....
Unfortunately, I only have access to a very basic software oscilloscope, but I thought it was better than nothing..... First I recorded the output I measured on Pin 12 of a 5V Arduino running the basic "BlinkWithResponse/Transmitter" sketch (which works) :
Then I compared this to exactly the same sketch running on Pin 25 of the ESP32 - I appreciate you can't tell much about the signal quality, but there's definitely something there :
But, when I measured the output of the PJON signal pin having been through a logic level convertor.... I just get a constant HIGH signal:
I wondered whether there was a problem with the logic level convertor itself, but I've tried two different sorts (one like https://www.adafruit.com/product/395 and one like https://www.sparkfun.com/products/12009) and neither could be detected on the PJON network, and neither seemed to generate a sensible output on my (admittedly very basic) scope. But I don't think the convertors themselves are "broken", since I then tried using them to create a serial connection between the ESP32 and the Arduino, and they worked fine for that... but perhaps not rising/falling fast enough or something?
But, having verified the ESP32 was generating a signal on Pin 25, I then tried something else: I connected the ESP32 to an ESP8266-based Wemos D1 Mini, both operating at 3.3V, and that worked! So the ESP32 was working correctly all along...
So, to summarise, having a load of 5V Arduinos on the same network works great. Having two 3.3V ESP32/ESP8266 works great. But the problem is trying to have a network of devices operating at mixed logic levels? Is that a supported scenario?
Thankyou!
Thankyou for the proposed workaround and simulation - I'll have a rummage around as I'm not sure if I have any 1N34s, but I'll try to order some to test out.
I'm also trying to understand exactly how that solution would work when there were many various 3.3V/5V devices sharing the same bus, rather than just a pair of devices? In the past I've been accustomed to just chucking in one of those logic convertors per-device I've had to share an SPI/I2C bus, so I'm not very familiar with thinking through the details involved!
@alastaira I think should work fine even if both devices are 3v3, the voltage drop should be still around 1v per "converter" so 2v overall, the receiver device should see around 1.3 volt, looks more than enough for a 1 to be detected. Consider that the low voltage level used (3.3v) and the voltage drop of diodes (2v) reduces the maximum range, so it should be tested. Another option is to run 2 parallel networks operating at 2 different voltage levels and a switch being the only device implementing the proposed circuit bridging them. Join our gitter chat if you need any help https://gitter.im/gioblu/PJON
What do you think about using a simple resistor divider, with diode on the 3.3V side, like this instead?
@alastaira nice design, I suspect it would work with 100/200k, 1M looks too much to let the signal pass through, although I may be wrong. Let me know how it goes.
I always use this circuit to shift the levels.
Thanks for the suggestion. My understanding is that design is the same approach used in the Sparkfun logic level convertor which I first tried (https://www.sparkfun.com/products/12009) and have used successfully for other mixed voltage devices on a shared bus but, for whatever reason, just wouldn't play ball with PJON. Interesting to know it works for you... do you specifically use it on a PJON network between an ESP32 and Arduinos? Perhaps it's just very tight timing tolerances that make it somewhat network configuration-dependent whether it works or not.
@alastaira nice design, I suspect it would work with 100/200k, 1M looks too much to let the signal pass through, although I may be wrong. Let me know how it goes.
Initial tests are good! I'm actually using 42kΩ/84kΩ resistors at the divider and a 1N914 diode, because those are the components I had to hand. It's been running for a little while now, connected first time and doesn't appear to be dropping any packets - yay! I'm closing this issue, since my problem is fixed and it appears to not actually be a problem related to the ESP32 anyway. But may I suggest adding a note in the wiki somewhere about connecting 3.3/5V devices to the same bus, since I can't believe I'll be the only person that encounters this issue!
Thanks for your help.
Ciao @alastaira thank you for your suggestion, I will add the required info in the wiki.
I don't think the diode is required, you can probably remove it, since there is no current in the diode and the voltage across the diode is never enough to make it pass.
I'm curious, could the issue with the standard FET level shifter be that it holds the bus high while there is no voltage on the low side input to the converter? The bus will be held high all the while the pin doesn't drive the input to the converter low, which includes while the pin is high impedance/input.
My feeling is that PJON's idle state is low, so this configuration might be likely to interfere with the bus operation, as simply unexpected signals. Contrast with both UART and I2C which are idle in the high state, so the converter makes no difference.
Does that make any sense?
(he says having just manufactured a PCB with a FET shifter on it...)
Or, to put it another way, PJON (I think...) is active high, while UART and I2C are both active low.
There is a problem with this 3.3V interface design - it won't allow two 3.3V devices to communicate with each other on the 5V bus, since the device driving the bus to ~3.3V, through another 3.3V device's potential divider, won't reach anything like the high input voltage for the GPIO.
You can't mix device with different voltage VCC with PJDL (what PJON depends on). See here
This is because the architecture of the bus is pulled down by default, so one device must pull up the bus to set a high bit. Thus a device connected to 3.3V can't pull up to the required high level threshold for a 5V device (not even speaking of the power drain through the serial resistor on the bus and the risk of burning your low voltage GPIO if it's not tolerant).
If you need to connect different technologies technologies, you'd need a level shifter circuit.
You can't use usual "one mosfet" level shifter like this:
because this is for open drain bus (like I2C or UART) with pullup.
I guess one could implement a symmetric of this schematic for a pull-down push/pull GPIO like in PJDL, likely by removing the 2 pull up in the schematic above and making them pull down and changing the N-channel FET to a P-channel FET like the BS250
The other solution would be to use such circuit, and change all logical level in PJDL to their inverted state to make it pull up by default like I2C. But in that case, why not make it I2C or OneWire directly ?
@X-Ryl669 I don't think you can make PJDL pull-up, who pulls the bus up, and how far will be detected as up? Generally the PJDL bus is low and who wants to use it just bangs bits in, for that reason it can be used in multi-master mode and also has a very long range (you filter out interference with a pull-down and hope transmitted bits will reach the other end, that may be 2kms away). Not sure if the same can be obtained inverting the state and if the bus would be easily detected high on the other end, @fredilarsen what do you think?
I2C doesn't work long distance because it's detecting front on the signal and this doesn't play well with the capacitance of a long line. Also, any noise will trigger the clock counter, which also isn't very clean approach.
IIUC, in PJDL, there isn't any front detection, but level detection so a long capacitive wire should work.
There is logically, no difference between detecting +5V (from GND to signal) or -5V (from VCC to signal), it's just a shift of voltage level. It's more inconvenient however to continuously power a line because:
- It starts to have a continuous resistive and capacitive loss,
- Any shortcut in the line will likely burn the devices too (unless they are each protected against overcurrent)
- When trying to detect if some device is speaking, the device must discharge the whole line (that's powered by all device) instead of charging it alone
On the pro side, the device could be powered by the line (with a large capacitor to support its own consumption), so it could be power independent.
This is why I don't think it's a good idea all in all, anyway.
Anyway, I'm planning to use a schottky clamp diode for my 3.3V PJDL devices like this
Haven't tested yet but I will.
In my opinion this has a number of advantages:
- 3.3V device doesn't sense, nor drive, the bus through a potential divider, so can drive the bus close to 3.3V, and 3.3V devices can talk to each other. It can also drive the bus closer to 3.3V than the diode/potential divider circuit can
- This method doesn't add increasingly more coupling to ground for each 3.3V device added, so the bus remains low current.
- The input impedance can be lower
There could be a couple of other options using zeners, but these have some disadvantages (imo) over this design, bandwidth limiting and unnecessary current spent driving 3.3V directly into a 3.3V zener, for example.
Any thoughts?
Your solution might not work for 3.3V and 5V because for 5V CMOS high will read a logic high for above 3.5V to 5V (from CMOS typical values), so 3.3V you see on the 5V GPIO might not be enough to register as a 1. I guess it'll work most of the time since many chips allow down to 2.7V for detecting a high level, but you're on a limit that needs to be checked from chip to chip.
I would advice against using Zener diode for this, since Zener requires some current to actually set their voltage (too low and the zener will not present the expected voltage on its pin, only a lower voltage), the reverse leakage current is typically 0.1mA for 3.6V so that means that the bus must have an impedance an order of magniture lower than 36kOhm to work and it'll consume energy the whole time. The Schottky diode is much better anyway.
I'm curious, could the issue with the standard FET level shifter be that it holds the bus high while there is no voltage on the low side input to the converter? The bus will be held high all the while the pin doesn't drive the input to the converter low, which includes while the pin is high impedance/input.
@nuxeh you may be right, infact those chinese level shifter boards worked really well using PJON over serial, never worked when trying to shift a PJDL signal.
I agree with @X-Ryl669 that those solutions might not work because the threshold voltage is not reached in the case 3v3 to 5v.
It might be necessary to drive a transistor with the signal, although that could ruin the PJDL sync if not fast enough.
When trying to detect if some device is speaking, the device must discharge the whole line (that's powered by all device) instead of charging it alone
@X-Ryl669 in general with PJDL, in simple terms, you just look at the input, if it is high, obviously, or there is interference or someone is transmitting something. If you invert the state, if you find low, someone is transmitting something, and, obviously, you would need to drive the bus low to transmit a 1, but, who is pulling up the line? A master? Everyone? And how hard is to pull it down? And how far it will be detected as HIGH or LOW?
I never took time to solve those questions, and appreciated the simplicity of the initial design :)
@gioblu, @nuxeh, @X-Ryl669 I like the term "belt and braces approach" -- but could the bus be split in a 3.3V section and a 5V section with a series resistor in between and a 3.6V zener making sure the 3.3V input pins are protected against the 5V? 3.3V is enough for the 5V devices to receive, and when a 5V device pulls the bus up, there will be a voltage drop over the series resistor that makes sure the 3.3V bus is safe below 3.6V? If it works, minimal is elegant, yes? I have not tried this myself.
The problem with a zener diode is when it's not conducting in reverse in its avalanche regime. In that case, you've a reverse bias current (with a 3.6V zener, and a 3.3V GPIO pulling the bus high while the 5V's part is still opened, you're typically in this region). The current is usually not negligible, something like 0.1mA, so it's matching a 36kOhm impedance at 3.6V (that's a lot less than the pull down resistor in the MOhm range on the bus).
You also need only one of this zener on the bus, obviously, so this might cause issues with multiple 3.3V devices (or 5V device).
Also, there is no diode solution for the 3.3V domain of the bus to raise the voltage to the 3.5V required for the 5V part. A 3.3V device can not pull the bus up to 5V (or 3.5V). The zener is only there to avoid burning the 3.3V domain's GPIO.
If you want to split the 2 bus, I guess a mosfet is required between the 2 power domain, there's no way to skip it. It must be attached to the high voltage domain and used to control the low voltage domain (that's for the 5V to 3.3V conversion, maybe a diode will be enough). For the 3.3V to 5V conversion, another mosfet is required (that's connected to the 5V power domain on its drain/source and 3.3V on its gate). In the end, you'd be better to use a bidirectional level shifter chip that's not based on a single transistor, it'll be cheaper and easier.
If you invert the state, if you find low, someone is transmitting something, and, obviously, you would need to drive the bus low to transmit a 1, but, who is pulling up the line? A master? Everyone? And how hard is to pull it down? And how far it will be detected as HIGH or LOW?
The high voltage is the only one needing to have a pull up resistor. It can be a single device or any device on the high voltage power domain. Any device on the low voltage power domain will need to skip the pull up (forbidden). The current to sink will be high obviously, but I doubt it's an issue, if you keep a single pull up resistor/source. The issue is with the high voltage when the low voltage domain is not pulling down to send a 1. It'll be too large to use without a series resistor on the bus and a diode to limit the voltage on the pin (or an external mosfet to isolate the line). You'll need one diode or mosfet for the whole low voltage power domain, not one per device.
This will work because the low level logic threshold of 3.3V CMOS is the same as the low level of 5V CMOS (Vil ~= 0.8V), so it'll be detected on all power domain. It would even work with 1.8V CMOS see here.
So it's not as simple as PJDL is currently, but it's very close.
I have found this: https://i.stack.imgur.com/Dqnbp.png
I think @nuxeh approach may work, probably not with a huge range but it might work
According to the datasheet of the ATmega328, if the signal is more than 2.6v is considered 1 until goes below 2.2v, if the signal is low or below 2.2v, it will be considered low until goes above 2.6v.
So why this should not work?
That's what I said earlier. It might (will) work for many chips that are more tolerant to input voltage like the ATmega328. Yet the "official" level are like this:
Where 3.3V isn't enough for triggering a logic one. So, said differently, YMMV, this solution isn't a generic one (and it won't work for a 1.8V chip for example)
The part of the Atmel's datasheet for electrical characteristics is here:
where it says that a input high (Vih) must be higher than 0.6*Vcc, so it's 3.0V for a 5V VCC, not 2.6V as you've written (except for the Reset PIN and XTAL which is our case here where it's 3.5V).
@X-Ryl669 Just to be curious, which 5V boards are you using that are CMOS based?
I appreciate that you are working on a general solution, not just something that works for 5V Arduinos vs 3.3V devices.
For 5V, I use mostly Arduinos, and 3.3V is more than enough to pull an input high, so I cannot see why a simple solution should not work between these voltages for that setup. Only shifting 5V down, not shifting 3.3V up. I tested the threshold on an Arduino Uno, and it conforms to the 5V TTL bar of your diagram, going high when above 2.4V.
I'm not using many 5V chip anymore. I still have systems with PIC16F18xx with their very high input threshold (Vih = 0.8 * VDD), which is even above the CMOS level. Nowadays, most µcontroller are 3.3V or 1.8V anyway.
Honestly, I agree that using a Schottky diode will work most of the time. Yet, from a pure engineering work you can't promote this solution, it'll fail for some 5V chips if you rely on 2.4 or 2.6V (since the datasheet says 3V, you can't design a ystem for less than that). As we say at work, "it failed successfully", it's a situation where something should have failed, but it worked and you don't know why.
Using a level shifter will work 100% of the time, so I think we should advice this solution even if it's a bit more costly.
With no offense, I think we are going a little out of scope, I don't think PJDL has ever been tried on a CMOS, and is generally applied by amateurs for educational/experimental applications on arduino compatible boards. IIUC the scope of this particular issue is to let an ESP32 speak with a network of Arduino boards operating at 5v. Although I appreciate and, from an educational standpoint, find very interesting your contribution @X-Ryl669, let's not forget that "premature optimization is the root of all evil".
Just to be clear, I don't think I am competent enough to suggest any setup for real world use, if you are just experimenting for fun, the approach proposed by @nuxeh or earlier by @alastaira may be good enough, I agree that a proper level shifter is the best approach for real world use.
@alastaira Regarding the ESP 3.3 vs 5 volts, have you tried a simple voltage divider with two resistors? It seems that should work, with a 10k resistor between ground and the 3.3V bus and then a 5.1k resistor between the buses. Or perhaps 100k and 51k. That should let the 5V bus deliver 5V*(10/15.1) = 3.31V to the 3.3V bus when the 5V bus is high, and deliver 3.3V to the 5V bus when the 3.3V bus is high. Extremely simple design with passive linear components only. I will test when I get the time.
Thanks for the interesting conversation, one and all. I don't really have much to add, except, yes - I am using AVR so have had Vih= Vcc * 0.6 as an assumption which presumably should be fine... still need to test it though, but have been distracted.
Also, i will say, @gioblu that I have wrapped my head around transistor driver circuits for this case without direction control, and it seems a little tricky that whatever drive circuit you have it will, it seems to me, inescapably end up feeding its output back to its input. Which to me seems like it could be problematic. You mention PJDL sync which seems to make sense, but propagation delays, driving the bus from multiple points, reflections on the bus all come to mind.
Also, auto direction control chips like the TI TXB* chips do seem like a solution which seems promising at first, but I think they could have issues in this application, specifically their sensitivity to capacitance, their design for short - even on a PCB scale - lines/traces, high input current drive requirements, and timing issues - they contain one shot detectors for direction detection. Soo, yeah, would be interesting to try, but personally have my doubts as to whether they are a good fit for the application.