📜 ⬆️ ⬇️

How we made smarter our foosball and ourselves



To make smart table football, we need:



Prehistory


In our company, the majority of employees are not averse to playing table football. Rather, they are even very fond and, of course, the matter is not limited to one batch. Therefore, at lunch and in the evening, a crowd of IT people waiting for their turn gathers around the table.
')
And so, once, when the mess with the queue got tired of everything in order, we had an idea:





And all fled to google.

Day 1


On Friday evening, a group of like-minded football fans gathered near the hero of the occasion, the table, for a meeting. They shared their data, decided on the basic requirements and technologies, distributed the roles, turned the microcontroller out of their bosses.

Day 2




The first thing on Saturday morning unscrewed the table. To teach him to track goals scored, hooked 2 lasers and 2 photoresistors on the gate and the Arduino controller in the middle. The system was invented as follows: when a ball hits the area between the laser and the photoresistor, the controller records the change in voltage on the sensor. Thus, the voltage change is a consequence of the change in resistance on the photoresistor. The schematic diagram is shown below.



Despite the extreme simplicity of the system, I had to face some problems. First, a change in lighting in a room with a football table could cause false-positive sensor triggers. Secondly, particularly strong vibrations of the table during the game could lead to mechanical damage to the system components.

The first problem was eliminated by recalibrating the photoresistor with each start of the game. The second one made it even easier - with the help of a screwdriver, superglue and, of course, blue electrical tape, all system components were securely fixed.

Arduino:





Lasers:





At the same time, we began work on the program component of the project. First of all, we specified the requirements:


I must say, we are very lucky that our creative designer loves to be cut into table football. Therefore, for dinner we already had pretty mokapes in our hands. Looking ahead, we will show what came out of them:







Development was carried out in parallel in three branches:
  1. Client side - Angular.js, Bootstrap.
    They created the main pages of the application, designed the design, implemented interaction with the server through the Rest API and Socket.io. Adapted layout for mobile devices.
  2. The server side is Node.js, Socket.io, MongoDB.
    We created the structure of the project, developed a data model, set up the relationship between the client and the server, and differentiated access rights. Implemented the logic of calculating statistics, collecting achievements, maintaining ratings. Made a client alert about the occurring events using Socket.io.
  3. The relationship between Arduino and the server.
    Wrote a layer between the controller and the server.


Here it should be noted that we decided to combine business with pleasure. Therefore, technologies were chosen unfamiliar to the project participants, at the same time to pump skills.

In general, there is no sense in writing more about the first and second points. Despite the fact that the development of these parts took up most of the time, there were no supertasks here, everything was rather trivial. Therefore, we turn to the most delicious - the interaction between the server and our smart table.

Of course, it would be better to organize wireless data transfer between Adruino and the server, using wi-fi or bluetooth modules to interact with the server. Or even use the Raspberry Pi as a server for our application. But we had neither the first, nor the second, nor the third, but there was a compote an old computer that could still serve us as a server. Therefore, our server is connected to the table using a USB cable, and all communication between the Arduino and the server takes place via the COM port.

Arduino receives from the port signals on / off lasers and, in turn, sends signals about recorded goals to the server.

Sketch for Arduino
//Pin for white gate laser #define WHITE_LED_PIN 13 //Pin for blue gate laser #define BLUE_LED_PIN 8 //Pin for white gate photoresistor #define WHITE_LDR_PIN A0 //Pin for blue gate photoresistor #define BLUE_LDR_PIN A1 //Commands to arduino: // 1 - start game const int START_COMMAND = 1; // 2 - stop game const int STOP_COMMAND = 2; //Commands from arduino: // 'GOAL:WHITE' - goal to white gate // 'GOAL:BLUE' - goal to blue gate const char GOAL_PREFIX[] = "GOAL:"; const char WHITE_TEAM_NAME[] = "WHITE"; const char BLUE_TEAM_NAME[] = "BLUE"; // 'LISTENING' - waiting for commands const char LISTENING_MESSAGE[] = "LISTENING"; // 'START' - game is started const char START_GAME_MESSAGE[] = "START"; // 'CALIBRATION' - sensors are in calibration const char CALIBRATION_MESSAGE[] = "CALIBRATION"; // 'STOP' - game is stopped const char STOP_GAME_MESSAGE[] = "STOP"; //Timeout after which game will be automatically stopped //15 minutes - 900000 ms const long INACTIVITY_TIMEOUT = 900000; //Minumum deviation that is necessary to count a goal //Eg if deviation is 1.1 it means that when value on photoresistor exceeds calibrated maximum at least on 10%, goal will be counted const int MINIMUM_SENSOR_DEVIATION = 1.1; //Time in milliseconds for photoresistors calibration const long CALIBRATION_TIME = 1000; //Delay in milliseconds after goals to avoid multiple counting of the same goal const long DELAY_AFTER_GOALS = 1000; //Delay in milliseconds to avoid interference on LDR right after lasers are 'ON' const long DELAY_BEFORE_CALIBRATION = 200; //Maximum values on photoresistors after calibration. int maxWhiteSensorValue = 0; int maxBlueSensorValue = 0; //Is game currently running boolean running = false; //Milliseconds from last activity (from game start or from last goal) long lastActivityTime = 0; void setup() { Serial.begin(9600); pinMode(WHITE_LED_PIN, OUTPUT); pinMode(BLUE_LED_PIN, OUTPUT); Serial.println(LISTENING_MESSAGE); } void loop() { if (running) { if (millis() - lastActivityTime >= INACTIVITY_TIMEOUT) { //Stop the game because of inactivity stopTheGame(); } else { checkFootballGate(WHITE_LDR_PIN, maxWhiteSensorValue, WHITE_TEAM_NAME); checkFootballGate(BLUE_LDR_PIN, maxBlueSensorValue, BLUE_TEAM_NAME); } } else { //If game isn't running, check serial port for incoming commands int serialValue = Serial.parseInt(); if (serialValue == START_COMMAND) { startTheGame(); } } } //Turn on lasers and calibrate the photoresistors void startTheGame() { digitalWrite(WHITE_LED_PIN, HIGH); digitalWrite(BLUE_LED_PIN, HIGH); //Delay to avoid interference on LDR right after lasers are 'ON' delay(DELAY_BEFORE_CALIBRATION); Serial.println(CALIBRATION_MESSAGE); calibrateSensors(); running = true; lastActivityTime = millis(); Serial.println(START_GAME_MESSAGE); } void stopTheGame() { Serial.println(STOP_GAME_MESSAGE); digitalWrite(WHITE_LED_PIN, LOW); digitalWrite(BLUE_LED_PIN, LOW); running = false; } //Measuring of maximum values on photoresistors during calibrationTime period void calibrateSensors() { maxWhiteSensorValue = 0; maxBlueSensorValue = 0; long startMillis = millis(); while (millis() - startMillis < CALIBRATION_TIME) { int whiteSensorValue = analogRead(WHITE_LDR_PIN); int blueSensorValue = analogRead(BLUE_LDR_PIN); // record the maximum sensor value if (whiteSensorValue > maxWhiteSensorValue) { maxWhiteSensorValue = whiteSensorValue; } if (blueSensorValue > maxBlueSensorValue) { maxBlueSensorValue = blueSensorValue; } } } void checkFootballGate(int ldrPin, int maxSensorValue, const char *teamName) { int sensorValue = analogRead(ldrPin); //If sensorValue is exceeds maxValue at least on configured minimum deviation (which means that light flow is interrupted) if (sensorValue >= (maxSensorValue * MINIMUM_SENSOR_DEVIATION)) { Serial.print(GOAL_PREFIX); Serial.println(teamName); lastActivityTime = millis(); checkForStopCommand(); delay(DELAY_AFTER_GOALS); } } //Check serial port for stop game command void checkForStopCommand() { int serialValue = Serial.parseInt(); if (serialValue == STOP_COMMAND) { stopTheGame(); } } 


