daid / EmptyEpsilon

Open source bridge simulator. Build with the SeriousProton engine.
https://daid.github.io/EmptyEpsilon/
GNU General Public License v2.0
526 stars 175 forks source link

How to use hardware events (f.e. blink lights when taking damage) #128

Closed jozan closed 8 years ago

jozan commented 8 years ago

Is there any possibilities to bind events such as "damage taken" etc. to lights. It's possible to check state frequently but configuration get quite lengthy.

I checked hardwareController.cpp and it does have support for some kind of events but it's not clear how to configure them in hardware.ini. My hunch for event configuration is something like this (doesn't work):

[hardware]
device = VirtualOutputDevice
port = COM3
channels = 512

[channels]
WarningLights = 411

# EVENT HIT
[event]
target = WarningLights
runtime = 1.0
effect = Blink
trigger_variable = ?
compare_operator = ?
previous_value = ?
previous_valid = ?
triggered = ?

So what's the correct configuration? ;)

daid commented 8 years ago

This currently really lacks a lot of documentation, as well as being pretty much untested.

My local test file looks like this:

#[hardware]
#device = DMX512SerialDevice
#device = EnttecDMXProDevice
#device = VirtualOutputDevice
#port = COM1

[hardware]
# Arduino Mega 2560 with an ATMega8U2 is idenfied with \Device\USBSER@@@ on windows.
# Use the DMX512SerialDevice which can be used to talk to firmware with the driver from: https://github.com/mathertel/DMXSerial directly without extra hardware.
device = DMX512SerialDevice
port = \Device\USBSER@@@
channels = 32

#[hardware]
# Virtual output device, for debugging. Will show up as white squares on the screen.
#device = VirtualOutputDevice

[channels]
MainCabinLights = 1
WarningLights = 2

[event]
trigger = HasShip
target = WarningLights
runtime = 1.0
value = 1.0

[event]
trigger = Energy
target = MainCabinLights
runtime = 1.0
value = 1.0
jozan commented 8 years ago

Thanks for the reply and the config. Seems like there's nothing for hit/damage events. Or is there? Ideally I'd like to blink lights for every hit and and something special when shields are pierced.

daid commented 8 years ago

Documentation is lacking a LOT. Hit/damage events can be done like this:

[event]
trigger = <Hull
target = WarningLights
runtime = 1.0
value = 1.0

It says, when "hull" lowers, put the "WarningLights" output on for 1 second.

jozan commented 8 years ago

Ah, thanks a lot! I hope these snippets will find a way to wiki :)

jozan commented 8 years ago

I quickly wrote some documentation. Feel free to modify if something isn't correct. https://github.com/daid/EmptyEpsilon/wiki/Configure-hardware.ini

daid commented 8 years ago

Looks good :D You're much better at writing documentation then I am. I'm amazed how much you managed to pick from just the code and my quick example.

What you most likely did not notice, is you can have multiple hardware definitions. And thus more then 512 channels. And or more then hardware device connected.

[hardware]
device = DMX512SerialDevice
port = COM4
channels = 32

[hardware]
device = DMX512SerialDevice
port = COM5
channels = 32

Will give me 64 channels. The first 32 on the hardware connected to COM4. And the 32 to 64 on COM5.

If the amount of channels for a device is not defined, it defaults to 512.

And the serial port can be defined with a "pseudo" name. On windows you can say "port = \Device\USBSER@@@" to connect to any Arduino connected to the system, no matter the port number.

Also, do you already have a hardware setup? I haven't had much feedback on if this code actually works other then with my local setup.

daid commented 8 years ago

I also added all the options for condition/trigger names.

jozan commented 8 years ago

Yes, @HenriVesala has tested those configurations on real hardware (4ChannelSpot so far). We've plans to build a bridge with multiple spotlights, led strips, etc. in the near future. We've used that hardware while playing Artemis and hope we can get them to work with EE. Expect our report from it. :)

I hope more data would get exposed to be used in hardware.ini. I'll create a new issue when we run into problems or really lack something.

Thanks for correcting my mistakes! @HenriVesala did also some quick edits! :)

I probably write next article about httpserver and its API. It's lacking a lot of documentation and it's really cool feature. I hope it get's some love, f.e. stops overwriting options.ini every time I close the game. It makes EE much more flexible and provides a tool to build own subsystems such as a machine which interferes randomly with the game. When the subsystem machine gets broken it could set shields active every second and thus draining energy and this machine needs to be fixed by someone before it's too late. With the current API this is easy to do compared to Artemis.

HenriVesala commented 8 years ago

I managed to run simple test with some 4-channel rgb par 32 spotlight with some usb2dmx cable thingy. Everything went quite smoothly but I noticed that i need to do many repeats for events for every channel of the device. It would be neat feature if you could use channel range in states / events and value would then accept list of values.

[channels]
rangeExample = 1...4

[event]
trigger = <Hull
target = rangeExample
runtime = 1.0
value = 1.0, 1.0, 0, 0.5

Also for rgb colors it would be cool if you could simply put integer of 0...255 or hexadesimal number to value field.

value = #CC
daid commented 8 years ago

@HenriVesala You're the first to confirm the code actually works with an USB2DMX cable. So that is great news :-) I'll think about the multi-channel mapping. Makes sense for RGB lighting.

@jozan the HTTP API is really intended to have full and in depth control options of the game. It pretty much has all the functionality that mission scripting has. It was initially the only external interface. And I added the hardware.ini stuff to get more compatibility with Artemis, and make it easier to add simple external hardware to the game. It can also access all the playership:command[xxx] functions, which allows for external input instead of only output that the DMX hardware can do.

daid commented 8 years ago

Just pushed a patch that allows multi-channel mapping.

You can do:

[channels]
RGBLight = 1,2,3,4

[event]
trigger = <Hull
target = RGBLight
runtime = 1.0
value = 1.0, 1.0, 0, 0.5

Will work for state as well as events, and you can customize all settings per channel if you supply a comma separated list instead of a single value.

jozan commented 8 years ago

Thank you for the quick patch! We'll test it soonish.

HenriVesala commented 8 years ago

Nice work on multi-channel mapping. I need to try this out.

Meanwhile I tried to get same 4 channel work with mac os x el capitan with bad results. I managed to turn on the lights with some dmx controlling software but the game just didin't recognize my usb2dmx devices port. There might be an issue in game or os x is trolling with me. Can't really say.

daid commented 8 years ago

The serial driver lacks code for OSX: https://github.com/daid/EmptyEpsilon/blob/master/src/hardware/serialDriver.cpp

I think it can just use the same code as linux. So only the ifdefs need to be changed.

HenriVesala commented 8 years ago

yeah. It should. No wonder i didin't work :)

daid commented 8 years ago

Just thought of something, the code is making use of linux ioctls to set the proper baudrate, that might not work on OSX. This code might help if you want to set this up properly: https://developer.apple.com/library/mac/samplecode/SerialPortSample/Listings/SerialPortSample_SerialPortSample_c.html

HenriVesala commented 8 years ago

Ill tried to code for OSX. Unfortunately I got only limited success. Game is definitely recognizing my usb2dmx as some indication led lights up when game launches. Thats an improvement but still no lights. You were right about baudrate. Setting non posix baudrate is bit wierd in OSX. Thanks for the hint. Currently I'm not sure whats wrong with it, but I suspect that its me. As I haven't work with serial ports & c++ for years. Ill continue to work with this tomorrow.

daid commented 8 years ago

I cannot really help there, I do not have access to an OSX machine (well, only remote access, so that does not help)

HenriVesala commented 8 years ago

Managed to get it working. I will do grooming & pr shortly.

HenriVesala commented 8 years ago

While doing os x support i ran to following problem:

        //Configure the serial port for fake break.
        port->configure(100000, 8, SerialPort::EvenParity, SerialPort::TwoStopbits);
        //Send the fake break. 8 bits of 0, 1 parity bit which is 0. Which gives 9 bits at 10uSec. Which is 90uSec, more then the required 88uSec
        port->send(start_code, sizeof(start_code));

https://github.com/daid/EmptyEpsilon/blob/master/src/hardware/dmx512SerialDevice.cpp#L74-L77

In my experiments i got my dmx light to work only when "fake break" didin't have EvenParity. Whit OddParity or NoParity I had success.

daid commented 8 years ago

That's odd...

What that code does, it tries to simulate a "Serial Break". There is a normal system call for that (see the sendBreak function) however, it did not work for me on Windows.

You can try this change to see if the tcsendbreak works properly on OSX.

    while(run_thread)
    {
        //Send a break to initiate transfer
        port->sendBreak(); //Does not seem to work? (Windows with Arduino running: https://github.com/mathertel/DMXSerial )

        //Configure the serial port for fake break.
        //port->configure(100000, 8, SerialPort::EvenParity, SerialPort::TwoStopbits);
        //Send the fake break. 8 bits of 0, 1 parity bit which is 0. Which gives 9 bits at 10uSec. Which is 90uSec, more then the required 88uSec
        //port->send(start_code, sizeof(start_code));

        //Configure the port for straight DMX-512 protocol.
        port->configure(250000, 8, SerialPort::NoParity, SerialPort::TwoStopbits);

        //Send the start code.
        port->send(start_code, sizeof(start_code));
        //Send the channel data.
        port->send(channel_data, channel_count);

        //Delay a bit before sending again.
        sf::sleep(sf::milliseconds(25));
    }
HenriVesala commented 8 years ago

I need to try this out. Also I noticed that there might be issue that device was blinking somewhat. However this might be due bad cables. I need to do some testing & research about it

daid commented 8 years ago

You can also try to see what happens if you configure a different time between DMX messages. I made the delay configurable: https://github.com/daid/EmptyEpsilon/commit/5bce55fe378bd0ef2f636052b39818399a5f0d65

HenriVesala commented 8 years ago

port->sendBreak() didin't work with os x either. I just got some random red color blinking out of rgb spot when every color should have been lit.

I quickly tried different delays. Rising delay caused blinking to be much less frequent, but "black outs" were also longer. I suspect that the problem in this blinking is in breaking logic. Bad breaking could explain a lot.

I need to some comparison how same devices work on os x and windows.

daid commented 8 years ago

DMX512 is a bit of an "odd" protocol, and the dmx512SerialDevice tries it's best to simulate this protocol as good as it can by tricking an normal USB2Serial device in generating the right bit stream.

There are 4 parts of this protocol. 1) Break 2) Mark-After-Break (MAB) 3) Start byte 4) 512 (or less) data bytes

Part 3 and 4 are easy. This is just 250kbit/s data with 1 start bit, 8 data bits, and 2 stop bits (you can see the code configuring the serial port like this)

Part 1 and 2, the break and MAB is where it gets tricky. This means the line needs to be "low" for at least 88uSec, and after that high for minimum of 8uSec. What I do, is I set the baudrate to 100k, and send 1 byte of zeros, with a parity bit and 2 stop bits. This should generate a 9 low bits (8 data, 1 parity) and 2 high bits (stop bits). The start bit is "unused" the data bits with the parity bit is the break(1) and the stop bits are the MAB(2).

In theory, this should generate both the break and the MAB required. However, this does not seem to function correctly on your setup.

EvenParity in combination with TwoStopbits might not work on OSX. So that could be a cause for some problems. As you removed the parity bit. And thus shortened the break(1).

Now, as far as I can see, the latest DMX512 specs only specify a minimum time for the Break and MAB. And with the current implementation we are close to those timings. When you removed the parity bit, you are actually a bit below those timings. So we can lengthen the break(1) and MAB(2) a bit.

So, what you could try is this code:

    while(run_thread)
    {
        //Send a break to initiate transfer
        //port->sendBreak(); //Does not seem to work? (Windows with Arduino running: https://github.com/mathertel/DMXSerial )

        //Configure the serial port for fake break.
        port->configure(75000, 8, SerialPort::NoParity, SerialPort::TwoStopbits);
        //Send the fake break. 8 bits of 0. Which gives 8 bits at 13.3uSec. Which is 106.6uSec, more then the required 88uSec. The MAB is implemented with the stop bits, 2 stop bits of 13.3uSec is 26.6uSec, more then the required 8uSec.
        port->send(start_code, sizeof(start_code));

        //Configure the port for straight DMX-512 protocol.
        port->configure(250000, 8, SerialPort::NoParity, SerialPort::TwoStopbits);

        //Send the start code.
        port->send(start_code, sizeof(start_code));
        //Send the channel data.
        port->send(channel_data, channel_count);

        //Delay a bit before sending again.
        sf::sleep(sf::milliseconds(resend_delay));
    }

If that is still causing odd flickering, you could also introduce a sf::sleep of 1ms before the 2nd port->configure to see if that helps.

HenriVesala commented 8 years ago

Thanks. Ill try this out when I'm back at home. I was just roaming trough dmx512 protocol description and had similar idea about what could cause the problem. I also need to recheck how spot works in windows as I wasn't paying attention about it when I tested last time.

HenriVesala commented 8 years ago

Good news. I didin't notice any flickering in windows. Is dmx tested on linux yet?

daid commented 8 years ago

Nope. You're the first user that has done more then a quick test :-)

HenriVesala commented 8 years ago

Ok. Well then we need to do that too. Bad news: code above didin't work. Iight still flickers maybe bit less than before. Ill try to tweak the timing bit.

daid commented 8 years ago

Damn, this is hard to diagnose without a scope or digital analyzer trace.

But good to hear that it works on Windows :-)

HenriVesala commented 8 years ago

Note: i need to buy a terminator for my spot. It might help. However it doesn't explain why it works perfectly in windows and flickers in os x.

HenriVesala commented 8 years ago

Good news. I got my better quality dmx spot light back. I tried to run EE with it on mac and I didn't notice any flickering. So it might just be sub optimal device + mac related problem.

daid commented 8 years ago

That's good to hear.

Note that I added E1.31 (sACN) support to the code base. Which is sending DMX512 trough normal Ethernet networks. I have no actual hardware that talks this. But as I'm working to build my own hardware based on WiFi equipment, I needed something to send trough Ethernet. So I figured I better implement some standard then implement something custom. Once again, I have no actual hardware to test on, but all software that I tried sees it as valid E1.31 data.

HenriVesala commented 8 years ago

We also tried to test dmx on linux. As we didn't have native linux setup around we tried to run EE on virtual xubuntu that was running on OS X. It failed apparently due some 3d graphic failure. We should be able to test it on some native device at somepoint in future.

daid commented 8 years ago

Just updating you (figured posting here would notify you).

I hooked up a logic analyzer to an USB->serial convertor this morning and noticed some slight timing problems in the protocol implementation. I've corrected this in the latest code, which could help with the light flickering issue. I've build my own simple hardware now, based on an Arduino, and there I noticed one of my relays ticked sometimes. I'm not sure if this change fixes that problem as that hardware is still in transport right now.