AT/Ps2 keyboard is absolutely bi-directional. XT keyboard is unidirectional:
The PS/2 Keyboard Interface
http://web.archive.org/web/20170808001304if_/http://www.computer-engineering.org/ps2keyboard/The PS/2 Mouse/Keyboard Protocol
http://web.archive.org/web/20170806153007/http://www.computer-engineering.org/ps2protocol/Thanks for the suggestions of using various level shifter chips. I do have TSX0108 chips, but to be honest I have had poor luck using them. My dad is an old salty EE type with a huge horde of basic electronics parts like I normally use for simple projects, so the ZVN3306A transistors likely came along in the bag with a request for something else. Parts for free beats ordering and waiting. Yes I have to build stuff but that's available off the shelf, but soldering up few transistors and resistors is not a big deal to me.
I would like to bring this thread to closure. The ZVN3306A level shifter appears to work just fine. I actually figured out what my problem was. It is in fact a hardware problem, but it's not my hardware problem.
(1) Hooking the ZVN3306A-based level shifter up to a keyboard and hooking to my scope, I see only clock and data when the keyboard generates clock pulses
(2) Using a Raspberry PI as a logic analzyer (piscipe) which has up to 1us resolution I can see the clock and data just fine and actually decode the data coming from the keyboard.
This leads me to believe that the ZVN3306A-based level shifter is working well enough.
Loading the software on the ESP32 board, either this one
https://github.com/PaulStoffregen/PS2Keyboard or this one
https://github.com/techpaul/PS2KeyAdvanced, I get good key presses most of the time, but sometimes there are glitches. Again my test results above suggest there are no glitches coming from the keyboard or the level shifter.
I went over to my dad's place and grabbed some 2N7000 transistors. I put those in, repeated all the testing above the result is exactly the same.
So, I spent an evening doing some RTFMing.
Both of those keyboard reading sketches use the same code to read the data. Ps2 data from device to host is read on the FALLING clock edge. Both software configure an IRQ to fire on the FALLING edge of the keyboard clock line, and then each time they read(or write) one bit from(to) the keyboard.
To help diagnose the glitchy behavior I added a bit of code to the sketches so that when the ISR is entered it turns a LED on and when the entire 11-bit packet is read it turns the light off.
The behavior I was observing was that mostly pressing keys the LED blinks as it is supposed to. Occasionally, it gets stuck. The LED turns on and stays on. This essentially means that that an IRQ came in when it wasn't expected -- Started reading a packet and never finished it. The code has a 250ms timeout, so if you press another key after that it will reset back to the beginning.
I'm using ESP32 here, so I did some googling and found out that there are some bugs in ESP32 handing edge trigger interrupts --
ESP32 INTERRUPT ON BOTH FALLING AND RISING EDGE
https://github.com/espressif/arduino-esp32/issues/1111#issuecomment-659694716This is starting to sound like my problem. And further, Espressif themselves acknowleges the problem in their release notes:
ESP32 ECO and Workarounds for Bugs
https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdfGiven this information, (a) according to my testing the level shifter is working well enough (b) clock in data only on FALLING clock edge (c) ESP32 hardware bugs, I can add 4 lines of code to workaround this problem:
#ifdef ESP32
if(digitalRead(PS2_IrqPin))
return;
#endif
Basically, since I know I only want falling edge, the current value of the IRQ pin when inside of the ISR routine should be low. If it happens to be high then reject that state.
That's it. Now the keyboard is being read 100%. And again in conclusion, both ZVN3306A and 2N7000 seem to work well enough for general purpose bi-directional level shifter for this simple application.