Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to add physical interactions #13

Closed
ErikDeBruijn opened this issue Feb 20, 2015 · 29 comments
Closed

Ability to add physical interactions #13

ErikDeBruijn opened this issue Feb 20, 2015 · 29 comments

Comments

@ErikDeBruijn
Copy link
Contributor

My plan is to add a signal light, smoke generator and stroboscope to an Arduino which has a shield with relays. The physical devices should augment the game with effects that are being synchronised with what happens in the game. E.g. shields go up, alarm light goes off.

It runs this really basic sketch that I can build and upload with inotool.org. I can use inotool serial and press captial A to turn on relay 1, and lowercase a to turn it off. B, C and D are relay 2, 3 and 4 respectively.

// By mail@erikdebruijn.nl


int Relay1 = 7;    // Digital Arduino Pin
int Relay2 = 6;    // Digital Arduino Pin
int Relay3 = 5;    // Digital Arduino Pin
int Relay4 = 4;    // Digital Arduino Pin

int incomingByte = 0;   // for incoming serial data

// the setup routine runs once when you press reset:
void setup()  {
  // declare pin 5 to be an output:
  pinMode(Relay1, OUTPUT);
  pinMode(Relay2, OUTPUT);
  pinMode(Relay3, OUTPUT);
  pinMode(Relay4, OUTPUT);

  Serial.begin(9600);
}

// the loop routine runs over and over again forever:
void loop()  {

        // send data only when you receive data:
        if (Serial.available() > 0) {
                // read the incoming byte:
                incomingByte = Serial.read();

                // say what you got:
                Serial.print("I received: ");
                Serial.println(incomingByte, DEC);

                switch(incomingByte) {
                  case 48://0
                  digitalWrite(Relay1,LOW);
                  digitalWrite(Relay2,LOW);
                  digitalWrite(Relay3,LOW);
                  digitalWrite(Relay4,LOW);
                  break;
                  case 65:// "A"
                  digitalWrite(Relay1,HIGH);
                  break;
                  case 66:// "B"
                  digitalWrite(Relay2,HIGH);
                  break;
                  case 67:// "C"
                  digitalWrite(Relay3,HIGH);
                  break;
                  case 68:// "D"
                  digitalWrite(Relay4,HIGH);
                  break;
                  case 97:// "a"
                  digitalWrite(Relay1,LOW);
                  break;
                  case 98:// "b"
                  digitalWrite(Relay2,LOW);
                  break;
                  case 99:// "c"
                  digitalWrite(Relay3,LOW);
                  break;
                  case 100:// "d"
                  digitalWrite(Relay4,LOW);
                  break;
                  case 33:// "!" = demo
                  demo();
                  demo();
                  demo();
                  demo();
                  break;
                }
        }

}

void demo () {
  digitalWrite(Relay1,HIGH);// NO3 and COM3 Connected (the motor is running)
  delay(1000); // wait 1000 milliseconds (1 second)
  digitalWrite(Relay2,HIGH);// NO3 and COM3 Connected (the motor is running)
  delay(1000); // wait 1000 milliseconds (1 second)
  digitalWrite(Relay3,HIGH);// NO3 and COM3 Connected (the motor is running)
  delay(1000); // wait 1000 milliseconds (1 second)
  digitalWrite(Relay4,HIGH);// NO3 and COM3 Connected (the motor is running)
  delay(1000); // wait 1000 milliseconds (1 second)
  digitalWrite(Relay1,LOW);
  digitalWrite(Relay2,LOW);
  digitalWrite(Relay3,LOW);
  digitalWrite(Relay4,LOW);
  delay(1000); // wait 1000 milliseconds (1 second)

}

I need some way of knowing what happens in the game. Either a "minimal" client that uses no GUI, or a lua binding that can send serial commands. E.g. https://github.com/edartuz/lua-serial

