flycast icon indicating copy to clipboard operation
flycast copied to clipboard

VMU/Maple support for DreamConn+ controller

Open chrisvcpp opened this issue 2 years ago • 45 comments

Hi there! My name is Chris Daioglou, and I'm the creator of the DreamConn+ wireless controller, and the later VM2 for the Dreamcast It's a while now that I was thinking of suggesting fellow developers for an addition to the Flycast emulator.

As you may know, the DreamConn+ is the only wireless controller, that supports actual VMU & Rumble Pak, giving the same experience as the original wired controller. It's a while now, that I've added support for using the controller with Windows PC, making it compatible with Steam, games and emulators.

The only feature that lacks from any current emulation system, is the transmitting of Maple data regarding the VMU, or Rumble Pak. So, I was thinking that we could collaborate, in order for Flycast (Windows version at least) to transmit/communicate with an intermediate channel (i.e. local IP port) that the DreamConn+ could use to gather data and use with the actual VMU plugged into the controller. This should give the ultimate experience with using the emulator along with the DreamConn+, giving an experience as close to the original console as possible.

chrisvcpp avatar Nov 17 '23 18:11 chrisvcpp

Hi Chris, Sorry for not getting back to you earlier (!!) How do you see an integration of DreamConn+ with Flycast so that maple data can be exchanged? I see that it can be used with a PC, and I guess the controller part works with flycast through SDL. If using a local IP socket for maple data, would that work with more than one controller? Do you have an idea for the data format/protocol?

flyinghead avatar Dec 11 '24 12:12 flyinghead

Hi, Thank you for getting back to me! I actually already did dome progress with it. Please check a video preview here: https://x.com/CDaioglou/status/1852811345684533310 I used the later 2.4 version from the Git and made a custom built - only for Windows x64 since the DreamConnS controller is compatible only with Windows. At the moment I used something similar to named pipeline(instead of IP socket), and using only the LCD Maple messages for all Slot[1] ports : A1, B1, C1, D1 Didn't commit the changes to the project, as I know that Flycast aims into being cross-platform.. But, it would be great if you'd be willing to officially add support for the DreamConnS controller. I can imagine that using local IP sockets (one for each port, so 4 in total) would be awesome! The data format/protocol would be the Maple commands as they're! No need to make any special formatting. Just forwarding the Maple commands through the corresponding IP port would be more than enough to me. This way, we can add support for even more functionalities than just LCD data.For example, I could use all of the Maple commands for actually using all of the VMU features : LCD, save/load, buzzer, clock/date, etc. Let me know if this's something feasible and that you'd like to do..

Many thanks,- Chris

Στις Τετάρτη 11 Δεκεμβρίου 2024 στις 02:24:39 μ.μ. EET, ο χρήστης flyinghead ***@***.***> έγραψε:  

Hi Chris, Sorry for not getting back to you earlier (!!) How do you see an integration of DreamConn+ with Flycast so that maple data can be exchanged? I see that it can be used with a PC, and I guess the controller part works with flycast through SDL. If using a local IP socket for maple data, would that work with more than one controller? Do you have an idea for the data format/protocol?

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

chrisvcpp avatar Dec 11 '24 12:12 chrisvcpp

Yeah, I saw your video when you posted it on Twitter. Impressive work! Is there any way you could share your flycast changes? I could integrate them upstream. I don't have a device to test with so I need at least some working prototype code to boot me up.

flyinghead avatar Dec 11 '24 13:12 flyinghead

Sure thing! I only added a few lines of code to the maple_devs.cpp -> function u32 dma(u32 cmd) override -> section case MFID_2_LCD :

