Volume knob on custom keyboard?

User avatar
Plasmodium

10 Oct 2015, 16:54

Does anyone have any experience adding a volume knob to a Teensy-powered keyboard?

I've done a little reading, and on the hardware side, it seems you could use a rotary encoder or a potentiometer. I'm a bit of an electronics noob, so...what are those and which would be better?

Secondly, on the software side, do any of the pre-built Teensy-compatible keyboard firmware projects have support for a volume knob built in? Or in this case (see below for specifics) would it be better just to write code from scratch?

Essentially, I'm thinking of making a little media controller with the leftover aluminium, switches, LEDs, spare Teensy, etc I will have from my home-made keyboard project!
If I can get it all figured out, it will hopefully include ~4-6 media keys, a volume knob, an audio switch (literally just a DPDT switch so I can switch between headphones and speakers without unplugging and re-plugging - not connected to the Teensy at all) and a USB hub (re-use the innards of a commercial one). The volume knob is the only bit yet I don't have figured out...

User avatar
Muirium
µ

10 Oct 2015, 17:03

Definitely doable.

The Teensy firmware I use all the time — Soarer's Controller — can take input from direct pins on the Teensy. You could use a volume knob that sends pulses (one pin for up and one pin for down I assume), and map them to volume up and volume down media keys, without any actual coding. Ditto for whatever functions you'd like your other switches to do. So long as a standard USB keyboard has keys for the functions you want.

If you like C, you can try Hasu's TMK. I literally do not understand it (you must edit source code) but those who do think it's great.

Findecanor

10 Oct 2015, 17:19

I have only thought about the problem and not built anything yet, but....

Do select a rotary encoder. Potentiometers give absolute readings and the USB HID standard supports only relative controls. I don't know of any firmware that does it, though. I have done some USB HID programming so maybe I could help you with modifying the source code of a firmware of your choosing.

It seems to me as if polling the encoder once every ms or so would be more than sufficient. You could poll the encoder in the same loop that you would use for scanning the keyboard matrix.
There is a lot of info on the web for using rotary encoders and Arduino - but the majority of that is for the purpose of controlling electric motors used in robotics, and they have other requirements than an input device. With an input device, the user is also in the loop, and the user gets some kind of feedback that he/she can use to adjust his speed.

Rotary encoders do not give a pulse for up and a pulse for down: they give off two pulse trains that are out of phase with one-another. However, thinking of pulses out of phases just gives me a headache. I find it easier to think of them as producing 2-bit Gray codes - which is actually the exact same thing.
In case you would find a 3-signal or 4-signal rotary encoder then that should also produce Gray code - If not, then it is not a rotary encoder but a rotary switch.

User avatar
Plasmodium

10 Oct 2015, 17:26

Muirium:
That's great - so you'd recommend Soarer's Controller for a full board as well? I had been planning to use the one in Matt3o's guide (Hasu's, I think?), but I'll have a look at Soarer's if you think it's more convenient.

Do you know if you can control the increments in which the volume goes up? Because the built in hotkeys on my laptop keyboard do 8% at a time (well, specifically, it goes from 0%-12% with the first press and 8% on subsequent presses) which is too much, whereas my current mech does 2% at a time via the hotkey (about right, really).

Also, how OS-specific are the media key functions with this software? do they send a universally 'understood' signal? It's just I use Linux and Windows, so it'd be nice for it to be consistent on both!

Findecanor:
Thanks for the detailed explanation and offer of help! I probably won't be tackling this for a month or so (after I'm done with my main keyboard project), but I may take you up on the offer then!
I was looking at something like this: (just what I found when I searched eBay for rotary encoder earlier)
rotaryencoder.JPG
rotaryencoder.JPG (38.88 KiB) Viewed 18710 times
Would that work? If not, what sort of thing should I be looking at?

Findecanor

10 Oct 2015, 17:44

