-
Analog Prototype
02/09/2019 at 20:45 • 1 commentThe prototype of the analog joystick shield is now assembled.
I also modified the original firmware to return additional four bytes for the positions of the sticks:
---------- more ----------#include "USI_TWI_Slave/USI_TWI_Slave.c" #define BUTTONS_COUNT 4 #define JOY_COUNT 4 #define INT_PIN 5 #define I2C_ADDRESS 0x11 const uint8_t button_pins[BUTTONS_COUNT] = {8, 2, 9, 10}; const uint8_t joy_pins[JOY_COUNT] = {0, 1, 7, 3}; volatile bool clear = false; volatile uint8_t buttons = 0; volatile uint8_t joy_values[JOY_COUNT] = {127, 127, 127, 127}; void request() { USI_TWI_Transmit_Byte(buttons); for (uint8_t i = 0; i < JOY_COUNT; ++i) { USI_TWI_Transmit_Byte(joy_values[i]); } clear = true; } void setup() { for (uint8_t i = 0; i < BUTTONS_COUNT; ++i) { pinMode(button_pins[i], INPUT_PULLUP); } pinMode(INT_PIN, INPUT); USI_TWI_Slave_Initialise(I2C_ADDRESS); USI_TWI_On_Slave_Transmit = request; } void loop() { static uint8_t last_buttons = 0; static bool interrupt = false; uint8_t current_buttons = 0; for (uint8_t i = 0; i < BUTTONS_COUNT; ++i) { current_buttons <<= 1; current_buttons |= !digitalRead(button_pins[i]); } if (clear) { Flush_TWI_Buffers(); clear = false; buttons = 0; if (interrupt) { pinMode(INT_PIN, INPUT); interrupt = false; } } buttons |= last_buttons & current_buttons; if (buttons && !interrupt) { interrupt = true; pinMode(INT_PIN, OUTPUT); } last_buttons = current_buttons; for (uint8_t i = 0; i < JOY_COUNT; ++i) { joy_values[i] = (analogRead(joy_pins[i]) >> 2) & 0xff; } delay(16); }
I throw away 2 bits worth of precision here, because they are mostly garbage anyways, and because it makes it easier to transmit. Initial test looks good:
>>> import machine >>> i2c = machine.I2C(-1, machine.Pin(5), machine.Pin(4)) >>> i2c.readfrom(11, 5) b'\x00y\xff\x8a\x88'
But there seems to be a bit of a problem: the right stick returns funky values — the first one is too low, and the second one is always 0xff. A little bit of investigation, and I found the culprit — I didn't connect the ground of that side of the board — both the stick and the button — to the ground of the rest. Nothing a quick bodge can't fix — I added a wire, and now everything works perfectly.
-
Analog PCBs
02/07/2019 at 21:44 • 0 commentsThe PCBs for the analog joysticks arrived while I was away, and they look good so far:
I will probably need to add some kapton tape on the metal back of the joysticks, so that they don't short the vias/buttons, but that's just a detail (and I could do tented vias anyways, was just too lazy).
Next step is assembling this and modifying the firmware for the attiny24.
-
Analog Joysticks
01/12/2019 at 23:02 • 4 commentsI'm very happy with the version 6.0 and don't plan to make any further changes, at least for now. It's perfect both for user interfaces and for games. However, there is one more use case I have in mind, and that is controlling mobile robots. For that analog joysticks would be much better. So I sat down and designed another version, this time with two thumb joysticks, two shoulder buttons, and two buttons on the side for switching modes:
I kept the size the same, but this time I centered the board (the d-pad version is a bit shifted to make room for the four fire buttons). Also, since the thumb sticks are so big, I had to put the shoulder buttons on the back of the board, where the microcontroller is:
Four analog pins of the AtTiny24 are enough to handle the joystick, and the remaining four are for the buttons — again I didn't do anything fancy. I kept the interrupt pin and the two footprints for displays — a robot controller could use this for telemetry, menus, or even camera image (but that would probably require an esp32 board).
Of course the final version will have rounded corners. I should have the boards in a few weeks, then I will adapt the code to send not only the button status, but also the four analog measurements.
-
Version 6.0
05/17/2018 at 09:22 • 4 commentsAfter using an ATtiny24 for handling buttons in #Micro:Boy I decided that this is also the way to go with the X-Pad shield. I designed version 5.0 which used the same buttons and layout as 4.0, but a different chip, and I tested the firmware with it. Turns out that it works very well, and does de-bouncing and buffering for you, so that you don't have to poll the chip frantically for presses, afraid that you miss any. All button presses now wait nicely in the buffer until you read them. The shield looked like this:
But then I have some problems with the shields I sold on Tindie (due to poor solder joints on the buttons), and I removed the shield from my store. I decided to work on the design a little bit more and make a version that will really be the best I can make. Behold, version 6.0:
It's bigger than the original X-Pad shield, and has mounting holes. It also has all the components, except buttons, on the back, and away from the middle part of the board — so it can lie flat against the D1 Mini and other shields. It also has friction-fit headers, so no soldering is necessary and you can add it to another shield easily, like this:
There are also two headers for popular display modules. One for the 128×128 ST7735R module (you might need to trim that corner):
And one for the popular SPI-based OLED modules, such as SSD1331 or SSD1306:
I still need to add some last finishing touches to the firmware and test it all properly, and it's going into my Tindie store.
-
Version 3.0
01/25/2018 at 20:41 • 0 commentsI have been working a little on this project in the recent months, designing a new PCB using different parts and so on, but I didn't really have time to assemble and test it properly. Recently I found the PCBs in a drawer, found the matching parts, and decided to give it a go. Behold, version 3.0:
and the back side:
---------- more ----------I'm using a much smaller PCA9554 here, which has pretty much the same I²C interface. I also put all the parts on the back, so that you can stack this board with the display more tightly.
Just like version 2.0, it has friction-fit pin headers, so you don't even need to solder anything. As a bonus, I added a header for a SSD1331 display module, which you can simply plug in there. Only one problem, the module is too wide and covers some of the buttons.
I also used different buttons — initially I wanted to use the same kind that is on the Pro Mini boards for reset, but then realized that the "quiet" ALPS buttons fit that footprint too. Well, they almost fit — they are a bit too wide, and stick a little bit out on the D-pad side. Oh well.
Overall, I don't think the improvements are worth rolling a new version on Tindie — and I still have a bunch of version 2.0 ones to sell. However, I'm already thinking about version 4.0, which will be bigger, have footprints for multiple different display modules, and will use an ATtiny24 to do de-bouncing and buffering, just like on #Micro:Boy. It can take a while to finalize that one, though.
-
Example Code
10/11/2017 at 18:13 • 0 commentsThere has been some questions as to how to actually handle this shield in the code, so I'm providing some simple examples:
MicroPython
from machine import I2C, Pin import time i2c = I2C(sda=Pin(4), scl=Pin(5)) while True: print(hex(i2c.readfrom(0x20, 1)[0])) time.sleep(1)
NodeMCU
i2c.setup(0, 1, 2, i2c.SLOW) tmr.alarm(0, 1000, 1, function () i2c.start(0) i2c.address(0, 30, i2c.RECEIVER) print(string.toHex(i2c.read(0, 1))) i2c.stop(0) end)
Arduino
#include <Wire.h> void setup() { Wire.begin(); Serial.begin(); } void loop() { Wire.requestFrom(0x20, 1); Serial.print(Wire.read(), HEX); delay(1000); }
All of the above examples scan the buttons once a second and print their state as a hexadecimal number on the serial console.
-
Buy it on Tindie
10/09/2017 at 15:06 • 0 commentsI have added this to my shop at Tindie, so you can now buy it: https://www.tindie.com/products/deshipu/x-pad-20-buttons-shield-for-d1-mini/
It's the cheapest shield I have made so far. I am not entirely happy with it yet (the buttons are too small and I should have put the chip on the underside), but it's actually very useful, so why not let people get it. It goes well together with the official OLED shield:
-
Pull-ups? We need no @#$@$ pull-ups!
09/28/2017 at 09:50 • 3 commentsTurns out that I have over-engineered this shield a little bit — I wanted to make sure the pins are pulled up when the buttons are not pressed, so I added a pull-up resistor to each button. But looking at the #LAMEBOY - another ESP12 handheld project, I noticed that @davedarko didn't use any pull-ups, and it works reliably for him. We dived into the datasheet a bit and did some experiments, and it turns out you really don't need them. So now I'm leaving the pads for the pull-up resistors un-populated. Perhaps I will design another version of the PCB, with the chip on the bottom side and without any pull-ups whatsoever, but for now this simplified second version works well for me.
-
New Version Assembled
09/07/2017 at 11:41 • 1 commentIt took me a while to assemble this, because by mistake I used 4x4mm button footprints instead of the 5x5mm, so I had to order matching switches. But it's there, and it's working. Here's a comparison photo with the old version:
The friction-fit alternating pins work really well, and make the whole thing much smaller. The chip, even though SMD, is still pretty huge — I'd love to find a small, preferably QFN, chip that can handle buttons over I²C, but I'm too lazy to look for it specially. The buttons are much more convenient than the old "joystick", even though they are still not very friendly to your fingertips.
-
Upgrade
08/03/2017 at 10:40 • 0 commentsI went ahead and upgraded the design of this shield a little bit. It now uses all SMD components (including the PCA8574 chip), has all parts on one side, and uses buttons in place of that tiny joystick thing. Oh, and I dropped the address selection jumpers, since you are not going to have more than one of this on a single dev board anyways.
The pin headers now are staggered for a pressure fit, so that you should be able to put this between your dev board and a display shield without additional headers increasing the height.