From 0749f130e1f629087f38297c8a5344389ad9aa20 Mon Sep 17 00:00:00 2001 From: Jon Trulson Date: Tue, 31 Jan 2017 13:06:26 -0700 Subject: [PATCH] nunchuck: C port; FTI; C++ wraps C Some API changes were made as well, see docs/apichanges.md. Signed-off-by: Jon Trulson --- docs/apichanges.md | 8 ++ examples/c++/nunchuck.cxx | 28 ++-- examples/c/CMakeLists.txt | 1 + examples/c/nunchuck.c | 84 ++++++++++++ examples/java/NUNCHUCKSample.java | 60 ++++----- examples/javascript/nunchuck.js | 12 +- examples/python/nunchuck.py | 10 +- include/fti/upm_buttons.h | 46 +++++++ include/upm_fti.h | 5 +- src/nunchuck/CMakeLists.txt | 14 +- src/nunchuck/nunchuck.c | 206 ++++++++++++++++++++++++++++++ src/nunchuck/nunchuck.cxx | 116 ++--------------- src/nunchuck/nunchuck.h | 123 ++++++++++++++++++ src/nunchuck/nunchuck.hpp | 51 ++------ src/nunchuck/nunchuck_fti.c | 190 +++++++++++++++++++++++++++ 15 files changed, 740 insertions(+), 214 deletions(-) create mode 100644 examples/c/nunchuck.c create mode 100644 include/fti/upm_buttons.h create mode 100644 src/nunchuck/nunchuck.c create mode 100644 src/nunchuck/nunchuck.h create mode 100644 src/nunchuck/nunchuck_fti.c diff --git a/docs/apichanges.md b/docs/apichanges.md index 0171cff7..8227e04f 100644 --- a/docs/apichanges.md +++ b/docs/apichanges.md @@ -4,6 +4,14 @@ API Changes {#apichanges} Here's a list of other API changes made to the library that break source/binary compatibility between releases: + * **nunchuck** This driver no longer supports the init() function. + All initialization is now done in the C nunchuck_init() function, + or the C++ constructor. In addition, the *NUNCHUCK_I2C_ADDR* + define is no longer exposed, as it is not possible to use any other + I2C address than *0x52*. The readBytes() and writeByte() functions + are also no longer exposed, since aside from what the driver does + to initialize and read data, there are no other options available. + * **enc03r** This driver no longer supports the value() function. In addition, an update() function has been added. This function must be called prior to calling angularVelocity(). angularVelocity() no diff --git a/examples/c++/nunchuck.cxx b/examples/c++/nunchuck.cxx index 03d2cbb3..b004df5d 100644 --- a/examples/c++/nunchuck.cxx +++ b/examples/c++/nunchuck.cxx @@ -43,37 +43,29 @@ int main(int argc, char **argv) signal(SIGINT, sig_handler); //! [Interesting] - // Instantiate a nunchuck controller bus 0 - upm::NUNCHUCK *nunchuck = new upm::NUNCHUCK(0); - - // always do this first - cout << "Initializing... " << endl; - if (!nunchuck->init()) - { - cerr << "nunchuck->init() failed." << endl; - return 0; - } - + // Instantiate a nunchuck controller bus 3 + upm::NUNCHUCK *nunchuck = new upm::NUNCHUCK(3); + while (shouldRun) { nunchuck->update(); - cout << "stickX: " << nunchuck->stickX + cout << "stickX: " << nunchuck->stickX << ", stickY: " << nunchuck->stickY << endl; - cout << "accelX: " << nunchuck->accelX - << ", accelY: " << nunchuck->accelY + cout << "accelX: " << nunchuck->accelX + << ", accelY: " << nunchuck->accelY << ", accelZ: " << nunchuck->accelZ << endl; - - cout << "button C: " + + cout << "button C: " << ((nunchuck->buttonC) ? "pressed" : "not pressed") << endl; - cout << "button Z: " + cout << "button Z: " << ((nunchuck->buttonZ) ? "pressed" : "not pressed") << endl; cout << endl; usleep(100000); } //! [Interesting] - + delete nunchuck; return 0; } diff --git a/examples/c/CMakeLists.txt b/examples/c/CMakeLists.txt index 2d885b40..e1687614 100644 --- a/examples/c/CMakeLists.txt +++ b/examples/c/CMakeLists.txt @@ -148,6 +148,7 @@ add_example (cjq4435) add_example (hmc5883l) add_example (wfs) add_example (enc03r) +add_example (nunchuck) # Custom examples add_custom_example (nmea_gps_i2c-example-c nmea_gps_i2c.c nmea_gps) diff --git a/examples/c/nunchuck.c b/examples/c/nunchuck.c new file mode 100644 index 00000000..c95fdd9e --- /dev/null +++ b/examples/c/nunchuck.c @@ -0,0 +1,84 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include + +#include +#include "nunchuck.h" + +bool shouldRun = true; + +void sig_handler(int signo) +{ + if (signo == SIGINT) + shouldRun = false; +} + +int main(int argc, char **argv) +{ + signal(SIGINT, sig_handler); + +//! [Interesting] + // Instantiate a nunchuck controller bus 3 + nunchuck_context sensor = nunchuck_init(3); + + if (!sensor) + { + printf("%s: nunchuck_init() failed\n", __FUNCTION__); + return 1; + } + + while (shouldRun) + { + if (nunchuck_update(sensor)) + { + printf("%s: nunchuck_update() failed\n", __FUNCTION__); + nunchuck_close(sensor); + return 1; + } + + int x, y, z; + nunchuck_get_stick(sensor, &x, &y); + printf("stickX: %d stickY: %d\n", x, y); + + nunchuck_get_acceleration(sensor, &x, &y, &z); + printf("accelX: %d accelY: %d accelZ: %d\n", x, y, z); + + bool bc, bz; + nunchuck_get_buttons(sensor, &bc, &bz); + printf("button C: %s\n", + ((bc) ? "pressed" : "not pressed")); + printf("button Z: %s\n\n", + ((bz) ? "pressed" : "not pressed")); + + upm_delay_ms(100); + } + + nunchuck_close(sensor); + + //! [Interesting] + return 0; +} diff --git a/examples/java/NUNCHUCKSample.java b/examples/java/NUNCHUCKSample.java index 0a26d997..c9bfb418 100644 --- a/examples/java/NUNCHUCKSample.java +++ b/examples/java/NUNCHUCKSample.java @@ -24,38 +24,38 @@ public class NUNCHUCKSample { - public static void main(String[] args) throws InterruptedException { - // ! [Interesting] - // Instantiate a nunchuck controller bus 0 - upm_nunchuck.NUNCHUCK nunchuck = new upm_nunchuck.NUNCHUCK(0); + public static void main(String[] args) throws InterruptedException { + // ! [Interesting] + // Instantiate a nunchuck controller bus 0 + upm_nunchuck.NUNCHUCK nunchuck = new upm_nunchuck.NUNCHUCK(3); - // always do this first - System.out.println("Initializing... "); - if (!nunchuck.init()) { - System.err.println("nunchuck->init() failed."); - return; - } + while (true) + { + nunchuck.update(); + System.out.println("stickX: " + + nunchuck.getStickX() + + ", stickY: " + + nunchuck.getStickY()); + System.out.println("accelX: " + + nunchuck.getAccelX() + + ", accelY: " + + nunchuck.getAccelY() + + ", accelZ: " + + nunchuck.getAccelZ()); - while (true) { - nunchuck.update(); - System.out.println("stickX: " + nunchuck.getStickX() + ", stickY: " - + nunchuck.getStickY()); - System.out.println("accelX: " + nunchuck.getAccelX() + ", accelY: " - + nunchuck.getAccelY() + ", accelZ: " + nunchuck.getAccelZ()); + if (nunchuck.getButtonC()) + System.out.println("Button C pressed"); + else + System.out.println("Button C not pressed"); - if (nunchuck.getButtonC()) - System.out.println("Button C pressed"); - else - System.out.println("Button C not pressed"); + if (nunchuck.getButtonZ()) + System.out.println("Button Z pressed"); + else + System.out.println("Button Z not pressed"); - if (nunchuck.getButtonZ()) - System.out.println("Button Z pressed"); - else - System.out.println("Button Z not pressed"); + Thread.sleep(100); + } + // ! [Interesting] + } - Thread.sleep(1000); - } - // ! [Interesting] - } - -} \ No newline at end of file +} diff --git a/examples/javascript/nunchuck.js b/examples/javascript/nunchuck.js index 9fadb4c0..02e4d2fe 100644 --- a/examples/javascript/nunchuck.js +++ b/examples/javascript/nunchuck.js @@ -24,16 +24,8 @@ var nunchuck_lib = require('jsupm_nunchuck'); -// Instantiate a nunchuck controller bus 0 -var nunchuck_obj = new nunchuck_lib.NUNCHUCK(0); - -// always do this first -console.log("Initializing... "); -if (!nunchuck_obj.init()) -{ - console.log("nunchuck->init() failed."); - process.exit(0); -} +// Instantiate a nunchuck controller bus 3 +var nunchuck_obj = new nunchuck_lib.NUNCHUCK(3); setInterval(function() { diff --git a/examples/python/nunchuck.py b/examples/python/nunchuck.py index fd993b61..c25a8b29 100755 --- a/examples/python/nunchuck.py +++ b/examples/python/nunchuck.py @@ -26,8 +26,8 @@ import time, sys, signal, atexit from upm import pyupm_nunchuck as upmNunchuck def main(): - # Instantiate a nunchuck controller bus 0 on I2C - myNunchuck = upmNunchuck.NUNCHUCK(0) + # Instantiate a nunchuck controller bus 3 on I2C + myNunchuck = upmNunchuck.NUNCHUCK(3) ## Exit handlers ## # This function stops python from printing a stacktrace when you hit control-C @@ -43,12 +43,6 @@ def main(): atexit.register(exitHandler) signal.signal(signal.SIGINT, SIGINTHandler) - # always do this first - print("Initializing... ") - if (not myNunchuck.init()): - print("nunchuck->init() failed.") - sys.exit(0); - def buttonStateStr(buttonState): return "pressed" if buttonState else "not pressed" diff --git a/include/fti/upm_buttons.h b/include/fti/upm_buttons.h new file mode 100644 index 00000000..ac025fca --- /dev/null +++ b/include/fti/upm_buttons.h @@ -0,0 +1,46 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef UPM_BUTTONS_H_ +#define UPM_BUTTONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +// Buttons function table +typedef struct _upm_buttons_ft { + // This function is used to query button state. The num_button + // specifies the total number of buttons present, and the values + // indicates each button's current value as an array of bools. + upm_result_t (*upm_buttons_get_num_buttons) (const void *dev, + unsigned int *num_buttons); + upm_result_t (*upm_buttons_get_values) (const void *dev, + bool *values); +} upm_buttons_ft; + +#ifdef __cplusplus +} +#endif + +#endif /* UPM_BUTTONS_H_ */ diff --git a/include/upm_fti.h b/include/upm_fti.h index daff287c..785f01e6 100644 --- a/include/upm_fti.h +++ b/include/upm_fti.h @@ -75,7 +75,8 @@ typedef enum { UPM_STREAM, UPM_ORP, UPM_BINARY, - UPM_ROTARYENCODER + UPM_ROTARYENCODER, + UPM_BUTTONS } upm_sensor_t; /* Supported IO protocols via MRAA */ @@ -127,6 +128,8 @@ typedef struct _upm_sensor_ft* (*func_get_upm_sensor_ft)(upm_sensor_t sensor_typ #include #include #include +#include +#include #ifdef __cplusplus } diff --git a/src/nunchuck/CMakeLists.txt b/src/nunchuck/CMakeLists.txt index f720c370..584c148e 100644 --- a/src/nunchuck/CMakeLists.txt +++ b/src/nunchuck/CMakeLists.txt @@ -1,5 +1,9 @@ -set (libname "nunchuck") -set (libdescription "Wii nunchuck module") -set (module_src ${libname}.cxx) -set (module_hpp ${libname}.hpp) -upm_module_init() +upm_mixed_module_init (NAME nunchuck + DESCRIPTION "I2C nunchuck driver" + C_HDR nunchuck.h + C_SRC nunchuck.c + CPP_HDR nunchuck.hpp + CPP_SRC nunchuck.cxx + FTI_SRC nunchuck_fti.c + CPP_WRAPS_C + REQUIRES mraa) diff --git a/src/nunchuck/nunchuck.c b/src/nunchuck/nunchuck.c new file mode 100644 index 00000000..e8a916cd --- /dev/null +++ b/src/nunchuck/nunchuck.c @@ -0,0 +1,206 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include "nunchuck.h" + +// This is not changeable +#define NUNCHUCK_I2C_ADDR 0x52 + +// static functions for r/w the device. No user-serviceable parts +// inside. + +static upm_result_t nunchuck_write_byte(const nunchuck_context dev, + uint8_t reg, uint8_t byte) +{ + assert(dev != NULL); + + if (mraa_i2c_write_byte_data(dev->i2c, byte, reg)) + { + printf("%s: mraa_i2c_write_byte_data() failed\n", __FUNCTION__); + return UPM_ERROR_OPERATION_FAILED; + } + + return UPM_SUCCESS; +} + +static int nunchuck_read_bytes(const nunchuck_context dev, uint8_t reg, + uint8_t *buffer, int len) +{ + assert(dev != NULL); + + if (!len || !buffer) + return 0; + + if (mraa_i2c_write_byte(dev->i2c, reg)) + { + printf("%s: mraa_i2c_write_byte() failed\n", __FUNCTION__); + return -1; + } + + return mraa_i2c_read(dev->i2c, buffer, len); +} + +// init +nunchuck_context nunchuck_init(int bus) +{ + // make sure MRAA is initialized + int mraa_rv; + if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) + { + printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); + return NULL; + } + + nunchuck_context dev = + (nunchuck_context)malloc(sizeof(struct _nunchuck_context)); + + if (!dev) + return NULL; + + memset((void *)dev, 0, sizeof(struct _nunchuck_context)); + + // setup our i2c link + if ( !(dev->i2c = mraa_i2c_init(bus)) ) + { + printf("%s: mraa_i2c_init() failed\n", __FUNCTION__); + nunchuck_close(dev); + return NULL; + } + + if (mraa_i2c_address(dev->i2c, NUNCHUCK_I2C_ADDR)) + { + printf("%s: mraa_i2c_address() failed\n", __FUNCTION__); + nunchuck_close(dev); + return NULL; + } + + // sleep for a second to let things settle + upm_delay(1); + + // disable encryption + if (nunchuck_write_byte(dev, 0xf0, 0x55) + || nunchuck_write_byte(dev, 0xfb, 0x00)) + { + printf("%s: nunchuck_write_byte() failed\n", __FUNCTION__); + nunchuck_close(dev); + return NULL; + } + + return dev; +} + +void nunchuck_close(nunchuck_context dev) +{ + assert(dev != NULL); + + if (dev->i2c) + mraa_i2c_stop(dev->i2c); + + free(dev); +} + +upm_result_t nunchuck_update(const nunchuck_context dev) +{ + assert(dev != NULL); + + const int bufsize = 6; + uint8_t buf[bufsize]; + int rv; + + rv = nunchuck_read_bytes(dev, 0x00, buf, bufsize); + + if (rv != bufsize) + { + printf("%s: nunchuck_read_bytes() failed. Expected %d, got %d.\n", + __FUNCTION__, bufsize, rv); + return UPM_ERROR_OPERATION_FAILED; + } + + // analog stick X + dev->stickX = buf[0]; + + // analog stick Y + dev->stickY = buf[1]; + + // accelerometer X + dev->accelX = ( (buf[2] << 2) | ((buf[5] & 0x0c) >> 2) ); + + // accelerometer Y + dev->accelY = ( (buf[3] << 2) | ((buf[5] & 0x30) >> 4) ); + + // accelerometer Z + dev->accelZ = ( (buf[4] << 2) | ((buf[5] & 0xc0) >> 6) ); + + // buttonC + if (buf[5] & 0x02) + dev->buttonC = false; + else + dev->buttonC = true; + + // buttonZ + if (buf[5] & 0x01) + dev->buttonZ = false; + else + dev->buttonZ = true; + + return UPM_SUCCESS; +} + +void nunchuck_get_stick(const nunchuck_context dev, int *x, int *y) +{ + assert(dev != NULL); + + if (x) + *x = dev->stickX; + if (y) + *y = dev->stickY; +} + +void nunchuck_get_acceleration(const nunchuck_context dev, + int *x, int *y, int *z) +{ + assert(dev != NULL); + + if (x) + *x = dev->accelX; + if (y) + *y = dev->accelY; + if (z) + *z = dev->accelZ; +} + +void nunchuck_get_buttons(const nunchuck_context dev, bool *c, bool *z) +{ + assert(dev != NULL); + + if (c) + *c = dev->buttonC; + if (z) + *z = dev->buttonZ; +} diff --git a/src/nunchuck/nunchuck.cxx b/src/nunchuck/nunchuck.cxx index a6f00d96..2eebc61c 100644 --- a/src/nunchuck/nunchuck.cxx +++ b/src/nunchuck/nunchuck.cxx @@ -1,6 +1,6 @@ /* * Author: Jon Trulson - * Copyright (c) 2015 Intel Corporation. + * Copyright (c) 2015-2017 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -33,117 +33,27 @@ using namespace upm; using namespace std; -NUNCHUCK::NUNCHUCK(int bus, uint8_t addr) +NUNCHUCK::NUNCHUCK(int bus) : + m_nunchuck(nunchuck_init(bus)) { - stickX = 0; - stickY = 0; - accelX = 0; - accelY = 0; - accelZ = 0; - buttonC = false; - buttonZ = false; - - // setup our i2c link - if ( !(m_i2c = mraa_i2c_init(bus)) ) - { - throw std::invalid_argument(std::string(__FUNCTION__) + - ": mraa_i2c_init() failed"); - return; - } - - mraa_result_t rv; - - if ( (rv = mraa_i2c_address(m_i2c, addr)) != MRAA_SUCCESS ) - { - throw std::invalid_argument(std::string(__FUNCTION__) + - ": mraa_i2c_address() failed"); - } + if (!m_nunchuck) + throw std::runtime_error(string(__FUNCTION__) + + ": nunchuck_init() failed"); } NUNCHUCK::~NUNCHUCK() { - mraa_i2c_stop(m_i2c); -} - -bool NUNCHUCK::writeByte(uint8_t reg, uint8_t byte) -{ - mraa_result_t rv; - - if ( (rv = mraa_i2c_write_byte_data(m_i2c, byte, reg)) != MRAA_SUCCESS ) - { - throw std::runtime_error(std::string(__FUNCTION__) + - ": mraa_i2c_write_byte_data() failed"); - return false; - } - - return true; -} - -int NUNCHUCK::readBytes(uint8_t reg, uint8_t *buffer, int len) -{ - if (!len || !buffer) - return 0; - - mraa_i2c_address(m_i2c, NUNCHUCK_I2C_ADDR); - mraa_i2c_write_byte(m_i2c, reg); - - return mraa_i2c_read(m_i2c, buffer, len); -} - -bool NUNCHUCK::init() -{ - usleep(1000000); - - // disable encryption - if (!writeByte(0xf0, 0x55)) - return false; - - if (!writeByte(0xfb, 0x00)) - return false; - - return true; + nunchuck_close(m_nunchuck); } void NUNCHUCK::update() { - const int bufsize = 6; - uint8_t buf[bufsize]; - int rv; + if (nunchuck_update(m_nunchuck)) + throw std::runtime_error(string(__FUNCTION__) + + ": nunchuck_update() failed"); - rv = readBytes(0x00, buf, bufsize); - - if (rv != bufsize) - { - throw std::runtime_error(std::string(__FUNCTION__) + - ": readBytes() failed"); - return; - } - - // analog stick X - stickX = buf[0]; - - // analog stick Y - stickY = buf[1]; - - // accelerometer X - accelX = ( (buf[2] << 2) | ((buf[5] & 0x0c) >> 2) ); - - // accelerometer Y - accelY = ( (buf[3] << 2) | ((buf[5] & 0x30) >> 4) ); - - // accelerometer Z - accelZ = ( (buf[4] << 2) | ((buf[5] & 0xc0) >> 6) ); - - // buttonC - if (buf[5] & 0x02) - buttonC = false; - else - buttonC = true; - - // buttonZ - if (buf[5] & 0x01) - buttonZ = false; - else - buttonZ = true; + nunchuck_get_stick(m_nunchuck, &stickX, &stickY); + nunchuck_get_acceleration(m_nunchuck, &accelX, &accelY, &accelZ); + nunchuck_get_buttons(m_nunchuck, &buttonC, &buttonZ); } diff --git a/src/nunchuck/nunchuck.h b/src/nunchuck/nunchuck.h new file mode 100644 index 00000000..1b0aa55b --- /dev/null +++ b/src/nunchuck/nunchuck.h @@ -0,0 +1,123 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + /** + * @file nunchuck.h + * @library nunchuck + * @brief C API for the nunchuck driver + * + * @include nunchuck.c + */ + + /** + * Device context + */ + typedef struct _nunchuck_context { + mraa_i2c_context i2c; + + // sticks - x, y + int stickX; + int stickY; + + // accel + int accelX; + int accelY; + int accelZ; + + // buttons + bool buttonC; + bool buttonZ; + + } *nunchuck_context; + + /** + * NUNCHUCK constructor + * + * @param bus I2C bus to use + * @return A Device context, or NULL on error + */ + nunchuck_context nunchuck_init(int bus); + + /** + * Close the device and deallocate all resources. + * + * @param dev Device context + */ + void nunchuck_close(nunchuck_context dev); + + /** + * Reads and updates the current state of the controller. + * + * @param dev Device context + * @return UPM result + */ + upm_result_t nunchuck_update(const nunchuck_context dev); + + /** + * Returns the current analog stick X and Y positions. + * nunchuck_update() must have been called prior to calling this + * function. + * + * @param dev Device context + * @param x Pointer in which the X value will be stored + * @param y Pointer in which the Y value will be stored + */ + void nunchuck_get_stick(const nunchuck_context dev, int *x, int *y); + + /** + * Returns the current accelerometer values. nunchuck_update() + * must have been called prior to calling this function. + * + * @param dev Device context + * @param x Pointer in which the X value will be stored + * @param y Pointer in which the Y value will be stored + * @param z Pointer in which the Z value will be stored + */ + void nunchuck_get_acceleration(const nunchuck_context dev, + int *x, int *y, int *z); + + /** + * Returns the current button values values. nunchuck_update() + * must have been called prior to calling this function. + * + * @param dev Device context + * @param c Pointer in which the C button value will be stored + * @param z Pointer in which the Z button value will be stored + */ + void nunchuck_get_buttons(const nunchuck_context dev, bool *c, bool *z); + +#ifdef __cplusplus +} +#endif diff --git a/src/nunchuck/nunchuck.hpp b/src/nunchuck/nunchuck.hpp index 24d509fa..05bc5a17 100644 --- a/src/nunchuck/nunchuck.hpp +++ b/src/nunchuck/nunchuck.hpp @@ -1,6 +1,6 @@ /* * Author: Jon Trulson - * Copyright (c) 2015 Intel Corporation. + * Copyright (c) 2015-2017 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -24,9 +24,8 @@ #pragma once #include -#include +#include "nunchuck.h" -#define NUNCHUCK_I2C_ADDR 0x52 namespace upm { @@ -48,17 +47,16 @@ namespace upm { * @brief API for the Wii* Nunchuk controller * * UPM module for the Wii Nunchuk controller. This module was tested with - * Wii Nunchuk connected to I2C via a Grove Wii Nunchuk adapter. + * Wii Nunchuck connected to I2C via a Grove Wii Nunchuck adapter. * * See http://wiibrew.org/wiki/Wiimote/Extension_Controllers and * http://wiibrew.org/wiki/Wiimote/Extension_Controllers/Nunchuck * for more details on the controller and its protocol. * - * A warning for the Grove Wii Nunchuk adapter: it has 2 traces on one - * side, and 3 traces on the other. Do not match these up with the - * Nunchuk connector's traces. The connector's 'Grove' - * should be on the same side as the Grove interface socket on the - * adapter. + * A warning for the Grove Wii Nunchuk adapter: it has 2 traces on + * one side, and 3 traces on the other. Do not match these up with + * the Nunchuk connector's traces. The connector's 'groove' should + * be on the same side as the Grove interface socket on the adapter. * * @image html nunchuck.jpg * @snippet nunchuck.cxx Interesting @@ -71,39 +69,12 @@ namespace upm { * @param bus I2C bus to use * @param addr I2C address to use */ - NUNCHUCK(int bus, uint8_t addr=NUNCHUCK_I2C_ADDR); + NUNCHUCK(int bus); /** * NUNCHUCK destructor */ - ~NUNCHUCK(); - - /** - * Writes value(s) into registers - * - * @param reg Register location to start writing into - * @param byte Byte to write - * @return True if successful - */ - bool writeByte(uint8_t reg, uint8_t byte); - - /** - * Reads value(s) from registers - * - * @param reg Register location to start reading from - * @param buffer Buffer for data storage - * @param len Number of bytes to read - * @return Number of bytes read - */ - int readBytes(uint8_t reg, uint8_t *buffer, int len); - - /** - * Initializes the controller. Here, we disable encryption after - * delaying for a time to ensure the controller is ready. - * - * @return True if initialization is successful - */ - bool init(); + virtual ~NUNCHUCK(); /** * Reads and updates the current state of the controller. @@ -153,8 +124,10 @@ namespace upm { */ bool buttonZ; + protected: + nunchuck_context m_nunchuck; + private: - mraa_i2c_context m_i2c; }; } diff --git a/src/nunchuck/nunchuck_fti.c b/src/nunchuck/nunchuck_fti.c new file mode 100644 index 00000000..31c9cb5f --- /dev/null +++ b/src/nunchuck/nunchuck_fti.c @@ -0,0 +1,190 @@ +/* + * Author: Jon Trulson + * Copyright (c) 2017 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "nunchuck.h" + +#include "upm_fti.h" + +/** + * This file implements the Function Table Interface (FTI) for this sensor + */ + +const char upm_nunchuck_name[] = "NUNCHUCK"; +const char upm_nunchuck_description[] = "Nunchuck controller"; +const upm_protocol_t upm_nunchuck_protocol[] = {UPM_I2C}; +const upm_sensor_t upm_nunchuck_category[] = +{ UPM_ACCELEROMETER, UPM_JOYSTICK, UPM_BUTTONS}; + +// forward declarations +const void* upm_nunchuck_get_ft(upm_sensor_t sensor_type); +void* upm_nunchuck_init_name(); +void upm_nunchuck_close(void *dev); +upm_result_t upm_nunchuck_get_accel_values(void *dev, float *value, upm_acceleration_u unit); +upm_result_t upm_nunchuck_get_joystick_value_x(const void *dev, float *value); +upm_result_t upm_nunchuck_get_joystick_value_y(const void *dev, float *value); +upm_result_t upm_nunchuck_get_num_buttons(const void *dev, + unsigned int *num_buttons); +upm_result_t upm_nunchuck_get_button_values(const void *dev, + bool *values); + +const upm_sensor_descriptor_t upm_nunchuck_get_descriptor() +{ + upm_sensor_descriptor_t usd; + usd.name = upm_nunchuck_name; + usd.description = upm_nunchuck_description; + usd.protocol_size = 1; + usd.protocol = upm_nunchuck_protocol; + usd.category_size = 3; + usd.category = upm_nunchuck_category; + return usd; +} + +static const upm_sensor_ft ft = +{ + .upm_sensor_init_name = upm_nunchuck_init_name, + .upm_sensor_close = upm_nunchuck_close, +}; + +static const upm_acceleration_ft aft = +{ + .upm_acceleration_get_value = upm_nunchuck_get_accel_values +}; + +static const upm_joystick_ft jft = +{ + .upm_joystick_get_value_x = upm_nunchuck_get_joystick_value_x, + .upm_joystick_get_value_y = upm_nunchuck_get_joystick_value_y +}; + +static const upm_buttons_ft bft = +{ + .upm_buttons_get_num_buttons = upm_nunchuck_get_num_buttons, + .upm_buttons_get_values = upm_nunchuck_get_button_values +}; + +const void* upm_nunchuck_get_ft(upm_sensor_t sensor_type) +{ + switch(sensor_type) + { + case UPM_SENSOR: + return &ft; + + case UPM_ACCELEROMETER: + return &aft; + + case UPM_JOYSTICK: + return &jft; + + case UPM_BUTTONS: + return &bft; + + default: + return NULL; + } +} + +void* upm_nunchuck_init_name() +{ + return NULL; +} + +void upm_nunchuck_close(void *dev) +{ + nunchuck_close((nunchuck_context)dev); +} + +upm_result_t upm_nunchuck_get_accel_values(void *dev, + float *value, + upm_acceleration_u unit) +{ + if (nunchuck_update((nunchuck_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + int x, y, z; + nunchuck_get_acceleration((nunchuck_context)dev, &x, &y, &z); + value[0] = (float)x; + value[1] = (float)y; + value[2] = (float)z; + + return UPM_SUCCESS; +} + +upm_result_t upm_nunchuck_get_joystick_value_x(const void *dev, float *value) +{ + // This is bad - separating getting X and Y stick values... You + // can't be sure when updating for one axis whether the other + // changed... Should be fixed by having a single function return + // both values from the same sampletime. Same goes for the rest + // of the values reported. + if (nunchuck_update((nunchuck_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + int x; + nunchuck_get_stick((nunchuck_context)dev, &x, NULL); + *value = (float)x; + + return UPM_SUCCESS; +} + +upm_result_t upm_nunchuck_get_joystick_value_y(const void *dev, float *value) +{ + // This is bad - separating getting X and Y stick values... You + // can't be sure when updating for one axis whether the other + // changed... Should be fixed by having a single function return + // both values from the same sampletime. Same goes for the rest + // of the values reported. + if (nunchuck_update((nunchuck_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + int y; + nunchuck_get_stick((nunchuck_context)dev, NULL, &y); + *value = (float)y; + + return UPM_SUCCESS; +} + +upm_result_t upm_nunchuck_get_num_buttons(const void *dev, + unsigned int *num_buttons) +{ + // always 2 buttons (C and Z) + + *num_buttons = 2; + + return UPM_SUCCESS; +} + +upm_result_t upm_nunchuck_get_button_values(const void *dev, + bool *values) +{ + if (nunchuck_update((nunchuck_context)dev)) + return UPM_ERROR_OPERATION_FAILED; + + bool bc, bz; + nunchuck_get_buttons((nunchuck_context)dev, &bc, &bz); + // hope they passed a bool[2].... + values[0] = bc; + values[1] = bz; + + return UPM_SUCCESS; +}