From 0c65283619fb72b16faf72058c81a706397745a8 Mon Sep 17 00:00:00 2001 From: AJ-Koenig <39343698+AJ-Koenig@users.noreply.github.com> Date: Thu, 30 May 2024 10:36:32 -0600 Subject: [PATCH 1/4] Updated Stroke Engine to create the pattern on the fly (#102) --- .../lib/StrokeEngine/src/StrokeEngine.cpp | 279 +++---- Software/lib/StrokeEngine/src/StrokeEngine.h | 740 +++++++++--------- Software/lib/StrokeEngine/src/pattern.h | 20 - Software/platformio.ini | 1 - Software/src/constants/copy/en-us.h | 11 +- Software/src/constants/copy/fr.h | 12 +- Software/src/main.cpp | 17 +- Software/src/ossm/OSSM.PatternControls.cpp | 14 +- Software/src/ossm/OSSM.SimplePenetration.cpp | 2 +- Software/src/ossm/OSSM.StrokeEngine.cpp | 40 +- Software/src/ossm/OSSM.cpp | 17 +- Software/src/ossm/OSSM.h | 16 +- Software/src/services/board.h | 8 +- Software/src/services/display.h | 6 + Software/src/services/stepper.h | 47 +- Software/src/services/tasks.h | 1 - Software/src/structs/LanguageStruct.h | 1 + Software/src/structs/SettingPercents.h | 12 +- Software/src/utils/StateLogger.h | 3 - Software/src/utils/StrokeEngineHelper.h | 7 +- Software/src/utils/update.h | 2 + 21 files changed, 635 insertions(+), 621 deletions(-) diff --git a/Software/lib/StrokeEngine/src/StrokeEngine.cpp b/Software/lib/StrokeEngine/src/StrokeEngine.cpp index 1c995f43..b1a8582d 100644 --- a/Software/lib/StrokeEngine/src/StrokeEngine.cpp +++ b/Software/lib/StrokeEngine/src/StrokeEngine.cpp @@ -2,18 +2,12 @@ #include -#include "FastAccelStepper.h" #include "pattern.h" -// static pointer to engine and servo -static FastAccelStepperEngine *engine; -static FastAccelStepper *servo; - +// static pointer to engine and _servo void StrokeEngine::begin(machineGeometry *physics, motorProperties *motor, - FastAccelStepperEngine _engine, - FastAccelStepper *_servo) { - engine = &_engine; - servo = _servo; + FastAccelStepper *servo) { + _servo = servo; // store the machine geometry and motor properties pointer _physics = physics; _motor = motor; @@ -30,7 +24,6 @@ void StrokeEngine::begin(machineGeometry *physics, motorProperties *motor, // Initialize with default values _state = UNDEFINED; _isHomed = false; - _patternIndex = 0; _index = 0; _depth = _maxStep; _previousDepth = _maxStep; @@ -39,16 +32,13 @@ void StrokeEngine::begin(machineGeometry *physics, motorProperties *motor, _timeOfStroke = 1.0; _sensation = 0.0; - // Setup FastAccelStepper -// engine.init(); -// servo = engine.stepperConnectToPin(_motor->stepPin); - if (servo) { - servo->setDirectionPin(_motor->directionPin, _motor->invertDirection); - servo->setEnablePin(_motor->enablePin, _motor->enableActiveLow); - servo->setAutoEnable(false); - servo->disableOutputs(); + if (_servo) { + _servo->setDirectionPin(_motor->directionPin, _motor->invertDirection); + _servo->setEnablePin(_motor->enablePin, _motor->enableActiveLow); + _servo->setAutoEnable(false); + _servo->disableOutputs(); } - Serial.println("Servo initialized"); + Serial.println("_servo initialized"); #ifdef DEBUG_TALKATIVE Serial.println("Stroke Engine State: " + verboseState[_state]); @@ -63,7 +53,7 @@ void StrokeEngine::setSpeed(float speed, bool applyNow = false) { // Constrain stroke time between 10ms and 120 seconds _timeOfStroke = constrain(60.0 / speed, 0.01, 120.0); - patternTable[_patternIndex]->setTimeOfStroke(_timeOfStroke); + pattern->setTimeOfStroke(_timeOfStroke); #ifdef DEBUG_TALKATIVE Serial.println("setTimeOfStroke: " + String(_timeOfStroke, 2)); @@ -96,7 +86,7 @@ void StrokeEngine::setDepth(float depth, bool applyNow = false) { _depth = constrain(int(depth * _motor->stepsPerMillimeter), _minStep, _maxStep); - patternTable[_patternIndex]->setDepth(_depth); + pattern->setDepth(_depth); #ifdef DEBUG_TALKATIVE Serial.println("setDepth: " + String(_depth)); @@ -135,7 +125,7 @@ void StrokeEngine::setStroke(float stroke, bool applyNow = false) { _stroke = constrain(int(stroke * _motor->stepsPerMillimeter), _minStep, _maxStep); - patternTable[_patternIndex]->setStroke(_stroke); + pattern->setStroke(_stroke); #ifdef DEBUG_TALKATIVE Serial.println("setStroke: " + String(_stroke)); @@ -173,7 +163,7 @@ void StrokeEngine::setSensation(float sensation, bool applyNow = false) { // Constrain sensation between -100 and 100 _sensation = constrain(sensation, -100, 100); - patternTable[_patternIndex]->setSensation(_sensation); + pattern->setSensation(_sensation); #ifdef DEBUG_TALKATIVE Serial.println("setSensation: " + String(_sensation)); @@ -201,68 +191,60 @@ void StrokeEngine::setSensation(float sensation, bool applyNow = false) { float StrokeEngine::getSensation() { return _sensation; } -bool StrokeEngine::setPattern(int patternIndex, bool applyNow = false) { - // Check wether pattern Index is in range - if ((patternIndex < patternTableSize) && (patternIndex >= 0) && - (patternIndex != _patternIndex)) { - _patternIndex = patternIndex; +bool StrokeEngine::setPattern(Pattern *NextPattern, + bool applyNow = false) { + // Free up memory from previous pattern - // Inject current motion parameters into new pattern - if (xSemaphoreTake(_patternMutex, portMAX_DELAY) == pdTRUE) { - patternTable[_patternIndex]->setSpeedLimit( - _maxStepPerSecond, _maxStepAcceleration, - _motor->stepsPerMillimeter); - patternTable[_patternIndex]->setTimeOfStroke(_timeOfStroke); - patternTable[_patternIndex]->setStroke(_stroke); - patternTable[_patternIndex]->setDepth(_depth); - patternTable[_patternIndex]->setSensation(_sensation); - - // When running a pattern and immediate update requested: - if ((_state == PATTERN) && (applyNow == true)) { - // set flag to apply update from stroking thread - _applyUpdate = true; + delete pattern; + pattern = NextPattern; -#ifdef DEBUG_TALKATIVE - Serial.println("Apply New Settings Now"); -#endif - } - - // Reset index counter - _index = -1; + // Inject current motion parameters into new pattern + if (xSemaphoreTake(_patternMutex, portMAX_DELAY) == pdTRUE) { + pattern->setSpeedLimit(_maxStepPerSecond, _maxStepAcceleration, + _motor->stepsPerMillimeter); + pattern->setTimeOfStroke(_timeOfStroke); + pattern->setStroke(_stroke); + pattern->setDepth(_depth); + pattern->setSensation(_sensation); - // give back mutex - xSemaphoreGive(_patternMutex); - } + // When running a pattern and immediate update requested: + if ((_state == PATTERN) && (applyNow == true)) { + // set flag to apply update from stroking thread + _applyUpdate = true; #ifdef DEBUG_TALKATIVE - Serial.println("setPattern: [" + String(_patternIndex) + "] " + - patternTable[_patternIndex]->getName()); - Serial.println("setTimeOfStroke: " + String(_timeOfStroke, 2)); - Serial.println("setDepth: " + String(_depth)); - Serial.println("setStroke: " + String(_stroke)); - Serial.println("setSensation: " + String(_sensation)); + Serial.println("Apply New Settings Now"); #endif - return true; + } + + // Reset index counter + _index = -1; + + // give back mutex + xSemaphoreGive(_patternMutex); } - // Return false on no match #ifdef DEBUG_TALKATIVE - Serial.println("Failed to set pattern: " + String(_patternIndex)); + Serial.println("setPattern: " + String(pattern->getName())); + Serial.println("setTimeOfStroke: " + String(_timeOfStroke, 2)); + Serial.println("setDepth: " + String(_depth)); + Serial.println("setStroke: " + String(_stroke)); + Serial.println("setSensation: " + String(_sensation)); #endif - return false; + return true; } -int StrokeEngine::getPattern() { return _patternIndex; } +int StrokeEngine::getPattern() { return 0; } bool StrokeEngine::startPattern() { // Only valid if state is ready if (_state == READY || _state == SETUPDEPTH) { // Stop current move, should one be pending (moveToMax or moveToMin) - if (servo->isRunning()) { - // Stop servo motor as fast as legally allowed - servo->setAcceleration(_maxStepAcceleration); - servo->applySpeedAcceleration(); - servo->stopMove(); + if (_servo->isRunning()) { + // Stop _servo motor as fast as legally allowed + _servo->setAcceleration(_maxStepAcceleration); + _servo->applySpeedAcceleration(); + _servo->stopMove(); } // Set state to PATTERN @@ -271,13 +253,12 @@ bool StrokeEngine::startPattern() { // Reset Stroke and Motion parameters _index = -1; if (xSemaphoreTake(_patternMutex, portMAX_DELAY) == pdTRUE) { - patternTable[_patternIndex]->setSpeedLimit( - _maxStepPerSecond, _maxStepAcceleration, - _motor->stepsPerMillimeter); - patternTable[_patternIndex]->setTimeOfStroke(_timeOfStroke); - patternTable[_patternIndex]->setStroke(_stroke); - patternTable[_patternIndex]->setDepth(_depth); - patternTable[_patternIndex]->setSensation(_sensation); + pattern->setSpeedLimit(_maxStepPerSecond, _maxStepAcceleration, + _motor->stepsPerMillimeter); + pattern->setTimeOfStroke(_timeOfStroke); + pattern->setStroke(_stroke); + pattern->setDepth(_depth); + pattern->setSensation(_sensation); xSemaphoreGive(_patternMutex); } @@ -325,24 +306,24 @@ void StrokeEngine::stopMotion() { // Set state _state = READY; - // Stop servo motor as fast as legally allowed - servo->setAcceleration(_maxStepAcceleration); - servo->applySpeedAcceleration(); - servo->stopMove(); + // Stop _servo motor as fast as legally allowed + _servo->setAcceleration(_maxStepAcceleration); + _servo->applySpeedAcceleration(); + _servo->stopMove(); #ifdef DEBUG_TALKATIVE Serial.println("Motion stopped"); #endif - // Wait for servo stopped - while (servo->isRunning()) + // Wait for _servo stopped + while (_servo->isRunning()) ; // Send telemetry data if (_callbackTelemetry != NULL) { - _callbackTelemetry( - float(servo->getCurrentPosition() / _motor->stepsPerMillimeter), - 0.0, false); + _callbackTelemetry(float(_servo->getCurrentPosition() / + _motor->stepsPerMillimeter), + 0.0, false); } } @@ -377,8 +358,8 @@ void StrokeEngine::enableAndHome(endstopProperties *endstop, float speed) { // first stop current motion and delete stroke task stopMotion(); - // Enable Servo - servo->enableOutputs(); + // Enable _servo + _servo->enableOutputs(); // Create homing task xTaskCreatePinnedToCore( @@ -400,19 +381,19 @@ void StrokeEngine::thisIsHome(float speed) { _homeingSpeed = speed * _motor->stepsPerMillimeter; if (_state == UNDEFINED) { - // Enable Servo - servo->enableOutputs(); + // Enable _servo + _servo->enableOutputs(); // Stet current position as home - servo->setCurrentPosition(-_motor->stepsPerMillimeter * - _physics->keepoutBoundary); + _servo->setCurrentPosition(-_motor->stepsPerMillimeter * + _physics->keepoutBoundary); // Set feedrate for homing - servo->setSpeedInHz(_homeingSpeed); - servo->setAcceleration(_maxStepAcceleration / 10); + _servo->setSpeedInHz(_homeingSpeed); + _servo->setAcceleration(_maxStepAcceleration / 10); // drive free of switch and set axis to 0 - servo->moveTo(_minStep); + _servo->moveTo(_minStep); // Change state _isHomed = true; @@ -441,10 +422,10 @@ bool StrokeEngine::moveToMax(float speed) { // Set feedrate for safe move // Constrain speed between 1 step/sec and _maxStepPerSecond - servo->setSpeedInHz(constrain(speed * _motor->stepsPerMillimeter, 1, - _maxStepPerSecond)); - servo->setAcceleration(_maxStepAcceleration / 10); - servo->moveTo(_maxStep); + _servo->setSpeedInHz(constrain(speed * _motor->stepsPerMillimeter, 1, + _maxStepPerSecond)); + _servo->setAcceleration(_maxStepAcceleration / 10); + _servo->moveTo(_maxStep); // Send telemetry data if (_callbackTelemetry != NULL) { @@ -476,10 +457,10 @@ bool StrokeEngine::moveToMin(float speed) { // Set feedrate for safe move // Constrain speed between 1 step/sec and _maxStepPerSecond - servo->setSpeedInHz(constrain(speed * _motor->stepsPerMillimeter, 1, - _maxStepPerSecond)); - servo->setAcceleration(_maxStepAcceleration / 10); - servo->moveTo(_minStep); + _servo->setSpeedInHz(constrain(speed * _motor->stepsPerMillimeter, 1, + _maxStepPerSecond)); + _servo->setAcceleration(_maxStepAcceleration / 10); + _servo->moveTo(_minStep); // Send telemetry data if (_callbackTelemetry != NULL) { @@ -517,9 +498,9 @@ bool StrokeEngine::setupDepth(float speed, bool fancy) { // Set feedrate for safe move // Constrain speed between 1 step/sec and _maxStepPerSecond - servo->setSpeedInHz(constrain(speed * _motor->stepsPerMillimeter, 1, - _maxStepPerSecond)); - servo->setAcceleration(_maxStepAcceleration / 10); + _servo->setSpeedInHz(constrain(speed * _motor->stepsPerMillimeter, 1, + _maxStepPerSecond)); + _servo->setAcceleration(_maxStepAcceleration / 10); // Set new state _state = SETUPDEPTH; @@ -542,8 +523,8 @@ void StrokeEngine::disable() { _state = UNDEFINED; _isHomed = false; - // Disable servo motor - servo->disableOutputs(); + // Disable _servo motor + _servo->disableOutputs(); // Delete homing Task if (_taskHomingHandle != NULL) { @@ -552,17 +533,13 @@ void StrokeEngine::disable() { } #ifdef DEBUG_TALKATIVE - Serial.println("Servo disabled. Call home to continue."); + Serial.println("_servo disabled. Call home to continue."); Serial.println("Stroke Engine State: " + verboseState[_state]); #endif } String StrokeEngine::getPatternName(int index) { - if (index >= 0 && index <= patternTableSize) { - return String(patternTable[index]->getName()); - } else { - return String("Invalid"); - } + return String("Invalid"); } void StrokeEngine::setMaxSpeed(float maxSpeed) { @@ -571,9 +548,8 @@ void StrokeEngine::setMaxSpeed(float maxSpeed) { // Convert speed into steps _maxStepPerSecond = int(0.5 + _motor->maxSpeed * _motor->stepsPerMillimeter); - patternTable[_patternIndex]->setSpeedLimit(_maxStepPerSecond, - _maxStepAcceleration, - _motor->stepsPerMillimeter); + pattern->setSpeedLimit(_maxStepPerSecond, _maxStepAcceleration, + _motor->stepsPerMillimeter); xSemaphoreGive(_patternMutex); } } @@ -588,9 +564,8 @@ void StrokeEngine::setMaxAcceleration(float maxAcceleration) { // Convert acceleration into steps _maxStepAcceleration = int(0.5 + _motor->maxAcceleration * _motor->stepsPerMillimeter); - patternTable[_patternIndex]->setSpeedLimit(_maxStepPerSecond, - _maxStepAcceleration, - _motor->stepsPerMillimeter); + pattern->setSpeedLimit(_maxStepPerSecond, _maxStepAcceleration, + _motor->stepsPerMillimeter); xSemaphoreGive(_patternMutex); } } @@ -607,56 +582,56 @@ void StrokeEngine::registerTelemetryCallback(void (*callbackTelemetry)(float, void StrokeEngine::_homingProcedure() { // Set feedrate for homing - servo->setSpeedInHz(_homeingSpeed); - servo->setAcceleration(_maxStepAcceleration / 10); + _servo->setSpeedInHz(_homeingSpeed); + _servo->setAcceleration(_maxStepAcceleration / 10); // Check if we are already at the homing switch if (digitalRead(_homeingPin) == !_homeingActiveLow) { // back off 5 mm from switch - servo->move(_motor->stepsPerMillimeter * 2 * _physics->keepoutBoundary * - _homeingToBack); + _servo->move(_motor->stepsPerMillimeter * 2 * + _physics->keepoutBoundary * _homeingToBack); // wait for move to complete - while (servo->isRunning()) { + while (_servo->isRunning()) { // Pause the task for 100ms while waiting for move to complete vTaskDelay(100 / portTICK_PERIOD_MS); } // move back towards endstop - servo->move(-_motor->stepsPerMillimeter * 4 * - _physics->keepoutBoundary * _homeingToBack); + _servo->move(-_motor->stepsPerMillimeter * 4 * + _physics->keepoutBoundary * _homeingToBack); } else { // Move MAX_TRAVEL towards the homing switch - servo->move(-_motor->stepsPerMillimeter * _physics->physicalTravel * - _homeingToBack); + _servo->move(-_motor->stepsPerMillimeter * _physics->physicalTravel * + _homeingToBack); } // Poll homing switch - while (servo->isRunning()) { + while (_servo->isRunning()) { // Switch is active low if (digitalRead(_homeingPin) == !_homeingActiveLow) { // Set home position if (_homeingToBack == 1) { // Switch is at -KEEPOUT_BOUNDARY - servo->forceStopAndNewPosition(-_motor->stepsPerMillimeter * - _physics->keepoutBoundary); + _servo->forceStopAndNewPosition(-_motor->stepsPerMillimeter * + _physics->keepoutBoundary); // drive free of switch and set axis to lower end - servo->moveTo(_minStep); + _servo->moveTo(_minStep); } else { - servo->forceStopAndNewPosition( + _servo->forceStopAndNewPosition( _motor->stepsPerMillimeter * (_physics->physicalTravel - _physics->keepoutBoundary)); // drive free of switch and set axis to front end - servo->moveTo(_maxStep); + _servo->moveTo(_maxStep); } _isHomed = true; // drive free of switch and set axis to 0 - servo->moveTo(0); + _servo->moveTo(0); // Break loop, home was found break; @@ -666,9 +641,9 @@ void StrokeEngine::_homingProcedure() { vTaskDelay(20 / portTICK_PERIOD_MS); } - // disable Servo if homing has not found the homing switch + // disable _servo if homing has not found the homing switch if (!_isHomed) { - servo->disableOutputs(); + _servo->disableOutputs(); _state = UNDEFINED; #ifdef DEBUG_TALKATIVE @@ -718,19 +693,19 @@ void StrokeEngine::_stroking() { if (xSemaphoreTake(_patternMutex, 0) == pdTRUE) { if (_applyUpdate == true) { // Ask pattern for update on motion parameters - currentMotion = patternTable[_patternIndex]->nextTarget(_index); + currentMotion = pattern->nextTarget(_index); // Increase deceleration if required to avoid crash - if (servo->getAcceleration() > currentMotion.acceleration) { + if (_servo->getAcceleration() > currentMotion.acceleration) { #ifdef DEBUG_CLIPPING Serial.print("Crash avoidance! Set Acceleration from " + String(currentMotion.acceleration)); - Serial.println(" to " + String(servo->getAcceleration())); + Serial.println(" to " + String(_servo->getAcceleration())); #endif - currentMotion.acceleration = servo->getAcceleration(); + currentMotion.acceleration = _servo->getAcceleration(); } - // Apply new trapezoidal motion profile to servo + // Apply new trapezoidal motion profile to _servo _applyMotionProfile(¤tMotion); // clear update flag @@ -738,19 +713,19 @@ void StrokeEngine::_stroking() { } // If motor has stopped issue moveTo command to next position - else if (servo->isRunning() == false) { + else if (_servo->isRunning() == false) { // Increment index for pattern _index++; // Querey new set of pattern parameters - currentMotion = patternTable[_patternIndex]->nextTarget(_index); + currentMotion = pattern->nextTarget(_index); // Pattern may introduce pauses between strokes if (currentMotion.skip == false) { #ifdef DEBUG_STROKE Serial.println("Stroking Index: " + String(_index)); #endif - // Apply new trapezoidal motion profile to servo + // Apply new trapezoidal motion profile to _servo _applyMotionProfile(¤tMotion); } else { @@ -787,7 +762,7 @@ void StrokeEngine::_applyMotionProfile(motionParameter *motion) { float speed = 0.0; float position = 0.0; - // Apply new trapezoidal motion profile to servo if pattern does not skip + // Apply new trapezoidal motion profile to _servo if pattern does not skip if (motion->skip == false) { // Constrain speed to below _maxStepPerSecond if (motion->speed > _maxStepPerSecond) { @@ -823,10 +798,10 @@ void StrokeEngine::_applyMotionProfile(motionParameter *motion) { // Constrain stroke to motion envelope int pos = constrain((motion->stroke), _minStep, _maxStep); - // write values to servo - servo->setSpeedInHz(motion->speed); - servo->setAcceleration(motion->acceleration); - servo->moveTo(pos); + // write values to _servo + _servo->setSpeedInHz(motion->speed); + _servo->setAcceleration(motion->acceleration); + _servo->moveTo(pos); // Compile speed telemetry data speed = float(motion->speed / _motor->stepsPerMillimeter); @@ -866,13 +841,13 @@ void StrokeEngine::_setupDepths() { #endif } - // move servo to desired position - servo->moveTo(depth); + // move _servo to desired position + _servo->moveTo(depth); // Send telemetry data if (_callbackTelemetry != NULL) { _callbackTelemetry(float(depth / _motor->stepsPerMillimeter), - float(servo->getSpeedInMilliHz() * 1000 / + float(_servo->getSpeedInMilliHz() * 1000 / _motor->stepsPerMillimeter), false); } diff --git a/Software/lib/StrokeEngine/src/StrokeEngine.h b/Software/lib/StrokeEngine/src/StrokeEngine.h index a5c8059a..05fae41a 100644 --- a/Software/lib/StrokeEngine/src/StrokeEngine.h +++ b/Software/lib/StrokeEngine/src/StrokeEngine.h @@ -1,7 +1,7 @@ /** * StrokeEngine - * A library to create a variety of stroking motions with a stepper or servo motor on an ESP32. - * https://github.com/theelims/StrokeEngine + * A library to create a variety of stroking motions with a stepper or servo + * motor on an ESP32. https://github.com/theelims/StrokeEngine * * Copyright (C) 2022 theelims * @@ -17,10 +17,11 @@ #include "pattern.h" // Debug Levels -//#define DEBUG_TALKATIVE // Show debug messages from the StrokeEngine on Serial -//#define DEBUG_STROKE // Show debug messaged for each individual stroke on Serial -#define DEBUG_CLIPPING // Show debug messages when motions violating the machine - // physics are commanded +// #define DEBUG_TALKATIVE // Show debug messages from the +// StrokeEngine on Serial #define DEBUG_STROKE // Show debug +// messaged for each individual stroke on Serial +#define DEBUG_CLIPPING // Show debug messages when motions violating the + // machine physics are commanded /**************************************************************************/ /*! @@ -28,46 +29,49 @@ */ /**************************************************************************/ typedef struct { - float physicalTravel; /*> What is the maximum physical travel in mm */ - float keepoutBoundary; /*> Soft endstop preventing hard crashes in mm. Will be - * subtracted twice from physicalTravel. Should be - * sufficiently to completley drive clear from - * homing switch */ + float physicalTravel; /*> What is the maximum physical travel in mm */ + float keepoutBoundary; /*> Soft endstop preventing hard crashes in mm. Will + * be subtracted twice from physicalTravel. Should be + * sufficiently to completley drive clear from + * homing switch */ } machineGeometry; /**************************************************************************/ /*! - @brief Struct defining the motor (stepper or servo with STEP/DIR - interface) and the motion system translating the rotation into a + @brief Struct defining the motor (stepper or servo with STEP/DIR + interface) and the motion system translating the rotation into a linear motion. */ /**************************************************************************/ typedef struct { - float maxSpeed; /*> What is the maximum speed in mm/s */ - float maxAcceleration; /*> Maximum acceleration in mm/s^2 */ - float stepsPerMillimeter; /*> Number of steps per millimeter */ - bool invertDirection; /*> Set to true to invert the direction signal - * The firmware expects the home switch to be located at the - * end of an retraction move. That way the machine homes - * itself away from the body. Home position is -KEEPOUTBOUNDARY */ - bool enableActiveLow; /*> Polarity of the enable signal. True for active low. */ - int stepPin; /*> Pin connected to the STEP input */ - int directionPin; /*> Pin connected to the DIR input */ - int enablePin; /*> Pin connected to the ENA input */ + float maxSpeed; /*> What is the maximum speed in mm/s */ + float maxAcceleration; /*> Maximum acceleration in mm/s^2 */ + float stepsPerMillimeter; /*> Number of steps per millimeter */ + bool invertDirection; /*> Set to true to invert the direction signal + * The firmware expects the home switch to be located + * at the end of an retraction move. That way the + * machine homes itself away from the body. Home + * position is -KEEPOUTBOUNDARY */ + bool enableActiveLow; /*> Polarity of the enable signal. True for active + low. */ + int stepPin; /*> Pin connected to the STEP input */ + int directionPin; /*> Pin connected to the DIR input */ + int enablePin; /*> Pin connected to the ENA input */ } motorProperties; /**************************************************************************/ /*! - @brief Struct defining the endstop properties like pin, pinmode, polarity + @brief Struct defining the endstop properties like pin, pinmode, polarity and homing direction. */ /**************************************************************************/ typedef struct { - bool homeToBack; /*> Set to true to home to the back of the machine - * Set to false to home to the front of the machine */ - bool activeLow; /*> Polarity of the homing signal. True for active low. */ - int endstopPin; /*> Pin connected to home switch */ - uint8_t pinMode; /*> Pinmode of the switch INPUT, INPUT_PULLUP, INPUT_PULLDOWN */ + bool homeToBack; /*> Set to true to home to the back of the machine + * Set to false to home to the front of the machine */ + bool activeLow; /*> Polarity of the homing signal. True for active low. */ + int endstopPin; /*> Pin connected to home switch */ + uint8_t pinMode; /*> Pinmode of the switch INPUT, INPUT_PULLUP, + INPUT_PULLDOWN */ } endstopProperties; /**************************************************************************/ @@ -76,350 +80,358 @@ typedef struct { */ /**************************************************************************/ typedef enum { - UNDEFINED, //!< No power to the servo. We don't know its position - READY, //!< Servo is energized and knows it position. Not running. - PATTERN, //!< Stroke Engine is running and servo is moving according to defined pattern. - SETUPDEPTH, //!< Interactive adjustment mode to setup depth and stroke - STREAMING //!< Tracks the depth-position whenever depth is updated. + UNDEFINED, //!< No power to the servo. We don't know its position + READY, //!< Servo is energized and knows it position. Not running. + PATTERN, //!< Stroke Engine is running and servo is moving according to + //!< defined pattern. + SETUPDEPTH, //!< Interactive adjustment mode to setup depth and stroke + STREAMING //!< Tracks the depth-position whenever depth is updated. } ServoState; // Verbose strings of states for debugging purposes static String verboseState[] = { - "[0] Servo disabled", - "[1] Servo ready", - "[2] Servo pattern running", - "[3] Servo setup depth", - "[4] Servo position streaming" -}; + "[0] Servo disabled", "[1] Servo ready", "[2] Servo pattern running", + "[3] Servo setup depth", "[4] Servo position streaming"}; /**************************************************************************/ /*! @brief Stroke Engine provides a convenient package for stroking motions - created by stepper or servo motors. It's internal states are handled by a - finite state machine. A pattern generator allows to creat a variety of + created by stepper or servo motors. It's internal states are handled by a + finite state machine. A pattern generator allows to creat a variety of motion profiles. Under the hood FastAccelStepper is used for interfacing - a stepper or servo motor vie a STEP/DIR interface. + a stepper or servo motor vie a STEP/DIR interface. */ /**************************************************************************/ class StrokeEngine { - public: - - /**************************************************************************/ - /*! - @brief Initializes FastAccelStepper and configures all pins and outputs - accordingly. StrokeEngine is in state UNDEFINED - */ - /**************************************************************************/ - void begin(machineGeometry *physics, motorProperties *motor, FastAccelStepperEngine engine, FastAccelStepper *servo); - - /**************************************************************************/ - /*! - @brief Set the speed of a stroke. Speed is given in Strokes per Minute - and internally calculated to the time a full stroke needs to complete. - Settings tale effect with next stroke, or after calling - applyNewSettingsNow(). - @param speed Strokes per Minute. Is constrained from 0.5 to 6000 - @param applyNow Set to true if changes should take effect immediately - */ - /**************************************************************************/ - void setSpeed(float speed, bool applyNow); - - /**************************************************************************/ - /*! - @brief Get the speed of a stroke. Speed is returned as Strokes per Minute. - @return Strokes per Minute. - */ - /**************************************************************************/ - float getSpeed(); - - /**************************************************************************/ - /*! - @brief Set the depth of a stroke. Settings tale effect with next stroke, - or after calling applyNewSettingsNow(). - @param depth Depth in [mm]. Is constrained from 0 to TRAVEL - @param applyNow Set to true if changes should take effect immediately - */ - /**************************************************************************/ - void setDepth(float depth, bool applyNow); - - /**************************************************************************/ - /*! - @brief Returns the depth of a stroke. - @return Depth in [mm]. Is constrained from 0 to TRAVEL - */ - /**************************************************************************/ - float getDepth(); - - /**************************************************************************/ - /*! - @brief Set the stroke length of a stroke. Settings take effect with next - stroke, or after calling applyNewSettingsNow(). - @param stroke Stroke length in [mm]. Is constrained from 0 to TRAVEL - @param applyNow Set to true if changes should take effect immediately - */ - /**************************************************************************/ - void setStroke(float stroke, bool applyNow); - - /**************************************************************************/ - /*! - @brief Get the stroke length of a stroke. - @return Stroke length in [mm]. - */ - /**************************************************************************/ - float getStroke(); - - /**************************************************************************/ - /*! - @brief Set the sensation of a pattern. Sensation is an additional - parameter a pattern may use to alter its behaviour. Settings takes - effect with next stroke, or after calling applyNewSettingsNow(). - @param sensation Sensation in [a.u.]. Is constrained from -100 to 100 - with 0 beeing assumed as neutral. - @param applyNow Set to true if changes should take effect immediately - */ - /**************************************************************************/ - void setSensation(float sensation, bool applyNow); - - /**************************************************************************/ - /*! - @brief Get the sensation of a pattern. Sensation is an additional - parameter a pattern may use to alter its behaviour. - @return Sensation in [a.u.]. Is constrained from -100 to 100 - with 0 beeing assumed as neutral. - */ - /**************************************************************************/ - float getSensation(); - - /**************************************************************************/ - /*! - @brief Choose a pattern for the StrokeEngine. Settings take effect with - next stroke, or after calling applyNewSettingsNow(). - @param patternIndex Index of a pattern - @param applyNow Set to true if changes should take effect immediately - @return TRUE on success, FALSE, if patternIndex is invalid. Previous - pattern will be retained. - */ - /**************************************************************************/ - bool setPattern(int patternIndex, bool applyNow); - - /**************************************************************************/ - /*! - @brief Get the pattern index for the StrokeEngine. - @return Index of a pattern - */ - /**************************************************************************/ - int getPattern(); - - /**************************************************************************/ - /*! - @brief Creates a FreeRTOS task to run a stroking pattern. Only valid in - state READY. Pattern is initialized with the values from the set - functions. If the task is running, state is PATTERN. - @return TRUE when task was created and motion starts, FALSE on failure. - */ - /**************************************************************************/ - bool startPattern(); - - /**************************************************************************/ - /*! - @brief Stops the motion with MAX_ACCEL and deletes the stroking task. Is - in state READY afterwards. - */ - /**************************************************************************/ - void stopMotion(); - - /**************************************************************************/ - /*! - @brief Enable the servo/stepper and do the homing procedure. Drives towards - the endstop with HOMING_SPEED. Function is non-blocking and backed by a task. - Optionally a callback can be given to receive feedback if homing succeeded - going in state READY. If homing switch is not found after traveling - MAX_TRAVEL it times out, disables the servo and goes into UNDEFINED. - @param endstop Pointer to a endstopProperties struct defining all relevant - properties like pin, pinmode, homing direction & signal - polarity. - @param speed Speed in mm/s used for finding the homing switch. - Defaults to 5.0 mm/s - @param callBackHoming Callback function is called after homing is done. - Function parametere holds a bool containing the success (TRUE) - or failure (FALSE) of homing. - */ - /**************************************************************************/ - void enableAndHome(endstopProperties *endstop, float speed = 5.0); - void enableAndHome(endstopProperties *endstop, void(*callBackHoming)(bool), float speed = 5.0); - - /**************************************************************************/ - /*! - @brief If no homing switch is present homing can be done manually. Push - the endeffector all the way in and call thisIsHome(). This enables the - the servo and sets the position to -KEEPOUT_BOUNDARY - @param speed Speed in mm/s used for finding the homing switch. - Defaults to 5.0 mm/s - */ - /**************************************************************************/ - void thisIsHome(float speed = 5.0); - - /**************************************************************************/ - /*! - @brief In state PATTERN, SETUPDEPTH and READY this - moves the endeffector to TRAVEL. Can be used for adjustments. Stops any - running pattern and ends in state READY. - @param speed Speed in mm/s used for driving to max. - Defaults to 10.0 mm/s - @return TRUE on success, FALSE if state does not allow this. - */ - /**************************************************************************/ - bool moveToMax(float speed = 10.0); - - /**************************************************************************/ - /*! - @brief In state PATTERN, SETUPDEPTH and READY this - moves the endeffector to 0. Can be used for adjustments. Stops any running - pattern and ends in state READY. - @param speed Speed in mm/s used for driving to min. - Defaults to 10.0 mm/s - @return TRUE on success, FALSE if state does not allow this. - */ - /**************************************************************************/ - bool moveToMin(float speed = 10.0); - - /**************************************************************************/ - /*! - @brief In state PATTERN and READY this moves the endeffector - to DEPTH and enters state SETUPDEPTH. Follows the DEPTH postion - whenever setDepth() is called. Can be used for adjustments. Stops any running - pattern. - @param speed Speed in mm/s used for driving to min. - Defaults to 10.0 mm/s - @param fancy In fancy mode sensation allows to adjust both, depth and - stroke. +100 adjusts the depth position, -100 adjusts the - stroke position. 0 adjusts the midpoint depth-stroke/2. - @return TRUE on success, FALSE if state does not allow this. - */ - /**************************************************************************/ - bool setupDepth(float speed = 10.0, bool fancy = false); - - /**************************************************************************/ - /*! - @brief Retrieves the current servo state from the internal state machine. - @return Current state of the state machine - */ - /**************************************************************************/ - ServoState getState(); - - /**************************************************************************/ - /*! - @brief Disables the servo motor instantly and deletes any motion task. - Sets state machine to UNDEFINED. Must be followed by homing to enable - servo again. - */ - /**************************************************************************/ - void disable(); - - /**************************************************************************/ - /*! - @brief Makes the pattern list available for the main program to retreive - informations like pattern names. - @param index index of a pattern. - @return String holding a pattern name with a certain index. If index is - out of range it returns "Invalid" - */ - /**************************************************************************/ - String getPatternName(int index); - - /**************************************************************************/ - /*! - @brief Makes the pattern list available for the main program to retreive - informations like pattern names. - @return The number of pattern available. - */ - /**************************************************************************/ - unsigned int getNumberOfPattern() { - return patternTableSize; - }; - - /**************************************************************************/ - /*! - @brief Updates the maximum speed number of StrokeEngine. This value is - used to keep alle motions in check and as a safeguard. - @param maxSpeed maximum Speed in mm/s - */ - /**************************************************************************/ - void setMaxSpeed(float maxSpeed); - - /**************************************************************************/ - /*! - @brief Get the current set maximum speed - @return maximum speed in mm/s - */ - /**************************************************************************/ - float getMaxSpeed(); - - /**************************************************************************/ - /*! - @brief Updates the maximum acceleration number of StrokeEngine. This value - is used to keep alle motions in check and as a safeguard. - @param maxAcceleration maximum acceleration in mm/s² - */ - /**************************************************************************/ - void setMaxAcceleration(float maxAcceleration); - - /**************************************************************************/ - /*! - @brief Get the current set maximum acceleration - @return maximum acceleration in mm/s² - */ - /**************************************************************************/ - float getMaxAcceleration(); - - /**************************************************************************/ - /*! - @brief Register a callback function that will update telemetry information - about StrokeEngine. The provided function will be called whenever a motion - is executed by a manual command or by a pattern. The returned values are the - target position of this move, its top speed and wether clipping occurred. - @param callbackTelemetry Function must be of type: - void callbackTelemetry(float position, float speed, bool clipping) - */ - /**************************************************************************/ - void registerTelemetryCallback(void(*callbackTelemetry)(float, float, bool)); - - protected: - ServoState _state = UNDEFINED; - motorProperties *_motor; - machineGeometry *_physics; - float _travel; - int _minStep; - int _maxStep; - int _maxStepPerSecond; - int _maxStepAcceleration; - int _patternIndex = 0; - bool _isHomed = false; - int _index = 0; - int _depth; - int _previousDepth; - int _stroke; - int _previousStroke; - float _timeOfStroke; - float _sensation; - bool _applyUpdate = false; - static void _homingProcedureImpl(void* _this) { static_cast(_this)->_homingProcedure(); } - void _homingProcedure(); - static void _strokingImpl(void* _this) { static_cast(_this)->_stroking(); } - void _stroking(); - static void _streamingImpl(void* _this) { static_cast(_this)->_streaming(); } - void _streaming(); - TaskHandle_t _taskStrokingHandle = NULL; - TaskHandle_t _taskHomingHandle = NULL; - TaskHandle_t _taskStreamingHandle = NULL; - SemaphoreHandle_t _patternMutex = xSemaphoreCreateMutex(); - void _applyMotionProfile(motionParameter* motion); - void(*_callBackHomeing)(bool) = NULL; - void(*_callbackTelemetry)(float, float, bool) = NULL; - int _homeingSpeed; - int _homeingPin; - int _homeingToBack; - bool _homeingActiveLow; /*> Polarity of the homing signal*/ - bool _fancyAdjustment; - void _setupDepths(); + FastAccelStepper *_servo; + + public: + /**************************************************************************/ + /*! + @brief Initializes FastAccelStepper and configures all pins and outputs + accordingly. StrokeEngine is in state UNDEFINED + */ + /**************************************************************************/ + void begin(machineGeometry *physics, motorProperties *motor, + FastAccelStepper *servo); + + /**************************************************************************/ + /*! + @brief Set the speed of a stroke. Speed is given in Strokes per Minute + and internally calculated to the time a full stroke needs to complete. + Settings tale effect with next stroke, or after calling + applyNewSettingsNow(). + @param speed Strokes per Minute. Is constrained from 0.5 to 6000 + @param applyNow Set to true if changes should take effect immediately + */ + /**************************************************************************/ + void setSpeed(float speed, bool applyNow); + + /**************************************************************************/ + /*! + @brief Get the speed of a stroke. Speed is returned as Strokes per + Minute. + @return Strokes per Minute. + */ + /**************************************************************************/ + float getSpeed(); + + /**************************************************************************/ + /*! + @brief Set the depth of a stroke. Settings tale effect with next stroke, + or after calling applyNewSettingsNow(). + @param depth Depth in [mm]. Is constrained from 0 to TRAVEL + @param applyNow Set to true if changes should take effect immediately + */ + /**************************************************************************/ + void setDepth(float depth, bool applyNow); + + /**************************************************************************/ + /*! + @brief Returns the depth of a stroke. + @return Depth in [mm]. Is constrained from 0 to TRAVEL + */ + /**************************************************************************/ + float getDepth(); + + /**************************************************************************/ + /*! + @brief Set the stroke length of a stroke. Settings take effect with next + stroke, or after calling applyNewSettingsNow(). + @param stroke Stroke length in [mm]. Is constrained from 0 to TRAVEL + @param applyNow Set to true if changes should take effect immediately + */ + /**************************************************************************/ + void setStroke(float stroke, bool applyNow); + + /**************************************************************************/ + /*! + @brief Get the stroke length of a stroke. + @return Stroke length in [mm]. + */ + /**************************************************************************/ + float getStroke(); + + /**************************************************************************/ + /*! + @brief Set the sensation of a pattern. Sensation is an additional + parameter a pattern may use to alter its behaviour. Settings takes + effect with next stroke, or after calling applyNewSettingsNow(). + @param sensation Sensation in [a.u.]. Is constrained from -100 to 100 + with 0 beeing assumed as neutral. + @param applyNow Set to true if changes should take effect immediately + */ + /**************************************************************************/ + void setSensation(float sensation, bool applyNow); + + /**************************************************************************/ + /*! + @brief Get the sensation of a pattern. Sensation is an additional + parameter a pattern may use to alter its behaviour. + @return Sensation in [a.u.]. Is constrained from -100 to 100 + with 0 beeing assumed as neutral. + */ + /**************************************************************************/ + float getSensation(); + + /**************************************************************************/ + /*! + @brief Choose a pattern for the StrokeEngine. Settings take effect with + next stroke, or after calling applyNewSettingsNow(). + @param patternIndex Index of a pattern + @param applyNow Set to true if changes should take effect immediately + @return TRUE on success, FALSE, if patternIndex is invalid. Previous + pattern will be retained. + */ + /**************************************************************************/ + bool setPattern(Pattern *nextPattern, bool applyNow); + + /**************************************************************************/ + /*! + @brief Get the pattern index for the StrokeEngine. + @return Index of a pattern + */ + /**************************************************************************/ + int getPattern(); + + /**************************************************************************/ + /*! + @brief Creates a FreeRTOS task to run a stroking pattern. Only valid in + state READY. Pattern is initialized with the values from the set + functions. If the task is running, state is PATTERN. + @return TRUE when task was created and motion starts, FALSE on failure. + */ + /**************************************************************************/ + bool startPattern(); + + /**************************************************************************/ + /*! + @brief Stops the motion with MAX_ACCEL and deletes the stroking task. Is + in state READY afterwards. + */ + /**************************************************************************/ + void stopMotion(); + + /**************************************************************************/ + /*! + @brief Enable the servo/stepper and do the homing procedure. Drives + towards the endstop with HOMING_SPEED. Function is non-blocking and backed + by a task. Optionally a callback can be given to receive feedback if + homing succeeded going in state READY. If homing switch is not found after + traveling MAX_TRAVEL it times out, disables the servo and goes into + UNDEFINED. + @param endstop Pointer to a endstopProperties struct defining all relevant + properties like pin, pinmode, homing direction & signal + polarity. + @param speed Speed in mm/s used for finding the homing switch. + Defaults to 5.0 mm/s + @param callBackHoming Callback function is called after homing is done. + Function parametere holds a bool containing the success + (TRUE) or failure (FALSE) of homing. + */ + /**************************************************************************/ + void enableAndHome(endstopProperties *endstop, float speed = 5.0); + void enableAndHome(endstopProperties *endstop, void (*callBackHoming)(bool), + float speed = 5.0); + + /**************************************************************************/ + /*! + @brief If no homing switch is present homing can be done manually. Push + the endeffector all the way in and call thisIsHome(). This enables the + the servo and sets the position to -KEEPOUT_BOUNDARY + @param speed Speed in mm/s used for finding the homing switch. + Defaults to 5.0 mm/s + */ + /**************************************************************************/ + void thisIsHome(float speed = 5.0); + + /**************************************************************************/ + /*! + @brief In state PATTERN, SETUPDEPTH and READY this + moves the endeffector to TRAVEL. Can be used for adjustments. Stops any + running pattern and ends in state READY. + @param speed Speed in mm/s used for driving to max. + Defaults to 10.0 mm/s + @return TRUE on success, FALSE if state does not allow this. + */ + /**************************************************************************/ + bool moveToMax(float speed = 10.0); + + /**************************************************************************/ + /*! + @brief In state PATTERN, SETUPDEPTH and READY this + moves the endeffector to 0. Can be used for adjustments. Stops any running + pattern and ends in state READY. + @param speed Speed in mm/s used for driving to min. + Defaults to 10.0 mm/s + @return TRUE on success, FALSE if state does not allow this. + */ + /**************************************************************************/ + bool moveToMin(float speed = 10.0); + + /**************************************************************************/ + /*! + @brief In state PATTERN and READY this moves the endeffector + to DEPTH and enters state SETUPDEPTH. Follows the DEPTH postion + whenever setDepth() is called. Can be used for adjustments. Stops any + running pattern. + @param speed Speed in mm/s used for driving to min. + Defaults to 10.0 mm/s + @param fancy In fancy mode sensation allows to adjust both, depth and + stroke. +100 adjusts the depth position, -100 adjusts the + stroke position. 0 adjusts the midpoint depth-stroke/2. + @return TRUE on success, FALSE if state does not allow this. + */ + /**************************************************************************/ + bool setupDepth(float speed = 10.0, bool fancy = false); + + /**************************************************************************/ + /*! + @brief Retrieves the current servo state from the internal state machine. + @return Current state of the state machine + */ + /**************************************************************************/ + ServoState getState(); + + /**************************************************************************/ + /*! + @brief Disables the servo motor instantly and deletes any motion task. + Sets state machine to UNDEFINED. Must be followed by homing to enable + servo again. + */ + /**************************************************************************/ + void disable(); + + /**************************************************************************/ + /*! + @brief Makes the pattern list available for the main program to retreive + informations like pattern names. + @param index index of a pattern. + @return String holding a pattern name with a certain index. If index is + out of range it returns "Invalid" + */ + /**************************************************************************/ + String getPatternName(int index); + + /**************************************************************************/ + /*! + @brief Makes the pattern list available for the main program to retreive + informations like pattern names. + @return The number of pattern available. + */ + /**************************************************************************/ + unsigned int getNumberOfPattern() { return 0; }; + + /**************************************************************************/ + /*! + @brief Updates the maximum speed number of StrokeEngine. This value is + used to keep alle motions in check and as a safeguard. + @param maxSpeed maximum Speed in mm/s + */ + /**************************************************************************/ + void setMaxSpeed(float maxSpeed); + + /**************************************************************************/ + /*! + @brief Get the current set maximum speed + @return maximum speed in mm/s + */ + /**************************************************************************/ + float getMaxSpeed(); + + /**************************************************************************/ + /*! + @brief Updates the maximum acceleration number of StrokeEngine. This + value is used to keep alle motions in check and as a safeguard. + @param maxAcceleration maximum acceleration in mm/s² + */ + /**************************************************************************/ + void setMaxAcceleration(float maxAcceleration); + + /**************************************************************************/ + /*! + @brief Get the current set maximum acceleration + @return maximum acceleration in mm/s² + */ + /**************************************************************************/ + float getMaxAcceleration(); + + /**************************************************************************/ + /*! + @brief Register a callback function that will update telemetry + information about StrokeEngine. The provided function will be called + whenever a motion is executed by a manual command or by a pattern. The + returned values are the target position of this move, its top speed and + wether clipping occurred. + @param callbackTelemetry Function must be of type: + void callbackTelemetry(float position, float speed, bool clipping) + */ + /**************************************************************************/ + void registerTelemetryCallback(void (*callbackTelemetry)(float, float, + bool)); + + protected: + ServoState _state = UNDEFINED; + motorProperties *_motor; + machineGeometry *_physics; + float _travel; + int _minStep; + int _maxStep; + int _maxStepPerSecond; + int _maxStepAcceleration; + Pattern *pattern = new SimpleStroke("Simple Stroke"); + bool _isHomed = false; + int _index = 0; + int _depth; + int _previousDepth; + int _stroke; + int _previousStroke; + float _timeOfStroke; + float _sensation; + bool _applyUpdate = false; + static void _homingProcedureImpl(void *_this) { + static_cast(_this)->_homingProcedure(); + } + void _homingProcedure(); + static void _strokingImpl(void *_this) { + static_cast(_this)->_stroking(); + } + void _stroking(); + static void _streamingImpl(void *_this) { + static_cast(_this)->_streaming(); + } + void _streaming(); + TaskHandle_t _taskStrokingHandle = NULL; + TaskHandle_t _taskHomingHandle = NULL; + TaskHandle_t _taskStreamingHandle = NULL; + SemaphoreHandle_t _patternMutex = xSemaphoreCreateMutex(); + void _applyMotionProfile(motionParameter *motion); + void (*_callBackHomeing)(bool) = NULL; + void (*_callbackTelemetry)(float, float, bool) = NULL; + int _homeingSpeed; + int _homeingPin; + int _homeingToBack; + bool _homeingActiveLow; /*> Polarity of the homing signal*/ + bool _fancyAdjustment; + void _setupDepths(); }; diff --git a/Software/lib/StrokeEngine/src/pattern.h b/Software/lib/StrokeEngine/src/pattern.h index ef09b61f..1472b5fb 100644 --- a/Software/lib/StrokeEngine/src/pattern.h +++ b/Software/lib/StrokeEngine/src/pattern.h @@ -15,7 +15,6 @@ #include #include "PatternMath.h" -#include "StrokeEngine.h" #define DEBUG_PATTERN // Print some debug informations over Serial @@ -648,22 +647,3 @@ class Insist : public Pattern { _realStroke = int((float)_stroke * _strokeFraction); } }; - -/**************************************************************************/ -/* - Array holding all different patterns. Please include any custom pattern here. -*/ -/**************************************************************************/ -static Pattern *patternTable[] = { - new SimpleStroke("Simple Stroke"), - new TeasingPounding("Teasing or Pounding"), - new RoboStroke("Robo Stroke"), - new HalfnHalf("Half'n'Half"), - new Deeper("Deeper"), - new StopNGo("Stop'n'Go"), - new Insist("Insist") - // <-- insert your new pattern class here! -}; - -static const unsigned int patternTableSize = - sizeof(patternTable) / sizeof(patternTable[0]); diff --git a/Software/platformio.ini b/Software/platformio.ini index 35594ced..0e2f63e4 100644 --- a/Software/platformio.ini +++ b/Software/platformio.ini @@ -47,7 +47,6 @@ board = esp32dev framework = arduino monitor_speed = 115200 monitor_filters = colorize, esp32_exception_decoder, time -upload_port = /dev/cu.usbserial-0001 [env:debugWithoutMotor] build_flags = diff --git a/Software/src/constants/copy/en-us.h b/Software/src/constants/copy/en-us.h index 18b8326c..795aac69 100644 --- a/Software/src/constants/copy/en-us.h +++ b/Software/src/constants/copy/en-us.h @@ -42,7 +42,16 @@ static const LanguageStruct enUs = { "Stroke depth increases per cycle; sensation sets count.", "Pauses between strokes; sensation adjusts length.", "Modifies length, maintains speed; sensation influences direction." - } + }, + .StrokeEngineNames = { + "Simple Stroke", + "Teasing Pounding", + "Robo Stroke", + "Half'n'Half", + "Deeper", + "Stop'n'Go", + "Insist" + }, }; #endif // OSSM_SOFTWARE_EN_US_H diff --git a/Software/src/constants/copy/fr.h b/Software/src/constants/copy/fr.h index 114383c6..bcdee486 100644 --- a/Software/src/constants/copy/fr.h +++ b/Software/src/constants/copy/fr.h @@ -46,7 +46,17 @@ static const LanguageStruct fr = { "La profondeur des coups augmente à chaque cycle ; la sensation définit le nombre.", "Pauses entre les coups ; la sensation ajuste la longueur.", "Modifie la longueur, maintient la vitesse ; la sensation influe sur la direction.", - }}; + }, + .StrokeEngineNames = { + "Simple Stroke", + "Teasing Pounding", + "Robo Stroke", + "Half'n'Half", + "Deeper", + "Stop'n'Go", + "Insist", + } +}; #endif // OSSM_SOFTWARE_FR_H diff --git a/Software/src/main.cpp b/Software/src/main.cpp index 47c689c3..007a2332 100644 --- a/Software/src/main.cpp +++ b/Software/src/main.cpp @@ -39,15 +39,20 @@ void setup() { display.setBusClock(400000); display.begin(); - ossm = new OSSM(display, encoder); - + ossm = new OSSM(display, encoder, stepper); // link functions to be called on events. button.attachClick([]() { ossm->sm->process_event(ButtonPress{}); }); button.attachDoubleClick([]() { ossm->sm->process_event(DoublePress{}); }); button.attachLongPressStart([]() { ossm->sm->process_event(LongPress{}); }); + + xTaskCreate( + [](void* pvParameters) { + while (true) { + button.tick(); + vTaskDelay(10); + } + }, + "buttonTask", 4 * configMINIMAL_STACK_SIZE, nullptr, 1, nullptr); }; -void loop() { - button.tick(); - ossm->wm.process(); -}; \ No newline at end of file +void loop() { vTaskDelete(nullptr); }; \ No newline at end of file diff --git a/Software/src/ossm/OSSM.PatternControls.cpp b/Software/src/ossm/OSSM.PatternControls.cpp index d5f217b0..b68a2d9b 100644 --- a/Software/src/ossm/OSSM.PatternControls.cpp +++ b/Software/src/ossm/OSSM.PatternControls.cpp @@ -7,7 +7,7 @@ size_t numberOfDescriptions = sizeof(UserConfig::language.StrokeEngineDescriptions) / sizeof(UserConfig::language.StrokeEngineDescriptions[0]); -size_t numberOfPatterns = sizeof(patternTable) / sizeof(patternTable[0]); +size_t numberOfPatterns = 7; void OSSM::drawPatternControlsTask(void *pvParameters) { // parse ossm from the parameters @@ -19,9 +19,9 @@ void OSSM::drawPatternControlsTask(void *pvParameters) { return ossm->sm->is("strokeEngine.pattern"_s); }; - int nextPattern = ossm->setting.pattern; + int nextPattern = (int)ossm->setting.pattern; bool shouldUpdateDisplay = true; - String patternName = patternTable[nextPattern]->getName(); + String patternName = "nextPattern"; String patternDescription = UserConfig::language.StrokeEngineDescriptions[nextPattern]; @@ -33,13 +33,13 @@ void OSSM::drawPatternControlsTask(void *pvParameters) { while (isInCorrectState(ossm)) { nextPattern = ossm->encoder.readEncoder() / 3; shouldUpdateDisplay = - shouldUpdateDisplay || ossm->setting.pattern != nextPattern; + shouldUpdateDisplay || (int)ossm->setting.pattern != nextPattern; if (!shouldUpdateDisplay) { vTaskDelay(100); continue; } - patternName = patternTable[nextPattern]->getName(); + patternName = UserConfig::language.StrokeEngineNames[nextPattern]; if (nextPattern < numberOfDescriptions) { patternDescription = @@ -48,7 +48,7 @@ void OSSM::drawPatternControlsTask(void *pvParameters) { patternDescription = "No description available"; } - ossm->setting.pattern = nextPattern; + ossm->setting.pattern = (StrokePatterns)nextPattern; displayMutex.lock(); ossm->display.clearBuffer(); @@ -56,7 +56,7 @@ void OSSM::drawPatternControlsTask(void *pvParameters) { // Draw the title drawStr::title(patternName); drawStr::multiLine(0, 20, patternDescription); - drawShape::scroll(100* nextPattern / numberOfPatterns); + drawShape::scroll(100 * nextPattern / numberOfPatterns); ossm->display.sendBuffer(); displayMutex.unlock(); diff --git a/Software/src/ossm/OSSM.SimplePenetration.cpp b/Software/src/ossm/OSSM.SimplePenetration.cpp index 0b18073b..758f8d46 100644 --- a/Software/src/ossm/OSSM.SimplePenetration.cpp +++ b/Software/src/ossm/OSSM.SimplePenetration.cpp @@ -96,7 +96,7 @@ void OSSM::startSimplePenetrationTask(void *pvParameters) { } void OSSM::startSimplePenetration() { - int stackSize = 30 * configMINIMAL_STACK_SIZE; + int stackSize = 10 * configMINIMAL_STACK_SIZE; xTaskCreatePinnedToCore(startSimplePenetrationTask, "startSimplePenetrationTask", stackSize, this, diff --git a/Software/src/ossm/OSSM.StrokeEngine.cpp b/Software/src/ossm/OSSM.StrokeEngine.cpp index bed95aa5..59c521b9 100644 --- a/Software/src/ossm/OSSM.StrokeEngine.cpp +++ b/Software/src/ossm/OSSM.StrokeEngine.cpp @@ -1,6 +1,6 @@ #include "OSSM.h" -#include "../../lib/StrokeEngine/src/StrokeEngine.h" +#include "services/stepper.h" void OSSM::startStrokeEngineTask(void *pvParameters) { OSSM *ossm = (OSSM *)pvParameters; @@ -11,13 +11,11 @@ void OSSM::startStrokeEngineTask(void *pvParameters) { .keepoutBoundary = 6.0}; SettingPercents lastSetting = ossm->setting; - class StrokeEngine Stroker; - - Stroker.begin(&strokingMachine, &servoMotor, ossm->engine, ossm->stepper); + Stroker.begin(&strokingMachine, &servoMotor, ossm->stepper); Stroker.thisIsHome(); Stroker.setSensation(calculateSensation(ossm->setting.sensation), true); - Stroker.setPattern(int(ossm->setting.pattern), true); + Stroker.setDepth(0.01f * ossm->setting.depth * abs(measuredStrokeMm), true); Stroker.setStroke(0.01f * ossm->setting.stroke * abs(measuredStrokeMm), true); @@ -70,7 +68,35 @@ void OSSM::startStrokeEngineTask(void *pvParameters) { if (lastSetting.pattern != ossm->setting.pattern) { ESP_LOGD("UTILS", "change pattern: %d", ossm->setting.pattern); - Stroker.setPattern(ossm->setting.pattern, false); + + switch (ossm->setting.pattern) { + case StrokePatterns::SimpleStroke: + Stroker.setPattern(new SimpleStroke("Simple Stroke"), + false); + break; + case StrokePatterns::TeasingPounding: + Stroker.setPattern(new TeasingPounding("Teasing Pounding"), + false); + break; + case StrokePatterns::RoboStroke: + Stroker.setPattern(new RoboStroke("Robo Stroke"), false); + break; + case StrokePatterns::HalfnHalf: + Stroker.setPattern(new HalfnHalf("Half'n'Half"), false); + break; + case StrokePatterns::Deeper: + Stroker.setPattern(new Deeper("Deeper"), false); + break; + case StrokePatterns::StopNGo: + Stroker.setPattern(new StopNGo("Stop'n'Go"), false); + break; + case StrokePatterns::Insist: + Stroker.setPattern(new Insist("Insist"), false); + break; + default: + break; + } + lastSetting.pattern = ossm->setting.pattern; } @@ -83,7 +109,7 @@ void OSSM::startStrokeEngineTask(void *pvParameters) { } void OSSM::startStrokeEngine() { - int stackSize = 15 * configMINIMAL_STACK_SIZE; + int stackSize = 10 * configMINIMAL_STACK_SIZE; xTaskCreatePinnedToCore(startStrokeEngineTask, "startStrokeEngineTask", stackSize, this, configMAX_PRIORITIES - 1, diff --git a/Software/src/ossm/OSSM.cpp b/Software/src/ossm/OSSM.cpp index c89ea877..6ac7e2f8 100644 --- a/Software/src/ossm/OSSM.cpp +++ b/Software/src/ossm/OSSM.cpp @@ -11,26 +11,13 @@ using namespace sml; // Now we can define the OSSM constructor since OSSMStateMachine::operator() is // fully defined OSSM::OSSM(U8G2_SSD1306_128X64_NONAME_F_HW_I2C &display, - AiEsp32RotaryEncoder &encoder) + AiEsp32RotaryEncoder &encoder, FastAccelStepper *stepper) : display(display), encoder(encoder), + stepper(stepper), sm(std::make_unique< sml::sm, sml::logger>>(logger, *this)) { - engine.init(); - stepper = engine.stepperConnectToPin(Pins::Driver::motorStepPin); - if (stepper) { - stepper->setDirectionPin(Pins::Driver::motorDirectionPin, false); - stepper->setEnablePin(Pins::Driver::motorEnablePin, true); - stepper->setAutoEnable(false); - } - - // disable motor briefly in case we are against a hard stop. - digitalWrite(Pins::Driver::motorEnablePin, HIGH); - delay(600); - digitalWrite(Pins::Driver::motorEnablePin, LOW); - delay(100); - // NOTE: This is a hack to get the wifi credentials loaded early. wm.setConfigPortalBlocking(false); wm.startConfigPortal(); diff --git a/Software/src/ossm/OSSM.h b/Software/src/ossm/OSSM.h index 0c9a6718..74d2afbb 100644 --- a/Software/src/ossm/OSSM.h +++ b/Software/src/ossm/OSSM.h @@ -1,8 +1,6 @@ #ifndef OSSM_SOFTWARE_OSSM_H #define OSSM_SOFTWARE_OSSM_H -#include - #include "Actions.h" #include "AiEsp32RotaryEncoder.h" #include "Events.h" @@ -229,9 +227,7 @@ class OSSM { * //// * /////////////////////////////////////////// */ - FastAccelStepperEngine engine = FastAccelStepperEngine(); - FastAccelStepper *stepper = nullptr; - + FastAccelStepper *stepper; U8G2_SSD1306_128X64_NONAME_F_HW_I2C &display; StateLogger logger; AiEsp32RotaryEncoder &encoder; @@ -253,8 +249,11 @@ class OSSM { Menu menuOption; String errorMessage = ""; - SettingPercents setting = { - .speed = 0, .stroke = 0, .sensation = 50, .depth = 50, .pattern = 0}; + SettingPercents setting = {.speed = 0, + .stroke = 0, + .sensation = 50, + .depth = 50, + .pattern = StrokePatterns::SimpleStroke}; unsigned long sessionStartTime = 0; int sessionStrokeCount = 0; @@ -324,7 +323,8 @@ class OSSM { public: explicit OSSM(U8G2_SSD1306_128X64_NONAME_F_HW_I2C &display, - AiEsp32RotaryEncoder &rotaryEncoder); + AiEsp32RotaryEncoder &rotaryEncoder, + FastAccelStepper *stepper); std::unique_ptr< sml::sm, diff --git a/Software/src/services/board.h b/Software/src/services/board.h index 966e941a..2b4c9a7c 100644 --- a/Software/src/services/board.h +++ b/Software/src/services/board.h @@ -4,11 +4,14 @@ #include #include "constants/Pins.h" +#include "services/display.h" +#include "services/encoder.h" +#include "services/stepper.h" /** * This file changes the configuration of the board. */ -void initBoard() { +static void initBoard() { Serial.begin(115200); pinMode(Pins::Remote::encoderSwitch, @@ -26,6 +29,9 @@ void initBoard() { analogReadResolution(12); analogSetAttenuation(ADC_11db); // allows us to read almost full 3.3V range + initEncoder(); + initDisplay(); + initStepper(); } #endif // OSSM_SOFTWARE_BOARD_H diff --git a/Software/src/services/display.h b/Software/src/services/display.h index 731d611f..7aa4ee55 100644 --- a/Software/src/services/display.h +++ b/Software/src/services/display.h @@ -24,4 +24,10 @@ static U8G2_SSD1306_128X64_NONAME_F_HW_I2C display(U8G2_R0, Pins::Remote::displayClock, Pins::Remote::displayData); +static void initDisplay() { + // Display + display.setBusClock(400000); + display.begin(); +} + #endif // OSSM_SOFTWARE_DISPLAY_H diff --git a/Software/src/services/stepper.h b/Software/src/services/stepper.h index 9b01e485..073b9620 100644 --- a/Software/src/services/stepper.h +++ b/Software/src/services/stepper.h @@ -1,34 +1,27 @@ -#ifndef OSSM_SOFTWARE_STEPPER_H -#define OSSM_SOFTWARE_STEPPER_H +#ifndef SOFTWARE_STEPPER_H +#define SOFTWARE_STEPPER_H -#include - -#include "constants/Config.h" +#include "FastAccelStepper.h" #include "constants/Pins.h" -#include "services/tasks.h" - -/** - * Here are all the initialization steps for the flexyStepper motor. - * - * It is useful the call this function again if the motor is in an unknown - * state. - * - * @param flexyStepper - */ -static void initStepper(ESP_FlexyStepper &flexyStepper) { - flexyStepper.connectToPins(Pins::Driver::motorStepPin, - Pins::Driver::motorDirectionPin); - flexyStepper.setLimitSwitchActive(Pins::Driver::limitSwitchPin); - float stepsPerMm = - Config::Driver::motorStepPerRevolution / - (Config::Driver::pulleyToothCount * Config::Driver::beltPitchMm); +static FastAccelStepperEngine stepperEngine = FastAccelStepperEngine(); +static FastAccelStepper *stepper = nullptr; +static class StrokeEngine Stroker; - flexyStepper.setStepsPerMillimeter(stepsPerMm); +static void initStepper() { + stepperEngine.init(); + stepper = stepperEngine.stepperConnectToPin(Pins::Driver::motorStepPin); + if (stepper) { + stepper->setDirectionPin(Pins::Driver::motorDirectionPin, false); + stepper->setEnablePin(Pins::Driver::motorEnablePin, true); + stepper->setAutoEnable(false); + } - // This stepper must start on core 1, otherwise it may cause jitter. - // https://github.com/pkerspe/ESP-FlexyStepper#a-view-words-on-jitter-in-the-generated-step-signals - flexyStepper.startAsService(stepperCore); + // disable motor briefly in case we are against a hard stop. + digitalWrite(Pins::Driver::motorEnablePin, HIGH); + delay(600); + digitalWrite(Pins::Driver::motorEnablePin, LOW); + delay(100); } -#endif // OSSM_SOFTWARE_STEPPER_H +#endif // SOFTWARE_STEPPER_H diff --git a/Software/src/services/tasks.h b/Software/src/services/tasks.h index 07076383..ccb565d5 100644 --- a/Software/src/services/tasks.h +++ b/Software/src/services/tasks.h @@ -15,7 +15,6 @@ static TaskHandle_t drawHelloTaskH = nullptr; static TaskHandle_t drawMenuTaskH = nullptr; static TaskHandle_t drawPlayControlsTaskH = nullptr; static TaskHandle_t drawPatternControlsTaskH = nullptr; -static TaskHandle_t wmTaskH = nullptr; static TaskHandle_t drawPreflightTaskH = nullptr; static TaskHandle_t runHomingTaskH = nullptr; diff --git a/Software/src/structs/LanguageStruct.h b/Software/src/structs/LanguageStruct.h index 0fa7a18a..8a5847a4 100644 --- a/Software/src/structs/LanguageStruct.h +++ b/Software/src/structs/LanguageStruct.h @@ -31,6 +31,7 @@ struct LanguageStruct { String WiFiSetupLine2; String YouShouldNotBeHere; String StrokeEngineDescriptions[7]; + String StrokeEngineNames[7]; }; #endif // OSSM_SOFTWARE_LANGUAGESTRUCT_H diff --git a/Software/src/structs/SettingPercents.h b/Software/src/structs/SettingPercents.h index d1c38d2e..204290f4 100644 --- a/Software/src/structs/SettingPercents.h +++ b/Software/src/structs/SettingPercents.h @@ -1,12 +1,22 @@ #ifndef SOFTWARE_SETTINGPERCENTS_H #define SOFTWARE_SETTINGPERCENTS_H +enum class StrokePatterns { + SimpleStroke, + TeasingPounding, + RoboStroke, + HalfnHalf, + Deeper, + StopNGo, + Insist, +}; + struct SettingPercents { float speed; float stroke; float sensation; float depth; - int pattern; + StrokePatterns pattern; float speedKnob; }; diff --git a/Software/src/utils/StateLogger.h b/Software/src/utils/StateLogger.h index ba8a124e..a2de519c 100644 --- a/Software/src/utils/StateLogger.h +++ b/Software/src/utils/StateLogger.h @@ -49,9 +49,6 @@ struct StateLogger { ESP_LOGV(STATE_MACHINE_TAG, "%s", sml::aux::get_type_name()); // These are trace messages because lambda functions are very verbose. -// ESP_LOGV(STATE_MACHINE_TAG, "%s, %s", -// sml::aux::get_type_name(), -// sml::aux::get_type_name()); } template diff --git a/Software/src/utils/StrokeEngineHelper.h b/Software/src/utils/StrokeEngineHelper.h index bafb99d8..2c5fbd38 100644 --- a/Software/src/utils/StrokeEngineHelper.h +++ b/Software/src/utils/StrokeEngineHelper.h @@ -13,11 +13,7 @@ ##################################################################################################*/ // enum of stroke engine states -enum PlayControls { - STROKE, - DEPTH, - SENSATION -}; +enum PlayControls { STROKE, DEPTH, SENSATION }; static motorProperties servoMotor{ .maxSpeed = @@ -42,4 +38,5 @@ static float calculateSensation(float sensationPercentage) { return float((sensationPercentage * 200.0) / 100.0) - 100.0f; } + #endif \ No newline at end of file diff --git a/Software/src/utils/update.h b/Software/src/utils/update.h index 964581d4..2b6edee4 100644 --- a/Software/src/utils/update.h +++ b/Software/src/utils/update.h @@ -96,6 +96,8 @@ auto updateOSSM = []() { ESP_LOGD("UTILS", "HTTP_UPDATE_OK"); break; } + + client.stop(); }; #endif // SOFTWARE_UPDATE_H From d3870bcaf4d0f348ddd3d8b29aba06329b0f6afd Mon Sep 17 00:00:00 2001 From: AJ-Koenig <39343698+AJ-Koenig@users.noreply.github.com> Date: Thu, 30 May 2024 21:39:43 -0600 Subject: [PATCH 2/4] Aj/pairing menu option (#104) Allow Pairing from OSSM to RAD Dashboard --- Software/platformio.ini | 13 +- Software/src/constants/Menu.h | 14 ++- Software/src/constants/URLs.h | 12 ++ Software/src/constants/copy/en-us.h | 147 +++++++++++++++------- Software/src/constants/copy/fr.h | 151 ++++++++++++++-------- Software/src/extensions/u8g2Extensions.h | 69 ++++++++-- Software/src/main.cpp | 1 + Software/src/ossm/OSSM.Help.cpp | 37 +----- Software/src/ossm/OSSM.Pairing.cpp | 0 Software/src/ossm/OSSM.PlayControls.cpp | 18 +-- Software/src/ossm/OSSM.Preflight.cpp | 2 +- Software/src/ossm/OSSM.Update.cpp | 2 +- Software/src/ossm/OSSM.WiFi.cpp | 122 +++++++++++++----- Software/src/ossm/OSSM.h | 153 +++++++++++++---------- Software/src/services/tasks.h | 1 + Software/src/structs/LanguageStruct.h | 69 +++++----- Software/src/utils/getEfuseMac.h | 18 +++ Software/src/utils/uniqueId.h | 66 ++++++++++ Software/src/utils/update.h | 4 +- 19 files changed, 609 insertions(+), 290 deletions(-) create mode 100644 Software/src/constants/URLs.h create mode 100644 Software/src/ossm/OSSM.Pairing.cpp create mode 100644 Software/src/utils/getEfuseMac.h create mode 100644 Software/src/utils/uniqueId.h diff --git a/Software/platformio.ini b/Software/platformio.ini index 0e2f63e4..d3388a4f 100644 --- a/Software/platformio.ini +++ b/Software/platformio.ini @@ -24,6 +24,7 @@ lib_deps = ricmoo/QRCode@^0.0.1 igorantolic/Ai Esp32 Rotary Encoder @ ^1.6 mathertel/OneButton@^2.5.0 + robtillaart/UUID@^0.1.6 upload_speed = 921600 check_skip_packages = true check_tool = clangtidy @@ -38,9 +39,10 @@ build_flags = -D CORE_DEBUG_LEVEL=4 -D VERSIONDEV -D SW_VERSION=0 - -D HTTPCLIENT_1_1_COMPATIBLE=0 ; This flag is used for Stroke Engine. -D DEBUG_TALKATIVE + -D URL_RAD_SHORT="\"http://192.168.0.23:3000\"" + -D URL_RAD="\"http://192.168.0.23:3000\"" extends = common platform = espressif32 board = esp32dev @@ -55,8 +57,9 @@ build_flags = -D CORE_DEBUG_LEVEL=4 -D VERSIONDEV -D SW_VERSION=0 - -D HTTPCLIENT_1_1_COMPATIBLE=0 -D DEBUG_SKIP_HOMING + -D URL_RAD_SHORT="\"https://rdapp.link/d\"" + -D URL_RAD="\"http://localhost:3000\"" extends = common platform = espressif32 board = esp32dev @@ -69,7 +72,8 @@ build_flags = -std=gnu++17 -D CORE_DEBUG_LEVEL=1 -D VERSIONSTAGING - -D HTTPCLIENT_1_1_COMPATIBLE=0 + -D URL_RAD_SHORT="\"https://rdapp.link\"" + -D URL_RAD="\"https://dashboard.researchanddesire.com\"" extends = common platform = espressif32 board = esp32dev @@ -81,7 +85,8 @@ build_flags = -std=gnu++17 -D CORE_DEBUG_LEVEL=0 -D VERSIONLIVE - -D HTTPCLIENT_1_1_COMPATIBLE=0 + -D URL_RAD_SHORT="\"https://rdapp.link\"" + -D URL_RAD="\"https://dashboard.researchanddesire.com\"" extends = common platform = espressif32 board = esp32dev diff --git a/Software/src/constants/Menu.h b/Software/src/constants/Menu.h index 606ce0a4..3a5b3ecc 100644 --- a/Software/src/constants/Menu.h +++ b/Software/src/constants/Menu.h @@ -9,6 +9,7 @@ enum Menu { SimplePenetration, StrokeEngine, UpdateOSSM, + PairOSSM, WiFiSetup, Help, Restart, @@ -16,11 +17,12 @@ enum Menu { }; static String menuStrings[Menu::NUM_OPTIONS] = { - UserConfig::language.SimplePenetration, - UserConfig::language.StrokeEngine, - UserConfig::language.Update, - UserConfig::language.WiFiSetup, - UserConfig::language.GetHelp, - UserConfig::language.Restart}; + UserConfig::language.SimplePenetration, + UserConfig::language.StrokeEngine, + UserConfig::language.Update, + UserConfig::language.Pair, + UserConfig::language.WiFiSetup, + UserConfig::language.GetHelp, + UserConfig::language.Restart}; #endif // OSSM_SOFTWARE_MENU_H diff --git a/Software/src/constants/URLs.h b/Software/src/constants/URLs.h new file mode 100644 index 00000000..296b3857 --- /dev/null +++ b/Software/src/constants/URLs.h @@ -0,0 +1,12 @@ +#ifndef SOFTWARE_URLS_H +#define SOFTWARE_URLS_H + +#ifndef URL_RAD_SHORT +#define URL_RAD_SHORT "http://192.168.0.23:3000" +#endif + +#ifndef URL_RAD +#define URL_RAD "http://192.168.0.23:3000" +#endif + +#endif //SOFTWARE_URLS_H diff --git a/Software/src/constants/copy/en-us.h b/Software/src/constants/copy/en-us.h index 795aac69..d494c38c 100644 --- a/Software/src/constants/copy/en-us.h +++ b/Software/src/constants/copy/en-us.h @@ -2,56 +2,109 @@ #define OSSM_SOFTWARE_EN_US_H #include "structs/LanguageStruct.h" +#include + +// English strings in PROGMEM +const char en_DeepThroatTrainerSync[] PROGMEM = "DeepThroat Sync"; +const char en_Error[] PROGMEM = "Error"; +const char en_GetHelp[] PROGMEM = "Get Help"; +const char en_GetHelpLine1[] PROGMEM = "On Discord,"; +const char en_GetHelpLine2[] PROGMEM = "or GitHub"; +const char en_Homing[] PROGMEM = "Homing"; +const char en_HomingTookTooLong[] PROGMEM = "Homing took too long. Please check your wiring and try again."; +const char en_Idle[] PROGMEM = "Initializing"; +const char en_InDevelopment[] PROGMEM = "This feature is in development."; +const char en_MeasuringStroke[] PROGMEM = "Measuring Stroke"; +const char en_NoInternalLoop[] PROGMEM = "No display handler implemented."; +const char en_Restart[] PROGMEM = "Restart"; +const char en_Settings[] PROGMEM = "Settings"; +const char en_SimplePenetration[] PROGMEM = "Simple Penetration"; +const char en_Skip[] PROGMEM = "Click to exit"; +const char en_Speed[] PROGMEM = "Speed"; +const char en_SpeedWarning[] PROGMEM = "Decrease the speed to begin playing."; +const char en_StateNotImplemented[] PROGMEM = "State: %u not implemented."; +const char en_Stroke[] PROGMEM = "Stroke"; +const char en_StrokeEngine[] PROGMEM = "Stroke Engine"; +const char en_StrokeTooShort[] PROGMEM = "Stroke too short. Please check your drive belt."; +const char en_Pair[] PROGMEM = "Pair Device"; +const char en_PairingInstructions[] PROGMEM = "Enter the following code on the dashboard"; +const char en_Update[] PROGMEM = "Update"; +const char en_UpdateMessage[] PROGMEM = "Update is in progress. This may take up to 60s."; +const char en_WiFi[] PROGMEM = "Wi-Fi"; +const char en_WiFiSetup[] PROGMEM = "Wi-Fi Setup"; +const char en_WiFiSetupLine1[] PROGMEM = "Connect to"; +const char en_WiFiSetupLine2[] PROGMEM = "'Ossm Setup'"; +const char en_YouShouldNotBeHere[] PROGMEM = "You should not be here."; + +// Stroke Engine Descriptions +const char en_StrokeEngineDescriptions_0[] PROGMEM = "Acceleration, coasting, deceleration equally split; no sensation."; +const char en_StrokeEngineDescriptions_1[] PROGMEM = "Speed shifts with sensation; balances faster strokes."; +const char en_StrokeEngineDescriptions_2[] PROGMEM = "Sensation varies acceleration; from robotic to gradual."; +const char en_StrokeEngineDescriptions_3[] PROGMEM = "Full and half depth strokes alternate; sensation affects speed."; +const char en_StrokeEngineDescriptions_4[] PROGMEM = "Stroke depth increases per cycle; sensation sets count."; +const char en_StrokeEngineDescriptions_5[] PROGMEM = "Pauses between strokes; sensation adjusts length."; +const char en_StrokeEngineDescriptions_6[] PROGMEM = "Modifies length, maintains speed; sensation influences direction."; + +// Stroke Engine Names +const char en_StrokeEngineNames_0[] PROGMEM = "Simple Stroke"; +const char en_StrokeEngineNames_1[] PROGMEM = "Teasing Pounding"; +const char en_StrokeEngineNames_2[] PROGMEM = "Robo Stroke"; +const char en_StrokeEngineNames_3[] PROGMEM = "Half'n'Half"; +const char en_StrokeEngineNames_4[] PROGMEM = "Deeper"; +const char en_StrokeEngineNames_5[] PROGMEM = "Stop'n'Go"; +const char en_StrokeEngineNames_6[] PROGMEM = "Insist"; + // English copy static const LanguageStruct enUs = { - .DeepThroatTrainerSync = "DeepThroat Sync", - .Error = "Error", - .GetHelp = "Get Help", - .GetHelpLine1 = "On Discord,", - .GetHelpLine2 = "or GitHub", - .Homing = "Homing", - .HomingTookTooLong = - "Homing took too long. Please check your wiring and try again.", - .Idle = "Initializing", - .InDevelopment = "This feature is in development.", - .MeasuringStroke = "Measuring Stroke", - .NoInternalLoop = "No display handler implemented.", - .Restart = "Restart", - .Settings = "Settings", - .SimplePenetration = "Simple Penetration", - .Skip = "Click to exit", - .Speed = "Speed", - .SpeedWarning = "Decrease the speed to begin playing.", - .StateNotImplemented = "State: %u not implemented.", - .Stroke = "Stroke", - .StrokeEngine = "Stroke Engine", - .StrokeTooShort = "Stroke too short. Please check you drive belt.", - .Update = "Update", - .UpdateMessage = "Update is in progress. This may take up to 60s.", - .WiFi = "Wi-Fi", - .WiFiSetup = "Wi-Fi Setup", - .WiFiSetupLine1 = "Connect to", - .WiFiSetupLine2 = "'Ossm Setup'", - .YouShouldNotBeHere = "You should not be here.", - .StrokeEngineDescriptions = { - "Acceleration, coasting, deceleration equally split; no sensation.", - "Speed shifts with sensation; balances faster strokes.", - "Sensation varies acceleration; from robotic to gradual.", - "Full and half depth strokes alternate; sensation affects speed.", - "Stroke depth increases per cycle; sensation sets count.", - "Pauses between strokes; sensation adjusts length.", - "Modifies length, maintains speed; sensation influences direction." - }, - .StrokeEngineNames = { - "Simple Stroke", - "Teasing Pounding", - "Robo Stroke", - "Half'n'Half", - "Deeper", - "Stop'n'Go", - "Insist" - }, + .DeepThroatTrainerSync = en_DeepThroatTrainerSync, + .Error = en_Error, + .GetHelp = en_GetHelp, + .GetHelpLine1 = en_GetHelpLine1, + .GetHelpLine2 = en_GetHelpLine2, + .Homing = en_Homing, + .HomingTookTooLong = en_HomingTookTooLong, + .Idle = en_Idle, + .InDevelopment = en_InDevelopment, + .MeasuringStroke = en_MeasuringStroke, + .NoInternalLoop = en_NoInternalLoop, + .Restart = en_Restart, + .Settings = en_Settings, + .SimplePenetration = en_SimplePenetration, + .Skip = en_Skip, + .Speed = en_Speed, + .SpeedWarning = en_SpeedWarning, + .StateNotImplemented = en_StateNotImplemented, + .Stroke = en_Stroke, + .StrokeEngine = en_StrokeEngine, + .StrokeTooShort = en_StrokeTooShort, + .Pair = en_Pair, + .PairingInstructions = en_PairingInstructions, + .Update = en_Update, + .UpdateMessage = en_UpdateMessage, + .WiFi = en_WiFi, + .WiFiSetup = en_WiFiSetup, + .WiFiSetupLine1 = en_WiFiSetupLine1, + .WiFiSetupLine2 = en_WiFiSetupLine2, + .YouShouldNotBeHere = en_YouShouldNotBeHere, + .StrokeEngineDescriptions = { + en_StrokeEngineDescriptions_0, + en_StrokeEngineDescriptions_1, + en_StrokeEngineDescriptions_2, + en_StrokeEngineDescriptions_3, + en_StrokeEngineDescriptions_4, + en_StrokeEngineDescriptions_5, + en_StrokeEngineDescriptions_6 + }, + .StrokeEngineNames = { + en_StrokeEngineNames_0, + en_StrokeEngineNames_1, + en_StrokeEngineNames_2, + en_StrokeEngineNames_3, + en_StrokeEngineNames_4, + en_StrokeEngineNames_5, + en_StrokeEngineNames_6 + } }; #endif // OSSM_SOFTWARE_EN_US_H diff --git a/Software/src/constants/copy/fr.h b/Software/src/constants/copy/fr.h index bcdee486..e1967121 100644 --- a/Software/src/constants/copy/fr.h +++ b/Software/src/constants/copy/fr.h @@ -2,60 +2,111 @@ #define OSSM_SOFTWARE_FR_H #include "structs/LanguageStruct.h" +#include + + +// French strings in PROGMEM +const char fr_DeepThroatTrainerSync[] PROGMEM = "DeepThroat Sync"; +const char fr_Error[] PROGMEM = "Erreur"; +const char fr_GetHelp[] PROGMEM = "Aide"; +const char fr_GetHelpLine1[] PROGMEM = "Sur Discord,"; +const char fr_GetHelpLine2[] PROGMEM = "ou GitHub"; +const char fr_Homing[] PROGMEM = "FR - Homing"; +const char fr_HomingTookTooLong[] PROGMEM = "Le homing a pris trop de temps. Veuillez vérifier votre câblage et réessayer."; +const char fr_Idle[] PROGMEM = "Inactif"; +const char fr_InDevelopment[] PROGMEM = "Ceci est en développement."; +const char fr_MeasuringStroke[] PROGMEM = "Mesure de la course"; +const char fr_NoInternalLoop[] PROGMEM = "Aucun gestionnaire d'affichage implémenté."; +const char fr_Restart[] PROGMEM = "Redémarrage"; +const char fr_Settings[] PROGMEM = "Paramètres"; +const char fr_SimplePenetration[] PROGMEM = "Pénétration simple"; +const char fr_Skip[] PROGMEM = "Quitter ->"; +const char fr_Speed[] PROGMEM = "Vitesse"; +const char fr_SpeedWarning[] PROGMEM = "Réduisez la vitesse pour commencer à jouer."; +const char fr_StateNotImplemented[] PROGMEM = "État: %u non implémenté."; +const char fr_Stroke[] PROGMEM = "Coup"; +const char fr_StrokeEngine[] PROGMEM = "Stroke Engine"; +const char fr_StrokeTooShort[] PROGMEM = "Course trop courte. Veuillez vérifier votre courroie d'entraînement."; +const char fr_Pair[] PROGMEM = "Jumeler l'appareil"; +const char fr_PairingInstructions[] PROGMEM = "Entrez le code suivant sur le tableau de bord"; +const char fr_Update[] PROGMEM = "Mettre à jour"; +const char fr_UpdateMessage[] PROGMEM = "La mise à jour est en cours. Ça peut prendre jusqu'à 60 secondes."; +const char fr_WiFi[] PROGMEM = "Wi-Fi"; +const char fr_WiFiSetup[] PROGMEM = "Config. Wi-Fi"; +const char fr_WiFiSetupLine1[] PROGMEM = "Se connecter à"; +const char fr_WiFiSetupLine2[] PROGMEM = "'Ossm Setup'"; +const char fr_YouShouldNotBeHere[] PROGMEM = "Vous ne devriez pas être ici."; + +// Stroke Engine Descriptions +const char fr_StrokeEngineDescriptions_0[] PROGMEM = "Accélération, roulement, décélération également répartis ; sans sensation."; +const char fr_StrokeEngineDescriptions_1[] PROGMEM = "La vitesse change avec la sensation ; équilibre les coups rapides."; +const char fr_StrokeEngineDescriptions_2[] PROGMEM = "La sensation varie l'accélération ; de robotique à progressive."; +const char fr_StrokeEngineDescriptions_3[] PROGMEM = "Alternance de coups pleins et à demi-profondeur ; la sensation affecte la vitesse."; +const char fr_StrokeEngineDescriptions_4[] PROGMEM = "La profondeur des coups augmente à chaque cycle ; la sensation définit le nombre."; +const char fr_StrokeEngineDescriptions_5[] PROGMEM = "Pauses entre les coups ; la sensation ajuste la longueur."; +const char fr_StrokeEngineDescriptions_6[] PROGMEM = "Modifie la longueur, maintient la vitesse ; la sensation influe sur la direction."; + +// Stroke Engine Names +const char fr_StrokeEngineNames_0[] PROGMEM = "Simple Stroke"; +const char fr_StrokeEngineNames_1[] PROGMEM = "Teasing Pounding"; +const char fr_StrokeEngineNames_2[] PROGMEM = "Robo Stroke"; +const char fr_StrokeEngineNames_3[] PROGMEM = "Half'n'Half"; +const char fr_StrokeEngineNames_4[] PROGMEM = "Deeper"; +const char fr_StrokeEngineNames_5[] PROGMEM = "Stop'n'Go"; +const char fr_StrokeEngineNames_6[] PROGMEM = "Insist"; + // TODO: Requires validation by a native french speaker. // These have been translated by Google Translate. static const LanguageStruct fr = { - .DeepThroatTrainerSync = "DeepThroat Sync", - .Error = "Erreur", - .GetHelp = "Aide", - .GetHelpLine1 = "Sur Discord,", - .GetHelpLine2 = "ou GitHub", - .Homing = "FR - Homing", - .HomingTookTooLong = - "Le homing a pris trop de temps.Veuillez vérifier votre câblage et " - "réessayer.", - .Idle = "Inactif", - .InDevelopment = "Ceci est en développement.", - .MeasuringStroke = "Mesure de la course", - .NoInternalLoop = "Aucun gestionnaire d'affichage implémenté.", - .Restart = "Redémarrage", - .Settings = "Paramètres", - .SimplePenetration = "Pénétration simple", - .Skip = "Quitter ->", - .Speed = "Vitesse", - .SpeedWarning = "Réduisez la vitesse pour commencer à jouer.", - .StateNotImplemented = "État: %u non implémenté.", - .Stroke = "Coup", - .StrokeEngine = "Stroke Engine", - .StrokeTooShort = - "Course trop courte. Veuillez vérifier votre courroie d'entraînement.", - .Update = "Mettre à jour", - .UpdateMessage = - "La mise à jour est en cours. Ça peut prendre jusqu'à 60 secondes.", - .WiFi = "Wi-Fi", - .WiFiSetup = "Config. Wi-Fi", - .WiFiSetupLine1 = "Se connecter à", - .WiFiSetupLine2 = "'Ossm Setup'", - .YouShouldNotBeHere = "Vous ne devriez pas être ici.", - .StrokeEngineDescriptions = { - "Accélération, roulement, décélération également répartis ; sans sensation.", - "La vitesse change avec la sensation ; équilibre les coups rapides.", - "La sensation varie l'accélération ; de robotique à progressive.", - "Alternance de coups pleins et à demi-profondeur ; la sensation affecte la vitesse.", - "La profondeur des coups augmente à chaque cycle ; la sensation définit le nombre.", - "Pauses entre les coups ; la sensation ajuste la longueur.", - "Modifie la longueur, maintient la vitesse ; la sensation influe sur la direction.", - }, - .StrokeEngineNames = { - "Simple Stroke", - "Teasing Pounding", - "Robo Stroke", - "Half'n'Half", - "Deeper", - "Stop'n'Go", - "Insist", - } + .DeepThroatTrainerSync = fr_DeepThroatTrainerSync, + .Error = fr_Error, + .GetHelp = fr_GetHelp, + .GetHelpLine1 = fr_GetHelpLine1, + .GetHelpLine2 = fr_GetHelpLine2, + .Homing = fr_Homing, + .HomingTookTooLong = fr_HomingTookTooLong, + .Idle = fr_Idle, + .InDevelopment = fr_InDevelopment, + .MeasuringStroke = fr_MeasuringStroke, + .NoInternalLoop = fr_NoInternalLoop, + .Restart = fr_Restart, + .Settings = fr_Settings, + .SimplePenetration = fr_SimplePenetration, + .Skip = fr_Skip, + .Speed = fr_Speed, + .SpeedWarning = fr_SpeedWarning, + .StateNotImplemented = fr_StateNotImplemented, + .Stroke = fr_Stroke, + .StrokeEngine = fr_StrokeEngine, + .StrokeTooShort = fr_StrokeTooShort, + .Pair = fr_Pair, + .PairingInstructions = fr_PairingInstructions, + .Update = fr_Update, + .UpdateMessage = fr_UpdateMessage, + .WiFi = fr_WiFi, + .WiFiSetup = fr_WiFiSetup, + .WiFiSetupLine1 = fr_WiFiSetupLine1, + .WiFiSetupLine2 = fr_WiFiSetupLine2, + .YouShouldNotBeHere = fr_YouShouldNotBeHere, + .StrokeEngineDescriptions = { + fr_StrokeEngineDescriptions_0, + fr_StrokeEngineDescriptions_1, + fr_StrokeEngineDescriptions_2, + fr_StrokeEngineDescriptions_3, + fr_StrokeEngineDescriptions_4, + fr_StrokeEngineDescriptions_5, + fr_StrokeEngineDescriptions_6 + }, + .StrokeEngineNames = { + fr_StrokeEngineNames_0, + fr_StrokeEngineNames_1, + fr_StrokeEngineNames_2, + fr_StrokeEngineNames_3, + fr_StrokeEngineNames_4, + fr_StrokeEngineNames_5, + fr_StrokeEngineNames_6 + } }; diff --git a/Software/src/extensions/u8g2Extensions.h b/Software/src/extensions/u8g2Extensions.h index dbb3d8c0..d355d0a8 100644 --- a/Software/src/extensions/u8g2Extensions.h +++ b/Software/src/extensions/u8g2Extensions.h @@ -9,6 +9,10 @@ #include "constants/Config.h" #include "services/display.h" #include "structs/Points.h" +#include "qrcode.h" + +static QRCode qrcode; +const int scale = 2; // NOLINTBEGIN(hicpp-signed-bitwise) static int getUTF8CharLength(const unsigned char c) { @@ -103,8 +107,8 @@ namespace drawStr { // Check for space character; if found, remember its position. if (*glyph == ' ') lastSpace = currentChar - charLength; - // If not a space, increment the width (for the space between - // characters). + // If not a space, increment the width (for the space between + // characters). else ++currentLineWidth; @@ -112,7 +116,7 @@ namespace drawStr { // width. if (*glyph == '\n' || x + currentLineWidth > displayWidth) { int startingPosition = - x; // Remember the starting horizontal position. + x; // Remember the starting horizontal position. // Display characters up to the last space (or current position // if no space). @@ -180,7 +184,7 @@ namespace drawShape { int scrollbarHeight = 64 - topMargin; // Height of the scrollbar int scrollbarWidth = 3; // Width of the scrollbar int scrollbarX = - 125; // X position of the scrollbar (right edge of the screen) + 125; // X position of the scrollbar (right edge of the screen) int scrollbarY = (64 - scrollbarHeight + topMargin) / 2; // Y position of the scrollbar (centered) @@ -192,7 +196,7 @@ namespace drawShape { // Calculate the Y position of the rectangle based on the current // position int rectY = - scrollbarY + (scrollbarHeight - scrollbarWidth) * position / 100; + scrollbarY + (scrollbarHeight - scrollbarWidth) * position / 100; // Draw the rectangle to represent the current position display.drawBox(scrollbarX, rectY, scrollbarWidth, scrollbarWidth); @@ -211,15 +215,15 @@ namespace drawShape { // Calculate height of the bar based on the value and potential min/max float scaledValue = - constrain(value, minValue, maxValue) / maxValue * 100; + constrain(value, minValue, maxValue) / maxValue * 100; int boxHeight = ceil(h * scaledValue / 100); // Position calculations based on alignment int barStartX = (alignment == LEFT_ALIGNED) ? x : x - w; int textStartX = (alignment == LEFT_ALIGNED) - ? x + w + padding + textPadding - : x - display.getUTF8Width(name.c_str()) - - padding - w - textPadding; + ? x + w + padding + textPadding + : x - display.getUTF8Width(name.c_str()) - + padding - w - textPadding; // Draw the bar and its frame display.drawBox(barStartX, h - boxHeight, w, boxHeight); @@ -227,7 +231,7 @@ namespace drawShape { // Set font for label and draw it display.setFont( - Config::Font::bold); // Make sure Config::Font::base is defined + Config::Font::bold); // Make sure Config::Font::base is defined display.drawUTF8(textStartX, y + lh1, name.c_str()); int firstQuartile = y + h * 3 / 4; @@ -266,7 +270,7 @@ namespace drawShape { // Calculate height of the bar based on the value and potential min/max float scaledValue = - constrain(value, minValue, maxValue) / maxValue * 100; + constrain(value, minValue, maxValue) / maxValue * 100; int boxHeight = ceil(h * scaledValue / 100); // draw a single pixel line int lineH = boxHeight > 0 ? constrain(64 - boxHeight - 2, 0, 64) : 64; @@ -277,7 +281,7 @@ namespace drawShape { } // Function to draw lines between a variadic number of points - template + template void lines(std::initializer_list points) { auto it = points.begin(); Point start = *it; @@ -291,6 +295,47 @@ namespace drawShape { } } + /** + * Draw a QR code on the display. + * + * In practice, this function can draw strings up to about 39 to 40 characters. + * + * @param value The value to encode in the QR code. + * @param xOffset The X offset for the QR code. + * @param yOffset The Y offset for the QR code. + */ + static void drawQRCode(String value, int xOffset = -1, int yOffset = -1) { + // Assuming the version and error correction level are constants + const int version = 3; + const int ecc = 1; // Error Correction Level M + + uint8_t qrcodeData[qrcode_getBufferSize(version)]; + ESP_LOGD("QRCode", "Buffer size: %d", qrcode_getBufferSize(version)); + // Print the size of payload in memory + ESP_LOGD("QRCode", "Payload: %s", value.c_str()); + ESP_LOGD("QRCode", "Payload length: %d", value.length()); + // convert value to bytes and get the bytes memory size. + ESP_LOGD("QRCode", "Payload memory size: %d", value.length() * sizeof(char)); + + // Initialize the QR code with the input string + qrcode_initText(&qrcode, qrcodeData, version, ecc, value.c_str()); + + + // Calculate offsets for centering the QR code + + yOffset = yOffset == -1 ? constrain((64 - qrcode.size * scale) / 2, 0, 64) : yOffset; + xOffset = xOffset == -1 ? constrain((128 - qrcode.size * scale), 0, 128) : xOffset; + + // Draw the QR code + for (uint8_t y = 0; y < qrcode.size; y++) { + for (uint8_t x = 0; x < qrcode.size; x++) { + if (qrcode_getModule(&qrcode, x, y)) { + display.drawBox(xOffset + x * scale, yOffset + y * scale, scale, scale); + } + } + } + } + } #endif // OSSM_SOFTWARE_U8G2EXTENSIONS_H diff --git a/Software/src/main.cpp b/Software/src/main.cpp index 007a2332..ab61c0f4 100644 --- a/Software/src/main.cpp +++ b/Software/src/main.cpp @@ -49,6 +49,7 @@ void setup() { [](void* pvParameters) { while (true) { button.tick(); + ossm->wm.process(); vTaskDelay(10); } }, diff --git a/Software/src/ossm/OSSM.Help.cpp b/Software/src/ossm/OSSM.Help.cpp index 4cf56f7a..bceb6af6 100644 --- a/Software/src/ossm/OSSM.Help.cpp +++ b/Software/src/ossm/OSSM.Help.cpp @@ -2,47 +2,22 @@ #include "OSSM.h" #include "extensions/u8g2Extensions.h" -#include "qrcode.h" void OSSM::drawHelp() { displayMutex.lock(); display.clearBuffer(); - static QRCode qrcode; - const int scale = 2; - // This Version of QR Codes can handle ~61 alphanumeric characters with ECC - // LEVEL M - - // NOLINTBEGIN(modernize-avoid-c-arrays) - uint8_t qrcodeData[qrcode_getBufferSize(3)]; - // NOLINTEND(modernize-avoid-c-arrays) - - String url = "https://unbox.ossm.tech"; - - qrcode_initText(&qrcode, qrcodeData, 3, 0, url.c_str()); - - int yOffset = constrain((64 - qrcode.size * scale) / 2, 0, 64); - int xOffset = constrain((128 - qrcode.size * scale), 0, 128); - - // Draw the QR code - for (uint8_t y = 0; y < qrcode.size; y++) { - for (uint8_t x = 0; x < qrcode.size; x++) { - if (qrcode_getModule(&qrcode, x, y)) { - display.drawBox(xOffset + x * scale, yOffset + y * scale, scale, - scale); - } - } - } + drawShape::drawQRCode("https://unbox.ossm.tech"); display.setFont(Config::Font::bold); - display.drawUTF8(0, 10, UserConfig::language.GetHelp.c_str()); + display.drawUTF8(0, 10, UserConfig::language.GetHelp); // Draw line - display.drawHLine(0, 12, xOffset - 10); + display.drawHLine(0, 12, 64 - 10); display.setFont(Config::Font::base); - display.drawUTF8(0, 26, UserConfig::language.GetHelpLine1.c_str()); - display.drawUTF8(0, 38, UserConfig::language.GetHelpLine2.c_str()); - display.drawUTF8(0, 62, UserConfig::language.Skip.c_str()); + display.drawUTF8(0, 26, UserConfig::language.GetHelpLine1); + display.drawUTF8(0, 38, UserConfig::language.GetHelpLine2); + display.drawUTF8(0, 62, UserConfig::language.Skip); display.sendBuffer(); displayMutex.unlock(); } diff --git a/Software/src/ossm/OSSM.Pairing.cpp b/Software/src/ossm/OSSM.Pairing.cpp new file mode 100644 index 00000000..e69de29b diff --git a/Software/src/ossm/OSSM.PlayControls.cpp b/Software/src/ossm/OSSM.PlayControls.cpp index f9b546ba..f90f5247 100644 --- a/Software/src/ossm/OSSM.PlayControls.cpp +++ b/Software/src/ossm/OSSM.PlayControls.cpp @@ -4,10 +4,12 @@ #include "services/tasks.h" #include "utils/analog.h" #include "utils/format.h" +#include "utils/uniqueId.h" +#include "constants/URLs.h" void OSSM::drawPlayControlsTask(void *pvParameters) { // parse ossm from the parameters - OSSM *ossm = (OSSM *)pvParameters; + OSSM *ossm = (OSSM *) pvParameters; ossm->encoder.setAcceleration(10); ossm->encoder.setBoundaries(0, 100, false); ossm->encoder.setEncoderValue(0); @@ -52,7 +54,7 @@ void OSSM::drawPlayControlsTask(void *pvParameters) { static float encoder = 0; bool isStrokeEngine = - ossm->sm->is("strokeEngine"_s) || ossm->sm->is("strokeEngine.idle"_s); + ossm->sm->is("strokeEngine"_s) || ossm->sm->is("strokeEngine.idle"_s); bool shouldUpdateDisplay = false; @@ -64,7 +66,7 @@ void OSSM::drawPlayControlsTask(void *pvParameters) { shouldUpdateDisplay = false; next.speedKnob = - getAnalogAveragePercent(SampleOnPin{Pins::Remote::speedPotPin, 50}); + getAnalogAveragePercent(SampleOnPin{Pins::Remote::speedPotPin, 50}); ossm->setting.speedKnob = next.speedKnob; encoder = ossm->encoder.readEncoder(); @@ -85,8 +87,8 @@ void OSSM::drawPlayControlsTask(void *pvParameters) { case PlayControls::SENSATION: next.sensation = encoder; shouldUpdateDisplay = - shouldUpdateDisplay || - next.sensation - ossm->setting.sensation >= 1; + shouldUpdateDisplay || + next.sensation - ossm->setting.sensation >= 1; ossm->setting.sensation = next.sensation; break; case PlayControls::DEPTH: @@ -98,7 +100,7 @@ void OSSM::drawPlayControlsTask(void *pvParameters) { } shouldUpdateDisplay = - shouldUpdateDisplay || millis() - displayLastUpdated > 1000; + shouldUpdateDisplay || millis() - displayLastUpdated > 1000; if (!shouldUpdateDisplay) { vTaskDelay(100); @@ -171,7 +173,7 @@ void OSSM::drawPlayControlsTask(void *pvParameters) { } strokeString = - formatTime(displayLastUpdated - ossm->sessionStartTime).c_str(); + formatTime(displayLastUpdated - ossm->sessionStartTime).c_str(); stringWidth = ossm->display.getUTF8Width(strokeString.c_str()); ossm->display.drawUTF8(104 - stringWidth, lh4, strokeString.c_str()); @@ -188,4 +190,4 @@ void OSSM::drawPlayControls() { int stackSize = 3 * configMINIMAL_STACK_SIZE; xTaskCreate(drawPlayControlsTask, "drawPlayControlsTask", stackSize, this, 1, &drawPlayControlsTaskH); -} \ No newline at end of file +} diff --git a/Software/src/ossm/OSSM.Preflight.cpp b/Software/src/ossm/OSSM.Preflight.cpp index 88f5ea21..a4096e20 100644 --- a/Software/src/ossm/OSSM.Preflight.cpp +++ b/Software/src/ossm/OSSM.Preflight.cpp @@ -42,7 +42,7 @@ void OSSM::drawPreflightTask(void *pvParameters) { displayMutex.lock(); ossm->display.clearBuffer(); drawStr::title(menuString); - String speedString = UserConfig::language.Speed + ": " + + String speedString = String(UserConfig::language.Speed) + ": " + String((int)speedPercentage) + "%"; drawStr::centered(25, speedString); drawStr::multiLine(0, 40, UserConfig::language.SpeedWarning); diff --git a/Software/src/ossm/OSSM.Update.cpp b/Software/src/ossm/OSSM.Update.cpp index ac33ac6f..c5f40a26 100644 --- a/Software/src/ossm/OSSM.Update.cpp +++ b/Software/src/ossm/OSSM.Update.cpp @@ -18,7 +18,7 @@ void OSSM::drawNoUpdate() { display.clearBuffer(); String title = "No Update Available"; drawStr::title(title); - display.drawUTF8(0, 62, UserConfig::language.Skip.c_str()); + display.drawUTF8(0, 62, UserConfig::language.Skip); display.sendBuffer(); displayMutex.unlock(); } diff --git a/Software/src/ossm/OSSM.WiFi.cpp b/Software/src/ossm/OSSM.WiFi.cpp index b0b3c468..bad9a140 100644 --- a/Software/src/ossm/OSSM.WiFi.cpp +++ b/Software/src/ossm/OSSM.WiFi.cpp @@ -2,49 +2,115 @@ #include "OSSM.h" #include "extensions/u8g2Extensions.h" -#include "qrcode.h" +#include "utils/uniqueId.h" +#include "constants/URLs.h" +#include "utils/getEfuseMac.h" + void OSSM::drawWiFi() { displayMutex.lock(); display.clearBuffer(); - static QRCode qrcode; - const int scale = 2; - // This Version of QR Codes can handle ~61 alphanumeric characters with ECC - // LEVEL M + drawShape::drawQRCode("WIFI:S:OSSM Setup;T:nopass;;"); + + display.setFont(Config::Font::bold); + display.drawUTF8(0, 10, UserConfig::language.WiFiSetup); + // Draw line + display.drawHLine(0, 12, 64 - 10); + + display.setFont(Config::Font::base); + display.drawUTF8(0, 26, UserConfig::language.WiFiSetupLine1); + display.drawUTF8(0, 38, UserConfig::language.WiFiSetupLine2); + display.drawUTF8(0, 62, UserConfig::language.Restart); + display.sendBuffer(); + displayMutex.unlock(); - // NOLINTBEGIN(modernize-avoid-c-arrays) - uint8_t qrcodeData[qrcode_getBufferSize(3)]; - // NOLINTEND(modernize-avoid-c-arrays) + wm.startConfigPortal("OSSM Setup"); +} - String url = "WIFI:S:OSSM Setup;T:nopass;;"; +void OSSM::drawPairing() { - qrcode_initText(&qrcode, qrcodeData, 3, 0, url.c_str()); + String id = getId(); + String shortId = id.substring(id.length() - 5); + // get the last 5 digits of the id - int yOffset = constrain((64 - qrcode.size * scale) / 2, 0, 64); - int xOffset = constrain((128 - qrcode.size * scale), 0, 128); - // Draw the QR code - for (uint8_t y = 0; y < qrcode.size; y++) { - for (uint8_t x = 0; x < qrcode.size; x++) { - if (qrcode_getModule(&qrcode, x, y)) { - display.drawBox(xOffset + x * scale, yOffset + y * scale, scale, - scale); - } - } - } + displayMutex.lock(); + display.clearBuffer(); display.setFont(Config::Font::bold); - display.drawUTF8(0, 10, UserConfig::language.WiFiSetup.c_str()); - // Draw line - display.drawHLine(0, 12, xOffset - 10); + display.drawUTF8(0, 10, UserConfig::language.Pair); + display.drawHLine(0, 12, 64 - 10); display.setFont(Config::Font::base); - display.drawUTF8(0, 26, UserConfig::language.WiFiSetupLine1.c_str()); - display.drawUTF8(0, 38, UserConfig::language.WiFiSetupLine2.c_str()); - display.drawUTF8(0, 62, UserConfig::language.Restart.c_str()); + display.drawUTF8(0, 26, shortId.c_str()); + + String url = String(URL_RAD_SHORT) + "?oID=" + shortId; + drawShape::drawQRCode(url); + + // TODO - Add a spinner here display.sendBuffer(); displayMutex.unlock(); - wm.startConfigPortal("OSSM Setup"); + + // prepare the payload. + DynamicJsonDocument doc(1024); + String payload; + + // add the device details + doc["mac"] = getEfuseMacAddress(); + doc["chip"] = ESP.getChipModel(); + doc["md5"] = ESP.getSketchMD5(); + doc["device"] = "OSSM"; + doc["id"] = id; + + serializeJson(doc, payload); + + // print the payload + ESP_LOGD("PAIRING", "Payload: %s", payload.c_str()); + + // POST this payload to the RAD API. + // this will open a 5 minute pairing window for this device + + HTTPClient http; + http.begin(String(URL_RAD) + "/api/ossm/pair"); + http.addHeader("Content-Type", "application/json"); + http.addHeader("Authorization", "Bearer cchzYsEaUEy7zoqfYZO2loHg4pKIcIQAvCo3LW9aKYg="); + int httpCode = http.POST(payload); + + if (httpCode > 0) { + ESP_LOGD("PAIRING", "Response: %s", payload.c_str()); + } else { + ESP_LOGE("PAIRING", "Error: %s", http.errorToString(httpCode).c_str()); + } + + http.end(); + + // start a task to wait 5 minutes then throw an error: + xTaskCreate( + [](void *pvParameters) { + OSSM *ossm = (OSSM *) pvParameters; + int taskStart = millis(); + auto isInCorrectState = [](OSSM *ossm) { + // Add any states that you want to support here. + return ossm->sm->is("pair"_s) || ossm->sm->is("pair.idle"_s); + }; + + while (isInCorrectState(ossm)) { + vTaskDelay(10); + int timeElapsed = millis() - taskStart; + if (timeElapsed > 300000) { + // 5 minutes have passed + ossm->errorMessage = UserConfig::language.PairingTookTooLong; + ossm->sm->process_event(Error{}); + break; + } + + } + + vTaskDelete(nullptr); + + }, + "buttonTask", 3 * configMINIMAL_STACK_SIZE, this, 1, nullptr); + } diff --git a/Software/src/ossm/OSSM.h b/Software/src/ossm/OSSM.h index 74d2afbb..f849cc2c 100644 --- a/Software/src/ossm/OSSM.h +++ b/Software/src/ossm/OSSM.h @@ -20,11 +20,15 @@ #include "utils/analog.h" #include "utils/update.h" +#pragma once + namespace sml = boost::sml; class OSSM { - private: - /** +private: + void drawPairing(); + +/** * /////////////////////////////////////////// * //// * //// OSSM State Machine @@ -86,7 +90,7 @@ class OSSM { auto incrementControl = [](OSSM &o) { o.playControl = - static_cast((o.playControl + 1) % 3); + static_cast((o.playControl + 1) % 3); switch (o.playControl) { case PlayControls::STROKE: @@ -112,6 +116,7 @@ class OSSM { auto drawHelp = [](OSSM &o) { o.drawHelp(); }; auto drawWiFi = [](OSSM &o) { o.drawWiFi(); }; auto drawUpdate = [](OSSM &o) { o.drawUpdate(); }; + auto drawPairing = [](OSSM &o) { o.drawPairing(); }; auto drawNoUpdate = [](OSSM &o) { o.drawNoUpdate(); }; auto drawUpdating = [](OSSM &o) { o.drawUpdating(); }; auto stopWifiPortal = [](OSSM &o) { o.wm.stopConfigPortal(); }; @@ -146,75 +151,81 @@ class OSSM { auto isPreflightSafe = [](OSSM &o) { return getAnalogAveragePercent( - {Pins::Remote::speedPotPin, 50}) < + {Pins::Remote::speedPotPin, 50}) < Config::Advanced::commandDeadZonePercentage; }; - auto isNotHomed = [](OSSM &o) { return o.isHomed == false;}; + auto isNotHomed = [](OSSM &o) { return o.isHomed == false; }; auto setHomed = [](OSSM &o) { o.isHomed = true; }; auto setNotHomed = [](OSSM &o) { o.isHomed = false; }; return make_transition_table( - // clang-format off + // clang-format off #ifdef DEBUG_SKIP_HOMING - *"idle"_s + done / drawHello = "menu"_s, + *"idle"_s + done / drawHello = "menu"_s, #else - *"idle"_s + done / drawHello = "homing"_s, + *"idle"_s + done / drawHello = "homing"_s, #endif - "homing"_s / startHoming = "homing.forward"_s, - "homing.forward"_s + error = "error"_s, - "homing.forward"_s + done / startHoming = "homing.backward"_s, - "homing.backward"_s + error = "error"_s, - "homing.backward"_s + done[(isStrokeTooShort)] = "error"_s, - "homing.backward"_s + done[(isOption(Menu::SimplePenetration))] / setHomed = "simplePenetration"_s, - "homing.backward"_s + done[(isOption(Menu::StrokeEngine))] / setHomed = "strokeEngine"_s, - "homing.backward"_s + done / setHomed = "menu"_s, - - "menu"_s / (drawMenu, startWifi) = "menu.idle"_s, - "menu.idle"_s + buttonPress[(isOption(Menu::SimplePenetration))] = "simplePenetration"_s, - "menu.idle"_s + buttonPress[(isOption(Menu::StrokeEngine))] = "strokeEngine"_s, - "menu.idle"_s + buttonPress[(isOption(Menu::UpdateOSSM))] = "update"_s, - "menu.idle"_s + buttonPress[(isOption(Menu::WiFiSetup))] = "wifi"_s, - "menu.idle"_s + buttonPress[isOption(Menu::Help)] = "help"_s, - "menu.idle"_s + buttonPress[(isOption(Menu::Restart))] = "restart"_s, - - "simplePenetration"_s [isNotHomed] = "homing"_s, - "simplePenetration"_s [isPreflightSafe] / (resetSettings, drawPlayControls, startSimplePenetration) = "simplePenetration.idle"_s, - "simplePenetration"_s / drawPreflight = "simplePenetration.preflight"_s, - "simplePenetration.preflight"_s + done / (resetSettings, drawPlayControls, startSimplePenetration) = "simplePenetration.idle"_s, - "simplePenetration.idle"_s + longPress / (emergencyStop, setNotHomed) = "menu"_s, - - "strokeEngine"_s [isNotHomed] = "homing"_s, - "strokeEngine"_s [isPreflightSafe] / (resetSettings, drawPlayControls, startStrokeEngine) = "strokeEngine.idle"_s, - "strokeEngine"_s / drawPreflight = "strokeEngine.preflight"_s, - "strokeEngine.preflight"_s + done / (resetSettings, drawPlayControls, startStrokeEngine) = "strokeEngine.idle"_s, - "strokeEngine.idle"_s + buttonPress / incrementControl = "strokeEngine.idle"_s, - "strokeEngine.idle"_s + doublePress / drawPatternControls = "strokeEngine.pattern"_s, - "strokeEngine.pattern"_s + buttonPress / drawPlayControls = "strokeEngine.idle"_s, - "strokeEngine.pattern"_s + doublePress / drawPlayControls = "strokeEngine.idle"_s, - "strokeEngine.pattern"_s + longPress / (emergencyStop, setNotHomed) = "menu"_s, - "strokeEngine.idle"_s + longPress / (emergencyStop, setNotHomed) = "menu"_s, - - "update"_s [isOnline] / drawUpdate = "update.checking"_s, - "update"_s = "wifi"_s, - "update.checking"_s [isUpdateAvailable] / (drawUpdating, updateOSSM) = "update.updating"_s, - "update.checking"_s / drawNoUpdate = "update.idle"_s, - "update.idle"_s + buttonPress = "menu"_s, - "update.updating"_s = X, - - "wifi"_s / drawWiFi = "wifi.idle"_s, - "wifi.idle"_s + done / stopWifiPortal = "menu"_s, - "wifi.idle"_s + buttonPress / stopWifiPortal = "menu"_s, - - "help"_s / drawHelp = "help.idle"_s, - "help.idle"_s + buttonPress = "menu"_s, - - "error"_s / drawError = "error.idle"_s, - "error.idle"_s + buttonPress / drawHelp = "error.help"_s, - "error.help"_s + buttonPress / restart = X, - - "restart"_s / restart = X); + "homing"_s / startHoming = "homing.forward"_s, + "homing.forward"_s + error = "error"_s, + "homing.forward"_s + done / startHoming = "homing.backward"_s, + "homing.backward"_s + error = "error"_s, + "homing.backward"_s + done[(isStrokeTooShort)] = "error"_s, + "homing.backward"_s + done[(isOption(Menu::SimplePenetration))] / setHomed = "simplePenetration"_s, + "homing.backward"_s + done[(isOption(Menu::StrokeEngine))] / setHomed = "strokeEngine"_s, + "homing.backward"_s + done / setHomed = "menu"_s, + + "menu"_s / (drawMenu, startWifi) = "menu.idle"_s, + "menu.idle"_s + buttonPress[(isOption(Menu::SimplePenetration))] = "simplePenetration"_s, + "menu.idle"_s + buttonPress[(isOption(Menu::StrokeEngine))] = "strokeEngine"_s, + "menu.idle"_s + buttonPress[(isOption(Menu::UpdateOSSM))] = "update"_s, + "menu.idle"_s + buttonPress[(isOption(Menu::PairOSSM))] = "pair"_s, + "menu.idle"_s + buttonPress[(isOption(Menu::WiFiSetup))] = "wifi"_s, + "menu.idle"_s + buttonPress[isOption(Menu::Help)] = "help"_s, + "menu.idle"_s + buttonPress[(isOption(Menu::Restart))] = "restart"_s, + + "simplePenetration"_s[isNotHomed] = "homing"_s, + "simplePenetration"_s[isPreflightSafe] / (resetSettings, drawPlayControls, startSimplePenetration) = "simplePenetration.idle"_s, + "simplePenetration"_s / drawPreflight = "simplePenetration.preflight"_s, + "simplePenetration.preflight"_s + done / (resetSettings, drawPlayControls, startSimplePenetration) = "simplePenetration.idle"_s, + "simplePenetration.idle"_s + longPress / (emergencyStop, setNotHomed) = "menu"_s, + + "strokeEngine"_s[isNotHomed] = "homing"_s, + "strokeEngine"_s[isPreflightSafe] / (resetSettings, drawPlayControls, startStrokeEngine) = "strokeEngine.idle"_s, + "strokeEngine"_s / drawPreflight = "strokeEngine.preflight"_s, + "strokeEngine.preflight"_s + done / (resetSettings, drawPlayControls, startStrokeEngine) = "strokeEngine.idle"_s, + "strokeEngine.idle"_s + buttonPress / incrementControl = "strokeEngine.idle"_s, + "strokeEngine.idle"_s + doublePress / drawPatternControls = "strokeEngine.pattern"_s, + "strokeEngine.pattern"_s + buttonPress / drawPlayControls = "strokeEngine.idle"_s, + "strokeEngine.pattern"_s + doublePress / drawPlayControls = "strokeEngine.idle"_s, + "strokeEngine.pattern"_s + longPress / (emergencyStop, setNotHomed) = "menu"_s, + "strokeEngine.idle"_s + longPress / (emergencyStop, setNotHomed) = "menu"_s, + + "pair"_s[isOnline] / drawPairing = "pair.idle"_s, + "pair"_s = "wifi"_s, + "pair.idle"_s + buttonPress = "menu"_s, + "pair.idle"_s + error = "error"_s, + + "update"_s[isOnline] / drawUpdate = "update.checking"_s, + "update"_s = "wifi"_s, + "update.checking"_s[isUpdateAvailable] / (drawUpdating, updateOSSM) = "update.updating"_s, + "update.checking"_s / drawNoUpdate = "update.idle"_s, + "update.idle"_s + buttonPress = "menu"_s, + "update.updating"_s = X, + + "wifi"_s / drawWiFi = "wifi.idle"_s, + "wifi.idle"_s + done / stopWifiPortal = "menu"_s, + "wifi.idle"_s + buttonPress / stopWifiPortal = "menu"_s, + + "help"_s / drawHelp = "help.idle"_s, + "help.idle"_s + buttonPress = "menu"_s, + + "error"_s / drawError = "error.idle"_s, + "error.idle"_s + buttonPress / drawHelp = "error.help"_s, + "error.help"_s + buttonPress / restart = X, + + "restart"_s / restart = X); // clang-format on } @@ -250,10 +261,10 @@ class OSSM { String errorMessage = ""; SettingPercents setting = {.speed = 0, - .stroke = 0, - .sensation = 50, - .depth = 50, - .pattern = StrokePatterns::SimpleStroke}; + .stroke = 0, + .sensation = 50, + .depth = 50, + .pattern = StrokePatterns::SimpleStroke}; unsigned long sessionStartTime = 0; int sessionStrokeCount = 0; @@ -287,6 +298,7 @@ class OSSM { void drawMenu(); void drawPlayControls(); + void drawPatternControls(); /** @@ -305,14 +317,17 @@ class OSSM { static void drawMenuTask(void *pvParameters); static void drawPlayControlsTask(void *pvParameters); + static void drawPatternControlsTask(void *pvParameters); void drawUpdate(); + void drawNoUpdate(); void drawUpdating(); void drawPreflight(); + static void drawPreflightTask(void *pvParameters); static void startSimplePenetrationTask(void *pvParameters); @@ -321,15 +336,15 @@ class OSSM { bool isHomed; - public: +public: explicit OSSM(U8G2_SSD1306_128X64_NONAME_F_HW_I2C &display, AiEsp32RotaryEncoder &rotaryEncoder, FastAccelStepper *stepper); std::unique_ptr< - sml::sm, - sml::logger>> - sm = nullptr; // The state machine + sml::sm, + sml::logger>> + sm = nullptr; // The state machine WiFiManager wm; }; diff --git a/Software/src/services/tasks.h b/Software/src/services/tasks.h index ccb565d5..7db3d0f0 100644 --- a/Software/src/services/tasks.h +++ b/Software/src/services/tasks.h @@ -15,6 +15,7 @@ static TaskHandle_t drawHelloTaskH = nullptr; static TaskHandle_t drawMenuTaskH = nullptr; static TaskHandle_t drawPlayControlsTaskH = nullptr; static TaskHandle_t drawPatternControlsTaskH = nullptr; +static TaskHandle_t waitForPairingTaskH = nullptr; static TaskHandle_t drawPreflightTaskH = nullptr; static TaskHandle_t runHomingTaskH = nullptr; diff --git a/Software/src/structs/LanguageStruct.h b/Software/src/structs/LanguageStruct.h index 8a5847a4..9d115e86 100644 --- a/Software/src/structs/LanguageStruct.h +++ b/Software/src/structs/LanguageStruct.h @@ -2,36 +2,45 @@ #define OSSM_SOFTWARE_LANGUAGESTRUCT_H struct LanguageStruct { - String DeepThroatTrainerSync; - String Error; - String GetHelp; - String GetHelpLine1; - String GetHelpLine2; - String Homing; - String HomingTookTooLong; - String Idle; - String InDevelopment; - String MeasuringStroke; - String NoInternalLoop; - String Restart; - String Settings; - String SimplePenetration; - String Skip; - String Speed; - String SpeedWarning; - String StateNotImplemented; - String Stroke; - String StrokeEngine; - String StrokeTooShort; - String Update; - String UpdateMessage; - String WiFi; - String WiFiSetup; - String WiFiSetupLine1; - String WiFiSetupLine2; - String YouShouldNotBeHere; - String StrokeEngineDescriptions[7]; - String StrokeEngineNames[7]; + const char* DeepThroatTrainerSync; + const char* Error; + const char* GetHelp; + const char* GetHelpLine1; + const char* GetHelpLine2; + const char* Homing; + const char* HomingTookTooLong; + const char* Idle; + const char* InDevelopment; + const char* MeasuringStroke; + const char* NoInternalLoop; + const char* Restart; + const char* Settings; + const char* SimplePenetration; + const char* Skip; + const char* Speed; + const char* SpeedWarning; + const char* StateNotImplemented; + const char* Stroke; + const char* StrokeEngine; + const char* StrokeTooShort; + const char* Pair; + const char* PairingInstructions; + const char* PairingTookTooLong; + const char* Update; + const char* UpdateMessage; + const char* WiFi; + const char* WiFiSetup; + const char* WiFiSetupLine1; + const char* WiFiSetupLine2; + const char* YouShouldNotBeHere; + const char* StrokeEngineDescriptions[7]; + const char* StrokeEngineNames[7]; }; +//static const char* getString(const char* progmemString) { +// static char buffer[100]; // Adjust size as needed +// strcpy_P(buffer, progmemString); +// return buffer; +//} + #endif // OSSM_SOFTWARE_LANGUAGESTRUCT_H diff --git a/Software/src/utils/getEfuseMac.h b/Software/src/utils/getEfuseMac.h new file mode 100644 index 00000000..b680ca55 --- /dev/null +++ b/Software/src/utils/getEfuseMac.h @@ -0,0 +1,18 @@ +#ifndef SOFTWARE_GETEFUSMAC_H +#define SOFTWARE_GETEFUSMAC_H + +#include "Arduino.h" + +static String getEfuseMacAddress() { + uint64_t mac = ESP.getEfuseMac(); + uint8_t macArray[6]; + for (int i = 0; i < 6; i++) { + macArray[i] = (mac >> (8 * i)) & 0xff; + } + char macStr[18]; + snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X", macArray[5], macArray[4], macArray[3], + macArray[2], macArray[1], macArray[0]); + return String(macStr); +} + +#endif //SOFTWARE_GETEFUSMAC_H diff --git a/Software/src/utils/uniqueId.h b/Software/src/utils/uniqueId.h new file mode 100644 index 00000000..f59dcb0c --- /dev/null +++ b/Software/src/utils/uniqueId.h @@ -0,0 +1,66 @@ +#ifndef SOFTWARE_UNIQUEID_H +#define SOFTWARE_UNIQUEID_H + +#include + +#include "UUID.h" + +static String generateId() { + UUID uuid; + + // Generate two random seeds using esp_random() function + uint32_t seed1 = esp_random(); + uint32_t seed2 = esp_random(); + + // Seed the UUID generator with the generated seeds and set it to variant 4 + // mode + uuid.seed(seed1, seed2); + uuid.setVariant4Mode(); + + // Generate a new UUID + uuid.generate(); + + // Convert the generated UUID to a character array and assign it to the id + // variable + String id = uuid.toCharArray(); + + // Return the generated UUID + return id; +} + +/** + * Retrieves a unique identifier (UUID) from device preferences. If a valid UUID + * is found in the preferences, it returns that UUID. Otherwise, it generates a + * new UUID using random seeds and saves it to the preferences for future use. + * + * @return String - The unique identifier (UUID) string. + */ +static String getId() { +#ifdef VERSIONDEV + return "00000000-0000-0000-0000-000000000000"; +#endif + + Preferences prefs; + + prefs.begin("OSSM", false); + // Retrieve the UUID string from device preferences + String id = prefs.getString("uuid", ""); + + // If the retrieved UUID string has a valid length (36 characters), return + // it + if (id.length() == 36) { + prefs.end(); + return id; + } + + id = generateId(); + + // Save the generated UUID to device preferences for future use + prefs.putString("uuid", id); + prefs.end(); + + // Return the generated UUID + return id; +} + +#endif // SOFTWARE_UNIQUEID_H diff --git a/Software/src/utils/update.h b/Software/src/utils/update.h index 2b6edee4..6f0a38fe 100644 --- a/Software/src/utils/update.h +++ b/Software/src/utils/update.h @@ -37,8 +37,7 @@ static auto isUpdateAvailable = []() { // Making the POST request to the bubble server HTTPClient http; - WiFiClient client; - http.begin(client, serverNameBubble); + http.begin(serverNameBubble); http.addHeader("Content-Type", "application/json"); StaticJsonDocument<200> doc; // Add values in the document @@ -61,7 +60,6 @@ static auto isUpdateAvailable = []() { ESP_LOGD("UTILS", "Failed to reach update server"); } http.end(); - client.stop(); return response_needUpdate; }; From d73cc7b69d2dcfec8942ede1392e476319faded2 Mon Sep 17 00:00:00 2001 From: AJ Date: Fri, 31 May 2024 09:00:13 -0600 Subject: [PATCH 3/4] change development to hit remote --- Software/platformio.ini | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Software/platformio.ini b/Software/platformio.ini index d3388a4f..9ff62ada 100644 --- a/Software/platformio.ini +++ b/Software/platformio.ini @@ -32,7 +32,7 @@ check_flags = clangtidy: --checks=-\*,bugprone-*,boost-*,modernize-*,performance-*,clang-analyzer-*,cert-dcl03-c,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-oop54-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,hicpp-signed-bitwise,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof,-modernize-use-trailing-return-type,-readability-convert-member-functions-to-static,-bugprone-easily-swappable-parameters,-readability-make-member-function-const --fix extra_scripts = pre:pre_build_script.py -[env:development] +[env:localhost] build_flags = -std=gnu++17 ; CORE_DEBUG_LEVEL Details: https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level @@ -50,6 +50,24 @@ framework = arduino monitor_speed = 115200 monitor_filters = colorize, esp32_exception_decoder, time +[env:development] +build_flags = + -std=gnu++17 +; CORE_DEBUG_LEVEL Details: https://docs.platformio.org/en/latest/platforms/espressif32.html#debug-level + -D CORE_DEBUG_LEVEL=4 + -D VERSIONDEV + -D SW_VERSION=0 +; This flag is used for Stroke Engine. + -D DEBUG_TALKATIVE + -D URL_RAD_SHORT="\"https://rdapp.link\"" + -D URL_RAD="\"https://dashboard.researchanddesire.com\"" +extends = common +platform = espressif32 +board = esp32dev +framework = arduino +monitor_speed = 115200 +monitor_filters = colorize, esp32_exception_decoder, time + [env:debugWithoutMotor] build_flags = -std=gnu++17 @@ -58,8 +76,8 @@ build_flags = -D VERSIONDEV -D SW_VERSION=0 -D DEBUG_SKIP_HOMING - -D URL_RAD_SHORT="\"https://rdapp.link/d\"" - -D URL_RAD="\"http://localhost:3000\"" + -D URL_RAD_SHORT="\"https://rdapp.link\"" + -D URL_RAD="\"https://dashboard.researchanddesire.com\"" extends = common platform = espressif32 board = esp32dev From d6367fbf70e86d0aff478a6888fe5b7fe7d93bd8 Mon Sep 17 00:00:00 2001 From: AJ Date: Fri, 31 May 2024 11:45:30 -0600 Subject: [PATCH 4/4] change development to hit remote --- Software/src/constants/copy/en-us.h | 2 + Software/src/constants/copy/fr.h | 2 + Software/src/main.cpp | 17 ++++---- Software/src/ossm/OSSM.WiFi.cpp | 58 ++++++++++++++------------- Software/src/ossm/OSSM.h | 2 +- Software/src/structs/LanguageStruct.h | 1 + 6 files changed, 46 insertions(+), 36 deletions(-) diff --git a/Software/src/constants/copy/en-us.h b/Software/src/constants/copy/en-us.h index d494c38c..049aab7f 100644 --- a/Software/src/constants/copy/en-us.h +++ b/Software/src/constants/copy/en-us.h @@ -27,6 +27,7 @@ const char en_Stroke[] PROGMEM = "Stroke"; const char en_StrokeEngine[] PROGMEM = "Stroke Engine"; const char en_StrokeTooShort[] PROGMEM = "Stroke too short. Please check your drive belt."; const char en_Pair[] PROGMEM = "Pair Device"; +const char en_PairingFailed[] PROGMEM = "Pairing failed. Please try again."; const char en_PairingInstructions[] PROGMEM = "Enter the following code on the dashboard"; const char en_Update[] PROGMEM = "Update"; const char en_UpdateMessage[] PROGMEM = "Update is in progress. This may take up to 60s."; @@ -79,6 +80,7 @@ static const LanguageStruct enUs = { .StrokeEngine = en_StrokeEngine, .StrokeTooShort = en_StrokeTooShort, .Pair = en_Pair, + .PairingFailed = en_PairingFailed, .PairingInstructions = en_PairingInstructions, .Update = en_Update, .UpdateMessage = en_UpdateMessage, diff --git a/Software/src/constants/copy/fr.h b/Software/src/constants/copy/fr.h index e1967121..1b6dcd62 100644 --- a/Software/src/constants/copy/fr.h +++ b/Software/src/constants/copy/fr.h @@ -28,6 +28,7 @@ const char fr_Stroke[] PROGMEM = "Coup"; const char fr_StrokeEngine[] PROGMEM = "Stroke Engine"; const char fr_StrokeTooShort[] PROGMEM = "Course trop courte. Veuillez vérifier votre courroie d'entraînement."; const char fr_Pair[] PROGMEM = "Jumeler l'appareil"; +const char fr_PairingFailed[] PROGMEM = "L'appairage a échoué. Veuillez réessayer."; const char fr_PairingInstructions[] PROGMEM = "Entrez le code suivant sur le tableau de bord"; const char fr_Update[] PROGMEM = "Mettre à jour"; const char fr_UpdateMessage[] PROGMEM = "La mise à jour est en cours. Ça peut prendre jusqu'à 60 secondes."; @@ -81,6 +82,7 @@ static const LanguageStruct fr = { .StrokeEngine = fr_StrokeEngine, .StrokeTooShort = fr_StrokeTooShort, .Pair = fr_Pair, + .PairingFailed = fr_PairingFailed, .PairingInstructions = fr_PairingInstructions, .Update = fr_Update, .UpdateMessage = fr_UpdateMessage, diff --git a/Software/src/main.cpp b/Software/src/main.cpp index ab61c0f4..eae9b62e 100644 --- a/Software/src/main.cpp +++ b/Software/src/main.cpp @@ -46,14 +46,15 @@ void setup() { button.attachLongPressStart([]() { ossm->sm->process_event(LongPress{}); }); xTaskCreate( - [](void* pvParameters) { - while (true) { - button.tick(); - ossm->wm.process(); - vTaskDelay(10); - } - }, - "buttonTask", 4 * configMINIMAL_STACK_SIZE, nullptr, 1, nullptr); + [](void *pvParameters) { + while (true) { + button.tick(); + vTaskDelay(10); + ossm->wm.process(); + vTaskDelay(10); + } + }, + "buttonTask", 5 * configMINIMAL_STACK_SIZE, nullptr, 1, nullptr); }; void loop() { vTaskDelete(nullptr); }; \ No newline at end of file diff --git a/Software/src/ossm/OSSM.WiFi.cpp b/Software/src/ossm/OSSM.WiFi.cpp index bad9a140..06001560 100644 --- a/Software/src/ossm/OSSM.WiFi.cpp +++ b/Software/src/ossm/OSSM.WiFi.cpp @@ -28,6 +28,30 @@ void OSSM::drawWiFi() { wm.startConfigPortal("OSSM Setup"); } +auto pairingTask = [](void *pvParameters) { + OSSM *ossm = (OSSM *) pvParameters; + int taskStart = millis(); + auto isInCorrectState = [](OSSM *ossm) { + // Add any states that you want to support here. + return ossm->sm->is("pair"_s) || ossm->sm->is("pair.idle"_s); + }; + + while (isInCorrectState(ossm)) { + vTaskDelay(10); + int timeElapsed = millis() - taskStart; + if (timeElapsed > 300000) { +// 5 minutes have passed + ossm->errorMessage = UserConfig::language.PairingTookTooLong; + ossm->sm->process_event(Error{}); + break; + } + + } + + vTaskDelete(nullptr); + +}; + void OSSM::drawPairing() { String id = getId(); @@ -52,6 +76,10 @@ void OSSM::drawPairing() { display.sendBuffer(); displayMutex.unlock(); + // start a task to wait 5 minutes then throw an error: + xTaskCreate(pairingTask, + "pairingTask", 3 * configMINIMAL_STACK_SIZE, this, 1, nullptr); + // prepare the payload. DynamicJsonDocument doc(1024); @@ -78,39 +106,15 @@ void OSSM::drawPairing() { http.addHeader("Authorization", "Bearer cchzYsEaUEy7zoqfYZO2loHg4pKIcIQAvCo3LW9aKYg="); int httpCode = http.POST(payload); - if (httpCode > 0) { + if (httpCode == HTTP_CODE_OK) { ESP_LOGD("PAIRING", "Response: %s", payload.c_str()); } else { ESP_LOGE("PAIRING", "Error: %s", http.errorToString(httpCode).c_str()); + errorMessage = String(UserConfig::language.PairingFailed) + " Code: " + httpCode; + sm->process_event(Error{}); } http.end(); - // start a task to wait 5 minutes then throw an error: - xTaskCreate( - [](void *pvParameters) { - OSSM *ossm = (OSSM *) pvParameters; - int taskStart = millis(); - auto isInCorrectState = [](OSSM *ossm) { - // Add any states that you want to support here. - return ossm->sm->is("pair"_s) || ossm->sm->is("pair.idle"_s); - }; - - while (isInCorrectState(ossm)) { - vTaskDelay(10); - int timeElapsed = millis() - taskStart; - if (timeElapsed > 300000) { - // 5 minutes have passed - ossm->errorMessage = UserConfig::language.PairingTookTooLong; - ossm->sm->process_event(Error{}); - break; - } - - } - - vTaskDelete(nullptr); - - }, - "buttonTask", 3 * configMINIMAL_STACK_SIZE, this, 1, nullptr); } diff --git a/Software/src/ossm/OSSM.h b/Software/src/ossm/OSSM.h index f849cc2c..97bb85b0 100644 --- a/Software/src/ossm/OSSM.h +++ b/Software/src/ossm/OSSM.h @@ -258,7 +258,6 @@ class OSSM { bool isForward = true; Menu menuOption; - String errorMessage = ""; SettingPercents setting = {.speed = 0, .stroke = 0, @@ -347,6 +346,7 @@ class OSSM { sm = nullptr; // The state machine WiFiManager wm; + String errorMessage = ""; }; #endif // OSSM_SOFTWARE_OSSM_H diff --git a/Software/src/structs/LanguageStruct.h b/Software/src/structs/LanguageStruct.h index 9d115e86..5a1e604c 100644 --- a/Software/src/structs/LanguageStruct.h +++ b/Software/src/structs/LanguageStruct.h @@ -24,6 +24,7 @@ struct LanguageStruct { const char* StrokeEngine; const char* StrokeTooShort; const char* Pair; + const char* PairingFailed; const char* PairingInstructions; const char* PairingTookTooLong; const char* Update;