case MFID_2_LCD:{     DEBUG_LOG(MAPLE, "VMU %s LCD write", logical_port);     r32();     rptr(lcd_data,192);     if (!strcmp(logical_port, "A1") || !strcmp(logical_port, "B1") || !strcmp(logical_port, "C1") || !strcmp(logical_port, "D1"))     {         char sfile[30]; sprintf(sfile, "data\vmu_lcd_%s.dat", logical_port);         FILE* f = std::fopen(sfile, "wb");         if (f != NULL)         {             std::fwrite(lcd_data, 1, 192, f); std::fflush(f); std::fclose(f);         }     }    .     .     .

As you can see, I'm just writing the 192 bytes of the LCD-related data to a corresponding A1/B1/C1/D1 file - the rest of work is done by the DreamConn utility app.. In our case, working with IP ports will be much easier (and a better solution) : you will just have to forward the Maple messages related to VMU(A1/B1/C1/D1) to the IP port.Then I will handle everything through my application..

Στις Τετάρτη 11 Δεκεμβρίου 2024 στις 03:30:16 μ.μ. EET, ο χρήστης flyinghead ***@***.***> έγραψε:  

Yeah, I saw your video when you posted it on Twitter. Impressive work! Is there any way you could share your flycast changes? I could integrate them upstream. I don't have a device to test with so I need at least some working prototype code to boot me up.

— Reply to this email directly, view it on GitHub, or unsubscribe.

You are receiving this because you authored the thread.Message ID: @.***>

chrisvcpp avatar Dec 11 '24 15:12 chrisvcpp

Would it make sense for the DreamConn utility app to be the "network server", to which flycast would connect to? I suppose your utility must be running as long as you use a DreamConn+ controller. Whereas flycast may be opened and closed several times depending on which game the user is playing.

So the DreamConn utility could open a listening socket (or one socket per controller) on known ports. Flycast would then try to connect to this port at start up, and establish the connection. Then raw maple data would be sent to it.

A few more thoughts: It would help if flycast could detect that a given controller is a DreamConn+. That way flycast will only connect to the DreamConn utility when a DreamConn+ is detected, and correctly identify which maple port it's connected to. I guess a vendor/product ID is enough, or the controller name as reported by SDL.

As a first step, I can forward maple messages related to LCD screen update and beeper. These messages don't really need any answer from the controller or vmu. However, if we want to use other functions such as storage, flycast will have to wait for a maple response before proceeding.

Flycast will also need to know the controller configuration: which device is connected (vmu, rumble pack, none) on which port. I guess this can be done with the maple protocol on the network port.

flyinghead avatar Dec 12 '24 16:12 flyinghead

Hi, Yes, I could make the utility app to be the server. Whenever a controller is connected, I can open a specific TCP port (which corresponds to A1/B1/C1/D1). Regarding your thoughts:

  1. At the moment, when a DreamConn controller is connected, the utility app presents a virtual XBox360 controller (XInput) to Windows (up to 4 of them).This virtual controller can also report specific vendor/product ID, so yes, this is quite feasible and easy to do!

  2. You're quite right about this! Maple messages that do not require specific responses (other than "ACK") like LCD & BUZZER will be easier to begin with.For other more complex Maple commands (like saving/loading), we can do it later on - I can think of a few "tricks" that can make it easier and also keep Flycast as close to its original/current state as possible.

  3. For the controller configuration, this can easily be done by Flycast sending a "Get Condition" (command 9) to the TCP port.The utility will respond with the controller's state indicating which slots are occupied (Slot1 occupied means VMU is present, Slot2 occupied means Rumble is present).

    Στις Πέμπτη 12 Δεκεμβρίου 2024 στις 06:02:29 μ.μ. EET, ο χρήστης flyinghead @.***> έγραψε:

Would it make sense for the DreamConn utility app to be the "network server", to which flycast would connect to? I suppose your utility must be running as long as you use a DreamConn+ controller. Whereas flycast may be opened and closed several times depending on which game the user is playing.

So the DreamConn utility could open a listening socket (or one socket per controller) on known ports. Flycast would then try to connect to this port at start up, and establish the connection. Then raw maple data would be sent to it.

A few more thoughts: It would help if flycast could detect that a given controller is a DreamConn+. That way flycast will only connect to the DreamConn utility when a DreamConn+ is detected, and correctly identify which maple port it's connected to. I guess a vendor/product ID is enough, or the controller name as reported by SDL.

As a first step, I can forward maple messages related to LCD screen update and beeper. These messages don't really need any answer from the controller or vmu. However, if we want to use other functions such as storage, flycast will have to wait for a maple response before proceeding.

Flycast will also need to know the controller configuration: which device is connected (vmu, rumble pack, none) on which port. I guess this can be done with the maple protocol on the network port.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

chrisvcpp avatar Dec 12 '24 16:12 chrisvcpp

I committed a first prototype for DreamConn+ support (dev branch) Right now, each dreamconn must be enabled in emu.cfg:

[input]
DreamConn0 = yes
DreamConn1 = yes
DreamConn2 = yes
DreamConn3 = yes

At start up, flycast will connect to locahost:37393 for port 0, localhost:37394 for port 1 and so on. it will send a MDCF_GetCondition/MFID_0_Input maple message: 09 20 00 01 00 00 00 01 and wait for a reply, which is mostly ignored except to determine which expansion devices are connected to the controller.

When a game starts, flycast will forward maple messages for the VMU LCD and rumble pack to the DreamConn utility through the network socket. MDCF_BlockWrite/MFID_2_LCD for the VMU LCD and MDCF_BlockWrite and MDCF_SetCondition for the rumble pack. It doesn't expect a response in this case and won't even try to read from the socket (at least for now).

flyinghead avatar Dec 16 '24 17:12 flyinghead

Great! Will start modifying the Utility app and let you know how it goes.. Also, (in case it's of any use to you) I've already modified the Utility to register each virtual XBox360 controller with :Vendor ID: 0x4457Product ID : 0x4443

Στις Δευτέρα 16 Δεκεμβρίου 2024 στις 07:15:39 μ.μ. EET, ο χρήστης flyinghead ***@***.***> έγραψε:  

I committed a first prototype for DreamConn+ support (dev branch) Right now, each dreamconn must be enabled in emu.cfg: [input] DreamConn0 = yes DreamConn1 = yes DreamConn2 = yes DreamConn3 = yes

At start up, flycast will connect to locahost:37393 for port 0, localhost:37394 for port 1 and so on. it will send a MDCF_GetCondition/MFID_0_Input maple message: 09 20 00 01 00 00 00 01 and wait for a reply, which is mostly ignored except to determine which expansion devices are connected to the controller.

When a game starts, flycast will forward maple messages for the VMU LCD and rumble pack to the DreamConn utility through the network socket. MDCF_BlockWrite/MFID_2_LCD for the VMU LCD and MDCF_BlockWrite and MDCF_SetCondition for the rumble pack. It doesn't expect a response in this case and won't even try to read from the socket (at least for now).

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you authored the thread.Message ID: @.***>

chrisvcpp avatar Dec 16 '24 17:12 chrisvcpp

I've made the modifications to the Utility and now ready to test :

  1. Do you have a build somewhere that I can download, or do I have to build it myself?
  2. One minor change I would suggest is this : instead of transmitting the Maple messages in raw bytes, it would be better if you could transmit each message as a string with line feed at the end (\r\n). This will make it easier for me to distinguish between messages and buffer them in a FIFO pipeline. The bytes can be represented in HEX with a space separator.

chrisvcpp avatar Dec 20 '24 07:12 chrisvcpp

You can download the lastest dev build here: https://flyinghead.github.io/flycast-builds/#dev

So you basically want the message as an ASCII dump like in this C string "09 20 00 01 00 00 00 01\r\n". I assume you will use the same format for responses.

flyinghead avatar Dec 20 '24 08:12 flyinghead

That's right, the messages can be in the format : "09 20 00 01 00 00 00 01\r\n" For response, I can use any format that suits you - either the same string format, or raw bytes.. Once you have the updated build, please let me know so I can download it and start testing..

chrisvcpp avatar Dec 20 '24 09:12 chrisvcpp

New build available: Sending and receiving data as text. Should be able to detect DreamConn+ gamepad automatically.

flyinghead avatar Dec 20 '24 19:12 flyinghead

Great! So, no need to enable DreamConn through .cfg any more? (Like, used the Vendor/Product ID?)

chrisvcpp avatar Dec 20 '24 19:12 chrisvcpp

yes, I removed the emu.cfg config entries so I hope VID/PID detection works...

flyinghead avatar Dec 20 '24 19:12 flyinghead

Awesome! Will make the tests and update you further.

chrisvcpp avatar Dec 20 '24 20:12 chrisvcpp

Just started with the tests. So far communication with Flycast does not seem to be working. Once a DCS controller is connected, the Utility does the following actions:

  1. Creates a virtual XBox360 controller with VID: 0x4457, PID: 0x4443 (attached screenshot of device properties) DCS_Dev
  2. Opens a listening socket according to the selected Port (A: 37393, B: 37394, ..)

After the above actions, I'm running Flycast (dev build b9fdd50) but server status shows that no client is connected..

chrisvcpp avatar Dec 21 '24 09:12 chrisvcpp

Discard my previous message. Connection works just fine! I was just expecting to read the Maple message in the same order as it runs in the actual bus (BIG ENDIAN) Seems that you're sending it in LITTLE ENDIAN order, so it's OK..

chrisvcpp avatar Dec 21 '24 09:12 chrisvcpp

This is the order in which data is usually sent to the dreamcast maple interface. But it can be changed with a register and some diagnostic disk actually sends data in big endian mode. Anyway, glad to hear that it's working so far.

flyinghead avatar Dec 21 '24 10:12 flyinghead

In low-level, the bytes are actually inverted. Working for so many years directly with the Maple Bus, I'm used into reading it in BENDIAN 😄 No worries, I will handle the order through the Utility. For now, everything seems to be working as expected! 👍 Will upload some footage soon..

chrisvcpp avatar Dec 21 '24 10:12 chrisvcpp

OK, so far everything works great! One thing I noticed though: It seems like Flycast sends the same (current) LCD message multiple times. I guess there is something like a timer which constantly sends the last message? I would suggest that it transmits to the TCP port only when a new message arrives. This way we can avoid possible stuttering on the Flycast side and also latency (buffering) to the controller itself..

chrisvcpp avatar Dec 21 '24 12:12 chrisvcpp

Flycast only transmits messages when received from the game. There's no timer to resend anything. The only exception is when loading a state, where the screen is refreshed right after loading.

flyinghead avatar Dec 21 '24 12:12 flyinghead

Strange.. After the 1st LCD message is transmitted, the server is constantly receiving - even when no LCD change has occurred.. 🤔

chrisvcpp avatar Dec 21 '24 12:12 chrisvcpp

Yep. I also tested with a game using static LCD (i.e. Half Life). After the LCD message arrives, the server keeps receiving the same message again & again. This does not happen with the initial "Get Condition" message though.. So, I made this thought : Is there a chance that the underlying socket in Flycast keeps sending the same message because you don't read after transmitting? For example, if you make it read a generic "ACK\r\n" message after transmission? This could make this bug disappear..?

chrisvcpp avatar Dec 21 '24 15:12 chrisvcpp

No, it can't come from the tcp/ip socket itself, and reading from and writing to a socket are disconnected. I tested again and I only see messages when the VMU LCD is modified by the game. I used 240pSuite for this test. (I'm using a test tcp/ip server that dumps everything it receives).

Are you sure it's not coming from the network library or framework if you're using any? You can recompile flycast and add some logging for each message sent over the network.

flyinghead avatar Dec 21 '24 15:12 flyinghead

Tested Half-Life and I only see one LCD update when the game boots.

flyinghead avatar Dec 21 '24 15:12 flyinghead

Yes, actually you're right! Tested with Half-Life again, and actually sends only one message. Then launched RE:CV, and from the moment the 1st LCD came in, it started receiving the same message all over again... If you launch RE:CV, do you see everything to be in normal? If so, then I suppose it's some kind of bug with C# framework on my end..

chrisvcpp avatar Dec 21 '24 15:12 chrisvcpp

RE:CV is updating the LCD screen every other frame (15 times per second) so what you're seeing is normal.

flyinghead avatar Dec 21 '24 15:12 flyinghead

Man... I knew it did this when saving, but didn't occur to me doing it constantly! 😄 So, false alarm here! The network works as expected in both ends👍 I just have to make some "tricks" to the Utility as the controller over BT cannot of course handle such timings..

Oh, another thing : if you like you can also add MDCF_SetCondition for the CLOCK. This will enable the buzzer support and does not require a response too.

chrisvcpp avatar Dec 21 '24 16:12 chrisvcpp

OK, just an update : Everything works perfectly! Both VMU/LCD and Rumble Pak messages are coming properly from Flycast and forwarded to the controller accordingly. Great job! 👍

chrisvcpp avatar Dec 21 '24 18:12 chrisvcpp

Pushed a new build on the dev branch with support for buzzer messages. I used a new network library so there might some regressions. Let me know if anything is broken.

flyinghead avatar Dec 27 '24 17:12 flyinghead