Joystick.h
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package RobotAPI::ArmarXObjects::GamepadUnit
17  * @author Simon Ottenhaus ( simon dot ottenhaus at kit dot edu )
18  * @date 2017
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
23 #pragma once
24 
25 #include <cstdint>
26 
27 #include <fcntl.h>
28 #include <sys/poll.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 
33 
34 #include <linux/joystick.h>
35 
36 namespace armarx
37 {
38 
39  class Joystick
40  {
41 
42  private:
43  int fd = -1;
44  int fdEvent = -1;
45  js_event event;
46 
47  public:
48  std::vector<int16_t> axis;
49  std::vector<bool> buttonsPressed;
52  std::string name;
53 
54  bool
55  open(std::string const& deviceName, std::string const& deviceEventName)
56  {
57 
58  fd = ::open(deviceName.c_str(), O_RDONLY);
59 
60  if (!deviceEventName.empty())
61  {
62  ARMARX_INFO << "Force feedback enabled";
63  fdEvent = ::open(deviceEventName.c_str(), O_RDWR | O_CLOEXEC);
64 
65  ARMARX_CHECK(fdEvent != -1);
66  }
67  else
68  {
69  ARMARX_INFO << "Force feedback disabled";
70  }
71 
72  if (fd != -1)
73  {
74  // ARMARX_INFO << "before";
75  // executeEffect();
76  // ARMARX_INFO << "after";
77 
78 
79  ioctl(fd, JSIOCGAXES, &numberOfAxis);
80  ioctl(fd, JSIOCGBUTTONS, &numberOfButtons);
81  name.resize(255);
82  ioctl(fd, JSIOCGNAME(255), &name[0]);
83  axis.resize(numberOfAxis, 0);
84  name = name.c_str();
85  buttonsPressed.resize(numberOfButtons, false);
86  }
87 
88  ARMARX_INFO << "execute effect";
89  executeEffect();
90 
91  return fd != -1;
92  }
93 
94  bool
95  opened() const
96  {
97  return fd != -1;
98  }
99 
100  bool
102  {
103  int bytes = read(fd, &event, sizeof(event));
104 
105  // NOTE if this condition is not met, we're probably out of sync and this
106  // Joystick instance is likely unusable
107  if (bytes == -1 || bytes != sizeof(event))
108  {
109  return false;
110  }
111 
112  if (event.type & JS_EVENT_BUTTON)
113  {
114  buttonsPressed[event.number] = event.value != 0;
115  }
116  else if (event.type & JS_EVENT_AXIS)
117  {
118  axis[event.number] = event.value;
119  }
120  return true;
121  }
122 
123  void
124  executeEffect(int gain = 100, const int nTimes = 1)
125  {
126  // this feature is disabled
127  if (fdEvent < 0)
128  return;
129 
130  // see https://docs.kernel.org/input/ff.html
131 
132 
133  // https://xnux.eu/devices/feature/vibrator.html
134 
135  int ret;
136  // pollfd pfds[1];
137  int effects;
138 
139  // fd = open_event_dev("vibrator", O_RDWR | O_CLOEXEC);
140  // syscall_error(fd < 0, "Can't open vibrator event device");
141 
142  ret = ioctl(fdEvent, EVIOCGEFFECTS, &effects);
143  ARMARX_CHECK(ret >= 0);
144  // syscall_error(ret < 0, "EVIOCGEFFECTS failed");
145 
146  // ARMARX_CHECK(effects & FF_RUMBLE);
147 
148  // Set the gain of the device
149  {
150  // int gain; between 0 and 100
151  struct input_event ie; // structure used to communicate with the driver
152 
153  ie.type = EV_FF;
154  ie.code = FF_GAIN;
155  ie.value = 0xFFFFUL * gain / 100;
156 
157  if (write(fdEvent, &ie, sizeof(ie)) == -1)
158  {
159  perror("set gain");
160  }
161  }
162 
163 
164  ff_effect e;
165  // e.type = FF_RUMBLE;
166  // e.id = -1;
167  // e.replay.length = 5000;
168  // e.replay.delay = 500;
169  // e.u.rumble.strong_magnitude = 1;
170 
171  e.type = FF_PERIODIC;
172  e.id = -1;
173  e.replay.length = 5000;
174  e.replay.delay = 500;
175  e.u.periodic.waveform = FF_SQUARE;
176  e.u.periodic.period = 1000;
177  e.u.periodic.magnitude = 0xFF;
178  e.u.periodic.offset = 0xFF;
179 
180  ret = ioctl(fdEvent, EVIOCSFF, &e);
181  ARMARX_CHECK(ret >= 0);
182 
183  // syscall_error(ret < 0, "EVIOCSFF failed");
184 
185  ARMARX_INFO << VAROUT(e.id);
186 
187  input_event play;
188  play.type = EV_FF;
189  play.code = static_cast<std::uint16_t>(e.id);
190  play.value = 1;
191 
192  ret = write(fdEvent, &play, sizeof(play));
193  ARMARX_CHECK(ret >= 0);
194 
195  ARMARX_INFO << "Executing effect";
196 
197 
198  for (int i = 0; i < 5; i++)
199  {
200 
201  input_event statusIe; // structure used to communicate with the driver
202  statusIe.type = EV_FF_STATUS;
203  statusIe.code = e.id;
204  // statusIe.value = 0;
205 
206  ret = write(fdEvent, &statusIe, sizeof(statusIe));
207 
208  // ARMARX_CHECK()
209  // ret should be FF_STATUS_PLAYING
210  ARMARX_INFO << VAROUT(ret);
211 
212  sleep(1);
213  }
214 
215 
216  // syscall_error(ret < 0, "write failed");
217 
218  ARMARX_INFO << "Executing effect";
219  // sleep(6);
220 
221 
222  input_event stop;
223  stop.type = EV_FF;
224  stop.code = e.id;
225  stop.value = 0;
226  [[maybe_unused]] const int stopStatus =
227  write(fdEvent, static_cast<const void*>(&stop), sizeof(stop));
228 
229 
230  ret = ioctl(fdEvent, EVIOCRMFF, e.id);
231  ARMARX_CHECK(ret >= 0);
232 
233  // syscall_error(ret < 0, "EVIOCRMFF failed");
234 
235  // close(fdEvent);
236 
237 
238  /**/
239  // Set the gain of the device
240  // {
241  // // int gain; between 0 and 100
242  // struct input_event ie; // structure used to communicate with the driver
243 
244  // ie.type = EV_FF;
245  // ie.code = FF_GAIN;
246  // ie.value = 0xFFFFUL * gain / 100;
247 
248  // if (write(fd, &ie, sizeof(ie)) == -1)
249  // {
250  // perror("set gain");
251  // }
252  // }
253 
254 
255  // struct input_event play;
256  // struct input_event stop;
257 
258  // // upload request to device
259  // ff_effect effect;
260  // const auto uploadStatus = ioctl(fd, EVIOCSFF, &effect);
261 
262  // // Play n times
263  // play.type = EV_FF;
264  // play.code = effect.id;
265  // play.value = nTimes;
266 
267  // const int playStatus = write(fd, static_cast<const void*>(&play), sizeof(play));
268 
269  // // Stop an effect
270  // stop.type = EV_FF;
271  // stop.code = effect.id;
272  // stop.value = 0;
273  // const int stopStatus = write(fd, static_cast<const void*>(&stop), sizeof(stop));
274 
275  // // remove effect
276  // ioctl(fd, EVIOCRMFF, effect.id);
277  }
278 
279  void
281  {
282  ::close(fd);
283  fd = -1;
284  }
285  };
286 } // namespace armarx
armarx::Joystick::open
bool open(std::string const &deviceName, std::string const &deviceEventName)
Definition: Joystick.h:55
armarx::aron::ret
ReaderT::InputType T & ret
Definition: rw.h:13
armarx::Joystick::numberOfButtons
int numberOfButtons
Definition: Joystick.h:51
armarx::Joystick::name
std::string name
Definition: Joystick.h:52
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::Joystick::executeEffect
void executeEffect(int gain=100, const int nTimes=1)
Definition: Joystick.h:124
armarx::Joystick::buttonsPressed
std::vector< bool > buttonsPressed
Definition: Joystick.h:49
armarx::read
void read(auto &eigen, auto *table)
Definition: FTSensorCalibrationGuiWidgetController.cpp:503
armarx::Joystick::axis
std::vector< int16_t > axis
Definition: Joystick.h:48
armarx::Joystick::close
void close()
Definition: Joystick.h:280
Component.h
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
VAROUT
#define VAROUT(x)
Definition: StringHelpers.h:198
armarx::Joystick::opened
bool opened() const
Definition: Joystick.h:95
armarx::aron::write
requires data::isWriter< WriterT > void write(WriterT &aron_w, const Eigen::Matrix< EigenT, rows, cols, options > &input, typename WriterT::ReturnType &ret, const armarx::aron::Path &aron_p=armarx::aron::Path())
Definition: eigen.h:138
armarx::Joystick::pollEvent
bool pollEvent()
Definition: Joystick.h:101
armarx::Joystick
Definition: Joystick.h:39
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:27
armarx::Joystick::numberOfAxis
int numberOfAxis
Definition: Joystick.h:50