Soarer's code is closed-source and Soarer himself hasn't been active on the board for months from what I have heard. :(

The key codes for media keys are in the USB spec, but how much a volume step is is defined in the software on the host.
The firmware does need specific support for media keys because they are most often sent separate from regular keys. Hasu's TMK firmware does have support.

I'm sorry I can't tell anything about that encoder from just that image. I would need more info.

User avatar
Plasmodium

10 Oct 2015, 18:07

Findecanor wrote: I'm sorry I can't tell anything about that encoder from just that image. I would need more info.
Fair enough - this is the listing: http://www.ebay.co.uk/itm/1Pc-KY-040-Ro ... 5d4b76706c
There are plenty of similar-looking ones on ebay, this one just caught my eye because of the PCB mount.

User avatar
Plasmodium

10 Oct 2015, 18:42

Findecanor wrote: Soarer's code is closed-source and Soarer himself hasn't been active on the board for months from what I have heard. :(

The key codes for media keys are in the USB spec, but how much a volume step is is defined in the software on the host.
The firmware does need specific support for media keys because they are most often sent separate from regular keys. Hasu's TMK firmware does have support.
From what I've read, Soarer's code has the benefit of easy config, but the cons of being abandonware and lacking in certain advanced config options. Hasu's is more complex to set up (but has a fairly comprehensive guide, right?) and has more advanced features. Is this an accurate assessment? Are there any other firmware options?

Two of my housemates are engineers with a decent level of proficiency in C, so I may go with Hasu's for my main board and get help from them/the forum with that.
Does Hasu's support input straight from the pins like Muirium was saying about Soarer's? If not, it may be easier to use Soarer's for my little media control box, as it won't need any of the advanced features really.

malcomkern

10 Oct 2015, 19:04

Interesting thread, I will keep an eye on this because this is a project what is in my head for some time but I lack the overall knowledge to make it happen. I was looking at a Griffin Powermate but missed the hardware options you describe here. Headphone jack - Speaker output switch. USB hub and dedicated mediakeys.

User avatar
Muirium
µ

10 Oct 2015, 19:20

Soarer's Controller does volume control keys. I use Fn and +/- for those on all my old mechs thanks to this!

Soarer himself has been gone for about a year now I think. We've no idea what happened to him. Fortunately, his software is pretty much feature complete and rock solid. Hasu's can certainly be extended and modified in ways that Soarer's cannot, but I push it quite hard and never have a problem. It's certainly the one I feel more comfortable with, myself.

Bad news however that volume twiddlers don't work the way I thought. You might be stuck writing something yourself, built on top of Hasu's TMK because of that.

User avatar
Plasmodium

10 Oct 2015, 19:36

malcomkern wrote: Interesting thread, I will keep an eye on this because this is a project what is in my head for some time but I lack the overall knowledge to make it happen. I was looking at a Griffin Powermate but missed the hardware options you describe here. Headphone jack - Speaker output switch. USB hub and dedicated mediakeys.
Thanks! Like I said, it might be a month or so before I make any real visible progress on this, but I'll try to remember to update it when I do!

I found this about doing basically what I want to do but with a Pro Trinket (some sort of Arduino, I think). Does anyone know how hard would that code be to convert to run on a Teensy?

DaGameFace

10 Oct 2015, 20:24

http://hackaday.com/2015/01/17/dial-is- ... ontroller/

This guy made a wireless volume up/down song skip, play and pause arduino powered knob

User avatar
Plasmodium

16 May 2016, 13:52

I'm going to finally bring this back up again, as I'm finally in a position to start looking at this again.

First of all, how easy would it be to 'port' this to a Teensy: https://learn.adafruit.com/pro-trinket- ... me-control
It seems to do basically what I want, so no point re-inventing the wheel if I don't have to.

Secondly, how hard would it be to add media keys to that code? It doesn't seem worth having a matrix, as all I'd need is about 4 switches (play/pause, skip forward, skip back, maybe lock PC/sleep), thus I wouldn't need TMK or Soarer's etc.

Thanks in advance.

User avatar
Ray

16 May 2016, 19:32

About runnig that code on a teensy:
https://www.pjrc.com/teensy/ wrote:Key Features:
Compatible with Arduino Software & Libraries
...
So I guess it is not a problem at all.

You could also use a pro trinket, it is cheaper than the teensy and should do that job.

About more multimedia keys: that's a non-issue really as well. If you go through the code on your link, you will find a line that reads

Code: Select all

TrinketHidCombo.pressMultimediaKey(MMKEY_MUTE);
Exchange MMKEY_MUTE for what you are looking for.
That function does the whole button press and release thing, so if you want it like with better keyboard firmware, a keydown event when pressed and a keyup when released you would have to change that. The example code on adafruit is really the quick'n'dirty solution here. But it should do the job for you unless you want to hold the skip buttons pressed.

mohitgarg

16 May 2016, 21:23

This is interesting, as I have the option of adding a rotary encoder in two of my projects. The hardware side is in place, but I still need to add support to TMK and/or Easy AVR.

User avatar
Plasmodium

16 May 2016, 22:18

Thanks for the reply, Ray. I said Teensy simply because I happen to have a couple lying around, otherwise I would use a cheaper board.

So I guess I would copy that code and just change the names of the pins to the relevant ones on the teensy. To add extra media keys in, presumably I would copy & paste the whole IF statement section that handles the mute button, but changing the pins and keycodes?

Last time I properly did any coding was 4 years ago in high school! So I sort of know what I'm doing generally, but still need a bit of help with the ins and outs!

User avatar
Plasmodium

04 Jun 2016, 16:10

Right. I got the easy half of this done last week - the easy half being the audio in/out switch. It was subject to a wee bit of feature creep, as it now can not only select one of two outputs, but also one of four inputs. So, I could play music at my desk without having to turn my PC on or unplug and replug the cable into my iPod. Or, for example, when I move next year I might need to put my Wii on my desk instead of in my shared lounge. I will be able to switch between audio sources easily.

Here's a crappy photo: the rotary switch does the four inputs and the toggle switch does the two outputs.
IMG_20160526_161133~2.jpg
IMG_20160526_161133~2.jpg (911.64 KiB) Viewed 18288 times
The harder bit comes this afternoon. I've got a cup of tea made and a soldering iron warming up, so I'll be trying out the rotary encoder with the Teensy. I won't be putting the media keys in until I have the box I'm going to put it in (I have a few ideas about that) because you need to push MX switches in from the front of the plate, meaning you have to solder them up after they're mounted. But you all knew that...

User avatar
Plasmodium

08 Jun 2016, 20:07

Right. Ordered a Pro Trinket and re-wired it and it's all recognised. However, turning the knob makes it jump quite randomly. Turn it clockwise and it will more or less consistently go down, turn it anticlockwise and it will hop up and down. But only every 4 or so 'clicks'. Weird...

Findecanor

08 Jun 2016, 22:27

Hmm.. If that is an electromechanical rotary encoder, then it might debouncing on each signal pin.

User avatar
Plasmodium

08 Jun 2016, 22:31

Right...and how would I do that? Software or hardware change? As it stands, it's all identical to the Adafruit project linked above.

User avatar
Plasmodium

26 Jun 2016, 16:53

Well. After a wee bit of googling, trial and error, fiddling, ordering bits and bobs off eBay, we have a working volume knob. Using the code from the tutorial linked to above, but a different circuit design, due to my encoder having debouncing circuits built in. Works well enough - next step: integrate those six Gaterons you can see there! Any suggestions for features? I want to use 6, but can only think of four functions as of yet: play/pause, skip track forward/back, and lock screen.
IMG_20160626_154732.jpg
IMG_20160626_154732.jpg (1.08 MiB) Viewed 18122 times
Just noticed that tea-stain under the encoder! Remind me to clean my desk soon...

User avatar
Ratfink

26 Jun 2016, 17:12

If your encoder doesn't have a built-in pushbutton, how about using one of your MX-ripoff switches as a mute key?

User avatar
Plasmodium

26 Jun 2016, 18:02

It does have a pushbutton! Might make one Gateron a discrete mute key, though, if the action of pushing on the volume knob is awkward.

[Edit]: By awkward, I mean that in pushing down on the knob, it's easy to accidentally twist it one notch, thus unmuting it.

User avatar
Plasmodium

27 Aug 2016, 14:10

Right. This project is 95% complete now! Hooray! It's now drifted a little away from its keyboard roots, but it's still an input device, and it still has some MX clone switches in it, so I thought I'd post a sort of buildlog (thanks @Phenix for reminding me!).

All that's left to do now is:
1) drill one more hole in faceplate for USB cable to exit
2) screw faceplate into main box
3) Make some nice sleeved jack to jack cables for the audio switching part!

