The open source OpenXR runtime
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

d/arduino: Add new flexible arduino based input device

authored by

Jakob Bornecrantz and committed by
Jakob Bornecrantz
a8a4d8c3 bc53be85

+886 -1
+5
.gitignore
··· 37 37 # Ignore patches generated by scripts 38 38 patches/ 39 39 40 + # Imgui settings 40 41 imgui.ini 41 42 42 43 # files from package building 43 44 obj-*/ 44 45 .pc/ 46 + 47 + # Arduino toolchain files 48 + src/xrt/drivers/arduino/device/*.elf 49 + src/xrt/drivers/arduino/device/*.bin
+7 -1
CMakeLists.txt
··· 92 92 cmake_dependent_option(BUILD_WITH_OPENHMD "Enable OpenHMD driver" ON "OPENHMD_FOUND" OFF) 93 93 cmake_dependent_option(BUILD_WITH_SDL2 "Enable SDL2 based test application" ON "SDL2_FOUND" OFF) 94 94 cmake_dependent_option(BUILD_WITH_DAYDREAM "Enable Bluetooth LE via DBUS" ON "BUILD_WITH_DBUS" OFF) 95 + cmake_dependent_option(BUILD_WITH_ARDUINO "Enable Arduino input device with BLE via DBUS" ON "BUILD_WITH_DBUS" OFF) 95 96 96 97 # These all use the Monado internal hid wrapper which is assumed to be available. 97 98 option(BUILD_WITH_HDK "Enable HDK driver" ON) ··· 100 101 option(BUILD_WITH_NS "Enable North Star driver" ON) 101 102 102 103 # You can set this from a superproject to add a driver 103 - list(APPEND AVAILABLE_DRIVERS DUMMY HDK HYDRA NS OHMD PSMV PSVR RS V4L2 VIVE DAYDREAM) 104 + list(APPEND AVAILABLE_DRIVERS ARDUINO DUMMY HDK HYDRA NS OHMD PSMV PSVR RS V4L2 VIVE DAYDREAM) 104 105 105 106 ### 106 107 # Flags ··· 151 152 152 153 # SDL2 based gui 153 154 set(BUILD_TARGET_GUI TRUE) 155 + endif() 156 + 157 + 158 + if(BUILD_WITH_ARDUINO) 159 + set(BUILD_DRIVER_ARDUINO TRUE) 154 160 endif() 155 161 156 162 if(BUILD_WITH_DUMMY)
+1
doc/changes/drivers/mr.251.md
··· 1 + Added a Arduino based flexible input device driver, along with Arduino C++ code for it.
+13
src/xrt/drivers/CMakeLists.txt
··· 5 5 set(ENABLED_HEADSET_DRIVERS) 6 6 set(ENABLED_DRIVERS) 7 7 8 + 9 + if(BUILD_DRIVER_ARDUINO) 10 + set(ARDUINO_SOURCE_FILES 11 + arduino/arduino_device.c 12 + arduino/arduino_interface.h 13 + arduino/arduino_prober.c 14 + ) 15 + 16 + add_library(drv_arduino STATIC ${ARDUINO_SOURCE_FILES}) 17 + target_link_libraries(drv_arduino PRIVATE xrt-interfaces aux_util aux_os) 18 + list(APPEND ENABLED_DRIVERS arduino) 19 + endif() 20 + 8 21 if(BUILD_DRIVER_DAYDREAM) 9 22 set(DAYDREAM_SOURCE_FILES 10 23 daydream/daydream_device.c
+436
src/xrt/drivers/arduino/arduino_device.c
··· 1 + // Copyright 2019-2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Arduino felxable input device code. 6 + * @author Pete Black <pete.black@collabora.com> 7 + * @author Jakob Bornecrantz <jakob@collabora.com> 8 + * @ingroup drv_arduino 9 + */ 10 + 11 + #include "xrt/xrt_device.h" 12 + #include "xrt/xrt_prober.h" 13 + #include "xrt/xrt_tracking.h" 14 + 15 + #include "os/os_ble.h" 16 + #include "os/os_time.h" 17 + #include "os/os_threading.h" 18 + 19 + #include "math/m_api.h" 20 + #include "math/m_imu_pre.h" 21 + #include "math/m_imu_3dof.h" 22 + 23 + #include "util/u_var.h" 24 + #include "util/u_time.h" 25 + #include "util/u_misc.h" 26 + #include "util/u_debug.h" 27 + #include "util/u_device.h" 28 + #include "util/u_bitwise.h" 29 + 30 + #include "arduino_interface.h" 31 + 32 + #include <stdio.h> 33 + #include <math.h> 34 + #include <assert.h> 35 + 36 + 37 + /* 38 + * 39 + * Structs. 40 + * 41 + */ 42 + 43 + /*! 44 + * A parsed sample of accel and gyro. 45 + */ 46 + struct arduino_parsed_sample 47 + { 48 + uint32_t time; 49 + uint32_t delta; 50 + struct xrt_vec3_i32 accel; 51 + struct xrt_vec3_i32 gyro; 52 + }; 53 + 54 + struct arduino_parsed_input 55 + { 56 + uint32_t timestamp; 57 + struct arduino_parsed_sample sample; 58 + }; 59 + 60 + struct arduino_device 61 + { 62 + struct xrt_device base; 63 + struct os_ble_device *ble; 64 + struct os_thread_helper oth; 65 + 66 + 67 + 68 + struct 69 + { 70 + 71 + //! Device time. 72 + uint64_t device_time; 73 + 74 + //! Lock for last and fusion. 75 + struct os_mutex lock; 76 + 77 + uint64_t last_time; 78 + 79 + //! Pre filter for the IMU. 80 + struct m_imu_pre_filter pre_filter; 81 + 82 + struct m_imu_3dof fusion; 83 + }; 84 + 85 + 86 + struct 87 + { 88 + bool last; 89 + } gui; 90 + 91 + bool print_spew; 92 + bool print_debug; 93 + }; 94 + 95 + 96 + /* 97 + * 98 + * Smaller helper functions. 99 + * 100 + */ 101 + 102 + #define ARDUINO_SPEW(c, ...) \ 103 + do { \ 104 + if (c->print_spew) { \ 105 + fprintf(stderr, "%s - ", __func__); \ 106 + fprintf(stderr, __VA_ARGS__); \ 107 + fprintf(stderr, "\n"); \ 108 + } \ 109 + } while (false) 110 + 111 + #define ARDUINO_DEBUG(c, ...) \ 112 + do { \ 113 + if (c->print_debug) { \ 114 + fprintf(stderr, "%s - ", __func__); \ 115 + fprintf(stderr, __VA_ARGS__); \ 116 + fprintf(stderr, "\n"); \ 117 + } \ 118 + } while (false) 119 + 120 + #define ARDUINO_ERROR(c, ...) \ 121 + do { \ 122 + fprintf(stderr, "%s - ", __func__); \ 123 + fprintf(stderr, __VA_ARGS__); \ 124 + fprintf(stderr, "\n"); \ 125 + } while (false) 126 + 127 + static inline struct arduino_device * 128 + arduino_device(struct xrt_device *xdev) 129 + { 130 + return (struct arduino_device *)xdev; 131 + } 132 + 133 + static uint32_t 134 + calc_delta_and_handle_rollover(uint32_t next, uint32_t last) 135 + { 136 + uint32_t tick_delta = next - last; 137 + 138 + // The 24-bit tick counter has rolled over, 139 + // adjust the "negative" value to be positive. 140 + if (tick_delta > 0xffffff) { 141 + tick_delta += 0x1000000; 142 + } 143 + 144 + return tick_delta; 145 + } 146 + 147 + static int16_t 148 + read_i16(const uint8_t *buffer, size_t offset) 149 + { 150 + return (buffer[offset] << 8) | buffer[offset + 1]; 151 + } 152 + 153 + 154 + /* 155 + * 156 + * Internal functions. 157 + * 158 + */ 159 + 160 + static void 161 + update_fusion(struct arduino_device *ad, 162 + struct arduino_parsed_sample *sample, 163 + timepoint_ns timestamp_ns, 164 + time_duration_ns delta_ns) 165 + { 166 + struct xrt_vec3 accel, gyro; 167 + m_imu_pre_filter_data(&ad->pre_filter, &sample->accel, &sample->gyro, 168 + &accel, &gyro); 169 + 170 + ad->device_time += (uint64_t)sample->delta * 1000; 171 + 172 + m_imu_3dof_update(&ad->fusion, ad->device_time, &accel, &gyro); 173 + 174 + double delta_device_ms = (double)sample->delta / 1000.0; 175 + double delta_host_ms = (double)delta_ns / (1000.0 * 1000.0); 176 + ARDUINO_DEBUG(ad, "%+fms %+fms", delta_host_ms, delta_device_ms); 177 + ARDUINO_DEBUG( 178 + ad, "fusion sample %u (ax %d ay %d az %d) (gx %d gy %d gz %d)", 179 + sample->time, sample->accel.x, sample->accel.y, sample->accel.z, 180 + sample->gyro.x, sample->gyro.y, sample->gyro.z); 181 + ARDUINO_DEBUG(ad, "\n"); 182 + } 183 + 184 + static void 185 + arduino_parse_input(struct arduino_device *ad, 186 + void *data, 187 + struct arduino_parsed_input *input) 188 + { 189 + U_ZERO(input); 190 + unsigned char *b = (unsigned char *)data; 191 + ARDUINO_SPEW( 192 + ad, 193 + "raw input: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x " 194 + "%02x %02x %02x %02x %02x %02x %02x %02x %02x", 195 + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], 196 + b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]); 197 + 198 + uint32_t time = b[5] | b[4] << 8 | b[3] << 16; 199 + 200 + input->sample.time = time; 201 + input->sample.delta = 202 + calc_delta_and_handle_rollover(time, ad->last_time); 203 + ad->last_time = time; 204 + 205 + input->sample.accel.x = read_i16(b, 6); 206 + input->sample.accel.y = read_i16(b, 8); 207 + input->sample.accel.z = read_i16(b, 10); 208 + input->sample.gyro.x = read_i16(b, 12); 209 + input->sample.gyro.y = read_i16(b, 14); 210 + input->sample.gyro.z = read_i16(b, 16); 211 + } 212 + 213 + /*! 214 + * Reads one packet from the device,handles locking and checking if 215 + * the thread has been told to shut down. 216 + */ 217 + static bool 218 + arduino_read_one_packet(struct arduino_device *ad, uint8_t *buffer, size_t size) 219 + { 220 + os_thread_helper_lock(&ad->oth); 221 + 222 + while (os_thread_helper_is_running_locked(&ad->oth)) { 223 + int retries = 5; 224 + int ret = -1; 225 + os_thread_helper_unlock(&ad->oth); 226 + 227 + while (retries > 0) { 228 + ret = os_ble_read(ad->ble, buffer, size, 500); 229 + if (ret == (int)size) { 230 + break; 231 + } 232 + retries--; 233 + } 234 + if (ret == 0) { 235 + fprintf(stderr, "%s\n", __func__); 236 + // Must lock thread before check in while. 237 + os_thread_helper_lock(&ad->oth); 238 + continue; 239 + } 240 + if (ret < 0) { 241 + ARDUINO_ERROR(arduino, "Failed to read device '%i'!", 242 + ret); 243 + return false; 244 + } 245 + return true; 246 + } 247 + 248 + return false; 249 + } 250 + 251 + static void * 252 + arduino_run_thread(void *ptr) 253 + { 254 + struct arduino_device *ad = (struct arduino_device *)ptr; 255 + uint8_t buffer[20]; 256 + timepoint_ns then_ns, now_ns; 257 + struct arduino_parsed_input input; // = {0}; 258 + 259 + // wait for a package to sync up, it's discarded but that's okay. 260 + if (!arduino_read_one_packet(ad, buffer, 20)) { 261 + return NULL; 262 + } 263 + 264 + then_ns = os_monotonic_get_ns(); 265 + while (arduino_read_one_packet(ad, buffer, 20)) { 266 + 267 + // As close to when we get a packet. 268 + now_ns = os_monotonic_get_ns(); 269 + 270 + // Parse the data we got. 271 + arduino_parse_input(ad, buffer, &input); 272 + 273 + time_duration_ns delta_ns = now_ns - then_ns; 274 + then_ns = now_ns; 275 + 276 + // Lock last and the fusion. 277 + os_mutex_lock(&ad->lock); 278 + 279 + // Process the parsed data. 280 + update_fusion(ad, &input.sample, now_ns, delta_ns); 281 + 282 + // Now done. 283 + os_mutex_unlock(&ad->lock); 284 + } 285 + 286 + return NULL; 287 + } 288 + 289 + 290 + /* 291 + * 292 + * Device functions. 293 + * 294 + */ 295 + 296 + static void 297 + arduino_get_fusion_pose(struct arduino_device *ad, 298 + enum xrt_input_name name, 299 + timepoint_ns when, 300 + struct xrt_space_relation *out_relation) 301 + { 302 + out_relation->pose.orientation = ad->fusion.rot; 303 + 304 + //! @todo assuming that orientation is actually currently tracked. 305 + out_relation->relation_flags = (enum xrt_space_relation_flags)( 306 + XRT_SPACE_RELATION_ORIENTATION_VALID_BIT | 307 + XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT); 308 + } 309 + 310 + static void 311 + arduino_device_destroy(struct xrt_device *xdev) 312 + { 313 + struct arduino_device *ad = arduino_device(xdev); 314 + 315 + // Destroy the thread object. 316 + os_thread_helper_destroy(&ad->oth); 317 + 318 + // Now that the thread is not running we can destroy the lock. 319 + os_mutex_destroy(&ad->lock); 320 + 321 + // Remove the variable tracking. 322 + u_var_remove_root(ad); 323 + 324 + // Destroy the fusion. 325 + m_imu_3dof_close(&ad->fusion); 326 + 327 + // Does null checking and zeros. 328 + os_ble_destroy(&ad->ble); 329 + 330 + free(ad); 331 + } 332 + 333 + static void 334 + arduino_device_update_inputs(struct xrt_device *xdev, 335 + struct time_state *timekeeping) 336 + { 337 + struct arduino_device *ad = arduino_device(xdev); 338 + 339 + int64_t now = time_state_get_now(timekeeping); 340 + 341 + // Lock the data. 342 + os_mutex_lock(&ad->lock); 343 + 344 + ad->base.inputs[0].timestamp = now; 345 + ad->base.inputs[1].timestamp = now; 346 + ad->base.inputs[2].timestamp = now; 347 + ad->base.inputs[3].timestamp = now; 348 + ad->base.inputs[4].timestamp = now; 349 + ad->base.inputs[5].timestamp = now; 350 + ad->base.inputs[6].timestamp = now; 351 + ad->base.inputs[7].timestamp = now; 352 + 353 + // Done now. 354 + os_mutex_unlock(&ad->lock); 355 + } 356 + 357 + static void 358 + arduino_device_get_tracked_pose(struct xrt_device *xdev, 359 + enum xrt_input_name name, 360 + struct time_state *timekeeping, 361 + int64_t *out_timestamp, 362 + struct xrt_space_relation *out_relation) 363 + { 364 + struct arduino_device *ad = arduino_device(xdev); 365 + 366 + timepoint_ns now = time_state_get_now(timekeeping); 367 + 368 + arduino_get_fusion_pose(ad, name, now, out_relation); 369 + } 370 + 371 + 372 + /* 373 + * 374 + * Prober functions. 375 + * 376 + */ 377 + 378 + struct xrt_device * 379 + arduino_device_create(struct os_ble_device *ble, 380 + bool print_spew, 381 + bool print_debug) 382 + { 383 + enum u_device_alloc_flags flags = 384 + (enum u_device_alloc_flags)(U_DEVICE_ALLOC_TRACKING_NONE); 385 + struct arduino_device *ad = 386 + U_DEVICE_ALLOCATE(struct arduino_device, flags, 8, 0); 387 + 388 + ad->base.name = XRT_DEVICE_DAYDREAM; 389 + ad->base.destroy = arduino_device_destroy; 390 + ad->base.update_inputs = arduino_device_update_inputs; 391 + ad->base.get_tracked_pose = arduino_device_get_tracked_pose; 392 + ad->base.inputs[0].name = XRT_INPUT_DAYDREAM_POSE; 393 + ad->base.inputs[1].name = XRT_INPUT_DAYDREAM_TOUCHPAD_CLICK; 394 + ad->base.inputs[2].name = XRT_INPUT_DAYDREAM_BAR_CLICK; 395 + ad->base.inputs[3].name = XRT_INPUT_DAYDREAM_CIRCLE_CLICK; 396 + ad->base.inputs[4].name = XRT_INPUT_DAYDREAM_VOLDN_CLICK; 397 + ad->base.inputs[5].name = XRT_INPUT_DAYDREAM_VOLUP_CLICK; 398 + ad->base.inputs[6].name = XRT_INPUT_DAYDREAM_TOUCHPAD_VALUE_X; 399 + ad->base.inputs[7].name = XRT_INPUT_DAYDREAM_TOUCHPAD_VALUE_Y; 400 + 401 + ad->ble = ble; 402 + ad->print_spew = print_spew; 403 + ad->print_debug = print_debug; 404 + 405 + m_imu_3dof_init(&ad->fusion, M_IMU_3DOF_USE_GRAVITY_DUR_300MS); 406 + 407 + #define DEG_TO_RAD ((double)M_PI / 180.0) 408 + float accel_ticks_to_float = (4.0 * MATH_GRAVITY_M_S2) / INT16_MAX; 409 + float gyro_ticks_to_float = (2000.0 * DEG_TO_RAD) / INT16_MAX; 410 + 411 + m_imu_pre_filter_init(&ad->pre_filter, accel_ticks_to_float, 412 + gyro_ticks_to_float); 413 + m_imu_pre_filter_set_switch_x_and_y(&ad->pre_filter); 414 + 415 + #if 0 416 + ad->pre_filter.gyro.bias.x = 10 * gyro_ticks_to_float; 417 + ad->pre_filter.gyro.bias.y = 10 * gyro_ticks_to_float; 418 + #endif 419 + 420 + // Everything done, finally start the thread. 421 + int ret = os_thread_helper_start(&ad->oth, arduino_run_thread, ad); 422 + if (ret != 0) { 423 + ARDUINO_ERROR(dd, "Failed to start thread!"); 424 + arduino_device_destroy(&ad->base); 425 + return NULL; 426 + } 427 + 428 + u_var_add_root(ad, "Arduino flexible input device", true); 429 + u_var_add_gui_header(ad, &ad->gui.last, "Last"); 430 + u_var_add_ro_vec3_f32(ad, &ad->fusion.last.accel, "last.accel"); 431 + u_var_add_ro_vec3_f32(ad, &ad->fusion.last.gyro, "last.gyro"); 432 + 433 + ARDUINO_DEBUG(ad, "Created device!"); 434 + 435 + return &ad->base; 436 + }
+54
src/xrt/drivers/arduino/arduino_interface.h
··· 1 + // Copyright 2019-2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Interer face @ref drv_arduino. 6 + * @author Pete Black <pete.black@collabora.com> 7 + * @author Jakob Bornecrantz <jakob@collabora.com> 8 + * @ingroup drv_arduino 9 + */ 10 + 11 + #pragma once 12 + 13 + #ifdef __cplusplus 14 + extern "C" { 15 + #endif 16 + 17 + 18 + struct os_ble_device; 19 + 20 + /*! 21 + * @defgroup drv_arduino Arduino flexible input device driver 22 + * @ingroup drv 23 + * 24 + * @brief Driver for the Monado Arduino based flexible input device. 25 + */ 26 + 27 + /*! 28 + * Probing function for the Arduino based flexible input device driver. 29 + * 30 + * @ingroup drv_arduino 31 + */ 32 + struct xrt_auto_prober * 33 + arduino_create_auto_prober(); 34 + 35 + /*! 36 + * Create a arduino device from a ble notify. 37 + * 38 + * @ingroup drv_arduino 39 + */ 40 + struct xrt_device * 41 + arduino_device_create(struct os_ble_device *ble, 42 + bool print_spew, 43 + bool print_debug); 44 + 45 + /*! 46 + * @dir drivers/arduino 47 + * 48 + * @brief @ref drv_arduino files. 49 + */ 50 + 51 + 52 + #ifdef __cplusplus 53 + } 54 + #endif
+109
src/xrt/drivers/arduino/arduino_prober.c
··· 1 + // Copyright 2019-2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Arduino felxable input device prober code. 6 + * @author Pete Black <pete.black@collabora.com> 7 + * @ingroup drv_arduino 8 + */ 9 + 10 + #include <stdio.h> 11 + #include <stdlib.h> 12 + #include <wchar.h> 13 + 14 + #include "os/os_ble.h" 15 + 16 + #include "xrt/xrt_prober.h" 17 + 18 + #include "util/u_misc.h" 19 + #include "util/u_debug.h" 20 + 21 + #include "arduino_interface.h" 22 + 23 + 24 + /* 25 + * 26 + * Defines & structs. 27 + * 28 + */ 29 + 30 + DEBUG_GET_ONCE_BOOL_OPTION(arduino_enable, "ARDUINO_ENABLE", true) 31 + DEBUG_GET_ONCE_BOOL_OPTION(arduino_spew, "ARDUINO_PRINT_SPEW", false) 32 + DEBUG_GET_ONCE_BOOL_OPTION(arduino_debug, "ARDUINO_PRINT_DEBUG", false) 33 + 34 + /*! 35 + * Arduino prober struct. 36 + * 37 + * @ingroup drv_arduino 38 + */ 39 + struct arduino_prober 40 + { 41 + struct xrt_auto_prober base; 42 + 43 + bool print_spew; 44 + bool print_debug; 45 + bool enabled; 46 + }; 47 + 48 + 49 + /* 50 + * 51 + * Static functions. 52 + * 53 + */ 54 + 55 + static inline struct arduino_prober * 56 + arduino_prober(struct xrt_auto_prober *p) 57 + { 58 + return (struct arduino_prober *)p; 59 + } 60 + 61 + static void 62 + arduino_prober_destroy(struct xrt_auto_prober *p) 63 + { 64 + struct arduino_prober *ap = arduino_prober(p); 65 + 66 + free(ap); 67 + } 68 + 69 + static struct xrt_device * 70 + arduino_prober_autoprobe(struct xrt_auto_prober *xap, 71 + bool no_hmds, 72 + struct xrt_prober *xp) 73 + { 74 + struct arduino_prober *ap = arduino_prober(xap); 75 + if (!ap->enabled) { 76 + return NULL; 77 + } 78 + 79 + const char *dev_uuid = "00004242-0000-1000-8000-004242424242"; 80 + const char *char_uuid = "00000001-1000-1000-8000-004242424242"; 81 + 82 + struct os_ble_device *ble = NULL; 83 + os_ble_notify_open(dev_uuid, char_uuid, &ble); 84 + if (ble == NULL) { 85 + return NULL; 86 + } 87 + 88 + return arduino_device_create(ble, ap->print_spew, ap->print_debug); 89 + } 90 + 91 + 92 + /* 93 + * 94 + * Exported functions. 95 + * 96 + */ 97 + 98 + struct xrt_auto_prober * 99 + arduino_create_auto_prober() 100 + { 101 + struct arduino_prober *ap = U_TYPED_CALLOC(struct arduino_prober); 102 + ap->base.destroy = arduino_prober_destroy; 103 + ap->base.lelo_dallas_autoprobe = arduino_prober_autoprobe; 104 + ap->enabled = debug_get_bool_option_arduino_enable(); 105 + ap->print_spew = debug_get_bool_option_arduino_spew(); 106 + ap->print_debug = debug_get_bool_option_arduino_debug(); 107 + 108 + return &ap->base; 109 + }
+248
src/xrt/drivers/arduino/device/device.ino
··· 1 + // Copyright 2019-2020, Collabora, Ltd. 2 + // SPDX-License-Identifier: BSL-1.0 3 + /*! 4 + * @file 5 + * @brief Arduino based felxable input device code. 6 + * @author Pete Black <pete.black@collabora.com> 7 + * @author Jakob Bornecrantz <jakob@collabora.com> 8 + * @ingroup drv_arduino 9 + */ 10 + 11 + #include "mbed.h" 12 + #include <stdint.h> 13 + #include <ArduinoBLE.h> 14 + #include <Arduino_LSM9DS1.h> 15 + 16 + 17 + #define USE_SERIAL 18 + #ifdef USE_SERIAL 19 + #define LOG(...) Serial.print(__VA_ARGS__) 20 + #define LOG_LN(...) Serial.println(__VA_ARGS__) 21 + #else 22 + #define LOG(...) (void)NULL 23 + #define LOG_LN(...) (void)NULL 24 + #endif 25 + 26 + #define SET(o, v) \ 27 + do { \ 28 + uint32_t val = v; \ 29 + buffer[o + 0] = val >> 8; \ 30 + buffer[o + 1] = val; \ 31 + } while (false) 32 + 33 + struct Sample 34 + { 35 + int32_t acc_x = 0; 36 + int32_t acc_y = 0; 37 + int32_t acc_z = 0; 38 + int32_t gyr_x = 0; 39 + int32_t gyr_y = 0; 40 + int32_t gyr_z = 0; 41 + uint32_t time = 0; 42 + }; 43 + 44 + 45 + /* 46 + * 47 + * Global variables 48 + * 49 + */ 50 + 51 + volatile bool isConnected; 52 + 53 + BLEService service("00004242-0000-1000-8000-004242424242"); // create service 54 + 55 + // create switch characteristic and allow remote device to read and write 56 + BLECharacteristic notify("00000001-1000-1000-8000-004242424242", 57 + BLERead | BLENotify, 58 + 20, 59 + true); 60 + 61 + rtos::Mutex mutex; 62 + rtos::Thread thread; 63 + rtos::Mail<Sample, 100> mail; 64 + 65 + 66 + /* 67 + * 68 + * IMU functions. 69 + * 70 + */ 71 + 72 + void 73 + imu_loop() 74 + { 75 + if (isConnected == false) { 76 + return; 77 + } 78 + 79 + if (IMU.accelerationAvailable() && IMU.gyroscopeAvailable()) { 80 + uint32_t time = micros(); 81 + float gyr_x, gyr_y, gyr_z; 82 + float acc_x, acc_y, acc_z; 83 + IMU.readGyroscope(gyr_x, gyr_y, gyr_z); 84 + IMU.readAcceleration(acc_x, acc_y, acc_z); 85 + 86 + Sample *sample = mail.alloc(); 87 + sample->time = time; 88 + sample->acc_x = acc_x * (32768.0 / 4.0); 89 + sample->acc_y = acc_y * (32768.0 / 4.0); 90 + sample->acc_z = acc_z * (32768.0 / 4.0); 91 + 92 + sample->gyr_x = gyr_x * (32768.0 / 2000.0); 93 + sample->gyr_y = gyr_y * (32768.0 / 2000.0); 94 + sample->gyr_z = gyr_z * (32768.0 / 2000.0); 95 + mail.put(sample); 96 + } 97 + } 98 + 99 + void 100 + imu_thread() 101 + { 102 + while (true) { 103 + imu_loop(); 104 + delay(1); 105 + } 106 + } 107 + 108 + 109 + /* 110 + * 111 + * BLE functions. 112 + * 113 + */ 114 + 115 + void 116 + loop() 117 + { 118 + bool got_mail = false; 119 + int32_t acc_x = 0, acc_y = 0, acc_z = 0; 120 + int32_t gyr_x = 0, gyr_y = 0, gyr_z = 0; 121 + int32_t count = 0; 122 + uint32_t time; 123 + 124 + uint8_t buffer[20]; 125 + static int countPacket = 0; 126 + 127 + BLE.poll(); 128 + if (isConnected == false) { 129 + return; 130 + } 131 + 132 + while (true) { 133 + osEvent evt = mail.get(0); 134 + if (evt.status != osEventMail) { 135 + break; 136 + } 137 + 138 + Sample *sample = (Sample *)evt.value.p; 139 + acc_x += sample->acc_x; 140 + acc_y += sample->acc_y; 141 + acc_z += sample->acc_z; 142 + gyr_x += sample->gyr_x; 143 + gyr_y += sample->gyr_y; 144 + gyr_z += sample->gyr_z; 145 + time = sample->time; 146 + mail.free(sample); 147 + 148 + count++; 149 + got_mail = true; 150 + } 151 + 152 + if (!got_mail) { 153 + delay(1); 154 + return; 155 + } 156 + 157 + buffer[0] = countPacket++; 158 + buffer[1] = count; 159 + buffer[2] = 0; 160 + 161 + buffer[3] = time >> 16; 162 + buffer[4] = time >> 8; 163 + buffer[5] = time; 164 + 165 + SET(6, (acc_x / count)); 166 + SET(8, (acc_y / count)); 167 + SET(10, (acc_z / count)); 168 + SET(12, (gyr_x / count)); 169 + SET(14, (gyr_y / count)); 170 + SET(16, (gyr_z / count)); 171 + buffer[18] = 0; 172 + buffer[19] = 0; 173 + 174 + notify.writeValue(buffer, 20); 175 + } 176 + 177 + void 178 + ble_peripheral_connect_handler(BLEDevice peer) 179 + { 180 + LOG("Connected event, central: "); 181 + LOG_LN(peer.address()); 182 + isConnected = true; 183 + BLE.poll(); 184 + } 185 + 186 + void 187 + ble_peripheral_disconnect_handler(BLEDevice peer) 188 + { 189 + LOG("Disconnected event, central: "); 190 + LOG_LN(peer.address()); 191 + isConnected = false; 192 + BLE.poll(); 193 + } 194 + 195 + 196 + /* 197 + * 198 + * Main functions. 199 + * 200 + */ 201 + 202 + void 203 + block() 204 + { 205 + while (true) { 206 + LOG_LN("BLOCKED"); 207 + delay(1000); 208 + } 209 + } 210 + 211 + void 212 + setup() 213 + { 214 + #ifdef USE_SERIAL 215 + Serial.begin(9600); 216 + while (!Serial) { 217 + } 218 + #endif 219 + 220 + if (!BLE.begin()) { 221 + LOG_LN("BLE initialisation failed!"); 222 + block(); 223 + } 224 + 225 + if (!IMU.begin()) { 226 + LOG_LN("IMU initialisation failed!"); 227 + block(); 228 + } 229 + 230 + BLE.setLocalName("Monado Flexible Controller"); 231 + BLE.setAdvertisedService(service); 232 + service.addCharacteristic(notify); 233 + BLE.addService(service); 234 + 235 + // Assign event handlers for connected, disconnected to peripheral. 236 + BLE.setEventHandler(BLEConnected, ble_peripheral_connect_handler); 237 + BLE.setEventHandler(BLEDisconnected, ble_peripheral_disconnect_handler); 238 + 239 + notify.setValue(0); 240 + 241 + // Start advertising. 242 + BLE.advertise(); 243 + 244 + isConnected = false; 245 + 246 + // IMU has it's own thread. 247 + thread.start(imu_thread); 248 + }
+4
src/xrt/targets/common/CMakeLists.txt
··· 10 10 target_include_directories(target_lists PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../drivers) 11 11 target_include_directories(target_lists PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 12 12 13 + if(BUILD_DRIVER_ARDUINO) 14 + target_link_libraries(target_lists PRIVATE drv_arduino) 15 + endif() 16 + 13 17 if(BUILD_DRIVER_DAYDREAM) 14 18 target_link_libraries(target_lists PRIVATE drv_daydream) 15 19 endif()
+9
src/xrt/targets/common/target_lists.c
··· 10 10 11 11 #include "target_lists.h" 12 12 13 + #ifdef XRT_BUILD_DRIVER_ARDUINO 14 + #include "arduino/arduino_interface.h" 15 + #endif 16 + 13 17 #ifdef XRT_BUILD_DRIVER_DUMMY 14 18 #include "dummy/dummy_interface.h" 15 19 #endif ··· 94 98 xrt_auto_prober_creator target_auto_list[] = { 95 99 #ifdef XRT_BUILD_DRIVER_PSVR 96 100 psvr_create_auto_prober, 101 + #endif 102 + 103 + #ifdef XRT_BUILD_DRIVER_ARDUINO 104 + // Before OpenHMD 105 + arduino_create_auto_prober, 97 106 #endif 98 107 99 108 #ifdef XRT_BUILD_DRIVER_DAYDREAM