Another way is that a client writes to a log file which I can tail and feed to my (independent) script. E.g. lines like: spaceShip:shieldUp would turn on the strobe. spaceShip:hullDamage=xx% would create smoke and an occasional flash.

@IvanSanchez
Copy link

Brief introduction: I created Artemis-Glitter a while back - https://github.com/IvanSanchez/artemis-glitter

My approach on DMX/physical relays/LCD screens right now would be to create a separate game client for that kind of hardware devices, and not tie it to the game core itself. I plan to do that in Glitter: write a small Glitter client which listens to very specific messages and triggers stuff in my RaspPi I2C interface.

So I'd vote for exposing these kind of interactions through the network, in a way clients can subscribe to certain events. e.g. I could have an Arduino listening to the energy level of the player ship and displaying it in a LCD 7-segment display, a RaspPi listening to red alerts and shield status and controlling a RGB LCD strips, and a laptop listening for comms and using text-to-speech.

If this functionality were to be integrated in the game core, there'd be no way of running it in an Arduino, I guess.

Now, which specific protocol to use, I don't know. I would consider some kind of lightweight custom UDP, or maybe some heavier protocol with publish-subscribe capabilities like RabbitMQ or websockets.

@nallath
Copy link
Collaborator

nallath commented Feb 20, 2015

I have to agree with Ivan. It makes much more sense to have a 'hardware' client that can subscribe to certain events.
This gives us much more flexibility, without having to completly merge things deeply inside the core of things (which will mess things up further down the stream).

Also, wouldn't it be possible to re-use the glitter code for empty epsilon? It looks like a lot of effort & time went in to it. I believe it could be possible with an aditional (swapable?) translator object of some kind.

@daid
Copy link
Owner

daid commented Feb 20, 2015

One of the problems would be to hooking into the EE protocol. As the protocol is "unstable", it changes with code changes. The code behind it makes it very easy and quick to make the network code, but a side effect is that there is no defined protocol other then "update variable X on object Y", and the variables linked to X change with code changes.

However, this does not mean the end of the world. I think hooking into the EE script engine would be the best place.
For example:

ship = getPlayerShip(1)
if ship ~= nil then
 front_shield_level = ship:getFrontShield() / ship:getFrontShieldMax()
end

Will get the shield level of the first player ship.

Now it's just looking into give a way to hook into this engine remotely. Maybe a http server where you can do lua script requests and get the output as response?

@IvanSanchez
Copy link

Now it's just looking into give a way to hook into this engine remotely.

I, for one, would like to have websockets or even socket.io. But I'm biased towards that because of how artemis-glitter works anyway.

I'm a bit worried about network&processor load, though. My RaspPi struggles to manage the world model from Glitter with more than a few dozen ships, so I don't want to think how an Arduino with an ethernet shield will do.

That's why I have a strong opinion towards a pub-sub (publish-subscribe) model. e.g. a "red alert arduino" would connect to EE and say "I'm only interested in game start, game end, and red alert events from player ship 1".

@ErikDeBruijn
Copy link
Contributor Author

I think either solution makes sense, but luckily they are not mutually exclusive. If someone adds a headless version of a client which becomes a pub/sub server that's great. Awesome client libraries like @IvanSanchez would definitely be nice to have. Especially for those who have web-development experience in writing aux. interfaces. As long as such a client shared most of the codebase and it compiles similarly, that's fine.
The server @daid is talking about is nice because it would be very easy to independently write code without worrying about protocol too much. I think it gives a lot of freedom that the lua-bindings now offer and I'd be able to work with that quickly without introducing a huge implementation.

@daid
Copy link
Owner

daid commented Feb 20, 2015

Not going to do socket.io or websockets, for the simple reason, it sucks for anything but node.js

And I don't think you want an Arduino with ethernet shield. I would run a small script on the server (or any client), attach the arduino to that, and have a small tool that translates the info from the server to information the Arduino needs and send that trough the USB line (cheaper and easier)

