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
36namespace armarx
37{
38
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";
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
#define VAROUT(x)
void executeEffect(int gain=100, const int nTimes=1)
Definition Joystick.h:124
bool opened() const
Definition Joystick.h:95
std::vector< bool > buttonsPressed
Definition Joystick.h:49
bool open(std::string const &deviceName, std::string const &deviceEventName)
Definition Joystick.h:55
std::vector< int16_t > axis
Definition Joystick.h:48
std::string name
Definition Joystick.h:52
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
This file offers overloads of toIce() and fromIce() functions for STL container types.
void read(auto &eigen, auto *table)