-
Notifications
You must be signed in to change notification settings - Fork 19
/
PyMyo.cpp
186 lines (157 loc) · 5.03 KB
/
PyMyo.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright (C) 2013-2014 Thalmic Labs Inc.
// Distributed under the Myo SDK license agreement. See LICENSE.txt for details.
// Modified to expose Myo data to Python
// Paul Lutz
// Scott Martin
// Fabricate.IO
#include <iostream>
#include <thread>
#include <mutex>
#include <myo/myo.hpp>
// Indicates the Myo is not on an arm
#define OFF_ARM 'A'
// Timeout to raise exception if no myo found
#define FIND_MYO_TIMEOUT_MS 10000
// Update loop period - 50ms = 20Hz
#define UPDATE_EVENT_PD_MS 50
// Sizes of rotation & accel arrays
#define QUAT_ARR_SZ 4
#define ACCEL_ARR_SZ 3
// Receives events from Myo devices
class DataCollector : public myo::DeviceListener {
private:
// Variables to hold current Myo state
bool onArm;
myo::Arm whichArm;
myo::Pose currentPose;
float quat_arr[QUAT_ARR_SZ]; //stores w, x, y, and z rotation
float accel_arr[ACCEL_ARR_SZ];
myo::Myo* myMyo;
std::mutex myoMutex; // Prevents vibration while polling for data
public:
DataCollector() : onArm(false), currentPose() {
for(int i = 0; i < QUAT_ARR_SZ; i++) {
quat_arr[i] = 0.0;
}
for(int i = 0; i < ACCEL_ARR_SZ; i++) {
accel_arr[i] = 0.0;
}
}
// This is used later by the vibrator
void setMyo(myo::Myo* myo) {
myMyo = myo;
}
void setLock(bool lock) {
if (lock) {
myoMutex.lock();
} else {
myoMutex.unlock();
}
}
// Called whenever the Myo device provides its current orientation, which is represented
// as a unit quaternion.
void onOrientationData(myo::Myo* myo, uint64_t timestamp, const myo::Quaternion<float>& quat) {
quat_arr[0] = quat.w();
quat_arr[1] = quat.x();
quat_arr[2] = quat.y();
quat_arr[3] = quat.z();
}
// Called whenever the Myo detects that the person wearing it has changed their pose, for example,
// making a fist, or not making a fist anymore.
void onPose(myo::Myo* myo, uint64_t timestamp, myo::Pose pose) {
currentPose = pose;
}
// Called whenever Myo has recognized a setup gesture after someone has put it on their
// arm. This lets Myo know which arm it's on and which way it's facing.
void onArmRecognized(myo::Myo* myo, uint64_t timestamp, myo::Arm arm, myo::XDirection xDirection) {
onArm = true;
whichArm = arm;
}
// Called whenever Myo has detected that it was moved from a stable position on a person's arm after
// it recognized the arm. Typically this happens when someone takes Myo off of their arm, but it can also happen
// when Myo is moved around on the arm.
void onArmLost(myo::Myo* myo, uint64_t timestamp) {
onArm = false;
}
// Called when the Myo sends acceleration data.
void onAccelerometerData (myo::Myo* myo, uint64_t timestamp, const myo::Vector3<float> &accel) {
accel_arr[0] = accel.x();
accel_arr[1] = accel.y();
accel_arr[2] = accel.z();
}
// Vibrates the Myo
void vibrate(myo::Myo::VibrationType duration) {
if (!myMyo) {
std::cerr << "Myo not set!";
exit(2);
}
myoMutex.lock();
myMyo->vibrate(duration);
myoMutex.unlock();
}
// Prints the current values that were updated by the on...() functions above.
void print() {
// Prints x, y, and z acceleration of the Myo
for(int i = 0; i < ACCEL_ARR_SZ; i++) {
char* bits = reinterpret_cast<char*>(&accel_arr[i]);
for(int n = 0; n < sizeof(float); ++n) {
std::cout << (bits[n]);
}
}
// Prints w, x, y, and z rotation of the Myo
for(int i = 0; i < QUAT_ARR_SZ; i++) {
char* bits = reinterpret_cast<char*>(&quat_arr[i]);
for(int n = 0; n < sizeof(float); ++n) {
std::cout << (bits[n]);
}
}
// Prints Pose byte
unsigned char poseByte;
poseByte = currentPose.type() & 0xFF;
std::cout << poseByte;
// Prints which arm the Myo is on: 0 = right, 1 = left
if(onArm) {
std::cout << (unsigned char)whichArm;
} else {
std::cout << OFF_ARM;
}
std::cout << '\n';
std::cout << std::flush; // Prevent caching multiple lines so each line is written immediately
}
};
DataCollector collector;
// Reads integer values from the console to control vibration
void inputThread() {
std::string input;
while(true) {
std::cin >> input;
if(std::cin.fail()) {
std::exit(0);
}
collector.vibrate((myo::Myo::VibrationType)input[0]);
}
}
int main(int argc, char** argv)
{
try {
myo::Hub hub("com.fabricate.pyo"); // This provides access to one or more Myos.
myo::Myo* myo = hub.waitForMyo(FIND_MYO_TIMEOUT_MS);
if (!myo) {
throw std::runtime_error("Unable to find a Myo!");
}
collector.setMyo(myo); // Used as a reference for vibration
hub.addListener(&collector); // Tell the hub to send data events to the collector
// This thread reads vibration commands
std::thread t1 (inputThread);
t1.detach();
while (true) {
collector.setLock(true); // Prevent vibration thread from writing while we read
hub.run(UPDATE_EVENT_PD_MS); // Allow myo to push events at the given rate
collector.setLock(false);
collector.print();
}
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
}