That your pi struggles with the data from artemis could be because of a couple of reasons. One of them could be that artemis is just sending way too much info, would be no surprise to mee, seeing the odd out-of-sync issues I've seen. Could also be that your code is just slow :-)

I'm much more for a polling model. Why? Because it's easy on my side (have all the elements, just need to hook them together) and you can load balance quite easy, if your Pi cannot keep up, you just request info less often.

Not to mention, if I add remote calls to the script engine, you can also modify the gameworld remotely if you wanted. As a lot of stuff is already done with script engine calls.

@ErikDeBruijn
Copy link
Contributor Author

Any physical setup people will make will be "one offs" possibly inspired by what others are making, and they will probably have simple scripts like @daid mentions. So it's best to let people work independently even when protocol is very much in flux.
O.t.o.h. @IvanSanchez may have seen uses of his client code that I didn't think off.

@IvanSanchez
Copy link

If someone adds a headless version of a client which becomes a pub/sub server that's great.

Definitely an option, but now I'm not in the mood to implement another low-level packet-parsing interface (specially if EE's network code is prone to change), thankyouverymuch.

Unless there would be an easy way to auto-generate packet descriptions, or some nodejs interface to seriousproton, I guess.

Maybe I will look into doing some EE+lua+nodejs+socketio mess in the future.

it sucks for anything but node.js

Every time someone talks badly about Node.js, I will kill a kitten. So think of the kittens. :-P

I would run a small script on the server (or any client), attach the arduino to that

Actually sounds good. I haven't really looked at EE's code, so: what kind of stuff could you do with lua scripts on a client?

Is there any easy way to create a headless EE client, able to run custom lua scripts?

That your pi struggles with the data from artemis could be because of a couple of reasons.

I'm pretty sure it's because of floating-point arithmetic to calculate the distances (for the proximity alert) O:-)

@daid
Copy link
Owner

daid commented Feb 20, 2015

Every time someone talks badly about Node.js, I will kill a kitten. So think of the kittens. :-P

Guess it's who you backed all those millions in exploding kittens then. As I've been ranting a LOT about node.js and specificly socket.io the last few weeks.
https://www.kickstarter.com/projects/elanlee/exploding-kittens

Actually sounds good. I haven't really looked at EE's code, so: what kind of stuff could you do with lua scripts on a client?

Scripts are normally run on the server, and not on the clients. Right now I setup the scenarios with
scripts. Example:
https://github.com/daid/EmptyEpsilon/blob/master/scripts/scenario_01_waves.lua

But supply drops are also handled with a script:
https://github.com/daid/EmptyEpsilon/blob/master/scripts/supply_drop.lua
This script is called from the comms station communication script:
https://github.com/daid/EmptyEpsilon/blob/master/scripts/comms_station.lua#L20
And keeps running till the drop is made or the ship is destroyed.

The functions available to the scripts are located in the code with each C++ object. For example, a spaceship has the following functions:
https://github.com/daid/EmptyEpsilon/blob/master/src/spaceship.cpp#L22
But, as a spaceship is also an spaceObject (an object in space) it also has these functions:
https://github.com/daid/EmptyEpsilon/blob/master/src/spaceObject.cpp#L9

The advantage of adding a function to the scripting engine is that it makes it available for a lot of places. Instead of making a custom system for each.

Is there any easy way to create a headless EE client, able to run custom lua scripts?

That can be done, but then the scripts should only query data, not modify it (as the server has authority). Does need some minor engine modifications, as the main loop is bound to the drawing routines. But nothing serious.

@IvanSanchez
Copy link

That can be done, but then the scripts should only query data, not modify it (as the server has authority).

What if CrewUI.cpp also exposed functions to the lua interface? I can only guess that will trigger client-to-server communication. And that should make happy those nerds with switches for physically raising/lowering shields.

@daid
Copy link
Owner

daid commented Feb 20, 2015