The controller on the server side
 var Arduino = function () { var self = this; // constans block self.LISTENING_MESSAGE = "LISTENING"; self.STOP_GAME_MESSAGE = "STOP"; ... var portIsReady = true; // init SerialPort var serialPort = new SerialPort(self.PORT_NUMBER, { parser: serialport.parsers.readline("\r\n"), baudrate: 9600, dataBits: 8, parity: 'none', stopBits: 1, flowControl: false }, true); // open connection and listening port serialPort.on(self.PORT_OPEN_COMMAND, function () { serialPort.on(self.PORT_RECEIVE_DATA_COMMAND, function (arduinoMessage) { if (arduinoMessage === self.LISTENING_MESSAGE) { // arduino is ready self.emit(self.ARDUINO_READY_COMMAND, arduinoMessage); } else if (arduinoMessage === self.STOP_GAME_MESSAGE) { // stop command or timeout stop self.emit(self.ARDUINO_IS_STOPPED, arduinoMessage); } else if (arduinoMessage === self.GOAL_WHITE_MESSAGE || arduinoMessage === self.GOAL_BLUE_MESSAGE) { // goal in white gate (point for blue team) self.emit(self.ARDUINO_GOAL, arduinoMessage); } }); }); serialPort.on(self.PORT_CLOSE_COMMAND, function () { portIsReady = false; }); serialPort.on(self.PORT_ERROR_COMMAND, function () { portIsReady = false; }); self.on(self.ARDUINO_READY_COMMAND, function () { portIsReady = true; }); self.on(self.ARDUINO_START_COMMAND, function () { if (portIsReady) { serialPort.write(self.START_GAME_COMMAND); } }); self.on(self.ARDUINO_STOP_COMMAND, function () { if (portIsReady) { serialPort.write(self.STOP_GAME_COMMAND); } }); self.start = function () { self.emit(self.ARDUINO_START_COMMAND); }; self.stop = function () { self.emit(self.ARDUINO_STOP_COMMAND); }; }; 


Here we monitor the port to which the Arduino is connected. When receiving a command, we generate this or that event. To start and stop the Arduino, we have two special functions, start and stop, which control the switching on and off of lasers.

Event handling example
 var GameController = function () { ... Arduino.on(Arduino.ARDUINO_GOAL, function (team) { goal(team); }); Arduino.on(Arduino.ARDUINO_IS_STOPPED, function () { _stop(currentGame, true); }); ... } 



Thus, by the end of the second day we got the working basic functionality of the client and server and the ready layer for interacting with Arduino.

Day 3


On Sunday we had to tie all the components together and fasten various bonuses like in-game achievements and a funny little music.

This day passed in a more creative way, we programmed less time, basically invented the levels of players, achievements and music for various game events.

Finally, everything is assembled, connected, launch - earned!

Started to functional testing. Okay, okay, playing football, what could be here)

A couple of bugfixes, a small finish and ... PROFIT! Smart football is ready.



Total


The result was a prototype of high-tech table football, which independently records and counts scored goals, maintains player ratings, forms a queue and, in general, makes our vacation much more convenient and more interesting. And we had a great time and improved our skills, of course.

We hope the article was at least somewhat useful and will inspire you to your own experiments. Good luck to all!

Source: https://habr.com/ru/post/245031/


All Articles