diff --git a/lib/joystick/src/AlignedJoy.cpp b/lib/joystick/src/AlignedJoy.cpp new file mode 100644 index 0000000..b62c555 --- /dev/null +++ b/lib/joystick/src/AlignedJoy.cpp @@ -0,0 +1,264 @@ +/********************************************************************************************************** + + @file AlignedJoy.cpp + @brief Joystick calibrated class + @author Marco Palladino + @version 1.0.1 + @date 2020/03/28 + @see https://github.com/PalladinoMarco/AlignedJoystick + Modified by len0rd to improve center calibration, rejection and loading of cal params + + @details + AlignedJoy is a class written in C++ for Arduino to interface classic analog joysticks. + Reading the analog values of a joystick with Arduino is very simple, so what is the purpose of this class? + Through the methods of the class you can calibrate one or more joysticks, read and memorize the calibration points. + For dynamic applications, calibrated points can be stored in eeprom. Or once the calibration points are obtained, + they can be set in constant mode. See description of methods. + The strength of the class is that having the calibration parameters is able to align the values of the axes + of the respective joysticks. It’s very useful when you want to manage something with a certain precision and + when you have a joystick with misaligned axis values. + + # LICENSE # + + MIT License + + Copyright (c) 2020 Marco Palladino + + 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 "AlignedJoy.h" + +AlignedJoy::AlignedJoy(uint8_t xPin, uint8_t yPin) +{ + this->xPin = xPin; + this->yPin = yPin; + this->joystickCentered = false; + this->joystickFullCalibrated = false; + + pinMode(this->xPin,INPUT); + pinMode(this->yPin,INPUT); +}; + +void AlignedJoy::axesAlign(void) +{ + // Imposed the smallest value between the two calibrated minima. + this->alignMin = min(this->xAxisCalibMinimum, this->yAxisCalibMinimum); + // Imposed the largest value between the two calibrated maxima. + this->alignMax = max(this->xAxisCalibMaximum, this->yAxisCalibMaximum); +} + +void AlignedJoy::middleCalibration(uint16_t timeOfCal) +{ + // New istance of the timer + calibrationTimer TIME_CAL_1; + + // Repeat for the required time + while(TIME_CAL_1 < timeOfCal) + { + // Assigns the value to each loop. + this->xAxisCalibCenter = analogRead(this->xPin); + this->yAxisCalibCenter = analogRead(this->yPin); + } + // set flag true + this->joystickCentered = true; +} + +bool AlignedJoy::axesCalibration(uint16_t timeOfCal) +{ + if(joystickCentered) + { + // New values for comparison + static uint16_t last_X_min = this->xAxisCalibCenter; + static uint16_t last_X_max = this->xAxisCalibCenter; + static uint16_t last_Y_min = this->yAxisCalibCenter; + static uint16_t last_Y_max = this->yAxisCalibCenter; + + // New istance of the timer + calibrationTimer TIME_CAL_2; + + // Repeat for the required time + while(TIME_CAL_2 < timeOfCal) + { + // Read analog value + uint16_t x = analogRead(this->xPin); + uint16_t y = analogRead(this->yPin); + + // Logic acquisition. Assigns the lowest or highest values to each loop. + if(x < this->xAxisCalibCenter && x < last_X_min) + { + last_X_min = this->xAxisCalibMinimum = x; + } + if(x > this->xAxisCalibCenter && x > last_X_max) + { + last_X_max = this->xAxisCalibMaximum = x; + } + if(y < this->yAxisCalibCenter && y < last_Y_min) + { + last_Y_min = this->yAxisCalibMinimum = y; + } + if(y > this->yAxisCalibCenter && y > last_Y_max) + { + last_Y_max = this->yAxisCalibMaximum = y; + } + } + + // verify that there is the minimum excursion between the minimum and the maximum of each axis. + if((this->xAxisCalibMaximum - this->xAxisCalibMinimum) >= AXIS_TRAVEL && + (this->yAxisCalibMaximum - this->yAxisCalibMinimum) >= AXIS_TRAVEL) + { + // set flag true + this->joystickFullCalibrated = true; + + // align axes + axesAlign(); + + return true; + } + else + { + return false; + } + } + else + { + return false; + } +} + +void AlignedJoy::setCalibratedPoint(axis_t axis, point_t point, uint16_t pointValue) +{ + switch(point) + { + case MIN: + switch(axis) + { + case X: + this->xAxisCalibMinimum = pointValue; + break; + case Y: + this->yAxisCalibMinimum = pointValue; + break; + } + break; + + case MAX: + switch(axis) + { + case X: + this->xAxisCalibMaximum = pointValue; + break; + case Y: + this->yAxisCalibMaximum = pointValue; + break; + } + break; + } + // verify that there is the minimum excursion between the minimum and the maximum of each axis. + if((this->xAxisCalibMaximum - this->xAxisCalibMinimum) >= AXIS_TRAVEL && + (this->yAxisCalibMaximum - this->yAxisCalibMinimum) >= AXIS_TRAVEL) + { + // set flag true + this->joystickFullCalibrated = true; + + // align axes + axesAlign(); + } +} + +uint16_t AlignedJoy::getCalibratedPoint(axis_t axis, point_t point) +{ + uint16_t pointValue; + + switch(point) + { + case MIN: + switch(axis) + { + case X: + pointValue = this->xAxisCalibMinimum; + break; + case Y: + pointValue = this->yAxisCalibMinimum; + break; + } + break; + + case MID: + switch(axis) + { + case X: + pointValue = this->xAxisCalibCenter; + break; + case Y: + pointValue = this->yAxisCalibCenter; + break; + } + break; + + case MAX: + switch(axis) + { + case X: + pointValue = this->xAxisCalibMaximum; + break; + case Y: + pointValue = this->yAxisCalibMaximum; + break; + } + break; + } + return pointValue; +} + +uint16_t AlignedJoy::read(axis_t axis) +{ + uint16_t axisValue; + + switch(axis) + { + case X: + if(joystickFullCalibrated) + { + axisValue = map(analogRead(this->xPin), this->xAxisCalibMinimum, this->xAxisCalibMaximum, this->alignMin, this->alignMax); + } + else + { + axisValue = analogRead(this->xPin); + } + break; + + case Y: + if(joystickFullCalibrated) + { + axisValue = map(analogRead(this->yPin), this->yAxisCalibMinimum, this->yAxisCalibMaximum, this->alignMin, this->alignMax); + } + else + { + axisValue = analogRead(this->yPin); + } + break; + } + return axisValue; +} + +uint16_t AlignedJoy::read(axis_t axis, int32_t out_min, int32_t out_max) +{ + return map(read(axis), this->alignMin, this->alignMax, out_min, out_max); +} diff --git a/lib/joystick/src/AlignedJoy.h b/lib/joystick/src/AlignedJoy.h new file mode 100644 index 0000000..7082539 --- /dev/null +++ b/lib/joystick/src/AlignedJoy.h @@ -0,0 +1,233 @@ +/********************************************************************************************************** + + @file AlignedJoy.h + @brief Joystick calibrated class + @author Marco Palladino + @version 1.0.1 + @date 2020/03/28 + @see https://github.com/PalladinoMarco/AlignedJoystick + Modified by len0rd to improve center calibration, rejection and loading of cal params + + @details + AlignedJoy is a class written in C++ for Arduino to interface classic analog joysticks. + Reading the analog values of a joystick with Arduino is very simple, so what is the purpose of this class? + Through the methods of the class you can calibrate one or more joysticks, read and memorize the calibration points. + For dynamic applications, calibrated points can be stored in eeprom. Or once the calibration points are obtained, + they can be set in constant mode. See description of methods. + The strength of the class is that having the calibration parameters is able to align the values of the axes + of the respective joysticks. It’s very useful when you want to manage something with a certain precision and + when you have a joystick with misaligned axis values. + + # LICENSE # + + MIT License + + Copyright (c) 2020 Marco Palladino + + 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 + +#if ARDUINO >= 100 +#include "Arduino.h" +#else +#include "WProgram.h" +#endif + +/** +AXIS_TRAVEL CONSTANT + @brief Minimum axis travel value from one end to the other (Xmax-Xmin). + @details + This parameter is used as a safety control during calibration or when setting extreme points + using the "Setcalibrationpoint" method, in order to ensure a minimum excursion between the two + points and in reference to the central point (joystick at rest). +*/ +#define AXIS_TRAVEL 550 + +/// @brief axes flags +typedef enum axis:uint8_t {X=0, Y} axis_t; + +/// @brief calibration point flags +typedef enum point:uint8_t{ + /// minimum point + MIN=0, + /// middle point + MID, + /// maximum point + MAX + } point_t; + +class AlignedJoy +{ + public: + + /** + @brief Class onstructor. Create a new object of the joystick and set the axes (X/Y) pin to input mode + @param xPin (x analog port pin of the device) + @param yPin (y analog port pin of the device) + */ + AlignedJoy(uint8_t xPin, uint8_t yPin); + /// Class destructor + ~AlignedJoy(){}; + + /** + @brief Start calibration of the joystick in center position. + @details + Use this method only if the calibration of the axles is desired. + Calibration is divided into two steps to allow defining the desired functionality + (messages, delays, etc.) between the two phases. + @param timeOfCal is the calibration time in milliseconds. + @return true o false. + */ + void middleCalibration(uint16_t timeOfCal); + + /** + @brief Calibration of the axes at the extreme points (min end max for each axis). + @details + It is recommended to rotate the joystick in a circular way along its perimeter (maximum radius + for each axis) throughout the calibration time. + @param timeOfCal is the calibration time in milliseconds. + @return true o false. + */ + bool axesCalibration(uint16_t timeOfCal); + + /** + @brief Method that allows you to set the minimum and maximum calibration points for each axis. + @details + Useful to set the values read by eeprom. Allows you to set the values individually, + useful for making changes in run time. + @warning + All calibration points (min and maximum) for each axis (x and y) must be defined to + obtain a scale reading of the two axes. + Only the extreme calibration points can be set, the center point is defined only for calibration purposes. + @param axis X or Y axle enumerator flag. + @param point MIN, MID or MAX enumertor flag of the calibration point. + @param value point value to set. For example to set value (876) of the X MAX + @code + objectname.setCalibratedPoint(X, MAX, 876); + @endcode + */ + void setCalibratedPoint(axis_t axis, point_t point, uint16_t value); + + /** + @brief This method returns the value of the required axis and point. + @details + It can be used to compare the saved values with the new ones, to read the values saved in + the eeprom with the "setCalibratedPoint" function or for debug. + @see https://github.com/PalladinoMarco/AlignedJoystick/wiki wiki documentation for more details. + @param axis X or Y axle enumerator flag. + @param point MIN, MID or MAX enumertor flag of the calibration point. + @return calibrated point of the axis request. For example to get the calibrated point of Y MIN + @code + objectname.getCalibratedPoint(Y, MIN); + @endcode + */ + uint16_t getCalibratedPoint(axis_t axis, point_t point); + + /** + @brief This method returns the required axis value. If calibrated, returns the aligned axis values. + @param axis X or Y axle enumerator flag. + @return analog read of the axis request. For example to read Y axis @code objectname.read(Y); @endcode + */ + uint16_t read(axis_t axis); + + /** + @brief This method returns the required joystick axis values in the set range (map functions) + @details + If you want to get a mapping by keeping the scale of calibrated values do not use the arduino map + unction (already included in the method) but use the optional parameters "out_min" and "out_max" + of the read method. + For arduino map @see https://www.arduino.cc/reference/en/language/functions/math/map/ + @param axis X or Y axle enumerator flag. + @param out_min minimum range output value. + @param out_max maximum range output value. + @return analog read of the relative axis in map mode, for example to control a servo motor, do this: + @code + objectname.read(X, 1000, 2000); + objectname.read(Y, 1000, 2000); + @endcode + */ + uint16_t read(axis_t axis, int32_t out_min, int32_t out_max); + + private: + /* + sets a single minimum and maximum value, equal for each axis of the joystick to scale them. + */ + void axesAlign(); + uint8_t xPin; + uint8_t yPin; + uint16_t alignMin; + uint16_t alignMax; + uint16_t xAxisCalibCenter; + uint16_t xAxisCalibMinimum; + uint16_t xAxisCalibMaximum; + uint16_t yAxisCalibCenter; + uint16_t yAxisCalibMinimum; + uint16_t yAxisCalibMaximum; + bool joystickCentered; + bool joystickFullCalibrated; +}; + +// class from elapsed-Millis +// https://github.com/pfeerick/elapsedMillis/wiki + +/* Elapsed time types - for easy-to-use measurements of elapsed time + * http://www.pjrc.com/teensy/ + * Copyright (c) 2011 PJRC.COM, LLC + * + * 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. + */ +class calibrationTimer +{ +private: + unsigned long ms; +public: + calibrationTimer(void) { ms = millis(); } + calibrationTimer(unsigned long val) { ms = millis() - val; } + calibrationTimer(const calibrationTimer &orig) { ms = orig.ms; } + operator unsigned long () const { return millis() - ms; } + calibrationTimer & operator = (const calibrationTimer &rhs) { ms = rhs.ms; return *this; } + calibrationTimer & operator = (unsigned long val) { ms = millis() - val; return *this; } + calibrationTimer & operator -= (unsigned long val) { ms += val ; return *this; } + calibrationTimer & operator += (unsigned long val) { ms -= val ; return *this; } + calibrationTimer operator - (int val) const { calibrationTimer r(*this); r.ms += val; return r; } + calibrationTimer operator - (unsigned int val) const { calibrationTimer r(*this); r.ms += val; return r; } + calibrationTimer operator - (long val) const { calibrationTimer r(*this); r.ms += val; return r; } + calibrationTimer operator - (unsigned long val) const { calibrationTimer r(*this); r.ms += val; return r; } + calibrationTimer operator + (int val) const { calibrationTimer r(*this); r.ms -= val; return r; } + calibrationTimer operator + (unsigned int val) const { calibrationTimer r(*this); r.ms -= val; return r; } + calibrationTimer operator + (long val) const { calibrationTimer r(*this); r.ms -= val; return r; } + calibrationTimer operator + (unsigned long val) const { calibrationTimer r(*this); r.ms -= val; return r; } +}; diff --git a/platformio.ini b/platformio.ini index 66306c4..7fd5129 100644 --- a/platformio.ini +++ b/platformio.ini @@ -12,4 +12,3 @@ platform = renesas-ra board = uno_r4_wifi framework = arduino -lib_deps = palladinomarco/AlignedJoy@^1.0.1