As you can see above, my original plan was to have an audio output switcher so I could switch between headphones and speakers with the flip of a switch. I planned to include a USB volume knob and some media controls just for the heck of it. I eventually decided to add an input switcher, so I could plug my PC, iPod, phone, tablet, Wii, etc in and switch between which was outputting sound through my speakers with the twist of a dial (though my dial only has 4 positions - I'm thinking PC, Wii and two spare aux-in's depending on what portable device(s) I might have on me).

So. Here's the front of the faceplate:
IMG_20160827_125240.jpg
IMG_20160827_125240.jpg (536.48 KiB) Viewed 17975 times
The four jacks at the left are the inputs, the dial next to them is a rotary switch with 4 positions. The wires then run to the switch in the middle, which selects between either of the jacks below (outputs).

The right dial is volume, and is connected to a rotary encoder which in turn is connected to an Arduino Pro Trinket, as are 4 of the switches to the right (I only wired up 4 because I couldn't think of uses for the other two and I'd run out of those little jumper cables. But I can always wire them up as and when I get ideas for them!). The switches (top left to bottom right) do the following:
1) Lock - this sends the Windows 'lock screen' key combo (Win+L). I set up a custom keyboard shortcut in Linux so it also works on my dualboot setup.
2) Play/pause media
3) Skip to previous track
4) Skip forwards

That's it on the hardware side really. Here's a picture of the wiring, though, spaghetti-ish though it may be:
IMG_20160827_125257.jpg
IMG_20160827_125257.jpg (880.74 KiB) Viewed 17975 times
The faceplate is made of 1.5mm plywood stuff (laser cut-able, though I just used a hand drill) and the box...wait for it...is an old military surplus ammo crate. It's the perfect size to use as a monitor stand! I did consider repainting it so it didn't look so 'military', but I quite like the vintage look it has with the big dials and toggle switch etc.
IMG_20160827_125321.jpg
IMG_20160827_125321.jpg (700.6 KiB) Viewed 17975 times
The blocks of wood are glued in with Gorilla Glue (my brother swears by the stuff, but we'll see how that holds up) and the faceplate will screw directly into them. It's slightly recessed (by about 30mm) so the lid can still be attached if I want. (The box is actually waterproof, which is kind of cool. Not that I need that feature, but still kind of cool).

I'll post pictures once the thing is finally assembled and I have my nice matching cables.
(Oh yeah, the code. That's on my Windows partition, so I'll go dig it out, tidy it up and post it)
Last edited by Plasmodium on 27 Aug 2016, 15:14, edited 1 time in total.

User avatar
Plasmodium

27 Aug 2016, 14:25

Most of my code I took from here: https://learn.adafruit.com/pro-trinket- ... r/overview
I added 4 more buttons by duplicating the code where pushing on the encoder mutes/unmutes. I could possibly have done it more efficiently, but I had struggled for ages with annoying debouncing issues and phantom keypresses (which turned out to be a hardware issue) and this way just worked.

Code: Select all

/**************************************************************************/
/*!
 * Original Intro:
 * 
    @file     ProTrinketVolumeKnobPlus.ino
    @author   Mike Barela for Adafruit Industries
    @license  MIT

    This is an example of using the Adafruit Pro Trinket with a rotary
    encoder as a USB HID Device.  Turning the knob controls the sound on 
    a multimedia computer, pressing the knob mutes/unmutes the sound

    Adafruit invests time and resources providing this open source code,
    please support Adafruit and open-source hardware by purchasing
    products from Adafruit!

    @section  HISTORY

    v1.0  - First release 1/26/2015  Mike Barela based on code by Frank Zhou
*/

 /*My code is mostly copied from the above project with the addition of four more pushbuttons
/**************************************************************************/

#include <ProTrinketHidCombo.h>

#define PIN_ENCODER_A      4
#define PIN_ENCODER_B      5
#define TRINKET_PINx       PIND
#define PIN_ENCODER_SWITCH 3
#define PIN_SWITCH_A      9
#define PIN_SWITCH_B      13 //Pin 10 was a bit dodgy and firing off inputs whenever the nearby pins were used (some kind of short?) so I had to move this one up a bit. Forgot pin 13 was the LED pin. Oh well, still works...
#define PIN_SWITCH_C      11
#define PIN_SWITCH_D      12

static uint8_t enc_prev_pos   = 0;
static uint8_t enc_flags      = 0;
static char    sw_was_pressed = 0;
static char    swa_was_pressed = 0;
static char    swb_was_pressed = 0;
static char    swc_was_pressed = 0;
static char    swd_was_pressed = 0;

void setup()
{
  // set pins as input with internal pull-up resistors enabled
  pinMode(PIN_ENCODER_A, INPUT_PULLUP);
  pinMode(PIN_ENCODER_B, INPUT_PULLUP);
  pinMode(PIN_ENCODER_SWITCH, INPUT_PULLUP);
  pinMode(PIN_SWITCH_A, INPUT_PULLUP);
  pinMode(PIN_SWITCH_B, INPUT_PULLUP);
  pinMode(PIN_SWITCH_C, INPUT_PULLUP);
  pinMode(PIN_SWITCH_D, INPUT_PULLUP);

  TrinketHidCombo.begin(); // start the USB device engine and enumerate

  // get an initial reading on the encoder pins
  if (digitalRead(PIN_ENCODER_A) == LOW) {
    enc_prev_pos |= (1 << 0);
  }
  if (digitalRead(PIN_ENCODER_B) == LOW) {
    enc_prev_pos |= (1 << 1);
  }
}

void loop()
{
  
  int8_t enc_action = 0; // 1 or -1 if moved, sign is direction

  // note: for better performance, the code will use
  // direct port access techniques
  // http://www.arduino.cc/en/Reference/PortManipulation
  uint8_t enc_cur_pos = 0;
  // read in the encoder state first
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_A)) {
    enc_cur_pos |= (1 << 0);
  }
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_B)) {
    enc_cur_pos |= (1 << 1);
  }

  // if any rotation at all
  if (enc_cur_pos != enc_prev_pos)
  {
    if (enc_prev_pos == 0x00)
    {
      // this is the first edge
      if (enc_cur_pos == 0x01) {
        enc_flags |= (1 << 0);
      }
      else if (enc_cur_pos == 0x02) {
        enc_flags |= (1 << 1);
      }
    }

    if (enc_cur_pos == 0x03)
    {
      // this is when the encoder is in the middle of a "step"
      enc_flags |= (1 << 4);
    }
    else if (enc_cur_pos == 0x00)
    {
      // this is the final edge
      if (enc_prev_pos == 0x02) {
        enc_flags |= (1 << 2);
      }
      else if (enc_prev_pos == 0x01) {
        enc_flags |= (1 << 3);
      }

      // check the first and last edge
      // or maybe one edge is missing, if missing then require the middle state
      // this will reject bounces and false movements
      if (bit_is_set(enc_flags, 0) && (bit_is_set(enc_flags, 2) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 2) && (bit_is_set(enc_flags, 0) || bit_is_set(enc_flags, 4))) {
        enc_action = 1;
      }
      else if (bit_is_set(enc_flags, 1) && (bit_is_set(enc_flags, 3) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }
      else if (bit_is_set(enc_flags, 3) && (bit_is_set(enc_flags, 1) || bit_is_set(enc_flags, 4))) {
        enc_action = -1;
      }

      enc_flags = 0; // reset for next time
    }
  }

  enc_prev_pos = enc_cur_pos;

  if (enc_action > 0) {
    TrinketHidCombo.pressMultimediaKey(MMKEY_VOL_UP);  // Clockwise, send multimedia volume up
  }
  else if (enc_action < 0) {
    TrinketHidCombo.pressMultimediaKey(MMKEY_VOL_DOWN); // Counterclockwise, is multimedia volume down
  }

  // remember that the switch is active low 
  if (bit_is_clear(TRINKET_PINx, PIN_ENCODER_SWITCH)) 
  {
    if (sw_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_MUTE); // Encoder pushed down, toggle mute or not
      delay(5); // debounce delay
    }
    sw_was_pressed = 1;
  }
  else
  {
    if (sw_was_pressed != 0) {
      delay(5); // debounce delay
    }
    sw_was_pressed = 0;
  }

  if (digitalRead(PIN_SWITCH_A) == LOW) 
  {
    if (swa_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_PLAYPAUSE ); // Play/pause
      delay(5); // debounce delay
    }
    swa_was_pressed = 1;
  }
  else
  {
    if (swa_was_pressed != 0) {
      delay(5); // debounce delay
    }
    swa_was_pressed = 0;
  }

 if (digitalRead(PIN_SWITCH_B) == LOW) 
  {
    if (swb_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressKey(KEYCODE_MOD_LEFT_GUI, KEYCODE_L); // Lock screen combo for Windows - set up custom keyboard actions for other OSes
      TrinketHidCombo.pressKey(0, 0); // This is important - otherwise the device will keep Win+L held down and weird stuff happens (like accessibility options randomly opening on the lock screen)
      delay(5); // debounce delay
    }
    swb_was_pressed = 1;
  }
  else
  {
    if (swb_was_pressed != 0) {
      delay(5); // debounce delay
    }
    swb_was_pressed = 0;
  }

 if (digitalRead(PIN_SWITCH_C) == LOW) 
  {
    if (swc_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_PREV_TRACK); // Previous track
      delay(5); // debounce delay
    }
    swc_was_pressed = 1;
  }
  else
  {
    if (swc_was_pressed != 0) {
      delay(5); // debounce delay
    }
    swc_was_pressed = 0;
  }

 if (digitalRead(PIN_SWITCH_D) == LOW) 
  {
    if (swd_was_pressed == 0) // only on initial press, so the keystroke is not repeated while the button is held down
    {
      TrinketHidCombo.pressMultimediaKey(MMKEY_SCAN_NEXT_TRACK); // Skip track
      delay(5); // debounce delay
    }
    swd_was_pressed = 1;
  }
  else
  {
    if (swd_was_pressed != 0) {
      delay(5); // debounce delay
    }
    swd_was_pressed = 0;
  }
  
  TrinketHidCombo.poll(); // check if USB needs anything done, do every 10 ms or so
}
Also note that I had to wire it up a wee bit differently to the tutorial, because my encoder had built-in debouncing circuits. Just had to connect the "+" pin to the 3.3v on the Arduino. (Whereas the tutorial uses no input current, just the pullup resistors. You can probably tell I'm a bit of a noob at this, this took me forever to figure out...)

User avatar
Ray

27 Aug 2016, 20:21

Nice to see a finished project. And not too shabby at that. I always like ammo-boxes as project boxes and the fact that you didn't need to drill into it and keep it waterproof is also nice. Totally unnecessary, but indeed kinda cool ;)

User avatar
Phenix
-p

27 Aug 2016, 21:15

this looks really good, congrats!
Badly I don't feel like I could do that, eventough I would really like to build some rotary dials in an Model F122..

User avatar
Plasmodium

27 Aug 2016, 23:39

Ray wrote: Nice to see a finished project. And not too shabby at that. I always like ammo-boxes as project boxes and the fact that you didn't need to drill into it and keep it waterproof is also nice. Totally unnecessary, but indeed kinda cool ;)
Thanks for the kind words! Not quite finished yet, though. :) Maybe tomorrow afternoon I'll find the time to do the last few little tasks!
Phenix wrote: this looks really good, congrats!
Badly I don't feel like I could do that, eventough I would really like to build some rotary dials in an Model F122..
It's not too bad! Especially if you use the jumper/breadboard wires, you don't have to solder anything permanently (so you don't have to worry about making mistakes!).

User avatar
Phenix
-p

27 Aug 2016, 23:43

I will look into the code more closely, the soldering is not the hardest part imo

User avatar
Plasmodium

28 Aug 2016, 14:57

Phenix wrote: I will look into the code more closely, the soldering is not the hardest part imo
Well, you don't really need to understand the code as long as it works, right? ;)

User avatar
Phenix
-p

28 Aug 2016, 21:38

Well, I like it to understand the code because then I can clearly see which points need to be wired where..

Post Reply

Return to “Workshop”