Loading [MathJax]/extensions/Safe.js

Special Aircraft Service

the SAS Hangar => The Lounge => Topic started by: SAS~Storebror on March 08, 2018, 01:18:18 AM

Title: Mike's new project
Post by: SAS~Storebror on March 08, 2018, 01:18:18 AM
...consists of some parts which will arive mostly from China during the coming month.
Wish me luck!

(https://s14.postimg.cc/6l25b714x/Geh_use.jpg)

(https://s14.postimg.cc/krhw6gu0h/Platine.jpg)

(https://s14.postimg.cc/8pmiccncx/Teensy.png)

(https://s14.postimg.cc/bwh1vxpsh/Stiftleiste.jpg)

(https://s14.postimg.cc/pq5el05j5/Poti.jpg)

(https://s14.postimg.cc/e11ex0bzl/Momentan-_Schalter.jpg)

(https://s14.postimg.cc/izoxbk2xt/Schalter.jpg)

(https://s14.postimg.cc/uoswzjh1t/Taster_blau_gelb.jpg)

(https://s14.postimg.cc/y8eupcrhd/Taster_gr_n.jpg)

(https://s14.postimg.cc/mw297kqi9/Taster_rot.jpg)

(https://s14.postimg.cc/q2wsr4qcx/Diode.jpg)

(https://s14.postimg.cc/fg2zlq5cx/Elko.jpg)

(https://s14.postimg.cc/qsfl3i6c1/Keramik-_Kondensator.jpg)

(https://s14.postimg.cc/a4o310ypt/Litze.jpg)

Cheers!
Mike
Title: Re: Mike's new project
Post by: WxTech on March 08, 2018, 01:54:08 AM
Oh, I know! I know! A controller for your Craftmatic fully motorized, massaging, vibrating bed!
Title: Re: Mike's new project
Post by: SAS~Bombsaway on March 08, 2018, 07:08:02 AM
Dont be silly. It's a time machine.
Title: Re: Mike's new project
Post by: GEORGES44 on March 08, 2018, 07:20:24 AM
Dont be silly. It's a time machine.
You are wrong!
It can only be a quantum hyperspace generator for a trans-galactic spaceship.
I thought only the CIA had the plans :D :D :D
Title: Re: Mike's new project
Post by: whistler on March 08, 2018, 07:37:40 AM
A SAS silly-post manager with pre-registered responses and topic-dumping automation? o_O We're doomed!

I thought of buying many times a panel switch but good old '46 does not support switches (which would be the main reason to get one). I mean in example: lights on/off toggles
Title: Re: Mike's new project
Post by: SAS~Bombsaway on March 08, 2018, 07:47:41 AM
Time machine.......

(https://s19.postimg.cc/kh5sbvqsj/future1-1.jpg) (https://postimg.cc/image/5l794afdr/)
Title: Re: Mike's new project
Post by: Gatrasz on March 08, 2018, 07:51:21 AM
(https://s14.postimg.cc/8p4lcenip/lightboxtest.jpg) (https://postimages.org/)

It's a lightbox for psychic power tests. It can't be wrong, I got a vision of it :D
Title: Re: Mike's new project
Post by: Blaubaer on March 08, 2018, 10:24:20 AM
What will it be? A replica or an own development?
Title: Re: Mike's new project
Post by: SAS~GJE52 on March 08, 2018, 10:30:52 AM



(https://houseofgeekery.files.wordpress.com/2015/11/classicscenecvr.jpg)
Title: Re: Mike's new project
Post by: SAS~Storebror on April 01, 2018, 01:49:53 AM
Still waiting for the last parts to arrive (board, pin strip, on/off switches), but here's the layout draft already (see attachment).
This is true size. You'll note that the layout is quite narrow, but I deem it still feasible.
Don't want to have a large box on the desk - my goal was rather to have distinctive hardware buttons and switches with labels so I don't always have to scan manuals for the right key combo for what I'm just intending to do.
The pushbuttons are about half of the drawing's diameter in real life, the ones on the layout were made larger to give an idea of the space requirement inside the casing.

By far not all of the controls have been assigned yet.
I'll have to make my way through IL-2 1946 and IL-2 Great Battles keys and axis to finally consider the right layout.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on April 11, 2018, 06:49:52 AM
Well the last part arrived yesterday, in a way.
It's been male/female board sockets for the Arduino/Teensy sub-board, 10pcs each ordered from China.
Took 5 weeks to arrive, and then only one single female socket reached the target. Dammit.
I've ordered parts from local source now, together with some extra switch diodes since the 50 I've had are not enough.

Preliminary Board layout for the meantime, created using Blackboard Circuit Designer (https://github.com/mpue/blackboard) (Freeware, Setup is in the "install" folder on GitHub)

(https://s7.postimg.cc/7ztxkgax7/Platine_v1.png)
https://www.mediafire.com/file/foj7nac9pjrpf7q/Platine2.bb

Cheers!
Mike
Title: Re: Mike's new project
Post by: Unca-Fester on April 11, 2018, 07:06:02 AM
An old employer once had me breadboard up a transcutaneous electrical simulator for his acupuncture side business.

It kind of looked a bit like Mike's project, had a bunch of mosfet quad op-amps ganged with dozens of Schottky lowpass diodes and capacitors.
Title: Re: Mike's new project
Post by: SAS~Storebror on April 11, 2018, 07:25:24 AM
I have just noticed that the Switches (On/Off and Momentary ones) of course don't need no separate rows for their two states, as there's just one center pin and just either of the states can be true for any of these at a given time, so the board becomes a tad simpler:

(https://s7.postimg.cc/mbdfpg0u3/Platine_v2.png)
https://www.mediafire.com/file/852twzxmx436aoa/Platine_v2.bb

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on April 15, 2018, 06:38:05 AM
All parts arrived, time for the mechanical works and... topping out!

(https://www.sas1946.rocks/flickr/storebror/883/40577896855_c24cbeb6ec_c.jpg) (https://www.sas1946.rocks/flickr/storebror/883/40577896855_baf88893c1_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/810/39662384960_e959c756ff_c.jpg) (https://www.sas1946.rocks/flickr/storebror/810/39662384960_80295d093e_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/896/40577895525_012582468a_c.jpg) (https://www.sas1946.rocks/flickr/storebror/896/40577895525_51f0641611_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/808/40757081634_eccd2e7c58_c.jpg) (https://www.sas1946.rocks/flickr/storebror/808/40757081634_4d7f055e34_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/866/40757080924_c11fc266a6_c.jpg) (https://www.sas1946.rocks/flickr/storebror/866/40757080924_7faf4f42c6_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/813/40757080454_4c26337f7f_c.jpg) (https://www.sas1946.rocks/flickr/storebror/813/40757080454_951cf9db25_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/805/26599196727_33860efccc_c.jpg) (https://www.sas1946.rocks/flickr/storebror/805/26599196727_d6b9a7d34f_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/889/40757078584_af90748683_c.jpg) (https://www.sas1946.rocks/flickr/storebror/889/40757078584_d839566dcd_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/872/26599194737_8047014a4f_c.jpg) (https://www.sas1946.rocks/flickr/storebror/872/26599194737_30949c54bd_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/797/40757076434_5fa8b7f589_c.jpg) (https://www.sas1946.rocks/flickr/storebror/797/40757076434_eb346df51c_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/869/26599193147_470f786cc5_c.jpg) (https://www.sas1946.rocks/flickr/storebror/869/26599193147_0d52f4e08d_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/886/40757074594_b871764570_c.jpg) (https://www.sas1946.rocks/flickr/storebror/886/40757074594_5c170c5710_o.jpg)

(https://www.sas1946.rocks/flickr/storebror/864/26599191197_82cbb09dbf_c.jpg) (https://www.sas1946.rocks/flickr/storebror/864/26599191197_f779b42f89_o.jpg)

Cheers!
Mike
Title: Re: Mike's new project
Post by: BalDaddy on April 15, 2018, 07:03:54 AM
Looks good Mike but I haven't a clue what it is meant to do :)

FYI the terms 'male' 'female' referring to fittings is deemed politically incorrect by some of the more left wing local authorities over here, and employees and sub-contractors are discouraged from using said terms.
Title: Re: Mike's new project
Post by: LameHawk on April 15, 2018, 07:46:33 AM
Does that mean that there are now transgender fittings?
Title: Re: Mike's new project
Post by: Unca-Fester on April 15, 2018, 09:24:26 AM
Just sayin'...

 (https://memegenerator.net/img/instances/66353259/switches-switches-everywhere.jpg)
Title: Re: Mike's new project
Post by: SAS~Storebror on April 15, 2018, 09:26:53 AM
I haven't a clue what it is meant to do :)
I think it's obvious that this tool is needed to remotely tickle my cat in every possible way.

the terms 'male' 'female' (...) is deemed politically incorrect
I'll let my wife socket know that her plug was instructed not to call her a female anymore.

Cheers!
Mike
Title: Re: Mike's new project
Post by: Dimlee on April 15, 2018, 11:10:51 AM
Well, there are five of this and five of that...
Five...Penta...Pentagram..

OMG, he is playing with the forces beyond.  o_O
Flightsim Universe is doomed.

(https://vignette.wikia.nocookie.net/mythsandlegends/images/c/ce/Cool.jpg/revision/latest?cb=20110602162814)
Title: Re: Mike's new project
Post by: SAS~Storebror on April 15, 2018, 11:39:18 AM
I think I'm gonna send a tweet with this box to someone with a really really big red button.
Mine might not be as big as his is, but I have plenty of them :P
Title: Re: Mike's new project
Post by: Gubi on April 15, 2018, 11:55:19 AM
Amen.

Cheers and Prost
Title: Re: Mike's new project
Post by: SAS~Ghost129er on April 15, 2018, 12:02:01 PM
I think I'm gonna send a tweet with this box to someone with a really really big red button.
Mine might not be as big as his is, but I have plenty of them :P

GO FOR IT.
*Private to public*
Title: Re: Mike's new project
Post by: Alfie Noakes on April 15, 2018, 12:08:33 PM
I knew I'd seen those switches before........

(https://s7.postimg.cc/5k8p7q5q3/tardisst--1a17.jpg) (https://postimages.org/)

(https://s7.postimg.cc/3sfqcu1sr/first-doctor-tardis.jpg) (https://postimages.org/)

(https://s7.postimg.cc/81kgf5hyj/Hartnell_Console_Layout.jpg) (https://postimages.org/)

Quote
  I think I'm gonna send a tweet with this box to someone with a really really big red button. 

You need to send a tweet to someone with a big blue box  8)

Cheers

Alfie
Title: Re: Mike's new project
Post by: Unca-Fester on April 15, 2018, 12:14:45 PM
 OK, I have no idea what you're all going on about.

But it sounds like fun.
Title: Re: Mike's new project
Post by: SAS~Storebror on April 18, 2018, 02:17:04 AM
Alright guys, time to proceed.
Today we see part of the software side.
I'm using MMjoy2 (https://github.com/MMjoy/mmjoy_en) in order to create a Game Controller Device Windows can use.
My controller is a Teensy++ 2.0 (https://www.pjrc.com/store/teensypp.html), it's important to know that MMjoy also supports other controller boards (https://github.com/MMjoy/mmjoy_en/wiki/Controllers-(compatible-base-boards)) so this setup is just a reference, if you have a different type of controller, several things might be slightly different for you.

The first thing you do in order to create a Game Controller (aka "Joystick") device is to download MMjoy2 (https://github.com/MMjoy/mmjoy_en/blob/master/firmware%20and%20software%20release/MMJoy2.7z), extract it and then run "MMJoySetup.exe".
Plug your Arduino (in my case: Teensy++ 2.0) board to a USB hub and put it into bootloader mode (in my case: Press the Reset Switch on the Teensy++ 2.0).
This will get you a "Human Interface Device" with a specific Vendor ID (in case of the Teensy++ 2.0 "16C0") and Product ID (in case of the Teensy++ 2.0 "0478") which you can select as step 1 in MMJoySetup:
(https://www.sas1946.rocks/flickr/storebror/888/27663883618_7576255b28_o.png)

Switch to the "Firmware" Tab and proceed with the Firmware file (2).
Firmware files can be found in the folder "Firmware" of MMjoy2, in case of the Teensy++ 2.0 we're using an Atmel 90USB1286 chipset, therefore the Firmware file to choose is "Firmware_lufa_[MMJOY2.AT90USB1286].hex". If you use a different board, choose the firmware that matches your board's chipset.
In the "Chip" dropdown list choose the chipset accordingly.
In the "Bootloader type" dropdown you either have to choose "Arduino" or - in case of Teensy boards like mine - Teensy. If you choose "Arduino", you likely have to set a COM port in the following box, but I have no personal experience with this - Google is your friend.

Finally, click "Upload Firmware" which should flash a DOS box briefly, and then you board will be re-attached to USB with a different ID:

(https://www.sas1946.rocks/flickr/storebror/918/39725030960_5480e8bc06_o.png)
If everything went well with the Firmware Upload, you will now find your board attached as "MMJ-reset" with VID and PID "8888" (1).
At 2 you can setup your own VID, PID and name for your Joystick Device.
The trick about VID and PID is that when multiple Game Controllers are attached to your Windows PC at boot time, they will be enumerated according to their VID and PID, meaning that if e.g. you set both values to "0001" your device will always be Joystick No.1, whereas of you turn them into "FFFF" it will always be the last joystick.

Now switch to the "Joystick axes" Tab:
(https://www.sas1946.rocks/flickr/storebror/834/27663883498_385086fdca_o.png)

"Source" (1) defines the type of Sensor. In my case I'm using simple Potentiometers, those are type "Internal".
"MCU Port" (2) defines the pin where your Sensor (here Poti) is attached.
"Assignment" (3) defines the logical Joystick Axis this Sensor should represent.
"Precision (bit)" (4) is usually "10" for Potis. For higher precision you need external A/D converters.
"Filter" (5) defines the Filter to be applied. The lower the value, the faster the reaction of your Sensor and the more distinct values you get. The higher the value, the more you can eliminate "Spikes" in the Sensor readout.
"Auto-calibration" (6) defines what it says. In my case I don't have "center" values on my Potis, so I'll let it calibrate automatically without any center applied.
Dead Zones (7) define how to deal with extreme values and changes below a certain treshold. We can discuss this separately if you need more explanations about this.

Next is the "Joystick buttons" Tab:
(https://www.sas1946.rocks/flickr/storebror/911/39725030870_838d91ed1e_o.png)

1 is the Button Matrix. I'm using an 8x10 matrix for a total of 80 buttons, consisting of 40 "real" buttons (10 of each color, red-yellow-green-blue) and 20 switches with 2 positions each.
2 is the logical Game Controller Button. You can setup max. 64 controller buttons in MMjoy2, but take care: Windows can read the first 32 of them only, the others have to be converted to keystrokes by a separate software tool - we will deal with this later.
3 is the hardware button on our button matrix.
4 is the mode. "Switch ON" means the logical button will be pressed when the hardware button is, and released if not.
5 is a mapping of hardware buttons to logical keys. I'm using this to get another 8 hardware buttons mapped, and I map them to F17-F24 cause I don't have these keys on my hardware PC keyboard so they're "free".
6 are "Coolie Hat" mappings. I'm using this for the final 8 hardware buttons and map them to 2 logical coolie hats.

When you have assigned everything correctly, it's time to safe the settings to a file (7) and then finally upload it to your board (8).

This will detach and re-attach your board to USB again, and now it turned into a game controller:
(https://www.sas1946.rocks/flickr/storebror/806/27663883408_7849def716_o.png)

If you click on "properties", you will see your axis, the first 32 buttons, and one of the coolie hats:
(https://www.sas1946.rocks/flickr/storebror/839/39725030780_1368731dc0_o.png)

That's it so far, further fine tuning, assignment of buttons 33-64 and other hardware-mappings will come later.
To be continued...

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on April 19, 2018, 08:17:18 AM
Just a small update today.
Due to lack of space and excessive wiring requirements when using the perfboard layout, I've decided to solder most parts like diodes, capacitors etc. directly to the switches, pushbuttons, potis etc.
That safes me from having to implement a perfboard at all, and instead of having to route 120+ wires in the box, I'll only need 18 of them.

This is the circuit diagram, click for scalable SVG vector graphic:
(https://storebror.it.cx/sas/switchbox_v1.png) (https://storebror.it.cx/sas/switchbox_v1.svg)

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on April 27, 2018, 06:29:13 AM
Small Update again.
Soldering parts to the upper half of the box is nearly finished.
Software is the difficult part...
Turns out that several limitations apply.
IL-2 1946 has a hard limit of 32 joystick buttons.
I've tried to overcome that limit, but it's in native code.
So I thought I could use F13-F24 function keys, but again in native code IL-2 1946 decides to ignore these.
IL-2 Great Battles has a rather irritating limit of 62 buttons (makes no sense, some say it'd be 64, but buttons 63 and 64 aren't working correctly either - from Windows point of view, the limit would be 128 like in DCS).
Function keys 13, 14 and 15 work, 16-24 don't.
Dammit.

What else?
Both games can handle up to 4 POV hat switches with up to 8 positions each - hooray!
However, IL-2 1946 throws all hats from all devices into a single list, meaning that hat no.1 on joystick no.1 is the same as hat no.1 on joystick no.2.
Dammit.
Anyway...

In the long run, I'm still planning to throw a full-featured self-contained firmware onto the device which will deal with this on it's own, but it's a PITA.
The plan would be to switch modes by pressing 4 keys simultaneously (for the sake of simplicity, it would be the 4 coloured buttons at the edge of the matrix) - this works great. I can catch this in software and switch operation modes there.
Then I'd treat the buttons according to the mode selected.
In IL-2 Great Battles Mode, the first 62 switch positions and buttons would be handed to the game as-is, the latter 18 would be handled as 3 independent 8-way hats.
The first part works great, the hat part gives me headaches at the moment, because the arduino USB device descriptors for POV hats contain 4-way hats only (and "continuous 360 degree" hats which aren't working for some reason).
In IL-2 1946 Mode, the first 32 switch positions would be handed to the game as-is, followed by 3x 8-way hats (this is because my X-52 Pro has one hat already, actually even more, but only the first one is recognized as a hat by IL-2 1946, the others are buttons). That'd be 56 out of 80 buttons and switches in total. The remaining 24 would have to be mapped to keystrokes.
Keystrokes work fine, even in combination with modifiers (Shift, Ctrl, Alt), however IL-2 1946 cannot distinguish between left and right modifiers, doesn't know the windows key, and cannot handle multiple modifiers at once.
The biggest headache about my own firmware however is the USB device descriptor and the raw usb protocol behind.
The Arduino sketches use 32 buttons + one 4-way hat only, so I'll have to rewrite the whole USB protocol myself.
While there are proof-of-concept USB decriptors for Teensy 3.x boards available, I'm stuck on my Teensy++ 2.0 and it seems no one did the same here yet.
Unfortunately the USB protocol is vastly different between Teensy 3 and 2, so that's a bit of research which is necessary here.

In the meantime, to get things started, I've followed the path of available tools.
This involves a combination of MMJoy2, VJoy and Joystick Gremlin.

MMJoy2 is available here: https://sites.google.com/site/mmjoyproject/
The site is in russian only, but the other spot where MMJoy2 is available (Github) holds outdated versions only.
When you visit the MMJoy2 site, don't use the currently latest version (at the moment that'd be v20161101), it's bugged. Use the previous one instead (at the moment v20160818upd1).
MMJoy2 enables us to configure all hardware buttons on the switchbox and map them to joystick buttons.
Beware: For some reason, logical buttons 74 and 75 don't work well with MMJoy2, therefore my 80-button (switches and buttons that is) box is configured to use logical buttons 1-73, 76-80, and then 81 and 82 for the hardware buttons 74 and 75. From Windows point of view this gives you an 82-button Joystick where buttons 74&75 will never fire up.

VJoy is available here: http://vjoystick.sourceforge.net/site/
It's required by Joystick Gremlin.
In VJoy you configure your "output" virtual joystick.
The virtual Joystick we create has 62 buttons and 4 8-way hats, plus 5 axis. That's the most we can use with our box in any of the targetted games.

Joystick Gremlin (available here: https://whitemagic.github.io/JoystickGremlin/) finally maps the input from our hardware switchbox to the virtual joystick of VJoy.
My Gremlin config maps hardware buttons 1-32 straight through, followed by another 32 hardware buttons mapped to the 4 hat switches, and then the remaining buttons mapped straight through again.
That's suitable for IL-2 Great Battles and to some extent for 1946. For the latter I could add a second Gremlin profile but that has to be done later.

That's it for the moment.
Excuse me that I have no pics or config files at the moment, it's all too much WIP - as you see my previous MMJoy2 screenshots, as tedious as it was to create them, were just waste of time.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Skylla on April 27, 2018, 10:12:30 AM
thanks a lot for the detailed description of the project status. You should know that - ableit being a little too silent - I follow this project with great interest.

And I don't mind the lack of pictures at all, I had to read through much worse publications :P
Title: Re: Mike's new project
Post by: sniperton on April 27, 2018, 11:44:13 AM
I follow this project with great interest.

Me too. My only concern is ergonomic, which hand of you is designated to fiddle with so many switches and buttons?
Title: Re: Mike's new project
Post by: SAS~GJE52 on April 27, 2018, 07:15:00 PM
I find this fascinating. Great project.

I have been trying to get 5 USB devices - Joystick, pedals, throttle and 2 MFD's - to work with IL2 46.  The game will only see 4 normally. (plus keyboard and mouse). I have tried a software solution called TARGET that was bundled with the MFD's which claims to allow more devices ..... but it can't seem to see the Saitek rudder pedals. Seeing your designs I wonder if there could be a hardware solution to combine the two MFD's to look like one device..

I shall continue to watch your progress  with interest..

G;
Title: Re: Mike's new project
Post by: SAS~Storebror on April 27, 2018, 11:15:46 PM
Plan change :D
Sometimes you just can't see the wood for all of the trees.
Actually the best solution for me in the long term will be to have the switchbox appear as two independent game devices, each with 3 axis, 32 buttons and one 8-way hat switch, and map the hardware axis, switches and buttons to these.
That way it'll work with any game, and there's even the very same short term solution in software:
Let MMJoy2 make the device appear as 5-axis, 82 button device (remember, buttons 74 and 75 are no-go).
Create two virtual VJoy devices each with 3 axis (one with 3 and one with 2 would be sufficient as well, or one with 5 and one without any), 32 buttons and one 8-way hat switch.
Let Joystick Gremlin map the axis and buttons of the MMJoy2 device to the two VJoy devices.
This will create the very same effect like the long term hardware solution and it will work perfectly fine with both IL-2 Great Battles and IL-2 1946.

which hand of you is designated to fiddle with so many switches and buttons?
I think the general misunderstanding here is that such game devices are always thought to be absolutely intuitive and user friendly.
This one serves the single purpose of providing additional axis for things like radiator, mixture, pitch and maybe some trim settings - simple potentiometers do the trick here because what's most important is that you have a label clearly telling which poti is what.
Similar thing with the switches and buttons: Instead of crawling through books of keyboard settings when you want to do something during flight, this box has labels clearly telling which switch or button is what, so you can toggle/actuate/press it without having to think of where to find the key mapping for what you're just intending to do.
That's the whole idea of this switchbox: Don't search through key mapping lists anymore, read the label and press the button instead.

I have been trying to get 5 USB devices - Joystick, pedals, throttle and 2 MFD's - to work with IL2 46.
The same software combo VJoy + Joystick Gremlin could do the trick too.
With Joystick Gremlin you can map axis, switches, buttons, hats etc. from hardware devices to virtual devices created in VJoy.
That way you can map your 5 USB devices to a single (or 2, 3, 4...) VJoy devices.
The only question is: Will IL-2 1946 see the VJoy devices first, or will the existing hardware devices blank them out?

Maybe I can shed some light on that question later.
VJoy has a "feeder" application to create virtual input to your virtual devices.
So I could prepare a test setup with my X-52 Pro, the Switchbox and 4 VJoy device(s).
Then I'd feed virtual input to VJoy devices 3+4 and check whether IL-2 1946 sees them.
If so, VJoy comes first and "overrides" hardware devices, which would mean that this would do the trick for you.
If not, bad luck. I have no idea whether it's possible to assign a custom VID/PID to VJoy devices - that's what matters most for the order of Joysticks to appear to Windows and with it, to other games.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on April 28, 2018, 12:10:52 AM
Oh man...
Turns out that VJoy, if you create more than one virtual device, inserts all virtual devices at the same time, with same VID/PID, so that windows will randomly sort them at will.
See here: http://vjoystick.sourceforge.net/site/index.php/forum/4-Help/825-strange-behaviour

I've got the same thing: With 4 VJoy devices configured, the device order now is:

What a bummer.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on April 30, 2018, 08:06:33 AM
Small Update again.
I've managed to get a clean USB device config working with 32 buttons and 3 hats on the Teensy. No idea why the device fails to get recognized when I add a 4th hat, but that's not an issue to me - 3 are sufficient for my plans.
The next step will be to try to get a 2nd joystick device to be recognized from the same physical thing.

The other (software) solution is working well.
Actually the mixed up device numbering isn't much of an issue.
The order of devices, VJoy and physical ones, stays the same even after reboot. Even if I unplug a hardware device and plug it in later again, it will rejoin at the very same place in the list.
Apparently, regardless how many VJoy devices I configure - all but one will be at the beginning of the list, and one will be at the end.
If it's just one VJoy device, it will always be at the end of the list.
Therefore, if you need to map existing hardware devices to a smaller number of VJoy devices, this will be possible. Just configure one more VJoy device than you actually need, so you have the needed ones in the beginning of the list.
Therefore that seems to be a viable solution for your issue Glynn.
For my purpose it definitely works well.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~GJE52 on April 30, 2018, 08:48:41 AM
Quote
The order of devices, VJoy and physical ones, stays the same even after reboot. Even if I unplug a hardware device and plug it in later again, it will rejoin at the very same place in the list.
Apparently, regardless how many VJoy devices I configure - all but one will be at the beginning of the list, and one will be at the end.
If it's just one VJoy device, it will always be at the end of the list.
Therefore, if you need to map existing hardware devices to a smaller number of VJoy devices, this will be possible. Just configure one more VJoy device than you actually need, so you have the needed ones in the beginning of the list.
Therefore that seems to be a viable solution for your issue Glynn.
For my purpose it definitely works well.   

Thanks Mike. I will investigate further. If I can use it to combine the two "28 button" MFD units into one effective 56 button device the rest should be OK. Or combine the rudder pedals with the joystick perhaps...

G;
Title: Re: Mike's new project
Post by: SAS~Storebror on April 30, 2018, 09:08:39 AM
Yes you can.
Per VJoy device you can have up to 8 axis, 128 buttons and 4 "continuous" POV hats (act like 8-way ones in IL-2 1946).
You can also have 11 different Force Feedback Effects if you want to.
There no limit in combining these, so you can definitely merge two 28 button MFD units into one, and you can even add the rudder pedals to it and most probably there's still room to add all joystick buttons and axis to that device too (and if not, no big deal either).
The mapping will be done with Joystick Gremlin.
Joystick Gremlin is a bit hard to get used to, at least it appeared so to me, but once you get around the macro scripts, it can do anything you want.
Remapping simple buttons is a no-brainer, remapping axis too.
Remapping POV hats requires macros but I can show you how that works.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~GJE52 on April 30, 2018, 10:19:25 AM
Ok, looks very promising...  ;)

Thanks for the offer of help as well. I will do some more research and get back to you.

I just found this "old" thread as well ....
  https://www.sas1946.com/main/index.php/topic,45118.25.html   (https://www.sas1946.com/main/index.php/topic,45118.25.html)

Off to search out and DL VJoy now.

G;
Title: Re: Mike's new project
Post by: max_thehitman on April 30, 2018, 07:51:12 PM

This sounds very cool and amazing Storebror. Very cool indeed!
Congratulations on the project
Title: Re: Mike's new project
Post by: SAS~Storebror on May 02, 2018, 02:21:43 AM
One more hint about VJoy:
Depending on which other Joystick Devices you have plugged into your PC, the VJoy installation process might get stuck at 100%, see report here:
https://vjoy.freeforums.net/thread/50/installation-stuck-finish-error?page=2&scrollTo=245

This is no real issue. If the progress bar of your VJoy installation reached 100% and is stuck there for a minute, just reboot your PC and everything will be fine.

Alright, small update again.
I've got my Teensy to be recognized as 32-Button, 5 Axis and 4x8-way hat controller now.
The latter is necessary because IL-2 1946 cannot distinguish between hats on different controllers, it throws all of them into one according to their index, so I need 3 at least to move them out of the way of each other, and 4 (windows max. number for hats) to be on the safe side.

Before I mess up my code in the attempt to turn the Teensy into a 2-Joystick-Device, let me dump all the related sources here.

This is the so called "Arduino Sketch" holding the internal logic. I called it "Mike_s_Controller_Box_v001":
Code: [Select]
#include "ButtonMatrix.h"

const byte ROWS = 10; //four rows
const byte COLS = 8; //three columns
byte rowPins[ROWS] = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {27, 0, 1, 2, 3, 4, 5, 7}; //connect to the column pinouts of the keypad

int16_t buttonDirs[8] = {0, 45, 90, 135, 180, 225, 270, 315};
ButtonMatrix buttonmatrix = ButtonMatrix( rowPins, colPins, ROWS, COLS );

unsigned long loopCount;
unsigned long startTime;
unsigned long ledTime;
unsigned long joystickTime;
const int ledPin = 6;
String msg;

const int numButtons = 80;

void setup() {
  Serial.begin(9600);
  Joystick.useManualSend(true);
  Serial.println("Begin Mike's Switchbox TEST");
  Serial.print("Maximum Number of Buttons: ");
  Serial.println(buttonmatrix.numButtons());
  loopCount = 0;
  startTime = millis();
  ledTime = millis();
  joystickTime = millis();
  pinMode(ledPin, OUTPUT);
  msg = "";

  buttonmatrix.getButtons();
  for (int i = 0; i < numButtons; i++) {
    if (buttonmatrix.button[i].bstate == PRESSED || buttonmatrix.button[i].bstate == HOLD) {
      Joystick.button(i + 1, 1);
    } else {
      Joystick.button(i + 1, 0);
    }
  }

  Joystick.send_now();

  Joystick.X(512);
  Joystick.Y(512);
  Joystick.Z(512);
  Joystick.Xrotate(512);
  Joystick.Yrotate(512);
  Joystick.hat(1, -1);
  Joystick.hat(2, -1);
  Joystick.hat(3, -1);

  for (int i = 0; i < 12; i++) {
    Serial.print("joystick_report_data[");
    Serial.print(i);
    Serial.print("]=");
    Serial.println(Joystick.get_joystick_report_data(i));
  }

  buttonmatrix.addEventListener(buttonEvent); // Add an event listener for this keypad
}

byte allButtons[numButtons];
byte prevButtons[numButtons];
int angle = 0;
int analog = 0;

void loop() {
  loopCount++;
  if ( (millis() - startTime) > 30000 ) {
    Serial.print("Average loops per second = ");
    Serial.println(loopCount / 30);
    startTime = millis();
    loopCount = 0;
  }

  if ( (millis() - ledTime) > 1 ) {
    ledTime = millis();
    digitalWrite(ledPin, LOW);
  }
  buttonmatrix.getButtons();

  if ( (millis() - joystickTime) > 10 ) {
    joystickTime = millis();
    Joystick.X(analogRead(0));
    Joystick.Y(analogRead(1));
    Joystick.Z(analogRead(2));
    Joystick.Xrotate(analogRead(3));
    Joystick.Yrotate(analogRead(4));
    Joystick.send_now();
  }
  delay(5);
}

void buttonEvent(ButtonEvent listIndex) {
  switch (buttonmatrix.button[listIndex].bstate) {
    case PRESSED:
      msg = " PRESSED.";
      break;

    case RELEASED:
      msg = " RELEASED.";
      break;

    case HOLD:
    case IDLE:
    default:
      return;
  }
  ledTime = millis();
  digitalWrite(ledPin, HIGH);
  Serial.print("Button ");
  Serial.print(buttonmatrix.button[listIndex].bindex);
  Serial.println(msg);
  int buttonIndex = buttonmatrix.button[listIndex].bindex;
  bool buttonPressed = false;
  if (buttonmatrix.button[listIndex].bstate == PRESSED || buttonmatrix.button[listIndex].bstate == HOLD) {
    buttonPressed = true;
  }

  if (buttonIndex < 32) {
    if (buttonPressed) {
      Joystick.button(buttonIndex + 1, 1);
    } else {
      Joystick.button(buttonIndex + 1, 0);
    }
  }
  else if (buttonIndex < 40) {
    if (buttonPressed) {
      Joystick.hat(1, buttonDirs[buttonIndex-32]);
    } else {
      Joystick.hat(1, -1);
    }
  }
  else if (buttonIndex < 48) {
    if (buttonPressed) {
      Joystick.hat(2, buttonDirs[buttonIndex-40]);
    } else {
      Joystick.hat(2, -1);
    }
  }
  else if (buttonIndex < 56) {
    if (buttonPressed) {
      Joystick.hat(3, buttonDirs[buttonIndex-48]);
    } else {
      Joystick.hat(3, -1);
    }
  }
  else if (buttonIndex < 64) {
    if (buttonPressed) {
      Joystick.hat(4, buttonDirs[buttonIndex-56]);
    } else {
      Joystick.hat(4, -1);
    }
  }
  //Joystick.send_now();

}

The Button Matrix is based on Keyboard Matrix Code samples available on the net. Here are the C++ and Header files:
Code: [Select]
#include "ButtonMatrix.h"

ButtonMatrix::ButtonMatrix(byte *row, byte *col, byte numRows, byte numCols) {
rowPins = row;
columnPins = col;
sizeBM.rows = numRows;
sizeBM.columns = numCols;

setDebounceTime(10);
setHoldTime(500);
buttonEventListener = 0;

startTime = 0;
single_button = false;
}

// Returns a single button only. Retained for backwards compatibility.
int ButtonMatrix::getButton() {
single_button = true;

if (getButtons() && button[0].stateChanged && (button[0].bstate==PRESSED))
return button[0].bindex;

single_button = false;

return NO_BUTTON;
}

// Populate the button list.
bool ButtonMatrix::getButtons() {
bool buttonActivity = false;

// Limit how often the button matrix is scanned. This makes the loop() run 10 times as fast.
if ( (millis()-startTime)>debounceTime ) {
scanButtons();
buttonActivity = updateList();
startTime = millis();
}

return buttonActivity;
}

// Private : Hardware scan
void ButtonMatrix::scanButtons() {
// Re-intialize the row pins. Allows sharing these pins with other hardware.
for (byte r=0; r<sizeBM.rows; r++) {
pin_mode(rowPins[r],INPUT_PULLUP);
}

// bitMap stores ALL the buttons that are being pressed.
for (byte c=0; c<sizeBM.columns; c++) {
pin_mode(columnPins[c],OUTPUT);
pin_write(columnPins[c], LOW); // Begin column pulse output.
for (byte r=0; r<sizeBM.rows; r++) {
bitWrite(bitMap[r], c, !pin_read(rowPins[r]));  // button press is active low so invert to high.
}
// Set pin to high impedance input. Effectively ends column pulse.
pin_write(columnPins[c],HIGH);
pin_mode(columnPins[c],INPUT);
}
}

// Manage the list without rearranging the keys. Returns true if any keys on the list changed state.
bool ButtonMatrix::updateList() {

bool anyActivity = false;

// Delete any IDLE buttons
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].bstate==IDLE) {
button[i].bindex = NO_BUTTON;
button[i].stateChanged = false;
}
}

// Add new buttons to empty slots in the button list.
for (byte r=0; r<sizeBM.rows; r++) {
for (byte c=0; c<sizeBM.columns; c++) {
boolean bbutton = bitRead(bitMap[r],c);
int buttonIndex = r * sizeBM.columns + c;
int idx = findInList (buttonIndex);
// Key is already on the list so set its next state.
if (idx > -1) {
nextButtonState(idx, bbutton);
}
// Button is NOT on the list so add it.
if ((idx == -1) && bbutton) {
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].bindex==NO_BUTTON) { // Find an empty slot or don't add button to list.
button[i].bindex = buttonIndex;
button[i].bstate = IDLE; // Buttons NOT on the list have an initial state of IDLE.
nextButtonState (i, bbutton);
break; // Don't fill all the empty slots with the same button.
}
}
}
}
}

// Report if the user changed the state of any button.
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].stateChanged) anyActivity = true;
}

return anyActivity;
}

// Private
// This function is a state machine but is also used for debouncing the buttons.
void ButtonMatrix::nextButtonState(byte idx, boolean bbutton) {
button[idx].stateChanged = false;

switch (button[idx].bstate) {
case IDLE:
if (bbutton==CLOSED) {
transitionTo (idx, PRESSED);
holdTimer = millis(); } // Get ready for next HOLD state.
break;
case PRESSED:
if ((millis()-holdTimer)>holdTime) // Waiting for a key HOLD...
transitionTo (idx, HOLD);
else if (bbutton==OPEN) // or for a key to be RELEASED.
transitionTo (idx, RELEASED);
break;
case HOLD:
if (bbutton==OPEN)
transitionTo (idx, RELEASED);
break;
case RELEASED:
transitionTo (idx, IDLE);
break;
}
}

bool ButtonMatrix::isPressed(int buttonIndex) {
for (byte i=0; i<LIST_MAX; i++) {
if ( button[i].bindex == buttonIndex ) {
if ( (button[i].bstate == PRESSED) && button[i].stateChanged )
return true;
}
}
return false; // Not pressed.
}

// Search by index for a button in the list of active buttons.
// Returns -1 if not found, returns the active buttons list index if found.
int ButtonMatrix::findInList (int buttonIndex) {
for (byte i=0; i<LIST_MAX; i++) {
if (button[i].bindex == buttonIndex) {
return i;
}
}
return -1;
}

int ButtonMatrix::waitForButton() {
int waitButton = NO_BUTTON;
while( (waitButton = getButton()) == NO_BUTTON ); // Block everything while waiting for a keypress.
return waitButton;
}

ButtonState ButtonMatrix::getState() {
return button[0].bstate;
}

// The end user can test for any changes in state before deciding
// if any variables, etc. needs to be updated in their code.
bool ButtonMatrix::buttonStateChanged() {
return button[0].stateChanged;
}

// The number of buttons on the button list, button[LIST_MAX], equals the number
// of bytes in the button list divided by the number of bytes in a Button object.
byte ButtonMatrix::numButtons() {
return sizeof(button)/sizeof(Button);
}

// Minimum debounceTime is 1 ms. Any lower *will* slow down the loop().
void ButtonMatrix::setDebounceTime(uint debounce) {
debounce<1 ? debounceTime=1 : debounceTime=debounce;
}

void ButtonMatrix::setHoldTime(uint hold) {
    holdTime = hold;
}

void ButtonMatrix::addEventListener(void (*listener)(int)){
buttonEventListener = listener;
}

void ButtonMatrix::transitionTo(byte idx, ButtonState nextState) {
button[idx].bstate = nextState;
button[idx].stateChanged = true;

// Sketch used the getButton() function.
// Calls buttonEventListener only when the first button in slot 0 changes state.
if (single_button)  {
  if ( (buttonEventListener!=NULL) && (idx==0) )  {
buttonEventListener(0);
}
}
// Sketch used the getButtons() function.
// Calls buttonEventListener on any button that changes state.
else {
  if (buttonEventListener!=NULL)  {
buttonEventListener(idx);
}
}
}

Code: [Select]
#ifndef BUTTON_MATRIX_H
#define BUTTON_MATRIX_H

#include "Button.h"

#ifndef INPUT_PULLUP
#warning "Using  pinMode() INPUT_PULLUP AVR emulation"
#define INPUT_PULLUP 0x2
#define pinMode(_pin, _mode) _mypinMode(_pin, _mode)
#define _mypinMode(_pin, _mode)  \
do { \
if(_mode == INPUT_PULLUP) \
pinMode(_pin, INPUT); \
digitalWrite(_pin, 1); \
if(_mode != INPUT_PULLUP) \
pinMode(_pin, _mode); \
}while(0)
#endif


#define OPEN LOW
#define CLOSED HIGH

typedef int ButtonEvent;
typedef unsigned int uint;
typedef unsigned long ulong;

// Made changes according to this post http://arduino.cc/forum/index.php?topic=58337.0
// by Nick Gammon. Thanks for the input Nick. It actually saved 78 bytes for me. :)
typedef struct {
    byte rows;
    byte columns;
} ButtonMatrixSize;

#define LIST_MAX 100 // Max number of keys on the active list.
#define MAPSIZE 10 // MAPSIZE is the number of rows (times 16 columns)

//class ButtonMatrix : public Button, public HAL_obj {
class ButtonMatrix : public Button {
public:

ButtonMatrix(byte *row, byte *col, byte numRows, byte numCols);

virtual void pin_mode(byte pinNum, byte mode) { pinMode(pinNum, mode); }
virtual void pin_write(byte pinNum, boolean level) { digitalWrite(pinNum, level); }
virtual int  pin_read(byte pinNum) { return digitalRead(pinNum); }

uint bitMap[MAPSIZE]; // 10 row x 16 column array of bits. Except Due which has 32 columns.
Button button[LIST_MAX];
unsigned long holdTimer;

int getButton();
bool getButtons();
ButtonState getState();
bool isPressed(int buttonIndex);
void setDebounceTime(uint);
void setHoldTime(uint);
void addEventListener(void (*listener)(int));
int findInList(int buttonIndex);
int waitForButton();
bool buttonStateChanged();
byte numButtons();

private:
unsigned long startTime;
  byte *rowPins;
  byte *columnPins;
ButtonMatrixSize sizeBM;
uint debounceTime;
uint holdTime;
bool single_button;

void scanButtons();
bool updateList();
void nextButtonState(byte n, boolean bbutton);
void transitionTo(byte n, ButtonState nextState);
void (*buttonEventListener)(int);
};

#endif

The Button Matrix uses a "Button" Class to represent each Button's State. These are the C++ and Header files for that class:
Code: [Select]
#include "Button.h"

Button::Button() {
bindex = NO_BUTTON;
bstate = IDLE;
stateChanged = false;
}

void Button::button_update (int userButtonIndex, ButtonState userState, boolean userStatus) {
bindex = userButtonIndex;
bstate = userState;
stateChanged = userStatus;
}

Code: [Select]
#ifndef BUTTON_H_
#define BUTTON_H_

#include <Arduino.h>

#define OPEN LOW
#define CLOSED HIGH

typedef unsigned int uint;
typedef enum{ IDLE, PRESSED, HOLD, RELEASED } ButtonState;

const int NO_BUTTON = -1;

class Button {
public:
// members
int bindex;
ButtonState bstate;
boolean stateChanged;

// methods
Button();
void button_update(int userButtonIndex, ButtonState userState, boolean userStatus);

private:

};

#endif

USB connector comes next...
Title: Re: Mike's new project
Post by: SAS~Storebror on May 02, 2018, 02:22:02 AM
Last but not least, the whole USB connector code needed to be modified.
This one is based on a Teensy++ 2.0 "Keyboard + Mouse + Joystick" HID sample, but it's vastly modified meanwhile.
In it's current state, it holds one Joystick Interface and one Debugging Interface, representing a virtual serial port to send debug messages back to the PC.
The API is C++, the USB interface is ANSI C internally, but with external API class references (dirty but works).

usb_private.h:
Code: [Select]
#ifndef usb_serial_h__
#define usb_serial_h__

#include <stdint.h>

#ifdef __cplusplus
extern "C"{
#endif

/**************************************************************************
 *
 *  Configurable Options
 *
 **************************************************************************/

#define VENDOR_ID               0x16C0
#define PRODUCT_ID              0x0482
#define TRANSMIT_FLUSH_TIMEOUT  4   /* in milliseconds */
#define TRANSMIT_TIMEOUT        25   /* in milliseconds */


/**************************************************************************
 *
 *  Endpoint Buffer Configuration
 *
 **************************************************************************/

// 0: control 64
// 1: debug IN 64x2
// 2: debug OUT 32x2
// 3: joystick IN 16x2

// Some operating systems, especially Windows, may cache USB device
// info.  Changes to the device name may not update on the same
// computer unless the vendor or product ID numbers change, or the
// "bcdDevice" revision code is increased.

#ifndef STR_PRODUCT
#define STR_PRODUCT             L"Mikes Switchbox WIP"
#endif

#define ENDPOINT0_SIZE          64

#define DEBUG_INTERFACE 1
#define DEBUG_TX_ENDPOINT 1
#define DEBUG_TX_SIZE 64
#define DEBUG_TX_BUFFER EP_DOUBLE_BUFFER
#define DEBUG_TX_INTERVAL 1
#define DEBUG_RX_ENDPOINT 2
#define DEBUG_RX_SIZE 32
#define DEBUG_RX_BUFFER EP_DOUBLE_BUFFER
#define DEBUG_RX_INTERVAL 2

#define JOYSTICK_INTERFACE 2
#define JOYSTICK_ENDPOINT 3
#define JOYSTICK_SIZE 16
#define JOYSTICK_BUFFER EP_DOUBLE_BUFFER
#define JOYSTICK_INTERVAL 2

#define NUM_ENDPOINTS 4
#define NUM_INTERFACE 2


// setup
void usb_init(void); // initialize everything
void usb_shutdown(void); // shut off USB

// variables
extern volatile uint8_t usb_configuration;
extern volatile uint8_t usb_suspended;
extern volatile uint8_t debug_flush_timer;
extern uint8_t joystick_report_data[13];

#ifdef __cplusplus
} // extern "C"
#endif

#endif

core_id.h:
Code: [Select]
#define CORE_TEENSY_HID
#if defined(__AVR_ATmega32U4__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
#define CORE_TEENSY_JOYSTICK
#endif

usb_api.cpp:
Code: [Select]
/* USB API for Teensy USB Development Board
 * http://www.pjrc.com/teensy/teensyduino.html
 * Copyright (c) 2008 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdint.h>
#include "usb_common.h"
#include "usb_private.h"
#include "usb_api.h"
#include "wiring.h"



void usb_joystick_class::send_now(void)
{
        uint8_t intr_state, timeout;

        if (!usb_configuration) return;
        intr_state = SREG;
        cli();
        UENUM = JOYSTICK_ENDPOINT;
        timeout = UDFNUML + 50;
        while (1) {
                // are we ready to transmit?
                if (UEINTX & (1<<RWAL)) break;
                SREG = intr_state;
                // has the USB gone offline?
                if (!usb_configuration) return;
                // have we waited too long?
                if (UDFNUML == timeout) return;
                // get ready to try checking again
                intr_state = SREG;
                cli();
                UENUM = JOYSTICK_ENDPOINT;
        }
        UEDATX = joystick_report_data[0];
        UEDATX = joystick_report_data[1];
        UEDATX = joystick_report_data[2];
        UEDATX = joystick_report_data[3];
        UEDATX = joystick_report_data[4];
        UEDATX = joystick_report_data[5];
        UEDATX = joystick_report_data[6];
        UEDATX = joystick_report_data[7];
        UEDATX = joystick_report_data[8];
        UEDATX = joystick_report_data[9];
        UEDATX = joystick_report_data[10];
        UEDATX = joystick_report_data[11];
        UEDATX = joystick_report_data[12];
        UEINTX = 0x3A;
        SREG = intr_state;
}

uint16_t usb_joystick_class::get_joystick_report_data(uint8_t index) {
return joystick_report_data[index];
}

static volatile uint8_t prev_byte=0;

void usb_serial_class::begin(long speed)
{
// make sure USB is initialized
usb_init();
uint16_t begin_wait = (uint16_t)millis();
while (1) {
if (usb_configuration) {
delay(200);  // a little time for host to load a driver
return;
}
if (usb_suspended) {
uint16_t begin_suspend = (uint16_t)millis();
while (usb_suspended) {
// must remain suspended for a while, because
// normal USB enumeration causes brief suspend
// states, typically under 0.1 second
if ((uint16_t)millis() - begin_suspend > 250) {
return;
}
}
}
// ... or a timout (powered by a USB power adaptor that
// wiggles the data lines to keep a USB device charging)
if ((uint16_t)millis() - begin_wait > 2500) return;
}
prev_byte = 0;
}

void usb_serial_class::end()
{
usb_shutdown();
delay(25);
}



// number of bytes available in the receive buffer
int usb_serial_class::available()
{
        uint8_t c;

c = prev_byte;  // assume 1 byte static volatile access is atomic
if (c) return 1;
c = readnext();
if (c) {
prev_byte = c;
return 1;
}
return 0;
}

// get the next character, or -1 if nothing received
int usb_serial_class::read()
{
uint8_t c;

c = prev_byte;
if (c) {
prev_byte = 0;
return c;
}
c = readnext();
if (c) return c;
return -1;
}

int usb_serial_class::peek()
{
uint8_t c;

c = prev_byte;
if (c) return c;
c = readnext();
if (c) {
prev_byte = c;
return c;
}
return -1;
}

// get the next character, or 0 if nothing
uint8_t usb_serial_class::readnext(void)
{
        uint8_t c, intr_state;

        // interrupts are disabled so these functions can be
        // used from the main program or interrupt context,
        // even both in the same program!
        intr_state = SREG;
        cli();
        if (!usb_configuration) {
                SREG = intr_state;
                return 0;
        }
        UENUM = DEBUG_RX_ENDPOINT;
try_again:
        if (!(UEINTX & (1<<RWAL))) {
                // no packet in buffer
                SREG = intr_state;
                return 0;
        }
        // take one byte out of the buffer
        c = UEDATX;
if (c == 0) {
// if we see a zero, discard it and
// discard the rest of this packet
UEINTX = 0x6B;
goto try_again;
}
        // if this drained the buffer, release it
        if (!(UEINTX & (1<<RWAL))) UEINTX = 0x6B;
        SREG = intr_state;
        return c;
}

// discard any buffered input
void usb_serial_class::flush()
{
        uint8_t intr_state;

        if (usb_configuration) {
                intr_state = SREG;
                cli();
UENUM = DEBUG_RX_ENDPOINT;
                while ((UEINTX & (1<<RWAL))) {
                        UEINTX = 0x6B;
                }
                SREG = intr_state;
        }
prev_byte = 0;
}

// transmit a character.
size_t usb_serial_class::write(uint8_t c)
{
        //static uint8_t previous_timeout=0;
        uint8_t timeout, intr_state;

        // if we're not online (enumerated and configured), error
        if (!usb_configuration) goto error;
        // interrupts are disabled so these functions can be
        // used from the main program or interrupt context,
        // even both in the same program!
        intr_state = SREG;
        cli();
        UENUM = DEBUG_TX_ENDPOINT;
        // if we gave up due to timeout before, don't wait again
#if 0
// this seems to be causig a lockup... why????
        if (previous_timeout) {
                if (!(UEINTX & (1<<RWAL))) {
                        SREG = intr_state;
                        return;
                }
                previous_timeout = 0;
        }
#endif
        // wait for the FIFO to be ready to accept data
        timeout = UDFNUML + TRANSMIT_TIMEOUT;
        while (1) {
                // are we ready to transmit?
                if (UEINTX & (1<<RWAL)) break;
                SREG = intr_state;
                // have we waited too long?  This happens if the user
                // is not running an application that is listening
                if (UDFNUML == timeout) {
                        //previous_timeout = 1;
goto error;
}
                // has the USB gone offline?
                if (!usb_configuration) goto error;
                // get ready to try checking again
                intr_state = SREG;
                cli();
                UENUM = DEBUG_TX_ENDPOINT;
        }
        // actually write the byte into the FIFO
        UEDATX = c;
        // if this completed a packet, transmit it now!
        if (!(UEINTX & (1<<RWAL))) {
UEINTX = 0x3A;
        debug_flush_timer = 0;
} else {
        debug_flush_timer = TRANSMIT_FLUSH_TIMEOUT;
}
        SREG = intr_state;
return 1;
error:
setWriteError();
return 0;
}


// These are Teensy-specific extensions to the Serial object

// immediately transmit any buffered output.
// This doesn't actually transmit the data - that is impossible!
// USB devices only transmit when the host allows, so the best
// we can do is release the FIFO buffer for when the host wants it
void usb_serial_class::send_now(void)
{
        uint8_t intr_state;

        intr_state = SREG;
        cli();
        if (debug_flush_timer) {
                UENUM = DEBUG_TX_ENDPOINT;
while ((UEINTX & (1<<RWAL))) {
UEDATX = 0;
}
                UEINTX = 0x3A;
                debug_flush_timer = 0;
        }
        SREG = intr_state;
}

uint32_t usb_serial_class::baud(void)
{
return ((uint32_t)DEBUG_TX_SIZE * 10000 / DEBUG_TX_INTERVAL);
}

uint8_t usb_serial_class::stopbits(void)
{
return 1;
}

uint8_t usb_serial_class::paritytype(void)
{
return 0;
}

uint8_t usb_serial_class::numbits(void)
{
return 8;
}

uint8_t usb_serial_class::dtr(void)
{
return 1;
}

uint8_t usb_serial_class::rts(void)
{
return 1;
}

usb_serial_class::operator bool()
{
if (usb_configuration) return true;
return false;
}


// Preinstantiate Objects //////////////////////////////////////////////////////

usb_serial_class Serial = usb_serial_class();
usb_joystick_class Joystick = usb_joystick_class();


usb_api.h:
Code: [Select]
#ifndef USBserial_h_
#define USBserial_h_

#include <inttypes.h>

#include "Print.h"
#include "Stream.h"

extern uint8_t joystick_report_data[13];

class usb_joystick_class
{
public:
usb_joystick_class() { manual_mode = 0; }
inline void button(uint8_t button, bool val) {
button--;
uint8_t mask = (1 << (button & 7));
if (val) {
if (button < 8) joystick_report_data[0] |= mask;
else if (button < 16) joystick_report_data[1] |= mask;
else if (button < 24) joystick_report_data[2] |= mask;
else if (button < 32) joystick_report_data[3] |= mask;
} else {
mask = ~mask;
if (button < 8) joystick_report_data[0] &= mask;
else if (button < 16) joystick_report_data[1] &= mask;
else if (button < 24) joystick_report_data[2] &= mask;
else if (button < 32) joystick_report_data[3] &= mask;
}
if (!manual_mode) send_now();
}

inline void X(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[4] = val;
joystick_report_data[5] = (joystick_report_data[5] & 0xFC) | (val >> 8);
if (!manual_mode) send_now();
}
inline void Y(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[5] = (joystick_report_data[5] & 0x03) | (val << 2);
joystick_report_data[6] = (joystick_report_data[6] & 0xF0) | (val >> 6);
if (!manual_mode) send_now();
}
inline void position(uint16_t x, uint16_t y) {
if (x > 1023) x = 1023;
if (y > 1023) y = 1023;
joystick_report_data[4] = x;
joystick_report_data[5] = (x >> 8) | (y << 2);
joystick_report_data[6] = (joystick_report_data[6] & 0xF0) | (y >> 6);
if (!manual_mode) send_now();
}
inline void Z(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[6] = (joystick_report_data[6] & 0x0F) | (val << 4);
joystick_report_data[7] = (joystick_report_data[7] & 0xC0) | (val >> 4);
if (!manual_mode) send_now();
}
inline void Xrotate(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[7] = (joystick_report_data[7] & 0x3F) | (val << 6);
joystick_report_data[8] = (val >> 2);
if (!manual_mode) send_now();
}
inline void Yrotate(uint16_t val) {
if (val > 1023) val = 1023;
joystick_report_data[9] = val;
joystick_report_data[10] = (joystick_report_data[10] & 0xFC) | (val >> 8);
if (!manual_mode) send_now();
}

inline void hat(uint8_t hatnum, int16_t dir) {
uint8_t val = 15;
if (dir < 0) val = 15;
else if (dir < 23) val = 0;
else if (dir < 68) val = 1;
else if (dir < 113) val = 2;
else if (dir < 158) val = 3;
else if (dir < 203) val = 4;
else if (dir < 245) val = 5;
else if (dir < 293) val = 6;
else if (dir < 338) val = 7;
switch(hatnum) {
case 1:
joystick_report_data[10] = (joystick_report_data[10] & 0x0F) | (val << 4);
break;
case 2:
joystick_report_data[11] = (joystick_report_data[11] & 0xF0) | val;
break;
case 3:
joystick_report_data[11] = (joystick_report_data[11] & 0x0F) | (val << 4);
break;
case 4:
joystick_report_data[12] = (joystick_report_data[12] & 0xF0) | val;
break;
default:
break;
}
if (!manual_mode) send_now();
}
inline void useManualSend(bool mode) {
manual_mode = mode;
}
uint16_t get_joystick_report_data(uint8_t index);
void send_now(void);
private:
uint8_t manual_mode;
};

extern usb_joystick_class Joystick;




class usb_serial_class : public Stream
{
public:
// standard Arduino functions
void begin(long);
void end();
virtual int available();
virtual int read();
virtual int peek();
virtual void flush();
virtual size_t write(uint8_t);
using Print::write;
operator bool();
// Teensy extensions
void send_now(void);
uint32_t baud(void);
uint8_t stopbits(void);
uint8_t paritytype(void);
uint8_t numbits(void);
uint8_t dtr(void);
uint8_t rts(void);
private:
uint8_t readnext(void);
};

extern usb_serial_class Serial;


#endif

usb.c:
Code: [Select]
/* USB Serial Example for Teensy USB Development Board
 * http://www.pjrc.com/teensy/usb_serial.html
 * Copyright (c) 2008 PJRC.COM, LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */


#include "usb_common.h"
#include "usb_private.h"



/**************************************************************************
 *
 *  Endpoint Buffer Configuration
 *
 **************************************************************************/


static const uint8_t PROGMEM endpoint_config_table[] = {
EP_TYPE_INTERRUPT_IN,  EP_SIZE(DEBUG_TX_SIZE) | DEBUG_TX_BUFFER,
EP_TYPE_INTERRUPT_OUT, EP_SIZE(DEBUG_RX_SIZE) | DEBUG_RX_BUFFER,
EP_TYPE_INTERRUPT_IN,  EP_SIZE(JOYSTICK_SIZE) | JOYSTICK_BUFFER,
};


/**************************************************************************
 *
 *  Descriptor Data
 *
 **************************************************************************/

// Descriptors are the data that your computer reads when it auto-detects
// this USB device (called "enumeration" in USB lingo).  The most commonly
// changed items are editable at the top of this file.  Changing things
// in here should only be done by those who've read chapter 9 of the USB
// spec and relevant portions of any USB class specifications!

static const uint8_t PROGMEM device_descriptor[] = {
18, // bLength
1, // bDescriptorType
0x00, 0x02, // bcdUSB
0, // bDeviceClass
0, // bDeviceSubClass
0, // bDeviceProtocol
ENDPOINT0_SIZE, // bMaxPacketSize0
LSB(VENDOR_ID), MSB(VENDOR_ID), // idVendor
LSB(PRODUCT_ID), MSB(PRODUCT_ID), // idProduct
0x05, 0x01, // bcdDevice
0, // iManufacturer
1, // iProduct
0, // iSerialNumber
1 // bNumConfigurations
};

#ifdef JOYSTICK_INTERFACE
static const uint8_t PROGMEM joystick_hid_report_desc[] = {
        0x05, 0x01,                     // Usage Page (Generic Desktop)
        0x09, 0x04,                     // Usage (Joystick)
        0xA1, 0x01,                     // Collection (Application)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x01, // Logical Maximum (1)
0x75, 0x01, // Report Size (1)
0x95, 0x20, // Report Count (32)
0x05, 0x09, // Usage Page (Button)
0x19, 0x01, // Usage Minimum (Button #1)
0x29, 0x20, // Usage Maximum (Button #32)
0x81, 0x02, // Input (variable,absolute)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
0x09, 0x01, // Usage (Pointer)
        0xA1, 0x00,                     // Collection ()
0x15, 0x00, //   Logical Minimum (0)
0x26, 0xFF, 0x03, //   Logical Maximum (1023)
0x75, 0x0A, //   Report Size (10)
0x95, 0x05, //   Report Count (5)
0x09, 0x30, //   Usage (X)
0x09, 0x31, //   Usage (Y)
0x09, 0x32, //   Usage (Z)
0x09, 0x33, //   Usage (Rx)
0x09, 0x34, //   Usage (Ry)
0x81, 0x02, //   Input (variable,absolute)
        0xC0,                           // End Collection
0x75, 0x02, // Report Size (2)
0x95, 0x01, // Report Count (1)
0x81, 0x03,     // INPUT (Cnst,Var,Abs)
0x15, 0x00, // Logical Minimum (0)
0x25, 0x07, // Logical Maximum (7)
0x35, 0x00, // Physical Minimum (0)
0x46, 0x3B, 0x01, // Physical Maximum (315)
0x75, 0x04, // Report Size (4)
0x95, 0x04, // Report Count (4)
0x65, 0x14, // Unit (20)
        0x05, 0x01,                     // Usage Page (Generic Desktop)
0x09, 0x39, // Usage (Hat switch)
0x09, 0x39, // Usage (Hat switch)
0x09, 0x39, // Usage (Hat switch)
0x09, 0x39, // Usage (Hat switch)
0x81, 0x42, // Input (variable,absolute,null_state)
0x75, 0x04, // Report Size (4)
0x95, 0x01, // Report Count (1)
0x81, 0x03,     // INPUT (Cnst,Var,Abs)
        0xC0                            // End Collection
};
#endif

static const uint8_t PROGMEM debug_hid_report_desc[] = {
        0x06, 0xC9, 0xFF,                       // Usage Page 0xFFC9 (vendor defined)
        0x09, 0x04,                             // Usage 0x04
        0xA1, 0x5C,                             // Collection 0x5C
        0x75, 0x08,                             // report size = 8 bits (global)
        0x15, 0x00,                             // logical minimum = 0 (global)
        0x26, 0xFF, 0x00,                       // logical maximum = 255 (global)
        0x95, DEBUG_TX_SIZE,                    // report count (global)
        0x09, 0x75,                             // usage (local)
        0x81, 0x02,                             // Input
        0x95, DEBUG_RX_SIZE,                    // report count (global)
        0x09, 0x76,                             // usage (local)
        0x91, 0x02,                             // Output
        0x95, 0x04,                             // report count (global)
        0x09, 0x76,                             // usage (local)
        0xB1, 0x02,                             // Feature
        0xC0                                    // end collection
};


#define DEBUG_HID_DESC_OFFSET ( 9 + 9 )
#define JOYSTICK_HID_DESC_OFFSET ( 9 + 9+9+7+7 + 9 )
#define CONFIG1_DESC_SIZE ( 9 + 9+9+7+7 + 9+9+7 )

static const uint8_t PROGMEM config1_descriptor[CONFIG1_DESC_SIZE] = {
// configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10
9, // bLength;
2, // bDescriptorType;
LSB(CONFIG1_DESC_SIZE), // wTotalLength
MSB(CONFIG1_DESC_SIZE),
NUM_INTERFACE, // bNumInterfaces
1, // bConfigurationValue
0, // iConfiguration
0xC0, // bmAttributes
50, // bMaxPower

        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
        9,                                      // bLength
        4,                                      // bDescriptorType
        DEBUG_INTERFACE,                        // bInterfaceNumber
        0,                                      // bAlternateSetting
        2,                                      // bNumEndpoints
        0x03,                                   // bInterfaceClass (0x03 = HID)
        0x00,                                   // bInterfaceSubClass
        0x00,                                   // bInterfaceProtocol
        0,                                      // iInterface
        // HID interface descriptor, HID 1.11 spec, section 6.2.1
        9,                                      // bLength
        0x21,                                   // bDescriptorType
        0x11, 0x01,                             // bcdHID
        0,                                      // bCountryCode
        1,                                      // bNumDescriptors
        0x22,                                   // bDescriptorType
        sizeof(debug_hid_report_desc),          // wDescriptorLength
        0,
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        DEBUG_TX_ENDPOINT | 0x80,               // bEndpointAddress
        0x03,                                   // bmAttributes (0x03=intr)
        DEBUG_TX_SIZE, 0,                       // wMaxPacketSize
        DEBUG_TX_INTERVAL,                      // bInterval
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        DEBUG_RX_ENDPOINT,                      // bEndpointAddress
        0x03,                                   // bmAttributes (0x03=intr)
        DEBUG_RX_SIZE, 0,                       // wMaxPacketSize
        DEBUG_RX_INTERVAL,                      // bInterval

        // interface descriptor, USB spec 9.6.5, page 267-269, Table 9-12
        9,                                      // bLength
        4,                                      // bDescriptorType
        JOYSTICK_INTERFACE,                     // bInterfaceNumber
        0,                                      // bAlternateSetting
        1,                                      // bNumEndpoints
        0x03,                                   // bInterfaceClass (0x03 = HID)
        0x00,                                   // bInterfaceSubClass
        0x00,                                   // bInterfaceProtocol
        0,                                      // iInterface
        // HID interface descriptor, HID 1.11 spec, section 6.2.1
        9,                                      // bLength
        0x21,                                   // bDescriptorType
        0x11, 0x01,                             // bcdHID
        0,                                      // bCountryCode
        1,                                      // bNumDescriptors
        0x22,                                   // bDescriptorType
        sizeof(joystick_hid_report_desc),       // wDescriptorLength
        0,
        // endpoint descriptor, USB spec 9.6.6, page 269-271, Table 9-13
        7,                                      // bLength
        5,                                      // bDescriptorType
        JOYSTICK_ENDPOINT | 0x80,               // bEndpointAddress
        0x03,                                   // bmAttributes (0x03=intr)
        JOYSTICK_SIZE, 0,               // wMaxPacketSize
        JOYSTICK_INTERVAL,                      // bInterval
};

// If you're desperate for a little extra code memory, these strings
// can be completely removed if iManufacturer, iProduct, iSerialNumber
// in the device desciptor are changed to zeros.
struct usb_string_descriptor_struct {
uint8_t bLength;
uint8_t bDescriptorType;
int16_t wString[];
};
static const struct usb_string_descriptor_struct PROGMEM string0 = {
4,
3,
{0x0409}
};
static const struct usb_string_descriptor_struct PROGMEM string1 = {
sizeof(STR_PRODUCT),
3,
STR_PRODUCT
};

// This table defines which descriptor data is sent for each specific
// request from the host (in wValue and wIndex).
static const struct descriptor_list_struct {
uint16_t wValue;
uint16_t wIndex;
const uint8_t *addr;
uint8_t length;
} PROGMEM descriptor_list[] = {
{0x0100, 0x0000, device_descriptor, sizeof(device_descriptor)},
{0x0200, 0x0000, config1_descriptor, sizeof(config1_descriptor)},
        {0x2200, DEBUG_INTERFACE, debug_hid_report_desc, sizeof(debug_hid_report_desc)},
        {0x2100, DEBUG_INTERFACE, config1_descriptor+DEBUG_HID_DESC_OFFSET, 9},
        {0x2200, JOYSTICK_INTERFACE, joystick_hid_report_desc, sizeof(joystick_hid_report_desc)},
        {0x2100, JOYSTICK_INTERFACE, config1_descriptor+JOYSTICK_HID_DESC_OFFSET, 9},
{0x0300, 0x0000, (const uint8_t *)&string0, 4},
{0x0301, 0x0409, (const uint8_t *)&string1, sizeof(STR_PRODUCT)},
};
#define NUM_DESC_LIST (sizeof(descriptor_list)/sizeof(struct descriptor_list_struct))


/**************************************************************************
 *
 *  Variables - these are the only non-stack RAM usage
 *
 **************************************************************************/

// zero when we are not configured, non-zero when enumerated
volatile uint8_t usb_configuration USBSTATE;
volatile uint8_t usb_suspended USBSTATE;

// the time remaining before we transmit any partially full
// packet, or send a zero length packet.
volatile uint8_t debug_flush_timer USBSTATE;

// joystick data
uint8_t joystick_report_data[13] USBSTATE;

/**************************************************************************
 *
 *  Public Functions - these are the API intended for the user
 *
 **************************************************************************/



// initialize USB serial
void usb_init(void)
{
uint8_t u;

u = USBCON;
if ((u & (1<<USBE)) && !(u & (1<<FRZCLK))) return;
HW_CONFIG();
        USB_FREEZE(); // enable USB
        PLL_CONFIG(); // config PLL
        while (!(PLLCSR & (1<<PLOCK))) ; // wait for PLL lock
        USB_CONFIG(); // start USB clock
        UDCON = 0; // enable attach resistor
usb_configuration = 0;
usb_suspended = 0;
debug_flush_timer = 0;
joystick_report_data[0] = 0;
joystick_report_data[1] = 0;
joystick_report_data[2] = 0;
joystick_report_data[3] = 0;
joystick_report_data[4] =  0;
joystick_report_data[5] =  0x02;
joystick_report_data[6] =  0x08;
joystick_report_data[7] =  0x20;
joystick_report_data[8] =  0x80;
joystick_report_data[9] =  0;
joystick_report_data[10] = 0xF2;
joystick_report_data[11] = 0xFF;
joystick_report_data[12] = 0xFF;
UDINT = 0;
        UDIEN = (1<<EORSTE)|(1<<SOFE);
//sei();  // init() in wiring.c does this
}

void usb_shutdown(void)
{
UDIEN = 0; // disable interrupts
UDCON = 1; // disconnect attach resistor
USBCON = 0; // shut off USB periperal
PLLCSR = 0; // shut off PLL
usb_configuration = 0;
usb_suspended = 1;
}


/**************************************************************************
 *
 *  Private Functions - not intended for general user consumption....
 *
 **************************************************************************/



// USB Device Interrupt - handle all device-level events
// the transmit buffer flushing is triggered by the start of frame
//
ISR(USB_GEN_vect)
{
uint8_t intbits, t;//, i;
// static uint8_t div4=0;

        intbits = UDINT;
        UDINT = 0;
        if (intbits & (1<<EORSTI)) {
UENUM = 0;
UECONX = 1;
UECFG0X = EP_TYPE_CONTROL;
UECFG1X = EP_SIZE(ENDPOINT0_SIZE) | EP_SINGLE_BUFFER;
UEIENX = (1<<RXSTPE);
usb_configuration = 0;
        }
        if ((intbits & (1<<SOFI)) && usb_configuration) {
                t = debug_flush_timer;
                if (t) {
                        debug_flush_timer = -- t;
                        if (!t) {
                                UENUM = DEBUG_TX_ENDPOINT;
                                while ((UEINTX & (1<<RWAL))) {
                                        UEDATX = 0;
                                }
                                UEINTX = 0x3A;
                        }
                }
        }
if (intbits & (1<<SUSPI)) {
// USB Suspend (inactivity for 3ms)
UDIEN = (1<<WAKEUPE);
usb_configuration = 0;
usb_suspended = 1;
#if (F_CPU >= 8000000L)
// WAKEUPI does not work with USB clock freeze
// when CPU is running less than 8 MHz.
// Is this a hardware bug?
USB_FREEZE(); // shut off USB
PLLCSR = 0; // shut off PLL
#endif
// to properly meet the USB spec, current must
// reduce to less than 2.5 mA, which means using
// powerdown mode, but that breaks the Arduino
// user's paradigm....
}
if (usb_suspended && (intbits & (1<<WAKEUPI))) {
// USB Resume (pretty much any activity)
#if (F_CPU >= 8000000L)
PLL_CONFIG();
while (!(PLLCSR & (1<<PLOCK))) ;
USB_CONFIG();
#endif
UDIEN = (1<<EORSTE)|(1<<SOFE)|(1<<SUSPE);
usb_suspended = 0;
return;
}
}


// Misc functions to wait for ready and send/receive packets
static inline void usb_wait_in_ready(void)
{
while (!(UEINTX & (1<<TXINI))) ;
}
static inline void usb_send_in(void)
{
UEINTX = ~(1<<TXINI);
}
static inline void usb_wait_receive_out(void)
{
while (!(UEINTX & (1<<RXOUTI))) ;
}
static inline void usb_ack_out(void)
{
UEINTX = ~(1<<RXOUTI);
}



// USB Endpoint Interrupt - endpoint 0 is handled here.  The
// other endpoints are manipulated by the user-callable
// functions, and the start-of-frame interrupt.
//
ISR(USB_COM_vect)
{
        uint8_t intbits;
const uint8_t *list;
        const uint8_t *cfg;
uint8_t i, n, len;
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
uint16_t desc_val;
const uint8_t *desc_addr;
uint8_t desc_length;

UENUM = 0;
intbits = UEINTX;
if (intbits & (1<<RXSTPI)) {
bmRequestType = UEDATX;
bRequest = UEDATX;
read_word_lsbfirst(wValue, UEDATX);
read_word_lsbfirst(wIndex, UEDATX);
read_word_lsbfirst(wLength, UEDATX);
UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
if (bRequest == GET_DESCRIPTOR) {
list = (const uint8_t *)descriptor_list;
for (i=0; ; i++) {
if (i >= NUM_DESC_LIST) {
UECONX = (1<<STALLRQ)|(1<<EPEN);  //stall
return;
}
pgm_read_word_postinc(desc_val, list);
if (desc_val != wValue) {
list += sizeof(struct descriptor_list_struct)-2;
continue;
}
pgm_read_word_postinc(desc_val, list);
if (desc_val != wIndex) {
list += sizeof(struct descriptor_list_struct)-4;
continue;
}
pgm_read_word_postinc(desc_addr, list);
desc_length = pgm_read_byte(list);
break;
}
len = (wLength < 256) ? wLength : 255;
if (len > desc_length) len = desc_length;
list = desc_addr;
do {
// wait for host ready for IN packet
do {
i = UEINTX;
} while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
if (i & (1<<RXOUTI)) return; // abort
// send IN packet
n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
for (i = n; i; i--) {
pgm_read_byte_postinc(UEDATX, list);
}
len -= n;
usb_send_in();
} while (len || n == ENDPOINT0_SIZE);
return;
                }
if (bRequest == SET_ADDRESS) {
usb_send_in();
usb_wait_in_ready();
UDADDR = wValue | (1<<ADDEN);
return;
}
if (bRequest == SET_CONFIGURATION && bmRequestType == 0) {
usb_configuration = wValue;
debug_flush_timer = 0;
usb_send_in();
cfg = endpoint_config_table;
for (i=1; i<7; i++) {
UENUM = i;
UECONX = 1;
pgm_read_byte_postinc(UECFG0X, cfg);
pgm_read_byte_postinc(UECFG1X, cfg);
}
        UERST = 0x1E;
        UERST = 0;
return;
}
if (bRequest == GET_CONFIGURATION && bmRequestType == 0x80) {
usb_wait_in_ready();
UEDATX = usb_configuration;
usb_send_in();
return;
}
if (bRequest == GET_STATUS) {
usb_wait_in_ready();
i = 0;
if (bmRequestType == 0x82) {
UENUM = wIndex;
if (UECONX & (1<<STALLRQ)) i = 1;
UENUM = 0;
}
UEDATX = i;
UEDATX = 0;
usb_send_in();
return;
}
if ((bRequest == CLEAR_FEATURE || bRequest == SET_FEATURE)
  && bmRequestType == 0x02 && wValue == 0) {
i = wIndex & 0x7F;
if (i >= 1 && i <= NUM_ENDPOINTS) {
usb_send_in();
UENUM = i;
if (bRequest == SET_FEATURE) {
UECONX = (1<<STALLRQ)|(1<<EPEN);
} else {
UECONX = (1<<STALLRQC)|(1<<RSTDT)|(1<<EPEN);
UERST = (1 << i);
UERST = 0;
}
return;
}
}
                if (wIndex == JOYSTICK_INTERFACE) {
                        if (bmRequestType == 0xA1) {
                                if (bRequest == HID_GET_REPORT) {
                                        usb_wait_in_ready();
for (i=0; i<13; i++) {
UEDATX = joystick_report_data[i];
}
                                        usb_send_in();
                                        return;
}
}
}
                if (wIndex == DEBUG_INTERFACE) {
                        if (bRequest == HID_GET_REPORT && bmRequestType == 0xA1) {
                                len = wLength;
                                do {
                                        // wait for host ready for IN packet
                                        do {
                                                i = UEINTX;
                                        } while (!(i & ((1<<TXINI)|(1<<RXOUTI))));
                                        if (i & (1<<RXOUTI)) return;    // abort
                                        // send IN packet
                                        n = len < ENDPOINT0_SIZE ? len : ENDPOINT0_SIZE;
                                        for (i = n; i; i--) {
                                                UEDATX = 0;
                                        }
                                        len -= n;
                                        usb_send_in();
                                } while (len || n == ENDPOINT0_SIZE);
                                return;
                        }
                        if (bRequest == HID_SET_REPORT && bmRequestType == 0x21) {
if (wValue == 0x0300 && wLength == 0x0004) {
uint8_t b1, b2, b3, b4;
                                        usb_wait_receive_out();
b1 = UEDATX;
b2 = UEDATX;
b3 = UEDATX;
b4 = UEDATX;
                                        usb_ack_out();
                                        usb_send_in();
if (b1 == 0xA9 && b2 == 0x45 && b3 == 0xC2 && b4 == 0x6B)
_reboot_Teensyduino_();
if (b1 == 0x8B && b2 == 0xC5 && b3 == 0x1D && b4 == 0x70)
_restart_Teensyduino_();
}
}
                }
if (bRequest == 0xC9 && bmRequestType == 0x40) {
usb_send_in();
usb_wait_in_ready();
_restart_Teensyduino_();
}
        }
UECONX = (1<<STALLRQ) | (1<<EPEN); // stall
}



Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on May 02, 2018, 03:32:06 AM
Yessss!
It works!
Needs a bit code cleaning now, but it works!
The single Teensy++ 2.0 can act as two independent Joysticks with 5 axis, 32 buttons and 4x8-way hats each!
No additional software needed!
Yippieh!
Code will follow once the cleaning is done...

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on May 02, 2018, 08:25:46 AM
Alright, here we are.
This is the full set of files required, including sources (including new USB Type), circuit diagram, compiled hex file for the Teensy++ 2.0 including "Loader" to load the hex into your Teensy.
The device needs no drivers, it will be detected automatically by Windows 7 or newer.

https://www.mediafire.com/file/fskhz3h3yy21qy6/Mike%27s_Controller_Box_v0.01.7z

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Storebror on May 07, 2018, 08:14:10 AM
The box survived it's first flight on sunday.

Few pictures of the wiring.
The beginning looks confusing, it's the "column" wiring between toggle switches, momentary switches and red buttons.
This has to look odd since the layout of the controls on our box isn't consistent.
(https://www.sas1946.rocks/flickr/storebror/960/27083107847_bf18b3c6a1_c.jpg) (https://www.sas1946.rocks/flickr/storebror/960/27083107847_19e0786314_o.jpg)

Next are the diodes in that section. My initial idea was to use the diodes' wires to build up the row matrix. Looks ugly and turns out to be the wrong idea. To top it off, I've made a mistake half way through when I've soldered some diodes in opposite way. I thought I could turn them around, but this caused an error later on. That error is still present in this picture but it's solved meanwhile. If you find the issue, feel free to name it!
(https://www.sas1946.rocks/flickr/storebror/968/41052956145_0f4e215bf9_c.jpg) (https://www.sas1946.rocks/flickr/storebror/968/41052956145_e58057e970_o.jpg)

Proceeding with the remaining buttons and potentiometers, I've used some solid copper wire for the row chain instead. Looks much better. Note the capacitors between poti wiper and ground, in order to eliminate readout spikes (works perfectly!):
(https://www.sas1946.rocks/flickr/storebror/866/27083106657_3634591edd_c.jpg) (https://www.sas1946.rocks/flickr/storebror/866/27083106657_0e542a8855_o.jpg)

Now to the wiring between rows/columns/potis and the Teensy's base board:
(https://www.sas1946.rocks/flickr/storebror/979/41233902574_0cb741f78f_c.jpg) (https://www.sas1946.rocks/flickr/storebror/979/41233902574_e55fbcde6a_o.jpg)

This is how the wires arrive at the Teensy.
Note that the pins are soldered to the "wrong" side of the Teensy.
This is intentional.
There's not enough room in the case to put the pins on the right (lower) side, but I didn't want to lose the ability to unplug the Teensy board either.
This was the only possible compromise and it works just well.
Note that there's a 10µF capacitor soldered to the +5V/GND pins right to the reset button, with the capacitor lying below the board, in order to stabilize power supply.
(https://www.sas1946.rocks/flickr/storebror/963/28081041188_ec0e0ce3bf_c.jpg) (https://www.sas1946.rocks/flickr/storebror/963/28081041188_8b56b4c9f8_o.jpg)

Another image showing how the row/column/poti wires are grouped by functionality:
(https://www.sas1946.rocks/flickr/storebror/911/41907762662_50c3ef7762_c.jpg) (https://www.sas1946.rocks/flickr/storebror/911/41907762662_4d7f115f9c_o.jpg)

Last but not least, the final result as closed box. The USB connector gives an idea how small it is - which was just my intention.
(https://www.sas1946.rocks/flickr/storebror/959/41951732281_5fd7a30b78_c.jpg) (https://www.sas1946.rocks/flickr/storebror/959/41951732281_8e9451039e_o.jpg)

]cheers[
Mike'
Title: Re: Mike's new project
Post by: SAS~Gerax on May 07, 2018, 08:43:58 AM
Dem Ingenieur ist nix zu schwör.  ;)
Title: Re: Mike's new project
Post by: Pivoyvo on May 08, 2018, 12:28:59 AM
Respekt für diese Arbeit
Title: Re: Mike's new project
Post by: Stainless on May 08, 2018, 04:50:13 AM
Mike have you had any problems with the USB stalling or crashing?

When I was writing my joystick drivers I initially started along the same sort of path you followed, but I found huge delays between physical inputs and the data in game.

I also found that if the USB was not read from fast enough, the USB buffer would overflow and crash the device.

I eventually gave up and switched to HID, but even that was complex.

I now have one cpu thread per connected device, they poll the HID interface every 8 milliseconds and do async reads everywhere.

This works very well, but I have a lot of thread management going on throughout my engine and I am starting to think HID was a bad idea.

Title: Re: Mike's new project
Post by: SAS~Storebror on May 08, 2018, 05:04:28 AM
The interface in use actually is the generic HID interface since that's the one which works without any need for additional drivers on Windows.
I've faced the stalled USB protocol issue once briefly, when my initial sketch just polled the current button/switch/axis status and forwarded it straight to the PC.
That's a bad idea.
Even the poor old 8-bit 16MHz AVR CPU of my Teensy++ 2.0 manages to operate roughly 50.000 loops per second in this polling mode, and it's simply impossible to offload the amount of data on USB reliably that way.
When I tried, all test applications reading the raw USB data stalled and DirectInput could hardly keep up.

What I did now is to smoothen out spikes from axis readout both physically (1µF Capacitor on all poti wipers, with 10k internal resistance this results in a 16Hz Low Pass) and programmatically (ignore any changes below 2 steps, and smoothen readout across last 10 results).
Then I'm reacting on changed values only, so instead of polling anything, I'll only send a fresh datagram when something really changed (except for startup that is).
This way I got rid of 99.99% of the data being transferred and didn't encounter any USB stall on my 4-hour test session last sunday.

Cheers!
Mike
Title: Re: Mike's new project
Post by: SAS~Malone on May 08, 2018, 08:12:04 AM
whoa, i've stumbled upon some dark arts magick trickery here, lol ..... :D
looking pretty interesting, i must say
Title: Re: Mike's new project
Post by: SAS~Storebror on May 08, 2018, 10:44:56 PM
Temporary Labels for finding the best layout:
(https://www.sas1946.rocks/flickr/storebror/952/41988781021_dc946107fd_c.jpg) (https://www.sas1946.rocks/flickr/storebror/952/41988781021_82506b0e1f_o.jpg)

Semi-Final Labelling:
(https://www.sas1946.rocks/flickr/storebror/831/41988780391_55e5b3a0da_c.jpg) (https://www.sas1946.rocks/flickr/storebror/831/41988780391_5cb98cf7ce_o.jpg)

Note the white stickers: These are non-working buttons (hardware issue). Replacements have been ordered and should arrive today.
With hindsight, the upper and lower momentary switch rows should have been swapped, to make trim controls easier to reach, but that's life: You always find something left to improve ;)

Cheers!
Mike
Title: Re: Mike's new project
Post by: whistler on May 09, 2018, 05:14:05 AM
Very good job Mike, congrats. Now, where is the shopping cart button in this forum and will v2 implement leds? :P
Title: Re: Mike's new project
Post by: SAS~Storebror on May 09, 2018, 06:27:11 AM
Well the shopping cart...
Let me try to prepare a list of sample parts you need for this project.

First there's the case. I chose a Hammond 1599HTSBK (https://www.hammfg.com/part/1599HTSBK), but any other short slop enclosure will do as well, however mind the size - mine turned out to be pretty small for the number of buttons, switches and potis I've used: https://www.hammfg.com/electronics/small-case/plastic/1599t.pdf
My Desk case is available e.g. here: https://www.conrad.com/ce/en/product/485739/Desk-casing-220-x-110-x-40-Acrylonitrile-butadiene-styrene-Black?ref=searchDetail
It's actually the most expensive part of the whole list.
(https://s14.postimg.cc/6l25b714x/Geh_use.jpg)

Then you need the Teensy Board - or any other Arduino one, if you are willing to follow my path of self-developing the whole software.
Note: There's full-featured tools like MMJoy2 out there, but they're limited to the purpose chosen by their designer, for instance my "two joysticks on one board" solution was not available anywhere.
And if you chose such ready-to-use tools, you need to have matching hardware.
MMJoy2 supported boards are listed here: https://github.com/MMjoy/mmjoy_en/wiki/Controllers-(compatible-base-boards)
My solution is strictly tied to the Teensy++ 2.0 (or Teensy 2.0, without "++", which is basically the same with less pins), which is supported by MMJoy2 as well, so for the moment, stick to that Teensy++ 2.0 to be on the safe side for both solutions.
Teensy 2.0 is available e.g. here: https://www.conrad.com/ce/en/product/1656375/PCB-design-board-Teensy20-Compatible-with-Arduino?ref=searchDetail
My Teensy++ 2.0 came from China, half the price for double the pins: https://www.ebay.com/itm/Teensy-2-0-USB-AVR-Development-Board-ISP-U-Disk-Experimental-Board-AT90USB1286/123112722663?hash=item1caa1710e7:g:6lcAAOSwn4da6sP0
(https://s14.postimg.cc/8pmiccncx/Teensy.png)

While you can solder all wires directly to the Teensy, I'd recommend to use a perfboard so you won't damage the Teensy so easily, and in order to be able to plug/unplug it.
Any solder point grid board will do, e.g. this: https://www.conrad.com/ce/en/product/529580/WR-Rademacher-VK-C-811-2-Solder-Point-Grid-Plate-L-x-W-100-mm-x-75-mm-HP/?ref=no_search_results&rt=no_search_results&rb=1
(https://s14.postimg.cc/krhw6gu0h/Platine.jpg)

In order to be able to plug/unplug the Teensy, you need pins and sockets.
Two of these: https://www.conrad.com/ce/en/product/737416/W-P-Products-943-13-020-00-Multi-Pin-Connector-Number-of-pins-1-x-20-Nominal-current-details-3-A/?ref=no_search_results&rt=no_search_results&rb=1
...and either two of these: https://www.conrad.com/ce/en/product/738335/W-P-Products-153-020-1-50-00-Precision-Socket-Connector-Number-of-pins-1-x-20-Nominal-current-details-3-A/?ref=bundles&rt=bundles&rb=1
...or one of these: https://www.conrad.com/ce/en/product/183817/IC-socket-Contact-spacing-1524-mm-Number-of-pins-40-ASSMANN-WSW?ref=searchDetail
(https://s14.postimg.cc/bwh1vxpsh/Stiftleiste.jpg)

Now in my box there are 5 Potis like these: https://www.amazon.com/Uxcell-a15011600ux0235-Linear-Rotary-Potentiometer/dp/B01DKCUVMQ/ref=sr_1_3?ie=UTF8&qid=1525866955&sr=8-3
They need to have a nominal resistance of 10k Ohms, as that's the internal resistance of the Teensy's I/O pins.
(https://s14.postimg.cc/pq5el05j5/Poti.jpg)

Furthermore we have 10 toggle switches, I chose the "On/On" type so we can have button actions regardless the switch position:
https://www.amazon.com/Cylewet-Positions-Connection-Arduino-CYT1015/dp/B01MY309G3/ref=pd_sbs_263_15?_encoding=UTF8&pd_rd_i=B01MY309G3
(https://s14.postimg.cc/izoxbk2xt/Schalter.jpg)

Another 10 of the momentary On/Off/On SPDT switches, e.g. for Trim Input:
https://www.amazon.com/SupportTM-Momentary-Miniature-Toggle-Dashboard/dp/B01LYGWWHA/ref=pd_sbs_469_1?_encoding=UTF8&pd_rd_i=B01LYGWWHA&pd_rd_r=MMP6MBFZBQ9KAEXAENVE&pd_rd_w=qtTNe&pd_rd_wg=17bLa&psc=1&refRID=MMP6MBFZBQ9KAEXAENVE
(https://s14.postimg.cc/e11ex0bzl/Momentan-_Schalter.jpg)

And the pushbuttons.
10 red: https://www.amazon.com/10pcs-Mini-Button-Momentary-Switch/dp/B01DWFPFA4/ref=sr_1_4?s=industrial&ie=UTF8&qid=1525867796
10 green: https://www.amazon.com/CynKen-Miniature-Momentary-Button-Switch/dp/B06WVCFNSN/ref=sr_1_41?s=industrial&ie=UTF8&qid=1525867828
10 yellow: https://www.amazon.com/10Pcs-Button-Momentary-Switch-Yellow/dp/B01DWFPGUI/ref=sr_1_4?s=industrial&ie=UTF8&qid=1525867845
10 blue: https://www.amazon.com/Exiron-PBS-110-Lockless-button-Switch/dp/B07CWXV9S9/ref=sr_1_19?s=industrial&ie=UTF8&qid=1525867887
and some spares, including white, black or anything else - you need one of these for reset at least: https://www.amazon.com/Cylewet-Momentary-Button-Switch-CYT1078/dp/B0752RMB7Q/ref=sr_1_10?s=industrial&ie=UTF8&qid=1525867975
(https://s14.postimg.cc/mw297kqi9/Taster_rot.jpg)

100pcs Switching Diodes. Use 1N4148, nothing else: https://www.amazon.com/100-1N4148-Switching-Signal-Diode/dp/B0714P3P6L/ref=sr_1_1?s=industrial&ie=UTF8&qid=1525868114
(https://s14.postimg.cc/q2wsr4qcx/Diode.jpg)

5 pcs 1µF Ceramic Capacitors for de-spiking Poti readouts: https://www.conrad.com/ce/en/product/1578616/Ceramic-capacitor-THT-1-F-50-V-20-?ref=searchDetail
(https://s14.postimg.cc/qsfl3i6c1/Keramik-_Kondensator.jpg)

1pc 10µF Elko for power supply buffering: https://www.conrad.com/ce/en/product/445656/Electrolytic-capacitor----Radial-lead--------25-mm----10-F----5/?ref=search&rt=search&rb=1
(https://s14.postimg.cc/fg2zlq5cx/Elko.jpg)

10 meters of wiring, at least: https://www.conrad.com/ce/en/product/1437297/Strand-1-x-004-mm-Red-BELI-BECO-L-10410-rot?queryFromSuggest=true
(https://s14.postimg.cc/a4o310ypt/Litze.jpg)

Some Bell wire for the collective row/column lines: https://www.amazon.com/Woods-0452-Bell-Wire-25-Feet/dp/B000UEAQ64/ref=sr_1_cc_2?s=aps&ie=UTF8&qid=1525868623

A Mini USB cable to connect your Teensy to the PC: https://www.amazon.com/Monoprice-10-Feet-mini-B-28AWG-103897/dp/B001UJE7FO/ref=sr_1_8?s=industrial&ie=UTF8&qid=1525868699

That's all folks.
I recommend to take a close look at some local shop.
While it's fine to order the Teensy from China (and this safes quite some money), I've finally ordered most of the smaller stuff (switches/buttons/diodes etc.) from local ebay dealers because the chinese stuff didn't always get through the customs and some of the parts were wrong, damaged, not in the right numbers etc. Better local than sorry.

Cheers!
Mike
Title: Re: Mike's new project
Post by: whistler on May 09, 2018, 07:08:08 AM
Oh, I meant if there were plans to produce more on demand! Thanks for all details

Resistors, capacitors, inductors... way beyond my understanding. I could solder it neatly and put it tidily together but half of the switches won't work, if any. Amperage, voltage... wouldn't know how to troubleshoot. The arduino boards are an awesome product, but you definitively need a good knowledge base to get anything interesting working. And then setting up the software... I'd rather pay but then, the joy when it all works.. that, is priceless.

Again, very good job, and thanks for having shared this project with us. I wonder why gaming switch panels are so rare and expensive, low demand sure but the components are cheap, firmware and software are out there. I guess facilities, personnel, patents, laws, licensing, packaging, marketing, warranties... get in the way. A home based startup would be the answer, just like the Ed Tracker Pro that I like so much.

Have fun with your toy!
Title: Re: Mike's new project
Post by: SAS~Storebror on May 09, 2018, 07:37:08 AM
Building this on demand is no option for me, as I simply don't have the time and shipping costs would be as much as it would take to build that thing on your own.
If anyone else wants to jump the train, be my guest and build this neat little thing.

Knowledge IMHO is not that much of an issue.
A soldering device for less than $10 would be enough.
You don't need to know how to program Arduino boards if you exactly rebuild my project.
The binary and the required loader are available. Start the loader, open binary file, push reset button on Arduino: Done.

Cheers!
Mike
Title: Re: Mike's new project
Post by: LuseKofte on February 13, 2019, 03:53:19 AM
For those wanting to buy good button boxes and throttles with them , there is a lot in the marked. Guys in the community that have made a little income. Like all devices buttons is probably the thing that increase price a lot.
Buy a Robinson helicopter setup and compare the price with a Huey setup. You will find the price to be up to 3 times higher. And only difference is amount of buttons.
That said, thank you Mike. Now visualize a guy that have wrecked his phone into a wall because a payment system did not work. (boy that did not make the payment cheaper)
For simplicity we call that guy me, I will read this topic 4 times more. But before I do I Need to ask one simple question that I could not read the first time.
Using the software like Joystick gremlin overcome the restriction of too many usb-card  connected for IL 2 to recognize?
Title: Re: Mike's new project
Post by: SAS~Storebror on February 13, 2019, 05:00:44 AM
I haven't ever been in the situation myself, so I can only answer half of the question, the other half you can probably answer better than myself.
What you can do with Joystick Gremlin is: You can map multiple Joysticks to one.
That way you can for instance turn two joysticks with two axis and 20 buttons each into one Joystick with four axis and 40 buttons.

The main issue with this is if you do so, in the example above where you've had two joysticks before, by adding Joystick Gremlin to the mix you will end up with having 3 Joysticks in your system.
The two physical ones plus the new "virtual" Gremlin device.
This is where you need to check yourself: With the limit of max. 4 devices in IL-2, can you select an arbitrary subset of 4 joysticks out of the <n> available ones on your system to use these arbitrary 4 in IL-2, or will IL-2 always pick the "first 4" it sees and use them?
If you have the choice of arbitrary subsets then everything's fine and you can use Gremlin to "group up" physical sticks.
If IL-2 always chooses the "first 4" then you're stuck here, because the Gremlin devices will come up in mixed order with the physical ones, there's no way to make all Gremlin devices to appear "first".

]cheers[
Mike
Title: Re: Mike's new project
Post by: LuseKofte on February 13, 2019, 05:22:10 AM
Problem is that it was always the rudder that was excluded, and when I rearranged the USB hub for switching off other usb cards they changed places and I had to reassign those USB devices all over again in other simulators.
I have to spend some time figuring out what to do, I miss old IL 2. Somehow SP in GB is plain boring and do not have the same atmosphere as old IL 2
Title: Re: Mike's new project
Post by: SAS~Storebror on March 31, 2019, 01:53:42 AM
Hi folks,

Blaubär decided to build the switchbox and on that occasion I had to realize that I forgot to post a couple of updates, including a very important one.

This is the new circuit diagram, click for scalable SVG vector graphic.
Note one important change: Pin D6 "6_LED" is not used for inputs anymore. The reason is that due to the onboard LED of the Teensy, this pin cannot reliably be used as INPUT_PULLUP pin.
(https://storebror.it.cx/sas/switchbox_v2.png) (https://storebror.it.cx/sas/switchbox_v2.svg)

The full set of project files can be found for download here:
Mike's Controller Box v2 (https://storebror.it.cx/sas/Mikes_Controller_Box_v0.02.zip)

It includes a few changes to the program files, mainly for further handling of potentiometer readout spikes and bouncing buttons.
Precompiled Hex file is included.
If you don't want to program using Arduino Tools yourself, these are the steps to get your board started:


Cheers!
Mike
Title: Re: Mike's new project
Post by: Blaubaer on March 31, 2019, 03:38:03 AM
Let's have a big hand for Mike! Thanks for the cool switchbox with 80 buttons/switches and 5 rotary potentiometers. (The inscription is still missing.)

Regards, Michael
(https://i.postimg.cc/RZG7Y581/20190331-155036.jpg) (https://postimg.cc/CRBnZtwd)
Title: Re: Mike's new project
Post by: W0W66 on March 31, 2019, 08:30:01 AM
This looks awesome.  Well beyond my abilities, but, looks like it is well worth a try.

Thanks for posting this.
Title: Re: Mike's new project
Post by: sniperton on March 31, 2019, 01:30:23 PM
Jesus, you must have exceptional muscle memory, this thing has as many buttons as an ordinary laptop keyboard. How can you remember and correctly locate the proper button or switch blindly and using only your left hand fingers? Just curious  :D
Title: Re: Mike's new project
Post by: SAS~Skylla on April 01, 2019, 02:55:34 AM
I suppose he is going to label them.
At least I would do so ;)

But aside from this, after having to look at the labels for a while I would certainly expect to find the right button "blind" at some point.

Anyway, congratulations Blaubär!
Title: Re: Mike's new project
Post by: SAS~Storebror on April 01, 2019, 02:59:16 AM
That's exactly the idea Skylla.
The Switchbox covers all the key commands you would usually have to look up from manuals first.
Most of the keys you will find blindly after a couple of days, otherwise the coloured keys can be used to group commands by their "topic", e.g. all engine commands on one colour, all weapon commands on another etc. so in case you have to look for the right key, you don't need to scan all 40 keys but just 10 of them.

To me the switchbox proved very valuable.
No more funny and fancy unintended things happening just because I pressed Alt+P where Shift+P would have been the right combo...

]cheers[
Mike
Title: Re: Mike's new project
Post by: sniperton on April 01, 2019, 07:36:36 AM
No more funny and fancy unintended things happening just because I pressed Alt+P where Shift+P would have been the right combo...

Don't take me wrong, I don't argue for using key combinations instead, it's just the sheer amount of buttons that makes me wonder what's the advantage over assigning e.g. engine controls to QWERTZ, weapon controls to ASDFGH, etc, etc. It's something like a very detailed 2D map without the keybord F and J bumps to help you locate them blindly.

I find it more ergonomic to define modifier keys on my left hand hotas and to multiply this way the functionality of my right hand joystick buttons. Flaps, pitch control, all three trim axes and supercharger are all on my hat switch for instance, the keyboard is only used for engine selection, bomb selection, start engine, level autopilot, i.e. for functions where my left hand can leave the throttle/hotas part without dire consequencies.

Anyway, congrats to both of you for designing and producing this masterpiece.  ;D
Title: Re: Mike's new project
Post by: Blaubaer on April 07, 2019, 04:29:07 PM
I suppose he is going to label them.
At least I would do so ;)
But aside from this, after having to look at the labels for a while I would certainly expect to find the right button "blind" at some point.
Anyway, congratulations Blaubär!
Thank you Skylla. The labels are helpful, you quickly get used to the switchboard and it gives me a feeling of more realism.

(https://i.postimg.cc/J04CCpy4/20190408-001058.jpg) (https://postimg.cc/jw9FzQQG)

Regards, Michael
Title: Re: Mike's new project
Post by: sniperton on June 19, 2019, 04:09:11 AM
Just an insanity check, please forgive me for my ignorance   ::)
I'm a little bit perplexed by KiCad's schematics for the Teensy++ 2.0.

Am I right assuming that...

... red pins #2 to #20 are for button matrix and correspond to the physical pins B7, D0 to D7, E0 to E1, and C0 to C7? (with the remark that #9/D6 should be skipped)

... red pins #24 to #31 are for analog input and correspond to the physical pins F0 to F7?

Title: Re: Mike's new project
Post by: SAS~Storebror on June 19, 2019, 04:33:09 AM
Yes, that's correct.
The PIN's names differ according to the software being used.
B7, D0 to D7 etc. are C language pin names:
(https://i.postimg.cc/Fz3hNw80/pinout4a.png)

The kicad drawing uses simple numbers for the pins, but I've added the Arduino language pin names to it, like "INT0_PWM" etc., according to the arduino naming specification:
(https://i.postimg.cc/436vH3c0/pinout4b.png)

]cheers[
Mike
Title: Re: Mike's new project
Post by: sniperton on June 19, 2019, 06:34:44 AM
Thanks Mike for the clarification.
As I see (e.g. here: https://forum.il2sturmovik.com/topic/18379-teensy-20-mmjoy2-firmware-joystick-controller/?tab=comments#comment-288080 (https://forum.il2sturmovik.com/topic/18379-teensy-20-mmjoy2-firmware-joystick-controller/?tab=comments#comment-288080)) MMJoy identifies MCU ports by the C language code, so I guess I'd better stick to them as I don't want to go into custom coding as you did (I only need a 6x6 matrix and 8 analog inputs).
Title: Re: Mike's new project
Post by: SAS~Storebror on June 19, 2019, 08:44:29 AM
Well in that case...
MMJoy works great within it's limits.
The main reason I went away from MMJoy was the fact that I wanted to turn my box into two controllers.
That required some deep hacks in the USB code of the Teensy library, but eventually it worked.
If 6x6 + 8 axis is enough for you, then MMJoy will definitely do the trick for you.

]cheers[
Mike
Title: Re: Mike's new project
Post by: Zechsau on March 06, 2021, 05:19:43 AM
Hi everyone I'm Paul, new to the forum, IL-2 and coding.
My dad saw this thread and asked me whether I could build something similar to this. Seems to me, he didn't got the memo about the non-intuitive handling of Mike's panel. Yet here I am, challenging myself to build a Panel for my old man and to enrichen my skillset by adding coding and microelectronics. I'll keep you updated with any problems, shenanigans, misunderstandings I ran into, so you all don't have to learn your own lessons.
@Mike, as we two agreed (well you asked me to, and hereby I agree) I'll post my questions here in order to make this accessible to everyone.
What I did so far: Layout on the box, prepared wiring and pushbuttons (those tiny ones that come with any arduino kit, I had them flying around in my drawer for some years now) on breadboards and connected according to your wiring diagram with a few minor differences, due to function and use of the switches.
Further I coded some debouncing routines for practice. Now I wanted to run the arduino sketch on my Board and connect that to the breadboard skidaddle, to get a clean sketch that I can start working from. I included all text lines and all files accordingly. The teensy as well as my breadboard layout are working as expected: perfectly fine. They get recognized as 'Gamecontroller' and there are two controllers but somehow both have the same name. But that doesn't bother me at all. After I found the pins you used for 'hat switches' i excluded them from my wiring, as I only use a total of around 55 functions.

Then I headed over to arduino to code my LED functions but ran into a Problem right off the start. Tried to check your Arduino sketch so I am sure to have a clean start, and it runs into the following message. I don't know if this is just some missing files in the correct arduino-folder, but I did everything exactly as you described in your readMes. I wish I could attach a screenshot but as I am new here, I'll have to post some more to get these rights.


The Arduino Error (can't even put code or the complete message in here)
/////


  return false;

         ^~~~~

cannot convert 'bool' to 'ListNode<SwitchButton*>*' in return
/////

I briefly went through your USB-Code and my mind dropped dead ;D. I intend to run my dad's box with only 35 pushbuttons and 20 toggle ON/ON switches, which further I intend to use as single use only and therefor I only have a total of 55 buttons plus the 5 potis. If I have to dive deeper than that into the hacks of dark sorcery and witchcraft, I'll put that on my bucket... eeeerrhm to-do list as well ;D. But for now everything works just a okay.

Unfortunately, I can't upload any files for you all to get a better idea of the whole project yet, as I am new to this forum and this is my very first post.

Cheers , Paul


Title: Re: Mike's new project
Post by: SAS~Storebror on March 06, 2021, 08:39:47 AM
Hi Paul,

They get recognized as 'Gamecontroller' and there are two controllers but somehow both have the same name. But that doesn't bother me at all.
That's a Windows/DirectX issue. In fact the two controllers have different names, but DirectX fails to query the 2nd node on the same physical device for it's name, that's why.
You can cross-check using Pointy's Joystick Test application: http://www.planetpointy.co.uk/joystick-test-application/
Using "Raw" input mode instead of DirectX, you will see two different controller names, whereas "DirectX" input mode shows only one.

The Arduino Error (can't even put code or the complete message in here)
/////


  return false;

         ^~~~~

cannot convert 'bool' to 'ListNode<SwitchButton*>*' in return
/////
Change that to "return NULL;" and you'll be fine.
The original version of LinkedList.h had that issue, but older Arduino versions didn't care. Newer do however, so LinkedList got updated: https://github.com/ivanseidel/LinkedList

Code posting option is available to you now, it's a Spambot protection that kept you from posting code in 1st post.

]cheers[
Mike
Title: Re: Mike's new project
Post by: Zechsau on March 06, 2021, 10:40:17 AM
Thanks Mike, that was a bullseye.

I'll keep you updated!

Here is Cider's Combat Case, for everyone interested. The layout shows the LEDs and intended Functions.
(https://i.postimg.cc/XvDZp0tk/Huntingbox-Layout.jpg) (https://postimg.cc/nsBVPNFX)

(https://i.postimg.cc/pdyrN6yh/img-1463.jpg) (https://postimg.cc/67Jtnzrt)


Cheers, Paul
Title: Re: Mike's new project
Post by: SAS~Storebror on March 07, 2021, 01:43:00 AM
Cheers Paul, looks great!

]cheers[
Mike
Title: Re: Mike's new project
Post by: Stainless on March 08, 2021, 11:00:29 AM
I just have a mental image of Mike as Rick Wakeman.

(https://upload.wikimedia.org/wikipedia/commons/8/84/Rickwakemanmoog.jpg)



Title: Re: Mike's new project
Post by: Zechsau on March 10, 2021, 03:02:11 PM
Back with a smaller update.

Controlling LEDs with joystickbuttons turns out to be quite the job. A little recap of what I did and had to abandon again.

At first I thought it'll be enough to get a wire to the respective row pins, but that didn't work. Reason: the matrix works with pulses (are to be found within in the code) not with the much simpler but slower voltage changes. Reason being that the chip is able to poll the whole matrix several times per millisecond when using pulses. For those who are interested in that, I'll have short explanation at the end of this post. Finding out the matrix or the so called event listener works with pulses inhibited me from using any further hardware solution for example transistors in order to get my LEDs working when the intended buttons are pushed.

@Mike: maybe you can enlighten me. I thought I can use the exact pin 'location' of each switch by the coordinates of rowPins and columnPins. Each time a Button is pressed, at least 2 specific pins used for the matrix are pulled to ground due to the pulses. That is the moment, the eventlistener implemented into the code registers a buttonpress and forwards that to the usb/pc. My idea was to use these coordinates to control my LED states similar to 'if pinColumnXYZ and pinRowXYZ are LOW, change LEDstate' (simplified so everyone can follow me here, if I have a working code I'll post that, ofcourse). Do you know of any way, to use the already built in eventlistener or do i have to write one myself and connect that to the existing code? Thanks for your answer in advance.

Cheers, Paul

P.S.: for those interested: The buttonmatrix consists of rows and pins, as shown in Mike's earlier posts. Each button terminal is attached to one 'row' and one 'column'. Theoretically, you can do any number of rows combined with any number of columns, as long as these can connect to your controllerboard. The benefit is that you can work 80 buttons (for Mike's example) with only 18 pins in total. If you wired each pin conventionally to each switch you'd need at least 80 pins, and that is a HUGE board. But i'm trailing off. By having all buttons arranged in a matrix where some are connected to rows and some to columns, you could easily distinguish each button. Basic state for colums AND rows is HIGH. Now if both pins are HIGH, how does the Teensy read a button press? The teensy sets the columns to a LOW pulse. One after the other. That happens so fast, that virtually it happens simultaneously on all column pins. If a button is pressed, while the LOW pulse is on the same column as pressed button, the teensy registers a LOW state and thus registers a buttonpress. As mentioned before this happens so fast, virutally all buttonpresses and even simultaneous buttonpresses are registered at same time, although technically one after the other. That speed of the LOW pulses is the problem I ran into when trying to hook in a 'listeneing' wire physically. It just doesn't work that way without the proper routine.
Title: Re: Mike's new project
Post by: SAS~Storebror on March 11, 2021, 03:36:15 AM
Hi Paul,

I have turned the rather general box design into something very customized for my case, where besides others changes, such an LED thing is implemented now as well.
I've added 3 LEDs to my board:

Furthermore (here it becomes a bit tricky), I've changed to box so that it can act in two different modes:

You can find the most recent sourcecode snapshot attached to this post.
Watch out for e.g. the green LED handling, it's all about remembering the time when a button got pressed, and keeping the LED lit until a timeout elapses.

]cheers[
Mike
Title: Re: Mike's new project
Post by: Zechsau on March 21, 2021, 09:45:20 AM
Thanks for the updated files, Mike!

I still have my problem to assign one specific button to light up one specific LED.Even your Sketch for the green LED doesn't work for me.

To get a bit more concise here: I want Button 65 (no 25 on Device 2) to deactivate the LED1 on Pin 22. My Problem is that I can't get the Arduino to recognize one specific buttonpress. I had an idea of using the eventlistener and the implemented getButtons function, yet that didn't work either.

 If I set the Buttonindex from your codesample to i == 65 instead of  i < 72, nothing happens. The device gets recognized by windows as well as the buttonpress itself. Even changing the code to work with button 25 just in case, nothing happens. Further I can't use your full codesample as you have the two-device mode implemented, and to be honest I don't want to include that level of complexety to my dad's device.

Just wanted to keep you posted.

I'll head back into all the forums trying to find a solution here.

Cheers, Paul
Title: Re: Mike's new project
Post by: SAS~Storebror on March 21, 2021, 11:33:35 AM
Not sure where the problem is honestly.
From my script, the LEDs are on Pins 24/25/26 (B4/B5/B6) (Blue/Yellow/Red).
In the loop() method, buttons get checked in the "buttonmatrix.getButtons()" call.
Whenever a button gets pressed, the Event handler "void buttonEvent(ButtonEvent listIndex)" will get called.
You can check the "buttonmatrix.button[listIndex].bindex" property for the index of the pressed button.
If that index matches your button number (note: The index is not necessarily the same number like what Windows shows you, it's a try&error thing after all), you should take note of the current time and add a few milliseconds to it (see "ledTime" filed in my script), turn on the LED of choice, and then in the loop() method check if the current time against the stored "ledTime" and if it's larger, turn off the LED again.

]cheers[
Mike