I would simply have to expose all the "command???" functions in playerSpaceship.cpp, these are all the commands that get send from the clients to the server. Should be easy. (no need to go trough CrewUI stuff for that, those just link the command functions to screen buttons)

@IvanSanchez
Copy link

Well, yeah, you've got my point. Sorry for not knowing how the innards of EE work :-)

@daid
Copy link
Owner

daid commented Feb 20, 2015

No problem in not knowing, that's why I'm here :-)
I hope to do some work on this this weekend.

Still got some hardware that could be hooked up at some point:
http://daid.eu/~daid/20150110_145649.small.jpg
http://daid.eu/~daid/IMG_20130626_211925.small.jpg (would make a nice jump drive button :) )

And we already have a touchscreen hardware setup:
http://daid.eu/~daid/20150219_204125.small.jpg

And not on photos, we have a strobe light, a red alarm light and a smoke generator as well. But also need to hook those up.

@nallath
Copy link
Collaborator

nallath commented Feb 20, 2015

Only daid knows how the innards work ;) Documentation is rather sparse.

@ErikDeBruijn
Copy link
Contributor Author

I've been reading most of the code EE and some of the Serious Proton to get an idea of how it works.

From my own script I can perform a HTTP POST /script/lua HTTP/1.1 with the following data:

local json = require( "json" )  -- Include the Corona JSON library

ship = getPlayerShip(1)
if ship ~= nil then
 shields_active = ship:getShieldsActive()
 hull = ship:getHull()
 hullMax = ship:getHullMax()
end
local returnValues = { shields_active = shields_active, hull = hull, hullMax =  hullMax }
print ( json.encode( returnValues ) )

I'm a bit uncertain about that last two lines. Basically I'd like to get back a json string that's easy to parse for me. Based on that data I can check if any relays need to change to do something physical, e.g. switch a relay :)

I noticed that you have a webserver already in serious proton, however it appears it's not run from the EE code. But it seems like everything is prepared to do this, since arbitrary lua code could be executed with something along these lines:

P<ScriptObject> so = new ScriptObject();
so->runCode(postBody);
so->destroy();

With a few hints I might be able to get something like this working, or not. I'd be happy to give it a try.

@ErikDeBruijn
Copy link
Contributor Author

I've now got a ~ 50 line ruby script that performs a HTTP request lua code, listens for a response, parses json (if present) and interprets if there are relevant changes. If so, it controls the Arduino resulting in switching of a relay, for example.

The Arduino is bootstrapped by the dino gem, which basically turns the Arduino into a dumb slave to be controlled through usbserial (and gets automatically discovered). No user code has to run on the Arduino.

Code is in this gist: https://gist.github.com/ErikDeBruijn/ebef5db43fe224969b40

@daid
Copy link
Owner

daid commented Feb 21, 2015

Note, the HTTP server code in SP is old code, just stored there for safekeeping. It needs fixes and updates to work proper.

daid added a commit that referenced this issue Feb 21, 2015
@daid
Copy link
Owner

daid commented Feb 22, 2015

Ok, updated the script engine, and the http server.

You can now add a "httpserver=80" in the options.ini and it will run a http server on port 80. It will serve files from a directory "www", and when you send a post request to "/exec.lua" with a script, you get the return value of that script as json result.

Example test HTML file:
https://gist.github.com/daid/585b589207c5a8f1d62b

@ErikDeBruijn
Copy link
Contributor Author

Awesome!!! Can't wait to use it. The example is clear to me too. Nice that I can just return and that it gets passed back as JSON.

B.t.w. I'm getting a compilation error since the new logging.
/home/emptyepsilon/Dev/SeriousProton/src/collisionable.cpp|180|error: ambiguous overload for ‘operator<<’ (operand types are ‘const Logging’ and ‘std::vector<sf::Vector2<float> >::size_type {aka long unsigned int}’)|
This << var.size() << occurs in a few places, removing those temporarily helped it getting compiled.
Finally I had a missing header file:
/home/emptyepsilon/Dev/EmptyEpsilon/src/main.cpp|10|fatal error: httpScriptAccess.h: No such file or directory|
Perhaps you still need to commit it to the repo?

daid added a commit that referenced this issue Feb 22, 2015
@ErikDeBruijn
Copy link
Contributor Author

Ok, with commit fd52d7 I get further:

[68%] g++  -Wall -I../SeriousProton/src -O2 -o _build_Linux_Linux_Release/commsScriptInterface.o -c src/commsScriptInterface.cpp
In file included from src/spaceObjects/spaceship.h:5:0,
                 from src/spaceObjects/playerSpaceship.h:4,
                 from src/commsScriptInterface.h:22,
                 from src/commsScriptInterface.cpp:1:
src/spaceObjects/spaceObject.h:5:18: fatal error: mesh.h: No such file or directory
 #include "mesh.h"
                  ^

@daid
Copy link
Owner

daid commented Feb 22, 2015

Commited the missing file, and possibly commited a fix for the other issues.

The JSON conversion might not be perfect right now, but I think it will be good enough. (Does not do arrays properly yet, but key value pairs work fine)

@ErikDeBruijn
Copy link
Contributor Author

okay, key val pairs are just fine! :)
It compile now! :)

@ErikDeBruijn
Copy link
Contributor Author

I couldn't figure out how to run EE not in full screen, so it was difficult to try it with one system. After some network changes I can access a linux VM on its own IP and run scripts from the host while the VM has the app running in fullscreen. I've tried your lua code from the gist, but I get back {"ERROR": "Script error"}. Even with simplified versions (e.g. return {hull = 1}). The good thing: I can access the webserver and it attempts running lua code, but it's not quite there yet.

@ErikDeBruijn
Copy link
Contributor Author

doh, and now I find the fullscreen option. :)

@daid
Copy link
Owner

daid commented Feb 23, 2015

The error from the script is logged in the terminal where you start the game. Not really user friendly yet.

@ErikDeBruijn
Copy link
Contributor Author

Error was syntax related because the requests were URL encoded (in the body).

@vslotman
Copy link
Contributor

vslotman commented Mar 8, 2015

I'm currently adding support for openHAB, a home-automation-hub which supports a huge amount of protocols and devices, web-interfaces and apps. Connecting EmptyEpsilon to real-world devices should be rather easy using this.

I've got a test-setup running where I'm using HTTP GET-request to poll the state of EmptyEpsilon twice per second, this works really nice :)
See daid/SeriousProton#1 for more info...

@ErikDeBruijn
Copy link
Contributor Author

I've uploaded my version of the physical interactions client to github, it can be found here:
https://github.com/ErikDeBruijn/epsilon-phys-client

Given that we now have a http server that responds to lua, I guess we can close this issue since that gives us a lot of options.

daid pushed a commit that referenced this issue Jun 13, 2016
Updating my fork to include current Daid/EmptyEpsilon
@Ghostkeeper
Copy link
Contributor

B.t.w. I'm getting a compilation error since the new logging.
/home/emptyepsilon/Dev/SeriousProton/src/collisionable.cpp|180|error: ambiguous overload for ‘operator<<’ (operand types are ‘const Logging’ and ‘std::vector<sf::Vector2<float> >::size_type {aka long unsigned int}’)|
This << var.size() << occurs in a few places, removing those temporarily helped it getting compiled.

I know that this is very long ago so this is just for posterity, but I just fixed this compile error that Erik had encountered, because I had encountered it as well.
The fix is here: daid/SeriousProton@0073134
It would be merged along with my pull request to build Serious Proton using CMake.

simchanu29 pushed a commit to simchanu29/EmptyEpsilon that referenced this issue Nov 11, 2018
extract `SectorsView` class out of `GuiRadarView`
StarryWisdom pushed a commit to StarryWisdom/EmptyEpsilon that referenced this issue Dec 11, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants